From ab6262eb5b2e88c41e6277d308f96cffe4aaeeaa Mon Sep 17 00:00:00 2001 From: George Gastaldi Date: Fri, 1 Sep 2023 19:04:51 -0300 Subject: [PATCH] Introduce EnhancedConfig --- .../io/smallrye/config/EnhancedConfig.java | 162 ++++++++++++++++++ .../io/smallrye/config/SmallRyeConfig.java | 115 +++---------- 2 files changed, 190 insertions(+), 87 deletions(-) create mode 100644 implementation/src/main/java/io/smallrye/config/EnhancedConfig.java diff --git a/implementation/src/main/java/io/smallrye/config/EnhancedConfig.java b/implementation/src/main/java/io/smallrye/config/EnhancedConfig.java new file mode 100644 index 000000000..fb10863df --- /dev/null +++ b/implementation/src/main/java/io/smallrye/config/EnhancedConfig.java @@ -0,0 +1,162 @@ +package io.smallrye.config; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Optional; +import java.util.function.IntFunction; + +import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.config.spi.ConfigSource; +import org.eclipse.microprofile.config.spi.Converter; + +import io.smallrye.common.annotation.Experimental; + +public interface EnhancedConfig extends Config, Serializable { + String SMALLRYE_CONFIG_PROFILE = "smallrye.config.profile"; + String SMALLRYE_CONFIG_PROFILE_PARENT = "smallrye.config.profile.parent"; + String SMALLRYE_CONFIG_LOCATIONS = "smallrye.config.locations"; + String SMALLRYE_CONFIG_MAPPING_VALIDATE_UNKNOWN = "smallrye.config.mapping.validate-unknown"; + String SMALLRYE_CONFIG_LOG_VALUES = "smallrye.config.log.values"; + + > C getValues(String name, Class itemClass, IntFunction collectionFactory); + + > C getValues(String name, Converter converter, IntFunction collectionFactory); + + > C getIndexedValues(String name, Converter converter, + IntFunction collectionFactory); + + List getIndexedProperties(String property); + + List getIndexedPropertiesIndexes(String property); + + /** + * Return the content of the direct sub properties as the requested type of Map. + * + * @param name The configuration property name + * @param kClass the type into which the keys should be converted + * @param vClass the type into which the values should be converted + * @param the key type + * @param the value type + * @return the resolved property value as an instance of the requested Map (not {@code null}) + * @throws IllegalArgumentException if a key or a value cannot be converted to the specified types + * @throws NoSuchElementException if no direct sub properties could be found. + */ + Map getValues(String name, Class kClass, Class vClass); + + /** + * Return the content of the direct sub properties as the requested type of Map. + * + * @param name The configuration property name + * @param keyConverter The converter to use for the keys. + * @param valueConverter The converter to use for the values. + * @param The type of the keys. + * @param The type of the values. + * @return the resolved property value as an instance of the requested Map or {@code null} if it could not be found. + * @throws IllegalArgumentException if a key or a value cannot be converted to the specified types + */ + Map getValuesAsMap(String name, Converter keyConverter, Converter valueConverter); + + /** + * This method handles calls from both {@link Config#getValue} and {@link Config#getOptionalValue}.
+ */ + T getValue(String name, Converter converter); + + /** + * This method handles converting values for both CDI injections and programatical calls.
+ *
+ *

+ * Calls for converting non-optional values ({@link Config#getValue} and "Injecting Native Values") + * should throw an {@link Exception} for each of the following:
+ *

+ * 1. {@link IllegalArgumentException} - if the property cannot be converted by the {@link Converter} to the specified type + *
+ * 2. {@link NoSuchElementException} - if the property is not defined
+ * 3. {@link NoSuchElementException} - if the property is defined as an empty string
+ * 4. {@link NoSuchElementException} - if the {@link Converter} returns {@code null}
+ *
+ *

+ * Calls for converting optional values ({@link Config#getOptionalValue} and "Injecting Optional Values") + * should only throw an {@link Exception} for #1 ({@link IllegalArgumentException} when the property cannot be converted to + * the specified type). + */ + T convertValue(ConfigValue configValue, Converter converter); + + /** + * Determine whether the raw value of a configuration property is exactly equal to the expected given + * value. + * + * @param name the property name (must not be {@code null}) + * @param expected the expected value (may be {@code null}) + * @return {@code true} if the values are equal, {@code false} otherwise + */ + boolean rawValueEquals(String name, String expected); + + @Override + ConfigValue getConfigValue(String name); + + /** + * Get the raw value of a configuration property. + * + * @param name the property name (must not be {@code null}) + * @return the raw value, or {@code null} if no property value was discovered for the given property name + */ + String getRawValue(String name); + + /** + * Return the content of the direct sub properties as the requested type of Map. + * + * @param name The configuration property name + * @param kClass the type into which the keys should be converted + * @param vClass the type into which the values should be converted + * @param the key type + * @param the value type + * @return the resolved property value as an instance of the requested Map (not {@code null}) + * @throws IllegalArgumentException if a key or a value cannot be converted to the specified types + */ + Optional> getOptionalValues(String name, Class kClass, Class vClass); + + Optional getOptionalValue(String name, Converter converter); + + > Optional getOptionalValues(String name, Class itemClass, + IntFunction collectionFactory); + + > Optional getOptionalValues(String name, Converter converter, + IntFunction collectionFactory); + + > Optional getIndexedOptionalValues(String name, Converter converter, + IntFunction collectionFactory); + + ConfigMappings getConfigMappings(); + + T getConfigMapping(Class type); + + T getConfigMapping(Class type, String prefix); + + /** + * Checks if a property is present in the {@link Config} instance. + *
+ * Because {@link ConfigSource#getPropertyNames()} may not include all available properties, it is not possible to + * reliably determine if the property is present in the properties list. The property needs to be retrieved to make + * sure it exists. The lookup is done without expression expansion, because the expansion value may not be + * available, and it is not relevant for the final check. + * + * @param name the property name. + * @return true if the property is present or false otherwise. + */ + @Experimental("Check if a property is present") + boolean isPropertyPresent(String name); + + Iterable getConfigSources(Class type); + + @Experimental("To retrieve a ConfigSource by name") + Optional getConfigSource(String name); + + T convert(String value, Class asType); + + Converter requireConverter(Class asType); + + List getProfiles(); +} diff --git a/implementation/src/main/java/io/smallrye/config/SmallRyeConfig.java b/implementation/src/main/java/io/smallrye/config/SmallRyeConfig.java index b2f0a1023..709e94f1e 100644 --- a/implementation/src/main/java/io/smallrye/config/SmallRyeConfig.java +++ b/implementation/src/main/java/io/smallrye/config/SmallRyeConfig.java @@ -49,12 +49,7 @@ /** * @author Jeff Mesnil (c) 2017 Red Hat inc. */ -public class SmallRyeConfig implements Config, Serializable { - public static final String SMALLRYE_CONFIG_PROFILE = "smallrye.config.profile"; - public static final String SMALLRYE_CONFIG_PROFILE_PARENT = "smallrye.config.profile.parent"; - public static final String SMALLRYE_CONFIG_LOCATIONS = "smallrye.config.locations"; - public static final String SMALLRYE_CONFIG_MAPPING_VALIDATE_UNKNOWN = "smallrye.config.mapping.validate-unknown"; - public static final String SMALLRYE_CONFIG_LOG_VALUES = "smallrye.config.log.values"; +public class SmallRyeConfig implements EnhancedConfig { private static final long serialVersionUID = 8138651532357898263L; @@ -97,10 +92,12 @@ public List getValues(final String propertyName, final Class propertyT return getValues(propertyName, propertyType, ArrayList::new); } + @Override public > C getValues(String name, Class itemClass, IntFunction collectionFactory) { return getValues(name, requireConverter(itemClass), collectionFactory); } + @Override public > C getValues(String name, Converter converter, IntFunction collectionFactory) { try { return getValue(name, Converters.newCollectionConverter(converter, collectionFactory)); @@ -109,6 +106,7 @@ public > C getValues(String name, Converter conver } } + @Override public > C getIndexedValues(String name, Converter converter, IntFunction collectionFactory) { List indexedProperties = getIndexedProperties(name); @@ -124,6 +122,7 @@ public > C getIndexedValues(String name, Converter return collection; } + @Override public List getIndexedProperties(final String property) { List indexes = getIndexedPropertiesIndexes(property); List indexedProperties = new ArrayList<>(); @@ -134,6 +133,7 @@ public List getIndexedProperties(final String property) { return indexedProperties; } + @Override public List getIndexedPropertiesIndexes(final String property) { Set indexes = new HashSet<>(); for (String propertyName : this.getPropertyNames()) { @@ -167,18 +167,7 @@ public T getValue(String name, Class aClass) { return getValue(name, requireConverter(aClass)); } - /** - * Return the content of the direct sub properties as the requested type of Map. - * - * @param name The configuration property name - * @param kClass the type into which the keys should be converted - * @param vClass the type into which the values should be converted - * @param the key type - * @param the value type - * @return the resolved property value as an instance of the requested Map (not {@code null}) - * @throws IllegalArgumentException if a key or a value cannot be converted to the specified types - * @throws NoSuchElementException if no direct sub properties could be found. - */ + @Override public Map getValues(String name, Class kClass, Class vClass) { final Map result = getValuesAsMap(name, requireConverter(kClass), requireConverter(vClass)); if (result == null) { @@ -187,17 +176,7 @@ public Map getValues(String name, Class kClass, Class vClass) return result; } - /** - * Return the content of the direct sub properties as the requested type of Map. - * - * @param name The configuration property name - * @param keyConverter The converter to use for the keys. - * @param valueConverter The converter to use for the values. - * @param The type of the keys. - * @param The type of the values. - * @return the resolved property value as an instance of the requested Map or {@code null} if it could not be found. - * @throws IllegalArgumentException if a key or a value cannot be converted to the specified types - */ + @Override public Map getValuesAsMap(String name, Converter keyConverter, Converter valueConverter) { final String prefix = name.endsWith(".") ? name : name + "."; final Map result = new HashMap<>(); @@ -220,10 +199,7 @@ public Map getValuesAsMap(String name, Converter keyConverter, C return result.isEmpty() ? null : result; } - /** - * - * This method handles calls from both {@link Config#getValue} and {@link Config#getOptionalValue}.
- */ + @Override @SuppressWarnings("unchecked") public T getValue(String name, Converter converter) { ConfigValue configValue = getConfigValue(name); @@ -241,24 +217,7 @@ public T getValue(String name, Converter converter) { return convertValue(configValue, converter); } - /** - * This method handles converting values for both CDI injections and programatical calls.
- *
- * - * Calls for converting non-optional values ({@link Config#getValue} and "Injecting Native Values") - * should throw an {@link Exception} for each of the following:
- * - * 1. {@link IllegalArgumentException} - if the property cannot be converted by the {@link Converter} to the specified type - *
- * 2. {@link NoSuchElementException} - if the property is not defined
- * 3. {@link NoSuchElementException} - if the property is defined as an empty string
- * 4. {@link NoSuchElementException} - if the {@link Converter} returns {@code null}
- *
- * - * Calls for converting optional values ({@link Config#getOptionalValue} and "Injecting Optional Values") - * should only throw an {@link Exception} for #1 ({@link IllegalArgumentException} when the property cannot be converted to - * the specified type). - */ + @Override public T convertValue(ConfigValue configValue, Converter converter) { List problems = configValue.getProblems(); if (!problems.isEmpty()) { @@ -306,29 +265,18 @@ public T convertValue(ConfigValue configValue, Converter converter) { return converted; } - /** - * Determine whether the raw value of a configuration property is exactly equal to the expected given - * value. - * - * @param name the property name (must not be {@code null}) - * @param expected the expected value (may be {@code null}) - * @return {@code true} if the values are equal, {@code false} otherwise - */ + @Override public boolean rawValueEquals(String name, String expected) { return Objects.equals(expected, getRawValue(name)); } + @Override public ConfigValue getConfigValue(String name) { final ConfigValue configValue = configSources.getInterceptorChain().proceed(name); return configValue != null ? configValue : ConfigValue.builder().withName(name).build(); } - /** - * Get the raw value of a configuration property. - * - * @param name the property name (must not be {@code null}) - * @return the raw value, or {@code null} if no property value was discovered for the given property name - */ + @Override public String getRawValue(String name) { final ConfigValue configValue = getConfigValue(name); return configValue != null && configValue.getValue() != null ? configValue.getValue() : null; @@ -339,34 +287,28 @@ public Optional getOptionalValue(String name, Class aClass) { return getValue(name, getOptionalConverter(aClass)); } - /** - * Return the content of the direct sub properties as the requested type of Map. - * - * @param name The configuration property name - * @param kClass the type into which the keys should be converted - * @param vClass the type into which the values should be converted - * @param the key type - * @param the value type - * @return the resolved property value as an instance of the requested Map (not {@code null}) - * @throws IllegalArgumentException if a key or a value cannot be converted to the specified types - */ + @Override public Optional> getOptionalValues(String name, Class kClass, Class vClass) { return Optional.ofNullable(getValuesAsMap(name, requireConverter(kClass), requireConverter(vClass))); } + @Override public Optional getOptionalValue(String name, Converter converter) { return getValue(name, Converters.newOptionalConverter(converter)); } + @Override public Optional> getOptionalValues(final String propertyName, final Class propertyType) { return getOptionalValues(propertyName, propertyType, ArrayList::new); } + @Override public > Optional getOptionalValues(String name, Class itemClass, IntFunction collectionFactory) { return getOptionalValues(name, requireConverter(itemClass), collectionFactory); } + @Override public > Optional getOptionalValues(String name, Converter converter, IntFunction collectionFactory) { final Optional optionalValue = getOptionalValue(name, @@ -378,6 +320,7 @@ public > Optional getOptionalValues(String name, C } } + @Override public > Optional getIndexedOptionalValues(String name, Converter converter, IntFunction collectionFactory) { List indexedProperties = getIndexedProperties(name); @@ -398,14 +341,17 @@ public > Optional getIndexedOptionalValues(String return Optional.empty(); } + @Override public ConfigMappings getConfigMappings() { return mappings; } + @Override public T getConfigMapping(Class type) { return mappings.getConfigMapping(type); } + @Override public T getConfigMapping(Class type, String prefix) { return mappings.getConfigMapping(type, prefix); } @@ -415,17 +361,7 @@ public Iterable getPropertyNames() { return configSources.getPropertyNames().get(); } - /** - * Checks if a property is present in the {@link Config} instance. - *
- * Because {@link ConfigSource#getPropertyNames()} may not include all available properties, it is not possible to - * reliably determine if the property is present in the properties list. The property needs to be retrieved to make - * sure it exists. The lookup is done without expression expansion, because the expansion value may not be - * available, and it is not relevant for the final check. - * - * @param name the property name. - * @return true if the property is present or false otherwise. - */ + @Override @Experimental("Check if a property is present") public boolean isPropertyPresent(String name) { return Expressions.withoutExpansion(() -> getConfigValue(name).getValue() != null); @@ -436,6 +372,7 @@ public Iterable getConfigSources() { return configSources.getSources(); } + @Override public Iterable getConfigSources(final Class type) { final List configSourcesByType = new ArrayList<>(); for (ConfigSource configSource : getConfigSources()) { @@ -446,6 +383,7 @@ public Iterable getConfigSources(final Class type) { return configSourcesByType; } + @Override @Experimental("To retrieve a ConfigSource by name") public Optional getConfigSource(final String name) { for (ConfigSource configSource : getConfigSources()) { @@ -457,6 +395,7 @@ public Optional getConfigSource(final String name) { return Optional.empty(); } + @Override public T convert(String value, Class asType) { return value != null ? requireConverter(asType).convert(value) : null; } @@ -477,6 +416,7 @@ public Optional> getConverter(Class asType) { return Optional.ofNullable(getConverterOrNull(asType)); } + @Override public Converter requireConverter(final Class asType) { final Converter conv = getConverterOrNull(asType); if (conv == null) { @@ -510,6 +450,7 @@ public T unwrap(final Class type) { throw ConfigMessages.msg.getTypeNotSupportedForUnwrapping(type); } + @Override public List getProfiles() { return configSources.getProfiles(); }