From 254f54af2b1b2439ac0bdc1d9b408021ecb49db4 Mon Sep 17 00:00:00 2001 From: Roberto Cortez Date: Wed, 23 Sep 2020 17:05:47 +0100 Subject: [PATCH] MP Config 2.0 Support. --- .mvn/extensions.xml | 13 ++ bom/application/pom.xml | 6 +- build-parent/pom.xml | 2 +- .../BuildTimeConfigurationReader.java | 2 +- .../DefaultValuesConfigurationSource.java | 5 + .../AbstractRawDefaultConfigSource.java | 5 + .../ApplicationPropertiesConfigSource.java | 28 --- .../configuration/ConfigInstantiator.java | 2 +- .../runtime/configuration/ConfigUtils.java | 30 ++-- ...QuarkusPropertiesConfigSourceProvider.java | 84 --------- .../runtime/configuration/Substitutions.java | 17 ++ .../arc/deployment/ConfigBuildStep.java | 138 ++++++++++---- .../deployment/ConfigPropertyBuildItem.java | 9 +- .../ClassConfigPropertiesUtil.java | 3 +- .../deployment/configproperties/DotNames.java | 2 + .../InterfaceConfigPropertiesUtil.java | 2 +- .../arc/test/config/ConfigPropertiesTest.java | 168 ++++++++++++++++++ .../arc/test/config/NullConverterTest.java | 49 +++++ .../arc/runtime/ConfigBeanCreator.java | 48 +---- .../arc/runtime/ConfigMappingCreator.java | 13 +- .../quarkus/arc/runtime/ConfigRecorder.java | 105 ++++++++--- .../runtime/config/VaultConfigSource.java | 7 + .../http/runtime/HttpHostConfigSource.java | 6 + .../io/quarkus/arc/processor/DotNames.java | 2 + .../it/corestuff/CustomConfigSource.java | 5 + .../smallrye/config/ConfigurableSource.java | 5 +- .../it/smallrye/config/ServerProperties.java | 28 +++ .../it/smallrye/config/ServerResource.java | 11 ++ .../smallrye/config/ServerResourceTest.java | 10 ++ srcdeps.yaml | 12 ++ tcks/microprofile-config/pom.xml | 28 +-- .../ConfigApplicationArchiveProcessor.java | 5 + .../src/test/resources/tck-suite.xml | 26 +-- tcks/pom.xml | 3 +- .../http/TestHTTPConfigSourceProvider.java | 5 + .../common/http/TestHTTPResourceManager.java | 23 +-- .../test/junit/NativeTestExtension.java | 5 + .../test/junit/RunningAppConfigResolver.java | 17 ++ 38 files changed, 641 insertions(+), 288 deletions(-) create mode 100644 .mvn/extensions.xml delete mode 100644 core/runtime/src/main/java/io/quarkus/runtime/configuration/QuarkusPropertiesConfigSourceProvider.java create mode 100644 extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/ConfigPropertiesTest.java create mode 100644 extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/NullConverterTest.java create mode 100644 integration-tests/smallrye-config/src/main/java/io/quarkus/it/smallrye/config/ServerProperties.java create mode 100644 srcdeps.yaml diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml new file mode 100644 index 0000000000000..d820426cb4d41 --- /dev/null +++ b/.mvn/extensions.xml @@ -0,0 +1,13 @@ + + + + org.srcdeps.mvn + srcdeps-maven-local-repository + 4.0.0 + + + org.srcdeps.mvn + srcdeps-maven-enforcer + 4.0.0 + + diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 89774fc3ea5d7..8430977100bdc 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -17,7 +17,7 @@ 1.67 1.0.2 2.2.2.Final - 4.5.8.Final + 4.6.0-SNAPSHOT 0.31.0 0.4.1 0.2.3 @@ -31,14 +31,14 @@ 1.4.3 1.6.1 0.22.0 - 1.4 + 2.0 2.3 1.0.1 1.3.3 1.0.1 1.4.1 1.5.0 - 1.9.3 + 2.0.0-RC3 2.2.5 2.4.4 2.0.16 diff --git a/build-parent/pom.xml b/build-parent/pom.xml index 295589ece619c..583e30b245375 100644 --- a/build-parent/pom.xml +++ b/build-parent/pom.xml @@ -60,7 +60,7 @@ 2.2 - 1.4 + 2.0 2.3.2 2.1.1 1.0 diff --git a/core/deployment/src/main/java/io/quarkus/deployment/configuration/BuildTimeConfigurationReader.java b/core/deployment/src/main/java/io/quarkus/deployment/configuration/BuildTimeConfigurationReader.java index 49a77b3ea6361..69f846870cac3 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/configuration/BuildTimeConfigurationReader.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/configuration/BuildTimeConfigurationReader.java @@ -674,7 +674,7 @@ private Converter getConverter(SmallRyeConfig config, Field field, ConverterT throw toError(e); } } else { - converter = config.getConverter(leaf.getLeafType()); + converter = config.requireConverter(leaf.getLeafType()); } } else if (valueType instanceof LowerBoundCheckOf) { // todo: add in bounds checker diff --git a/core/deployment/src/main/java/io/quarkus/deployment/configuration/DefaultValuesConfigurationSource.java b/core/deployment/src/main/java/io/quarkus/deployment/configuration/DefaultValuesConfigurationSource.java index 289f7d4d74a70..59df69b94ed5e 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/configuration/DefaultValuesConfigurationSource.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/configuration/DefaultValuesConfigurationSource.java @@ -2,6 +2,7 @@ import java.util.Collections; import java.util.Map; +import java.util.Set; import org.eclipse.microprofile.config.spi.ConfigSource; @@ -23,6 +24,10 @@ public Map getProperties() { return Collections.emptyMap(); } + public Set getPropertyNames() { + return Collections.emptySet(); + } + public String getValue(final String propertyName) { if (!propertyName.startsWith("quarkus.")) { return null; diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/AbstractRawDefaultConfigSource.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/AbstractRawDefaultConfigSource.java index 3b1d418c9a72b..8f5ded92781ef 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/AbstractRawDefaultConfigSource.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/AbstractRawDefaultConfigSource.java @@ -3,6 +3,7 @@ import java.io.Serializable; import java.util.Collections; import java.util.Map; +import java.util.Set; import org.eclipse.microprofile.config.spi.ConfigSource; @@ -19,6 +20,10 @@ public Map getProperties() { return Collections.emptyMap(); } + public Set getPropertyNames() { + return Collections.emptySet(); + } + public String getValue(final String propertyName) { return getValue(new NameIterator(propertyName)); } diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/ApplicationPropertiesConfigSource.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/ApplicationPropertiesConfigSource.java index d8415f850e0bd..155c16db826c6 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/ApplicationPropertiesConfigSource.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/ApplicationPropertiesConfigSource.java @@ -25,16 +25,11 @@ public abstract class ApplicationPropertiesConfigSource extends PropertiesConfig private static final long serialVersionUID = -4694780118527396798L; static final String APPLICATION_PROPERTIES = "application.properties"; - static final String MP_PROPERTIES = "META-INF/microprofile-config.properties"; ApplicationPropertiesConfigSource(InputStream is, int ordinal) { super(readProperties(is), APPLICATION_PROPERTIES, ordinal); } - ApplicationPropertiesConfigSource(InputStream is, String nm, int ordinal) { - super(readProperties(is), nm, ordinal); - } - private static Map readProperties(final InputStream is) { if (is == null) { return Collections.emptyMap(); @@ -72,29 +67,6 @@ private static InputStream openStream() { } } - /** - * Config that makes sure that the MP config in the application takes precedence over any other on the class path - */ - public static final class MpConfigInJar extends ApplicationPropertiesConfigSource { - public MpConfigInJar() { - super(openStream(), MP_PROPERTIES, 240); - } - - private static InputStream openStream() { - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - if (cl == null) { - cl = ApplicationPropertiesConfigSource.class.getClassLoader(); - } - InputStream is; - if (cl == null) { - is = ClassLoader.getSystemResourceAsStream(MP_PROPERTIES); - } else { - is = cl.getResourceAsStream(MP_PROPERTIES); - } - return is; - } - } - public static final class InFileSystem extends ApplicationPropertiesConfigSource { public InFileSystem() { super(openStream(), 260); diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigInstantiator.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigInstantiator.java index 96b763be8d38c..7215b796468dd 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigInstantiator.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigInstantiator.java @@ -118,7 +118,7 @@ private static Converter getConverterFor(Type type) { } else if (rawType == List.class) { return Converters.newCollectionConverter(getConverterFor(typeOfParameter(type, 0)), ArrayList::new); } else { - return config.getConverter(rawTypeOf(type)); + return config.requireConverter(rawTypeOf(type)); } } diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigUtils.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigUtils.java index 273525ababad2..f402bd266f2c5 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigUtils.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigUtils.java @@ -1,5 +1,8 @@ package io.quarkus.runtime.configuration; +import static io.smallrye.config.SmallRyeConfigBuilder.META_INF_MICROPROFILE_CONFIG_PROPERTIES; +import static io.smallrye.config.SmallRyeConfigBuilder.WEB_INF_MICROPROFILE_CONFIG_PROPERTIES; + import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; @@ -31,6 +34,8 @@ import org.eclipse.microprofile.config.spi.ConfigSourceProvider; import org.jboss.logging.Logger; +import io.smallrye.config.ProfileConfigSourceInterceptor; +import io.smallrye.config.PropertiesConfigSourceProvider; import io.smallrye.config.SmallRyeConfigBuilder; /** @@ -69,22 +74,18 @@ public static SmallRyeConfigBuilder configBuilder(final boolean runTime, final b final SmallRyeConfigBuilder builder = new SmallRyeConfigBuilder(); final ApplicationPropertiesConfigSource.InFileSystem inFileSystem = new ApplicationPropertiesConfigSource.InFileSystem(); final ApplicationPropertiesConfigSource.InJar inJar = new ApplicationPropertiesConfigSource.InJar(); - final ApplicationPropertiesConfigSource.MpConfigInJar mpConfig = new ApplicationPropertiesConfigSource.MpConfigInJar(); - builder.withSources(inFileSystem, inJar, mpConfig, new DotEnvConfigSource()); - builder.withProfile(ProfileManager.getActiveProfile()); + builder.withSources(inFileSystem, inJar, new DotEnvConfigSource()); + builder.withDefaultValue(ProfileConfigSourceInterceptor.SMALLRYE_PROFILE, ProfileManager.getActiveProfile()); builder.addDefaultInterceptors(); final ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); if (runTime) { builder.addDefaultSources(); } else { final List sources = new ArrayList<>(); - sources.addAll( - new QuarkusPropertiesConfigSourceProvider("META-INF/microprofile-config.properties", true, classLoader) - .getConfigSources(classLoader)); - // required by spec... - sources.addAll( - new QuarkusPropertiesConfigSourceProvider("WEB-INF/classes/META-INF/microprofile-config.properties", true, - classLoader).getConfigSources(classLoader)); + sources.addAll(new PropertiesConfigSourceProvider(META_INF_MICROPROFILE_CONFIG_PROPERTIES, classLoader) + .getConfigSources(classLoader)); + sources.addAll(new PropertiesConfigSourceProvider(WEB_INF_MICROPROFILE_CONFIG_PROPERTIES, classLoader) + .getConfigSources(classLoader)); sources.add(new EnvConfigSource()); sources.add(new SysPropConfigSource()); builder.withSources(sources); @@ -140,6 +141,10 @@ public Map getProperties() { return Collections.emptyMap(); } + public Set getPropertyNames() { + return Collections.emptySet(); + } + public String getValue(final String propertyName) { String val = cache.get(propertyName); if (val != null) { @@ -213,6 +218,11 @@ public Map getProperties() { return output; } + @Override + public Set getPropertyNames() { + return getProperties().keySet(); + } + public String getValue(final String propertyName) { return System.getProperty(propertyName); } diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/QuarkusPropertiesConfigSourceProvider.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/QuarkusPropertiesConfigSourceProvider.java deleted file mode 100644 index f5b7e5872c901..0000000000000 --- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/QuarkusPropertiesConfigSourceProvider.java +++ /dev/null @@ -1,84 +0,0 @@ -package io.quarkus.runtime.configuration; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.UncheckedIOException; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.List; -import java.util.Map; -import java.util.Properties; - -import org.eclipse.microprofile.config.spi.ConfigSource; -import org.eclipse.microprofile.config.spi.ConfigSourceProvider; - -import io.quarkus.runtime.util.ClassPathUtils; -import io.smallrye.config.common.MapBackedConfigSource; -import io.smallrye.config.common.utils.ConfigSourceUtil; - -public class QuarkusPropertiesConfigSourceProvider implements ConfigSourceProvider { - - private List configSources = new ArrayList<>(); - - public QuarkusPropertiesConfigSourceProvider(String propertyFileName, boolean optional, ClassLoader classLoader) { - try { - Enumeration propertyFileUrls = classLoader.getResources(propertyFileName); - - if (!optional && !propertyFileUrls.hasMoreElements()) { - throw new IllegalStateException(propertyFileName + " wasn't found."); - } - - while (propertyFileUrls.hasMoreElements()) { - URL propertyFileUrl = propertyFileUrls.nextElement(); - configSources.add(new PropertiesConfigSource(propertyFileUrl)); - } - } catch (IOException ioe) { - throw new IllegalStateException("problem while loading microprofile-config.properties files", ioe); - } - - } - - @Override - public List getConfigSources(ClassLoader forClassLoader) { - return configSources; - } - - private static class PropertiesConfigSource extends MapBackedConfigSource { - private static final long serialVersionUID = 1866835565147832432L; - - private static final String NAME_PREFIX = "PropertiesConfigSource[source="; - - /** - * Construct a new instance - * - * @param url a property file location - * @throws IOException if an error occurred when reading from the input stream - */ - public PropertiesConfigSource(URL url) throws IOException { - super(NAME_PREFIX + url.toString() + "]", urlToMap(url)); - } - - public PropertiesConfigSource(Properties properties, String source) { - super(NAME_PREFIX + source + "]", ConfigSourceUtil.propertiesToMap(properties)); - } - - public PropertiesConfigSource(Map properties, String source, int ordinal) { - super(NAME_PREFIX + source + "]", properties, ordinal); - } - } - - private static Map urlToMap(URL url) throws IOException { - final Properties props = new Properties(); - ClassPathUtils.consumeStream(url, is -> { - try (BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) { - props.load(reader); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - }); - return ConfigSourceUtil.propertiesToMap(props); - } -} diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/Substitutions.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/Substitutions.java index 3a35bda308015..c78282affeb93 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/Substitutions.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/Substitutions.java @@ -94,4 +94,21 @@ public byte[] getClassBytes() { return null; } } + + @TargetClass(className = "io.smallrye.config.ConfigMappingClass") + static final class Target_ConfigMappingClass { + @Alias + static ClassValue cv = null; + + // ClassValue is substituted by a regular ConcurrentHashMap - java.lang.ClassValue.get(JavaLangSubstitutions.java:514) + @Substitute + public static Target_ConfigMappingClass getConfigurationClass(Class classType) { + Assert.checkNotNullParam("classType", classType); + try { + return cv.get(classType); + } catch (NullPointerException e) { + return null; + } + } + } } diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigBuildStep.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigBuildStep.java index bdf964b77f242..6ba2d7049c6eb 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigBuildStep.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigBuildStep.java @@ -1,28 +1,36 @@ package io.quarkus.arc.deployment; +import static io.quarkus.arc.processor.BuildExtension.Key.INJECTION_POINTS; import static io.quarkus.deployment.annotations.ExecutionTime.RUNTIME_INIT; import static io.smallrye.config.ConfigMappings.ConfigMappingWithPrefix.configMappingWithPrefix; -import static java.util.stream.Collectors.groupingBy; -import static java.util.stream.Collectors.mapping; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toSet; +import static org.jboss.jandex.AnnotationInstance.create; +import static org.jboss.jandex.AnnotationTarget.Kind.CLASS; +import static org.jboss.jandex.AnnotationTarget.Kind.FIELD; +import static org.jboss.jandex.AnnotationTarget.Kind.METHOD_PARAMETER; +import static org.jboss.jandex.AnnotationValue.createStringValue; +import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.function.Supplier; import java.util.stream.Stream; import javax.enterprise.context.Dependent; import javax.enterprise.inject.CreationException; +import org.eclipse.microprofile.config.ConfigValue; +import org.eclipse.microprofile.config.inject.ConfigProperties; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.AnnotationTarget; import org.jboss.jandex.AnnotationValue; import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.ClassType; import org.jboss.jandex.DotName; import org.jboss.jandex.FieldInfo; import org.jboss.jandex.MethodInfo; @@ -30,13 +38,14 @@ import org.jboss.jandex.Type.Kind; import io.quarkus.arc.deployment.BeanRegistrationPhaseBuildItem.BeanConfiguratorBuildItem; +import io.quarkus.arc.processor.AnnotationsTransformer; import io.quarkus.arc.processor.BeanRegistrar; -import io.quarkus.arc.processor.BuildExtension; import io.quarkus.arc.processor.DotNames; import io.quarkus.arc.processor.InjectionPointInfo; import io.quarkus.arc.runtime.ConfigBeanCreator; import io.quarkus.arc.runtime.ConfigMappingCreator; import io.quarkus.arc.runtime.ConfigRecorder; +import io.quarkus.arc.runtime.ConfigRecorder.ConfigValidationMetadata; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.annotations.Record; @@ -59,14 +68,20 @@ */ public class ConfigBuildStep { - private static final DotName CONFIG_PROPERTY_NAME = DotName.createSimple(ConfigProperty.class.getName()); + private static final DotName MP_CONFIG_PROPERTY_NAME = DotName.createSimple(ConfigProperty.class.getName()); + private static final DotName MP_CONFIG_PROPERTIES_NAME = DotName.createSimple(ConfigProperties.class.getName()); + private static final DotName MP_CONFIG_VALUE_NAME = DotName.createSimple(ConfigValue.class.getName()); + private static final DotName CONFIG_MAPPING_NAME = DotName.createSimple(ConfigMapping.class.getName()); private static final DotName SET_NAME = DotName.createSimple(Set.class.getName()); private static final DotName LIST_NAME = DotName.createSimple(List.class.getName()); + private static final DotName SUPPLIER_NAME = DotName.createSimple(Supplier.class.getName()); + private static final DotName CONFIG_VALUE_NAME = DotName.createSimple(io.smallrye.config.ConfigValue.class.getName()); @BuildStep - AdditionalBeanBuildItem bean() { - return new AdditionalBeanBuildItem(ConfigProducer.class); + public void additionalBeans(BuildProducer additionalBeans) { + additionalBeans.produce(new AdditionalBeanBuildItem(ConfigProducer.class)); + additionalBeans.produce(new AdditionalBeanBuildItem(ConfigProperties.class)); } @BuildStep @@ -77,13 +92,13 @@ void analyzeConfigPropertyInjectionPoints(BeanRegistrationPhaseBuildItem beanReg Set customBeanTypes = new HashSet<>(); - for (InjectionPointInfo injectionPoint : beanRegistrationPhase.getContext().get(BuildExtension.Key.INJECTION_POINTS)) { + for (InjectionPointInfo injectionPoint : beanRegistrationPhase.getContext().get(INJECTION_POINTS)) { if (injectionPoint.hasDefaultedQualifier()) { // Defaulted qualifier means no @ConfigProperty continue; } - AnnotationInstance configProperty = injectionPoint.getRequiredQualifier(CONFIG_PROPERTY_NAME); + AnnotationInstance configProperty = injectionPoint.getRequiredQualifier(MP_CONFIG_PROPERTY_NAME); if (configProperty != null) { AnnotationValue nameValue = configProperty.value("name"); AnnotationValue defaultValue = configProperty.value("defaultValue"); @@ -113,16 +128,22 @@ void analyzeConfigPropertyInjectionPoints(BeanRegistrationPhaseBuildItem beanReg if (DotNames.OPTIONAL.equals(requiredType.name()) || DotNames.OPTIONAL_INT.equals(requiredType.name()) || DotNames.OPTIONAL_LONG.equals(requiredType.name()) - || DotNames.OPTIONAL_DOUBLE.equals(requiredType.name())) { - // Never validate Optional values + || DotNames.OPTIONAL_DOUBLE.equals(requiredType.name()) + || DotNames.PROVIDER.equals(requiredType.name()) + || SUPPLIER_NAME.equals(requiredType.name()) + || CONFIG_VALUE_NAME.equals(requiredType.name()) + || MP_CONFIG_VALUE_NAME.equals(requiredType.name())) { + // Never validate container objects continue; } - if (defaultValue != null && !ConfigProperty.UNCONFIGURED_VALUE.equals(defaultValue.asString())) { - // No need to validate properties with default values - continue; + + String propertyDefaultValue = null; + if (defaultValue != null && (ConfigProperty.UNCONFIGURED_VALUE.equals(defaultValue.asString()) + || !"".equals(defaultValue.asString()))) { + propertyDefaultValue = defaultValue.asString(); } - configProperties.produce(new ConfigPropertyBuildItem(propertyName, requiredType)); + configProperties.produce(new ConfigPropertyBuildItem(propertyName, requiredType, propertyDefaultValue)); } } @@ -136,7 +157,7 @@ void analyzeConfigPropertyInjectionPoints(BeanRegistrationPhaseBuildItem beanReg .creator(ConfigBeanCreator.class) .providerType(type) .types(type) - .qualifiers(AnnotationInstance.create(CONFIG_PROPERTY_NAME, null, Collections.emptyList())) + .qualifiers(create(MP_CONFIG_PROPERTY_NAME, null, Collections.emptyList())) .param("requiredType", type.name().toString()))); } } @@ -156,10 +177,14 @@ void validateConfigProperties(ConfigRecorder recorder, List> propNamesToClasses = configProperties.stream().collect( - groupingBy(ConfigPropertyBuildItem::getPropertyName, - mapping(c -> c.getPropertyType().name().toString(), toSet()))); - recorder.validateConfigProperties(propNamesToClasses); + Set propertiesToValidate = new HashSet<>(); + for (ConfigPropertyBuildItem configProperty : configProperties) { + propertiesToValidate.add(new ConfigValidationMetadata(configProperty.getPropertyName(), + configProperty.getPropertyType().name().toString(), + configProperty.getDefaultValue())); + } + + recorder.validateConfigProperties(propertiesToValidate); } @BuildStep @@ -188,6 +213,25 @@ public void register(RegistrationContext context) { }); } + @BuildStep + AnnotationsTransformerBuildItem vetoMPConfigProperties() { + return new AnnotationsTransformerBuildItem(new AnnotationsTransformer() { + public boolean appliesTo(org.jboss.jandex.AnnotationTarget.Kind kind) { + return CLASS.equals(kind); + } + + public void transform(TransformationContext context) { + if (context.getAnnotations().stream() + .anyMatch(annotation -> annotation.name().equals(MP_CONFIG_PROPERTIES_NAME))) { + context.transform() + .add(DotNames.VETOED) + .add(DotNames.UNREMOVABLE) + .done(); + } + } + }); + } + @BuildStep void generateConfigMappings( CombinedIndexBuildItem combinedIndex, @@ -197,14 +241,37 @@ void generateConfigMappings( BuildProducer configMappings, BuildProducer beanConfigurators) { - for (AnnotationInstance instance : combinedIndex.getIndex().getAnnotations(CONFIG_MAPPING_NAME)) { + List mappingAnnotations = new ArrayList<>(); + mappingAnnotations.addAll(combinedIndex.getIndex().getAnnotations(CONFIG_MAPPING_NAME)); + mappingAnnotations.addAll(combinedIndex.getIndex().getAnnotations(MP_CONFIG_PROPERTIES_NAME)); + + for (AnnotationInstance instance : mappingAnnotations) { AnnotationTarget target = instance.target(); - if (!target.kind().equals(AnnotationTarget.Kind.CLASS)) { + AnnotationValue annotationPrefix = instance.value("prefix"); + + if (target.kind().equals(FIELD)) { + if (annotationPrefix != null && !annotationPrefix.asString().equals(ConfigProperties.UNCONFIGURED_PREFIX)) { + configMappings.produce( + new ConfigMappingBuildItem(toClass(target.asField().type().name()), annotationPrefix.asString())); + continue; + } + } + + if (target.kind().equals(METHOD_PARAMETER)) { + if (annotationPrefix != null && !annotationPrefix.asString().equals(ConfigProperties.UNCONFIGURED_PREFIX)) { + ClassType classType = target.asMethodParameter().method().parameters() + .get(target.asMethodParameter().position()).asClassType(); + configMappings.produce(new ConfigMappingBuildItem(toClass(classType.name()), annotationPrefix.asString())); + continue; + } + } + + if (!target.kind().equals(CLASS)) { continue; } - Class type = toClass(target.asClass()); - String prefix = Optional.ofNullable(instance.value("prefix")).map(AnnotationValue::asString).orElse(""); + Class type = toClass(target.asClass().name()); + String prefix = Optional.ofNullable(annotationPrefix).map(AnnotationValue::asString).orElse(""); List configMappingsMetadata = ConfigMappingLoader.getConfigMappingsMetadata(type); configMappingsMetadata.forEach(mappingMetadata -> { @@ -218,12 +285,20 @@ void generateConfigMappings( configMappings.produce(new ConfigMappingBuildItem(type, prefix)); + List qualifiers = new ArrayList<>(); + if (instance.name().equals(MP_CONFIG_PROPERTIES_NAME)) { + qualifiers.add( + create(MP_CONFIG_PROPERTIES_NAME, null, new AnnotationValue[] { createStringValue("prefix", prefix) })); + } + beanConfigurators.produce(new BeanConfiguratorBuildItem( beanRegistrationPhase.getContext() .configure(type) .types(type) + .qualifiers(qualifiers.toArray(new AnnotationInstance[] {})) .creator(ConfigMappingCreator.class) - .param("type", type))); + .param("type", type) + .param("prefix", prefix))); } } @@ -244,13 +319,12 @@ void registerConfigMappings( .collect(toSet())); } - private static Class toClass(ClassInfo classInfo) { - String className = classInfo.name().toString(); + private static Class toClass(DotName dotName) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); try { - return classLoader.loadClass(className); + return classLoader.loadClass(dotName.toString()); } catch (ClassNotFoundException e) { - throw new IllegalStateException("The class (" + className + ") cannot be created during deployment.", e); + throw new IllegalStateException("The class (" + dotName.toString() + ") cannot be created during deployment.", e); } } @@ -285,7 +359,9 @@ private boolean isHandledByProducers(Type type) { DotNames.DOUBLE.equals(type.name()) || DotNames.SHORT.equals(type.name()) || DotNames.BYTE.equals(type.name()) || - DotNames.CHARACTER.equals(type.name()); + DotNames.CHARACTER.equals(type.name()) || + SUPPLIER_NAME.equals(type.name()) || + CONFIG_VALUE_NAME.equals(type.name()) || + MP_CONFIG_VALUE_NAME.equals(type.name()); } - } diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigPropertyBuildItem.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigPropertyBuildItem.java index 14003a02df1c5..ad5fef88ab6cf 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigPropertyBuildItem.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigPropertyBuildItem.java @@ -8,14 +8,14 @@ * Represents a mandatory config property that needs to be validated at runtime. */ public final class ConfigPropertyBuildItem extends MultiBuildItem { - private final String propertyName; - private final Type propertyType; + private final String defaultValue; - public ConfigPropertyBuildItem(String propertyName, Type propertyType) { + public ConfigPropertyBuildItem(final String propertyName, final Type propertyType, final String defaultValue) { this.propertyName = propertyName; this.propertyType = propertyType; + this.defaultValue = defaultValue; } public String getPropertyName() { @@ -26,4 +26,7 @@ public Type getPropertyType() { return propertyType; } + public String getDefaultValue() { + return defaultValue; + } } diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/configproperties/ClassConfigPropertiesUtil.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/configproperties/ClassConfigPropertiesUtil.java index 551cc2acf9383..a55a551ac2757 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/configproperties/ClassConfigPropertiesUtil.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/configproperties/ClassConfigPropertiesUtil.java @@ -382,7 +382,8 @@ private static ResultHandle populateConfigObject(ClassLoader classLoader, ClassI for (ConfigPropertyBuildItemCandidate candidate : configPropertyBuildItemCandidates) { configProperties - .produce(new ConfigPropertyBuildItem(candidate.getConfigPropertyName(), candidate.getConfigPropertyType())); + .produce(new ConfigPropertyBuildItem(candidate.getConfigPropertyName(), candidate.getConfigPropertyType(), + null)); } return configObject; diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/configproperties/DotNames.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/configproperties/DotNames.java index f2c543998c4b1..64afa862b880a 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/configproperties/DotNames.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/configproperties/DotNames.java @@ -24,6 +24,8 @@ private DotNames() { static final DotName SET = DotName.createSimple(Set.class.getName()); static final DotName COLLECTION = DotName.createSimple(Collection.class.getName()); static final DotName ENUM = DotName.createSimple(Enum.class.getName()); + static final DotName MP_CONFIG_PROPERTIES = DotName + .createSimple(org.eclipse.microprofile.config.inject.ConfigProperties.class.getName()); static final DotName CONFIG_PROPERTIES = DotName.createSimple(ConfigProperties.class.getName()); static final DotName CONFIG_PREFIX = DotName.createSimple(ConfigPrefix.class.getName()); static final DotName CONFIG_IGNORE = DotName.createSimple(ConfigIgnore.class.getName()); diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/configproperties/InterfaceConfigPropertiesUtil.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/configproperties/InterfaceConfigPropertiesUtil.java index bd919b970863c..2c5cb4288ad68 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/configproperties/InterfaceConfigPropertiesUtil.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/configproperties/InterfaceConfigPropertiesUtil.java @@ -237,7 +237,7 @@ private static void generateImplementationForInterfaceConfigPropertiesRec(ClassI methodCreator.returnValue(value); if (defaultValueStr == null || ConfigProperty.UNCONFIGURED_VALUE.equals(defaultValueStr)) { configProperties - .produce(new ConfigPropertyBuildItem(fullConfigName, returnType)); + .produce(new ConfigPropertyBuildItem(fullConfigName, returnType, defaultValueStr)); } } } diff --git a/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/ConfigPropertiesTest.java b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/ConfigPropertiesTest.java new file mode 100644 index 0000000000000..dd26cfdc5f2c0 --- /dev/null +++ b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/ConfigPropertiesTest.java @@ -0,0 +1,168 @@ +package io.quarkus.arc.test.config; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import javax.enterprise.context.Dependent; +import javax.enterprise.inject.spi.CDI; +import javax.inject.Inject; + +import org.eclipse.microprofile.config.inject.ConfigProperties; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.smallrye.config.ConfigMapping; +import io.smallrye.config.WithDefault; + +public class ConfigPropertiesTest { + @RegisterExtension + static final QuarkusUnitTest TEST = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addAsResource(new StringAsset("server.host=localhost\n" + + "server.port=8080\n" + + "cloud.host=cloud\n" + + "cloud.port=9090\n" + + "host=empty\n" + + "port=0\n"), + "application.properties")); + + @Inject + Server server; + + @Test + void configMapping() { + assertNotNull(server); + assertEquals("localhost", server.host()); + assertEquals(8080, server.port()); + } + + @Inject + Client client; + + @Test + void discoveredMapping() { + assertNotNull(client); + assertEquals("client", client.host()); + assertEquals(80, client.port()); + } + + @Inject + @ConfigMapping(prefix = "cloud") + Server cloud; + + @Test + void overridePrefix() { + assertNotNull(cloud); + assertEquals("cloud", cloud.host()); + assertEquals(9090, cloud.port()); + } + + @Inject + @ConfigProperties + ServerConfigProperties configProperties; + @Inject + @ConfigProperties(prefix = "cloud") + ServerConfigProperties configPropertiesCloud; + @Inject + @ConfigProperties(prefix = "") + ServerConfigProperties configPropertiesEmpty; + + @Test + void configProperties() { + assertNotNull(configProperties); + assertEquals("localhost", configProperties.host); + assertEquals(8080, configProperties.port); + + assertNotNull(configPropertiesCloud); + assertEquals("cloud", configPropertiesCloud.host); + assertEquals(9090, configPropertiesCloud.port); + + assertNotNull(configPropertiesEmpty); + assertEquals("empty", configPropertiesEmpty.host); + assertEquals(0, configPropertiesEmpty.port); + } + + @Test + void select() { + Server server = CDI.current().select(Server.class).get(); + assertNotNull(server); + assertEquals("localhost", server.host()); + assertEquals(8080, server.port()); + + ServerConfigProperties serverConfigProperties = CDI.current() + .select(ServerConfigProperties.class, ConfigProperties.Literal.NO_PREFIX).get(); + assertNotNull(serverConfigProperties); + assertEquals("localhost", serverConfigProperties.host); + assertEquals(8080, serverConfigProperties.port); + + ServerConfigProperties cloudConfigProperties = CDI.current() + .select(ServerConfigProperties.class, ConfigProperties.Literal.of("cloud")).get(); + assertNotNull(cloudConfigProperties); + assertEquals("cloud", cloudConfigProperties.host); + assertEquals(9090, cloudConfigProperties.port); + } + + @Inject + ConfigMappingBean configMappingBean; + + @Test + void overridePrefixBean() { + Server cloud = configMappingBean.getCloud(); + assertEquals("cloud", cloud.host()); + assertEquals(9090, cloud.port()); + + Server client = configMappingBean.getClient(); + assertEquals("client", client.host()); + assertEquals(80, client.port()); + } + + @ConfigMapping(prefix = "server") + public interface Server { + String host(); + + int port(); + } + + @ConfigMapping(prefix = "client") + public interface Client { + @WithDefault("client") + String host(); + + @WithDefault("80") + int port(); + } + + @ConfigProperties(prefix = "server") + public static class ServerConfigProperties { + public String host; + public int port; + } + + @Dependent + static class ConfigMappingBean { + private final Server cloud; + private Server client; + + @Inject + public ConfigMappingBean(@ConfigMapping(prefix = "cloud") Server cloud) { + this.cloud = cloud; + } + + public Server getCloud() { + return cloud; + } + + public Server getClient() { + return client; + } + + @Inject + public void setClient(@ConfigMapping(prefix = "client") final Server client) { + this.client = client; + } + } +} diff --git a/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/NullConverterTest.java b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/NullConverterTest.java new file mode 100644 index 0000000000000..afd8ecf2f1128 --- /dev/null +++ b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/NullConverterTest.java @@ -0,0 +1,49 @@ +package io.quarkus.arc.test.config; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.spi.DeploymentException; +import javax.inject.Inject; + +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.eclipse.microprofile.config.spi.Converter; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class NullConverterTest { + @RegisterExtension + static final QuarkusUnitTest TEST = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClass(NullConverterBean.class) + .addAsServiceProvider(Converter.class, CustomTypeConverter.class) + .addAsResource(new StringAsset("my.prop=1234\n"), "application.properties")) + .setExpectedException(DeploymentException.class); + + @Test + void nullProperty() { + + } + + @ApplicationScoped + static class NullConverterBean { + @Inject + @ConfigProperty(name = "my.prop", defaultValue = "1234") + CustomType customType; + } + + static class CustomType { + + } + + public static class CustomTypeConverter implements Converter { + @Override + public CustomTypeConverter convert(final String value) + throws IllegalArgumentException, NullPointerException { + return null; + } + } +} diff --git a/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ConfigBeanCreator.java b/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ConfigBeanCreator.java index 3f652d1dade3f..88092c81b6bb8 100644 --- a/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ConfigBeanCreator.java +++ b/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ConfigBeanCreator.java @@ -1,22 +1,18 @@ package io.quarkus.arc.runtime; -import java.lang.annotation.Annotation; import java.util.Map; -import java.util.Optional; import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.spi.DeploymentException; import javax.enterprise.inject.spi.InjectionPoint; -import org.eclipse.microprofile.config.Config; import org.eclipse.microprofile.config.ConfigProvider; -import org.eclipse.microprofile.config.inject.ConfigProperty; import io.quarkus.arc.BeanCreator; import io.quarkus.arc.impl.InjectionPointProvider; -import io.smallrye.config.SmallRyeConfig; +import io.smallrye.config.inject.ConfigProducerUtil; public class ConfigBeanCreator implements BeanCreator { - @Override public Object create(CreationalContext creationalContext, Map params) { String requiredType = params.get("requiredType").toString(); @@ -24,9 +20,9 @@ public Object create(CreationalContext creationalContext, Map clazz; + try { - clazz = Class.forName(requiredType, true, cl); + Class.forName(requiredType, true, cl); } catch (ClassNotFoundException e) { throw new IllegalStateException("Cannot load required type: " + requiredType); } @@ -36,38 +32,10 @@ public Object create(CreationalContext creationalContext, Map value = config.getOptionalValue(key, clazz); - if (value.isPresent()) { - return value.get(); - } else { - return ((SmallRyeConfig) config).convert(defaultValue, clazz); - } - } - } - - private Config getConfig() { - return ConfigProvider.getConfig(); - } - - private ConfigProperty getConfigProperty(InjectionPoint injectionPoint) { - for (Annotation qualifier : injectionPoint.getQualifiers()) { - if (qualifier.annotationType().equals(ConfigProperty.class)) { - return (ConfigProperty) qualifier; - } + try { + return ConfigProducerUtil.getValue(injectionPoint, ConfigProvider.getConfig()); + } catch (Exception e) { + throw new DeploymentException(e); } - return null; } - } diff --git a/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ConfigMappingCreator.java b/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ConfigMappingCreator.java index b038d663a18e6..7cedbcb1c47e6 100644 --- a/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ConfigMappingCreator.java +++ b/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ConfigMappingCreator.java @@ -1,19 +1,30 @@ package io.quarkus.arc.runtime; +import static io.smallrye.config.inject.ConfigMappingInjectionBean.getPrefixFromInjectionPoint; + import java.util.Map; import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.spi.InjectionPoint; import org.eclipse.microprofile.config.ConfigProvider; import io.quarkus.arc.BeanCreator; +import io.quarkus.arc.impl.InjectionPointProvider; import io.smallrye.config.SmallRyeConfig; public class ConfigMappingCreator implements BeanCreator { @Override public Object create(CreationalContext creationalContext, Map params) { + InjectionPoint injectionPoint = InjectionPointProvider.get(); + if (injectionPoint == null) { + throw new IllegalStateException("No current injection point found"); + } + Class interfaceType = (Class) params.get("type"); + String prefix = (String) params.get("prefix"); + SmallRyeConfig config = (SmallRyeConfig) ConfigProvider.getConfig(); - return config.getConfigMapping(interfaceType); + return config.getConfigMapping(interfaceType, getPrefixFromInjectionPoint(injectionPoint).orElse(prefix)); } } diff --git a/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ConfigRecorder.java b/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ConfigRecorder.java index 59bce34c1a713..68350c3b60d3f 100644 --- a/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ConfigRecorder.java +++ b/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ConfigRecorder.java @@ -1,7 +1,6 @@ package io.quarkus.arc.runtime; -import java.util.Map; -import java.util.Map.Entry; +import java.util.Objects; import java.util.Set; import javax.enterprise.inject.spi.DeploymentException; @@ -11,7 +10,9 @@ import io.quarkus.runtime.annotations.Recorder; import io.smallrye.config.ConfigMappings; +import io.smallrye.config.ConfigValidationException; import io.smallrye.config.SmallRyeConfig; +import io.smallrye.config.inject.ConfigProducerUtil; /** * @author Martin Kouba @@ -19,37 +20,36 @@ @Recorder public class ConfigRecorder { - public void validateConfigProperties(Map> properties) { + public void validateConfigProperties(Set properties) { Config config = ConfigProvider.getConfig(); ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (cl == null) { cl = ConfigRecorder.class.getClassLoader(); } - for (Entry> entry : properties.entrySet()) { - Set propertyTypes = entry.getValue(); - for (String propertyType : propertyTypes) { - Class propertyClass = load(propertyType, cl); - // For parameterized types and arrays, we only check if the property config exists without trying to convert it - if (propertyClass.isArray() || propertyClass.getTypeParameters().length > 0) { - propertyClass = String.class; - } - try { - if (!config.getOptionalValue(entry.getKey(), propertyClass).isPresent()) { - throw new DeploymentException( - "No config value of type " + entry.getValue() + " exists for: " + entry.getKey()); - } - } catch (IllegalArgumentException e) { - throw new DeploymentException( - "Failed to load config value of type " + entry.getValue() + " for: " + entry.getKey(), e); - } + + for (ConfigValidationMetadata property : properties) { + Class propertyClass = load(property.getType(), cl); + // For parameterized types and arrays, we only check if the property config exists without trying to convert it + if (propertyClass.isArray() || propertyClass.getTypeParameters().length > 0) { + propertyClass = String.class; + } + + try { + ConfigProducerUtil.getValue(property.getName(), propertyClass, property.getDefaultValue(), config); + } catch (Exception e) { + throw new DeploymentException( + "Failed to load config value of type " + propertyClass + " for: " + property.getName(), e); } } } - public void registerConfigMappings(final Set configMappingsWithPrefix) - throws Exception { - SmallRyeConfig config = (SmallRyeConfig) ConfigProvider.getConfig(); - ConfigMappings.registerConfigMappings(config, configMappingsWithPrefix); + public void registerConfigMappings(final Set configMappingsWithPrefix) { + try { + SmallRyeConfig config = (SmallRyeConfig) ConfigProvider.getConfig(); + ConfigMappings.registerConfigMappings(config, configMappingsWithPrefix); + } catch (ConfigValidationException e) { + throw new DeploymentException(e.getMessage(), e); + } } private Class load(String className, ClassLoader cl) { @@ -81,4 +81,61 @@ private Class load(String className, ClassLoader cl) { } } + public static class ConfigValidationMetadata { + private String name; + private String type; + private String defaultValue; + + public ConfigValidationMetadata() { + } + + public ConfigValidationMetadata(final String name, final String type, final String defaultValue) { + this.name = name; + this.type = type; + this.defaultValue = defaultValue; + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(final String type) { + this.type = type; + } + + public String getDefaultValue() { + return defaultValue; + } + + public void setDefaultValue(final String defaultValue) { + this.defaultValue = defaultValue; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final ConfigValidationMetadata that = (ConfigValidationMetadata) o; + return name.equals(that.name) && + type.equals(that.type) && + Objects.equals(defaultValue, that.defaultValue); + } + + @Override + public int hashCode() { + return Objects.hash(name, type, defaultValue); + } + } } diff --git a/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/config/VaultConfigSource.java b/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/config/VaultConfigSource.java index 15ff622aa8296..fc22a76e014f2 100644 --- a/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/config/VaultConfigSource.java +++ b/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/config/VaultConfigSource.java @@ -5,9 +5,11 @@ import static java.util.Optional.empty; import static java.util.stream.Collectors.toMap; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import org.eclipse.microprofile.config.spi.ConfigSource; @@ -49,6 +51,11 @@ public Map getProperties() { return emptyMap(); } + @Override + public Set getPropertyNames() { + return Collections.emptySet(); + } + @Override public String getValue(String propertyName) { return serverConfig.url.isPresent() ? getSecretConfig().get(propertyName) : null; diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HttpHostConfigSource.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HttpHostConfigSource.java index 0fffea179bd60..06a1c666a08c3 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HttpHostConfigSource.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HttpHostConfigSource.java @@ -3,6 +3,7 @@ import java.io.Serializable; import java.util.Collections; import java.util.Map; +import java.util.Set; import org.eclipse.microprofile.config.spi.ConfigSource; @@ -30,6 +31,11 @@ public Map getProperties() { return Collections.singletonMap(QUARKUS_HTTP_HOST, getValue(QUARKUS_HTTP_HOST)); } + @Override + public Set getPropertyNames() { + return Collections.singleton(QUARKUS_HTTP_HOST); + } + @Override public int getOrdinal() { return priority; diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/DotNames.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/DotNames.java index 7084fbc4619db..4e9534d35f5d8 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/DotNames.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/DotNames.java @@ -3,6 +3,7 @@ import io.quarkus.arc.AlternativePriority; import io.quarkus.arc.DefaultBean; import io.quarkus.arc.InjectableInstance; +import io.quarkus.arc.Unremovable; import io.quarkus.arc.impl.ComputingCache; import java.lang.annotation.Repeatable; import java.util.Optional; @@ -99,6 +100,7 @@ public final class DotNames { public static final DotName TRANSACTION_PHASE = create(TransactionPhase.class); public static final DotName INITIALIZED = create(Initialized.class); public static final DotName TRANSIENT_REFERENCE = create(TransientReference.class); + public static final DotName UNREMOVABLE = create(Unremovable.class); public static final DotName BOOLEAN = create(Boolean.class); public static final DotName BYTE = create(Byte.class); diff --git a/integration-tests/main/src/main/java/io/quarkus/it/corestuff/CustomConfigSource.java b/integration-tests/main/src/main/java/io/quarkus/it/corestuff/CustomConfigSource.java index 34995a5073ccd..0b91d310b4801 100644 --- a/integration-tests/main/src/main/java/io/quarkus/it/corestuff/CustomConfigSource.java +++ b/integration-tests/main/src/main/java/io/quarkus/it/corestuff/CustomConfigSource.java @@ -2,6 +2,7 @@ import java.util.Collections; import java.util.Map; +import java.util.Set; import org.eclipse.microprofile.config.spi.ConfigSource; @@ -13,6 +14,10 @@ public Map getProperties() { return THE_MAP; } + public Set getPropertyNames() { + return THE_MAP.keySet(); + } + public String getValue(final String propertyName) { return THE_MAP.get(propertyName); } diff --git a/integration-tests/smallrye-config/src/main/java/io/quarkus/it/smallrye/config/ConfigurableSource.java b/integration-tests/smallrye-config/src/main/java/io/quarkus/it/smallrye/config/ConfigurableSource.java index f4157ea9d4574..35b39ddb8129a 100644 --- a/integration-tests/smallrye-config/src/main/java/io/quarkus/it/smallrye/config/ConfigurableSource.java +++ b/integration-tests/smallrye-config/src/main/java/io/quarkus/it/smallrye/config/ConfigurableSource.java @@ -5,6 +5,7 @@ import java.util.HashMap; import java.util.Map; +import java.util.Set; import org.eclipse.microprofile.config.spi.ConfigSource; @@ -31,8 +32,8 @@ public DatabaseConfigSource() { } @Override - public Map getProperties() { - return values; + public Set getPropertyNames() { + return values.keySet(); } @Override diff --git a/integration-tests/smallrye-config/src/main/java/io/quarkus/it/smallrye/config/ServerProperties.java b/integration-tests/smallrye-config/src/main/java/io/quarkus/it/smallrye/config/ServerProperties.java new file mode 100644 index 0000000000000..0e9a47a84edad --- /dev/null +++ b/integration-tests/smallrye-config/src/main/java/io/quarkus/it/smallrye/config/ServerProperties.java @@ -0,0 +1,28 @@ +package io.quarkus.it.smallrye.config; + +import org.eclipse.microprofile.config.inject.ConfigProperties; + +import io.quarkus.runtime.annotations.RegisterForReflection; + +@RegisterForReflection +@ConfigProperties(prefix = "server") +public class ServerProperties { + private String host; + private int port; + + public String getHost() { + return host; + } + + public void setHost(final String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(final int port) { + this.port = port; + } +} diff --git a/integration-tests/smallrye-config/src/main/java/io/quarkus/it/smallrye/config/ServerResource.java b/integration-tests/smallrye-config/src/main/java/io/quarkus/it/smallrye/config/ServerResource.java index 13c05e224f415..3bd44522eae7d 100644 --- a/integration-tests/smallrye-config/src/main/java/io/quarkus/it/smallrye/config/ServerResource.java +++ b/integration-tests/smallrye-config/src/main/java/io/quarkus/it/smallrye/config/ServerResource.java @@ -5,13 +5,24 @@ import javax.ws.rs.Path; import javax.ws.rs.core.Response; +import org.eclipse.microprofile.config.inject.ConfigProperties; + @Path("/server") public class ServerResource { @Inject Server server; + @Inject + @ConfigProperties + ServerProperties serverProperties; @GET public Response getServer() { return Response.ok(server).build(); } + + @GET + @Path("/properties") + public Response getServerProperties() { + return Response.ok(serverProperties).build(); + } } diff --git a/integration-tests/smallrye-config/src/test/java/io/quarkus/it/smallrye/config/ServerResourceTest.java b/integration-tests/smallrye-config/src/test/java/io/quarkus/it/smallrye/config/ServerResourceTest.java index f0d4fd3a9dfa5..8cf962e17dc2a 100644 --- a/integration-tests/smallrye-config/src/test/java/io/quarkus/it/smallrye/config/ServerResourceTest.java +++ b/integration-tests/smallrye-config/src/test/java/io/quarkus/it/smallrye/config/ServerResourceTest.java @@ -29,4 +29,14 @@ void mapping() { .body("log.rotate", equalTo(true)) .body("log.pattern", equalTo("COMMON")); } + + @Test + void properties() { + given() + .get("/server/properties") + .then() + .statusCode(OK.getStatusCode()) + .body("host", equalTo("localhost")) + .body("port", equalTo(8080)); + } } diff --git a/srcdeps.yaml b/srcdeps.yaml new file mode 100644 index 0000000000000..7774fc4ab2bc1 --- /dev/null +++ b/srcdeps.yaml @@ -0,0 +1,12 @@ +configModelVersion: 3.0 +logToConsole: true +verbosity: info +repositories: + org.jboss.resteasy: + includes: + - org.jboss.resteasy + urls: + - git:https://github.com/radcortez/Resteasy + buildVersionPattern: .*-SNAPSHOT + buildRef: branch-4.6.0-SNAPSHOT-config-patched + skipTests: true diff --git a/tcks/microprofile-config/pom.xml b/tcks/microprofile-config/pom.xml index 7c56e92f8878d..691d6e160d803 100644 --- a/tcks/microprofile-config/pom.xml +++ b/tcks/microprofile-config/pom.xml @@ -23,11 +23,19 @@ + dummy 45 true haha woohoo + 45 + Bob + + dummy + Tennis + 120 + false @@ -52,30 +60,12 @@ io.quarkus quarkus-arquillian - - io.quarkus - quarkus-undertow - + org.eclipse.microprofile.config microprofile-config-tck ${microprofile-config-api.version} - - - - io.quarkus - quarkus-undertow-deployment - ${project.version} - pom - test - - - * - * - - - diff --git a/tcks/microprofile-config/src/test/java/io/quarkus/tck/config/ConfigApplicationArchiveProcessor.java b/tcks/microprofile-config/src/test/java/io/quarkus/tck/config/ConfigApplicationArchiveProcessor.java index 5f093c6056e0d..dd85d897e1c64 100644 --- a/tcks/microprofile-config/src/test/java/io/quarkus/tck/config/ConfigApplicationArchiveProcessor.java +++ b/tcks/microprofile-config/src/test/java/io/quarkus/tck/config/ConfigApplicationArchiveProcessor.java @@ -34,6 +34,11 @@ public void process(Archive applicationArchive, TestClass testClass) { if (archiveAsset.getArchive() instanceof JavaArchive) { JavaArchive libArchive = (JavaArchive) archiveAsset.getArchive(); libArchive.deleteClass(testClass.getName()); + // Remove inner classes + libArchive.getContent().keySet().stream() + .filter(archivePath -> archivePath.get() + .contains(testClass.getJavaClass().getSimpleName() + "$")) + .forEach(libArchive::delete); } } } diff --git a/tcks/microprofile-config/src/test/resources/tck-suite.xml b/tcks/microprofile-config/src/test/resources/tck-suite.xml index 72577f8b91eda..c5b961157ce21 100644 --- a/tcks/microprofile-config/src/test/resources/tck-suite.xml +++ b/tcks/microprofile-config/src/test/resources/tck-suite.xml @@ -1,28 +1,8 @@ - - - - + + - - + - - - - - - - - - - - - - - - - - diff --git a/tcks/pom.xml b/tcks/pom.xml index 32a37bc040f28..331b0ca6ddf33 100644 --- a/tcks/pom.xml +++ b/tcks/pom.xml @@ -24,7 +24,8 @@ microprofile-reactive-messaging microprofile-rest-client microprofile-openapi - microprofile-opentracing + + diff --git a/test-framework/common/src/main/java/io/quarkus/test/common/http/TestHTTPConfigSourceProvider.java b/test-framework/common/src/main/java/io/quarkus/test/common/http/TestHTTPConfigSourceProvider.java index 4d4c61e2e902f..bb9e0b3e66949 100644 --- a/test-framework/common/src/main/java/io/quarkus/test/common/http/TestHTTPConfigSourceProvider.java +++ b/test-framework/common/src/main/java/io/quarkus/test/common/http/TestHTTPConfigSourceProvider.java @@ -4,6 +4,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Set; import org.eclipse.microprofile.config.spi.ConfigSource; import org.eclipse.microprofile.config.spi.ConfigSourceProvider; @@ -43,6 +44,10 @@ public Map getProperties() { return entries; } + public Set getPropertyNames() { + return entries.keySet(); + } + public String getValue(final String propertyName) { return entries.get(propertyName); } diff --git a/test-framework/common/src/main/java/io/quarkus/test/common/http/TestHTTPResourceManager.java b/test-framework/common/src/main/java/io/quarkus/test/common/http/TestHTTPResourceManager.java index 4cf897443d072..0fd04b3a4c8e2 100644 --- a/test-framework/common/src/main/java/io/quarkus/test/common/http/TestHTTPResourceManager.java +++ b/test-framework/common/src/main/java/io/quarkus/test/common/http/TestHTTPResourceManager.java @@ -13,25 +13,20 @@ import org.eclipse.microprofile.config.ConfigProvider; import io.quarkus.bootstrap.app.RunningQuarkusApplication; +import io.quarkus.runtime.LaunchMode; import io.quarkus.runtime.test.TestHttpEndpointProvider; public class TestHTTPResourceManager { public static String getUri() { - try { - Config config = ConfigProvider.getConfig(); - String value = config.getValue("test.url", String.class); - if (value.equals(TestHTTPConfigSourceProvider.TEST_URL_VALUE)) { - //massive hack for dev mode tests, dev mode has not started yet - //so we don't have any way to load this correctly from config - return "http://" + config.getOptionalValue("quarkus.http.host", String.class).orElse("localhost") + ":" - + config.getOptionalValue("quarkus.http.port", String.class).orElse("8080"); - } - return value; - } catch (IllegalStateException e) { - //massive hack for dev mode tests, dev mode has not started yet - //so we don't have any way to load this correctly from config - return "http://localhost:8080"; + Config config = ConfigProvider.getConfig(); + if (LaunchMode.current().equals(LaunchMode.TEST)) { + return config.getValue(TestHTTPConfigSourceProvider.TEST_URL_KEY, String.class); + } else { + return "http://" + + config.getOptionalValue("quarkus.http.host", String.class).orElse("localhost") + + ":" + + config.getOptionalValue("quarkus.http.port", String.class).orElse("8080"); } } diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/NativeTestExtension.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/NativeTestExtension.java index c6d7775c12302..8489f48d09537 100644 --- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/NativeTestExtension.java +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/NativeTestExtension.java @@ -21,6 +21,8 @@ import org.junit.jupiter.api.extension.TestInstancePostProcessor; import org.junit.platform.commons.JUnitException; +import io.quarkus.runtime.LaunchMode; +import io.quarkus.runtime.configuration.ProfileManager; import io.quarkus.runtime.test.TestHttpEndpointProvider; import io.quarkus.test.common.NativeImageLauncher; import io.quarkus.test.common.PropertyTestUtil; @@ -68,6 +70,9 @@ public void beforeAll(ExtensionContext extensionContext) throws Exception { TestResourceManager testResourceManager = new TestResourceManager(testClass); try { testResourceManager.init(); + // To properly retrieve the test.url from io.quarkus.test.common.http.TestHTTPResourceManager.getUri(). + // This is a test run after all, so it should be fine to set the LaunchMode.TEST. + ProfileManager.setLaunchMode(LaunchMode.TEST); Map systemProps = testResourceManager.start(); NativeImageLauncher launcher = new NativeImageLauncher(testClass); launcher.addSystemProperties(systemProps); diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/RunningAppConfigResolver.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/RunningAppConfigResolver.java index 899aa624084ee..7ec88639ed2ad 100644 --- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/RunningAppConfigResolver.java +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/RunningAppConfigResolver.java @@ -4,9 +4,11 @@ import java.util.Optional; import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.config.ConfigValue; import org.eclipse.microprofile.config.spi.ConfigBuilder; import org.eclipse.microprofile.config.spi.ConfigProviderResolver; import org.eclipse.microprofile.config.spi.ConfigSource; +import org.eclipse.microprofile.config.spi.Converter; import io.quarkus.bootstrap.app.RunningQuarkusApplication; @@ -39,6 +41,21 @@ public Iterable getPropertyNames() { public Iterable getConfigSources() { return Collections.emptyList(); } + + @Override + public ConfigValue getConfigValue(final String propertyName) { + throw new UnsupportedOperationException(); + } + + @Override + public Optional> getConverter(final Class forType) { + throw new UnsupportedOperationException(); + } + + @Override + public T unwrap(final Class type) { + throw new UnsupportedOperationException(); + } }; }