diff --git a/bom/application/pom.xml b/bom/application/pom.xml
index 5cacf24150fd86..c884d18dfe2a96 100644
--- a/bom/application/pom.xml
+++ b/bom/application/pom.xml
@@ -42,7 +42,7 @@
2.0
1.2
1.6.0
- 2.2.0
+ 2.3.0
3.0.2
3.0.1
2.1.4
@@ -3179,6 +3179,11 @@
smallrye-config-common
${smallrye-config.version}
+
+ io.smallrye.config
+ smallrye-config-validator
+ ${smallrye-config.version}
+
io.smallrye.config
smallrye-config-source-yaml
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigBuildSteps.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigBuildSteps.java
index 0699b8d2d6d956..1019d415ea7564 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigBuildSteps.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigBuildSteps.java
@@ -30,6 +30,7 @@
import io.smallrye.config.ConfigSourceFactory;
import io.smallrye.config.ConfigSourceInterceptor;
import io.smallrye.config.ConfigSourceInterceptorFactory;
+import io.smallrye.config.ConfigValidator;
import io.smallrye.config.SmallRyeConfigProviderResolver;
class ConfigBuildSteps {
@@ -88,7 +89,8 @@ void nativeServiceProviders(
Converter.class,
ConfigSourceInterceptor.class,
ConfigSourceInterceptorFactory.class,
- ConfigSourceFactory.class)) {
+ ConfigSourceFactory.class,
+ ConfigValidator.class)) {
final String serviceName = serviceClass.getName();
final Set names = ServiceUtil.classNamesNamedIn(classLoader, SERVICES_PREFIX + serviceName);
final List list = names.stream()
diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/ApplicationPropertiesConfigSourceLoader.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/ApplicationPropertiesConfigSourceLoader.java
index f5ac2de74bf0a0..2aba6e8589a61c 100644
--- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/ApplicationPropertiesConfigSourceLoader.java
+++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/ApplicationPropertiesConfigSourceLoader.java
@@ -3,6 +3,7 @@
import java.io.IOException;
import java.net.URI;
import java.net.URL;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
@@ -24,35 +25,27 @@ protected ConfigSource loadConfigSource(final URL url, final int ordinal) throws
}
public static class InClassPath extends ApplicationPropertiesConfigSourceLoader implements ConfigSourceProvider {
- @Override
- protected ConfigSource loadConfigSource(final URL url, final int ordinal) throws IOException {
- return super.loadConfigSource(url, 250);
- }
-
@Override
public List getConfigSources(final ClassLoader classLoader) {
- return loadConfigSources("application.properties", classLoader);
+ return loadConfigSources("application.properties", 250, classLoader);
}
@Override
- protected List tryFileSystem(final URI uri) {
+ protected List tryFileSystem(final URI uri, final int ordinal) {
return new ArrayList<>();
}
}
public static class InFileSystem extends ApplicationPropertiesConfigSourceLoader implements ConfigSourceProvider {
- @Override
- protected ConfigSource loadConfigSource(final URL url, final int ordinal) throws IOException {
- return super.loadConfigSource(url, 260);
- }
-
@Override
public List getConfigSources(final ClassLoader classLoader) {
- return loadConfigSources("config/application.properties", classLoader);
+ return loadConfigSources(
+ Paths.get(System.getProperty("user.dir"), "config", "application.properties").toUri().toString(), 260,
+ classLoader);
}
@Override
- protected List tryClassPath(final URI uri, final ClassLoader classLoader) {
+ protected List tryClassPath(final URI uri, final int ordinal, final ClassLoader classLoader) {
return new ArrayList<>();
}
}
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 4c070f03f55b3a..1ea1cb9fc8b766 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,10 +1,10 @@
package io.quarkus.runtime.configuration;
-import static io.smallrye.config.AbstractLocationConfigSourceFactory.SMALLRYE_LOCATIONS;
import static io.smallrye.config.DotEnvConfigSourceProvider.dotEnvSources;
-import static io.smallrye.config.ProfileConfigSourceInterceptor.SMALLRYE_PROFILE;
-import static io.smallrye.config.ProfileConfigSourceInterceptor.SMALLRYE_PROFILE_PARENT;
import static io.smallrye.config.PropertiesConfigSourceProvider.classPathSources;
+import static io.smallrye.config.SmallRyeConfig.SMALLRYE_CONFIG_LOCATIONS;
+import static io.smallrye.config.SmallRyeConfig.SMALLRYE_CONFIG_PROFILE;
+import static io.smallrye.config.SmallRyeConfig.SMALLRYE_CONFIG_PROFILE_PARENT;
import static io.smallrye.config.SmallRyeConfigBuilder.META_INF_MICROPROFILE_CONFIG_PROPERTIES;
import java.io.IOException;
@@ -116,11 +116,11 @@ public static SmallRyeConfigBuilder configBuilder(final boolean runTime, final b
public static SmallRyeConfigBuilder emptyConfigBuilder() {
final SmallRyeConfigBuilder builder = new SmallRyeConfigBuilder();
- builder.withDefaultValue(SMALLRYE_PROFILE, ProfileManager.getActiveProfile());
+ builder.withDefaultValue(SMALLRYE_CONFIG_PROFILE, ProfileManager.getActiveProfile());
final Map relocations = new HashMap<>();
- relocations.put(SMALLRYE_LOCATIONS, "quarkus.config.locations");
- relocations.put(SMALLRYE_PROFILE_PARENT, "quarkus.config.profile.parent");
+ relocations.put(SMALLRYE_CONFIG_LOCATIONS, "quarkus.config.locations");
+ relocations.put(SMALLRYE_CONFIG_PROFILE_PARENT, "quarkus.config.profile.parent");
// Override the priority, because of the ProfileConfigSourceInterceptor and profile.parent.
builder.withInterceptorFactories(new ConfigSourceInterceptorFactory() {
@Override
@@ -137,6 +137,7 @@ public OptionalInt getPriority() {
builder.addDefaultInterceptors();
builder.addDiscoveredInterceptors();
builder.addDiscoveredConverters();
+ builder.addDiscoveredValidator();
return builder;
}
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 2dd8af0b069640..e9430dcd366522 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,9 +1,13 @@
package io.quarkus.arc.deployment;
+import static io.quarkus.arc.deployment.ConfigClassBuildItem.Type.MAPPING;
+import static io.quarkus.arc.deployment.ConfigClassBuildItem.Type.PROPERTIES;
import static io.quarkus.deployment.annotations.ExecutionTime.RUNTIME_INIT;
-import static io.smallrye.config.ConfigMappings.ConfigMappingWithPrefix.configMappingWithPrefix;
+import static io.smallrye.config.ConfigMappings.ConfigClassWithPrefix.configClassWithPrefix;
+import static java.util.Collections.emptySet;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
+import static org.eclipse.microprofile.config.inject.ConfigProperties.UNCONFIGURED_PREFIX;
import static org.jboss.jandex.AnnotationInstance.create;
import static org.jboss.jandex.AnnotationTarget.Kind.CLASS;
import static org.jboss.jandex.AnnotationTarget.Kind.FIELD;
@@ -57,14 +61,13 @@
import io.smallrye.config.ConfigMapping;
import io.smallrye.config.ConfigMappingLoader;
import io.smallrye.config.ConfigMappingMetadata;
-import io.smallrye.config.ConfigMappings.ConfigMappingWithPrefix;
+import io.smallrye.config.ConfigMappings.ConfigClassWithPrefix;
import io.smallrye.config.inject.ConfigProducer;
/**
* MicroProfile Config related build steps.
*/
public class ConfigBuildStep {
-
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());
@@ -226,13 +229,11 @@ public void transform(TransformationContext context) {
}
@BuildStep
- void generateConfigMappings(
+ void generateConfigClasses(
CombinedIndexBuildItem combinedIndex,
- BeanRegistrationPhaseBuildItem beanRegistrationPhase,
BuildProducer generatedClasses,
BuildProducer reflectiveClasses,
- BuildProducer configMappings,
- BuildProducer beanConfigurationRegistry) {
+ BuildProducer configClasses) {
List mappingAnnotations = new ArrayList<>();
mappingAnnotations.addAll(combinedIndex.getIndex().getAnnotations(CONFIG_MAPPING_NAME));
@@ -243,18 +244,20 @@ void generateConfigMappings(
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()));
+ if (annotationPrefix != null && !annotationPrefix.asString().equals(UNCONFIGURED_PREFIX)) {
+ configClasses.produce(
+ toConfigClassBuildItem(instance, toClass(target.asField().type().name()),
+ annotationPrefix.asString()));
continue;
}
}
if (target.kind().equals(METHOD_PARAMETER)) {
- if (annotationPrefix != null && !annotationPrefix.asString().equals(ConfigProperties.UNCONFIGURED_PREFIX)) {
+ if (annotationPrefix != null && !annotationPrefix.asString().equals(UNCONFIGURED_PREFIX)) {
ClassType classType = target.asMethodParameter().method().parameters()
.get(target.asMethodParameter().position()).asClassType();
- configMappings.produce(new ConfigMappingBuildItem(toClass(classType.name()), annotationPrefix.asString()));
+ configClasses
+ .produce(toConfigClassBuildItem(instance, toClass(classType.name()), annotationPrefix.asString()));
continue;
}
}
@@ -263,11 +266,12 @@ void generateConfigMappings(
continue;
}
- Class> type = toClass(target.asClass().name());
+ Class> configClass = toClass(target.asClass().name());
String prefix = Optional.ofNullable(annotationPrefix).map(AnnotationValue::asString).orElse("");
- List configMappingsMetadata = ConfigMappingLoader.getConfigMappingsMetadata(type);
- List mappingsInfo = new ArrayList<>();
+ List configMappingsMetadata = ConfigMappingLoader.getConfigMappingsMetadata(configClass);
+ Set generatedClassesNames = new HashSet<>();
+ Set mappingsInfo = new HashSet<>();
configMappingsMetadata.forEach(mappingMetadata -> {
generatedClasses.produce(
new GeneratedClassBuildItem(true, mappingMetadata.getClassName(), mappingMetadata.getClassBytes()));
@@ -276,6 +280,8 @@ void generateConfigMappings(
reflectiveClasses
.produce(ReflectiveClassBuildItem.builder(mappingMetadata.getClassName()).constructors(true).build());
+ generatedClassesNames.add(mappingMetadata.getClassName());
+
ClassInfo mappingInfo = combinedIndex.getIndex()
.getClassByName(DotName.createSimple(mappingMetadata.getInterfaceType().getName()));
if (mappingInfo != null) {
@@ -296,40 +302,63 @@ void generateConfigMappings(
}
}
- configMappings.produce(new ConfigMappingBuildItem(type, prefix));
+ configClasses.produce(toConfigClassBuildItem(instance, configClass, generatedClassesNames, prefix));
+ }
+ }
+
+ @BuildStep
+ void beanConfigClasses(
+ List configClasses,
+ BeanRegistrationPhaseBuildItem beanRegistrationPhase,
+ BuildProducer beanConfigurationRegistry) {
+
+ for (ConfigClassBuildItem configClass : configClasses) {
+ if (configClass.getGeneratedClasses().isEmpty()) {
+ continue;
+ }
List qualifiers = new ArrayList<>();
- if (instance.name().equals(MP_CONFIG_PROPERTIES_NAME)) {
+ if (configClass.isProperties()) {
qualifiers.add(
- create(MP_CONFIG_PROPERTIES_NAME, null, new AnnotationValue[] { createStringValue("prefix", prefix) }));
+ create(MP_CONFIG_PROPERTIES_NAME, null,
+ new AnnotationValue[] { createStringValue("prefix", configClass.getPrefix()) }));
}
beanConfigurationRegistry.produce(new BeanConfiguratorBuildItem(
beanRegistrationPhase.getContext()
- .configure(type)
- .types(type)
+ .configure(configClass.getConfigClass())
+ .types(configClass.getConfigClass())
.qualifiers(qualifiers.toArray(new AnnotationInstance[] {}))
.creator(ConfigMappingCreator.class)
- .param("type", type)
- .param("prefix", prefix)));
+ .param("type", configClass.getConfigClass())
+ .param("prefix", configClass.getPrefix())));
}
}
@BuildStep
@Record(RUNTIME_INIT)
- void registerConfigMappings(
+ void registerConfigClasses(
RecorderContext context,
ConfigRecorder recorder,
- List configMappings) throws Exception {
+ List configClasses) throws Exception {
context.registerNonDefaultConstructor(
- ConfigMappingWithPrefix.class.getDeclaredConstructor(Class.class, String.class),
- configMappingWithPrefix -> Stream.of(configMappingWithPrefix.getKlass(), configMappingWithPrefix.getPrefix())
+ ConfigClassWithPrefix.class.getDeclaredConstructor(Class.class, String.class),
+ configClassWithPrefix -> Stream.of(configClassWithPrefix.getKlass(), configClassWithPrefix.getPrefix())
.collect(toList()));
- recorder.registerConfigMappings(configMappings.stream()
- .map(configMapping -> configMappingWithPrefix(configMapping.getInterfaceType(), configMapping.getPrefix()))
- .collect(toSet()));
+ recorder.registerConfigMappings(
+ configClasses.stream()
+ .filter(ConfigClassBuildItem::isMapping)
+ .map(configMapping -> configClassWithPrefix(configMapping.getConfigClass(), configMapping.getPrefix()))
+ .collect(toSet()));
+
+ recorder.registerConfigProperties(
+ configClasses.stream()
+ .filter(ConfigClassBuildItem::isProperties)
+ .map(configProperties -> configClassWithPrefix(configProperties.getConfigClass(),
+ configProperties.getPrefix()))
+ .collect(toSet()));
}
private static Class> toClass(DotName dotName) {
@@ -341,6 +370,27 @@ private static Class> toClass(DotName dotName) {
}
}
+ private static ConfigClassBuildItem toConfigClassBuildItem(
+ AnnotationInstance instance,
+ Class> configClass,
+ String prefix) {
+ return toConfigClassBuildItem(instance, configClass, emptySet(), prefix);
+ }
+
+ private static ConfigClassBuildItem toConfigClassBuildItem(
+ AnnotationInstance instance,
+ Class> configClass,
+ Set generatedClasses,
+ String prefix) {
+ if (instance.name().equals(CONFIG_MAPPING_NAME)) {
+ return new ConfigClassBuildItem(configClass, generatedClasses, prefix, MAPPING);
+ } else if (instance.name().equals(MP_CONFIG_PROPERTIES_NAME)) {
+ return new ConfigClassBuildItem(configClass, generatedClasses, prefix, PROPERTIES);
+ } else {
+ throw new IllegalArgumentException();
+ }
+ }
+
private String getPropertyName(String name, ClassInfo declaringClass) {
StringBuilder builder = new StringBuilder();
if (declaringClass.enclosingClass() == null) {
diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigClassBuildItem.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigClassBuildItem.java
new file mode 100644
index 00000000000000..1d011ddbe5824d
--- /dev/null
+++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigClassBuildItem.java
@@ -0,0 +1,53 @@
+package io.quarkus.arc.deployment;
+
+import java.util.Set;
+
+import io.quarkus.builder.item.MultiBuildItem;
+
+public final class ConfigClassBuildItem extends MultiBuildItem {
+ private final Class> configClass;
+ private final Set generatedClasses;
+ private final String prefix;
+ private final Type type;
+
+ public ConfigClassBuildItem(
+ final Class> configClass,
+ final Set generatedClasses,
+ final String prefix,
+ final Type type) {
+
+ this.configClass = configClass;
+ this.generatedClasses = generatedClasses;
+ this.prefix = prefix;
+ this.type = type;
+ }
+
+ public Class> getConfigClass() {
+ return configClass;
+ }
+
+ public Set getGeneratedClasses() {
+ return generatedClasses;
+ }
+
+ public String getPrefix() {
+ return prefix;
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+ public boolean isMapping() {
+ return Type.MAPPING.equals(type);
+ }
+
+ public boolean isProperties() {
+ return Type.PROPERTIES.equals(type);
+ }
+
+ public enum Type {
+ MAPPING,
+ PROPERTIES;
+ }
+}
diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigMappingBuildItem.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigMappingBuildItem.java
deleted file mode 100644
index b97fa357e77e9c..00000000000000
--- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigMappingBuildItem.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package io.quarkus.arc.deployment;
-
-import io.quarkus.builder.item.MultiBuildItem;
-
-public final class ConfigMappingBuildItem extends MultiBuildItem {
- private final Class> interfaceType;
- private final String prefix;
-
- public ConfigMappingBuildItem(final Class> interfaceType, final String prefix) {
- this.interfaceType = interfaceType;
- this.prefix = prefix;
- }
-
- public Class> getInterfaceType() {
- return interfaceType;
- }
-
- public String getPrefix() {
- return prefix;
- }
-}
diff --git a/extensions/arc/runtime/src/main/java/io/quarkus/arc/config/ConfigIgnore.java b/extensions/arc/runtime/src/main/java/io/quarkus/arc/config/ConfigIgnore.java
index b73a65fdf54240..ff11ee45db0491 100644
--- a/extensions/arc/runtime/src/main/java/io/quarkus/arc/config/ConfigIgnore.java
+++ b/extensions/arc/runtime/src/main/java/io/quarkus/arc/config/ConfigIgnore.java
@@ -9,7 +9,11 @@
/**
* When applied to a field of class annotated with {@link ConfigProperties}, that field will be ignored
* for the purposes of configuration
+ *
+ * @deprecated Please, use {@link io.smallrye.config.ConfigMapping} instead. This will be removed in a future Quarkus
+ * version.
*/
+@Deprecated
@Target(FIELD)
@Retention(RUNTIME)
public @interface ConfigIgnore {
diff --git a/extensions/arc/runtime/src/main/java/io/quarkus/arc/config/ConfigPrefix.java b/extensions/arc/runtime/src/main/java/io/quarkus/arc/config/ConfigPrefix.java
index 6a0ccf5d7510a4..0a861dcfedb122 100644
--- a/extensions/arc/runtime/src/main/java/io/quarkus/arc/config/ConfigPrefix.java
+++ b/extensions/arc/runtime/src/main/java/io/quarkus/arc/config/ConfigPrefix.java
@@ -11,6 +11,9 @@
/**
* Uses in order to set the prefix for a configuration object at the injection point
+ *
+ * @deprecated Please, use {@link io.smallrye.config.ConfigMapping} instead. This will be removed in a future Quarkus
+ * version.
*/
@Qualifier
@Target({ FIELD, PARAMETER })
diff --git a/extensions/arc/runtime/src/main/java/io/quarkus/arc/config/ConfigProperties.java b/extensions/arc/runtime/src/main/java/io/quarkus/arc/config/ConfigProperties.java
index 6df0fcc1e725af..f7fb0886c80134 100644
--- a/extensions/arc/runtime/src/main/java/io/quarkus/arc/config/ConfigProperties.java
+++ b/extensions/arc/runtime/src/main/java/io/quarkus/arc/config/ConfigProperties.java
@@ -10,7 +10,11 @@
/**
* Allow configuration properties with a common prefix to be grouped into a single class
+ *
+ * @deprecated Please, use {@link io.smallrye.config.ConfigMapping} instead. This will be removed in a future Quarkus
+ * version.
*/
+@Deprecated
@Target({ TYPE })
@Retention(RUNTIME)
public @interface ConfigProperties {
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 68350c3b60d3f4..c232571d6c5130 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
@@ -10,6 +10,7 @@
import io.quarkus.runtime.annotations.Recorder;
import io.smallrye.config.ConfigMappings;
+import io.smallrye.config.ConfigMappings.ConfigClassWithPrefix;
import io.smallrye.config.ConfigValidationException;
import io.smallrye.config.SmallRyeConfig;
import io.smallrye.config.inject.ConfigProducerUtil;
@@ -43,10 +44,19 @@ public void validateConfigProperties(Set properties) {
}
}
- public void registerConfigMappings(final Set configMappingsWithPrefix) {
+ public void registerConfigMappings(final Set configClasses) {
try {
SmallRyeConfig config = (SmallRyeConfig) ConfigProvider.getConfig();
- ConfigMappings.registerConfigMappings(config, configMappingsWithPrefix);
+ ConfigMappings.registerConfigMappings(config, configClasses);
+ } catch (ConfigValidationException e) {
+ throw new DeploymentException(e.getMessage(), e);
+ }
+ }
+
+ public void registerConfigProperties(final Set configClasses) {
+ try {
+ SmallRyeConfig config = (SmallRyeConfig) ConfigProvider.getConfig();
+ ConfigMappings.registerConfigProperties(config, configClasses);
} catch (ConfigValidationException e) {
throw new DeploymentException(e.getMessage(), e);
}
diff --git a/extensions/config-yaml/runtime/src/main/java/io/quarkus/config/yaml/runtime/ApplicationYamlConfigSourceLoader.java b/extensions/config-yaml/runtime/src/main/java/io/quarkus/config/yaml/runtime/ApplicationYamlConfigSourceLoader.java
index cfb664721c4b60..2f063642c30b91 100644
--- a/extensions/config-yaml/runtime/src/main/java/io/quarkus/config/yaml/runtime/ApplicationYamlConfigSourceLoader.java
+++ b/extensions/config-yaml/runtime/src/main/java/io/quarkus/config/yaml/runtime/ApplicationYamlConfigSourceLoader.java
@@ -19,7 +19,6 @@ protected String[] getFileExtensions() {
"yaml",
"yml"
};
-
}
@Override
@@ -28,41 +27,31 @@ protected ConfigSource loadConfigSource(final URL url, final int ordinal) throws
}
public static class InClassPath extends ApplicationYamlConfigSourceLoader implements ConfigSourceProvider {
- @Override
- protected ConfigSource loadConfigSource(final URL url, final int ordinal) throws IOException {
- return super.loadConfigSource(url, 255);
- }
-
@Override
public List getConfigSources(final ClassLoader classLoader) {
List configSources = new ArrayList<>();
- configSources.addAll(loadConfigSources("application.yaml", classLoader));
- configSources.addAll(loadConfigSources("application.yml", classLoader));
+ configSources.addAll(loadConfigSources("application.yaml", 255, classLoader));
+ configSources.addAll(loadConfigSources("application.yml", 255, classLoader));
return configSources;
}
@Override
- protected List tryFileSystem(final URI uri) {
+ protected List tryFileSystem(final URI uri, final int ordinal) {
return new ArrayList<>();
}
}
public static class InFileSystem extends ApplicationYamlConfigSourceLoader implements ConfigSourceProvider {
- @Override
- protected ConfigSource loadConfigSource(final URL url, final int ordinal) throws IOException {
- return super.loadConfigSource(url, 265);
- }
-
@Override
public List getConfigSources(final ClassLoader classLoader) {
List configSources = new ArrayList<>();
- configSources.addAll(loadConfigSources("config/application.yaml", classLoader));
- configSources.addAll(loadConfigSources("config/application.yml", classLoader));
+ configSources.addAll(loadConfigSources("config/application.yaml", 265, classLoader));
+ configSources.addAll(loadConfigSources("config/application.yml", 265, classLoader));
return configSources;
}
@Override
- protected List tryClassPath(final URI uri, final ClassLoader classLoader) {
+ protected List tryClassPath(final URI uri, final int ordinal, final ClassLoader classLoader) {
return new ArrayList<>();
}
}
diff --git a/extensions/hibernate-validator/deployment/src/main/java/io/quarkus/hibernate/validator/deployment/HibernateValidatorProcessor.java b/extensions/hibernate-validator/deployment/src/main/java/io/quarkus/hibernate/validator/deployment/HibernateValidatorProcessor.java
index 2eb14a165ce059..8aa4bdcfefe1cf 100644
--- a/extensions/hibernate-validator/deployment/src/main/java/io/quarkus/hibernate/validator/deployment/HibernateValidatorProcessor.java
+++ b/extensions/hibernate-validator/deployment/src/main/java/io/quarkus/hibernate/validator/deployment/HibernateValidatorProcessor.java
@@ -48,6 +48,7 @@
import io.quarkus.arc.deployment.AutoAddScopeBuildItem;
import io.quarkus.arc.deployment.BeanArchiveIndexBuildItem;
import io.quarkus.arc.deployment.BeanContainerListenerBuildItem;
+import io.quarkus.arc.deployment.ConfigClassBuildItem;
import io.quarkus.arc.deployment.SyntheticBeanBuildItem;
import io.quarkus.arc.deployment.UnremovableBeanBuildItem;
import io.quarkus.arc.processor.BeanInfo;
@@ -182,6 +183,7 @@ public void build(HibernateValidatorRecorder recorder, RecorderContext recorderC
BuildProducer beanContainerListener,
BuildProducer beanValidationAnnotations,
ShutdownContextBuildItem shutdownContext,
+ List configClasses,
List additionalJaxRsResourceMethodAnnotations,
Capabilities capabilities,
LocalesBuildTimeConfig localesBuildTimeConfig,
@@ -274,6 +276,12 @@ public void build(HibernateValidatorRecorder recorder, RecorderContext recorderC
}
}
+ for (ConfigClassBuildItem configClass : configClasses) {
+ for (String generatedClass : configClass.getGeneratedClasses()) {
+ classNamesToBeValidated.add(DotName.createSimple(generatedClass));
+ }
+ }
+
// JAX-RS methods are handled differently by the transformer so those need to be gathered here.
// Note: The focus only on methods is basically an incomplete solution, since there could also be
// class-level JAX-RS annotations but currently the transformer only looks at methods.
diff --git a/extensions/hibernate-validator/deployment/src/test/java/io/quarkus/hibernate/validator/test/config/ConfigMappingValidatorTest.java b/extensions/hibernate-validator/deployment/src/test/java/io/quarkus/hibernate/validator/test/config/ConfigMappingValidatorTest.java
new file mode 100644
index 00000000000000..ec8b70552d8431
--- /dev/null
+++ b/extensions/hibernate-validator/deployment/src/test/java/io/quarkus/hibernate/validator/test/config/ConfigMappingValidatorTest.java
@@ -0,0 +1,41 @@
+package io.quarkus.hibernate.validator.test.config;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import javax.inject.Inject;
+import javax.validation.constraints.Max;
+
+import org.eclipse.microprofile.config.Config;
+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.ConfigValidationException;
+import io.smallrye.config.SmallRyeConfig;
+
+public class ConfigMappingValidatorTest {
+ @RegisterExtension
+ static final QuarkusUnitTest UNIT_TEST = new QuarkusUnitTest().setArchiveProducer(
+ () -> ShrinkWrap.create(JavaArchive.class)
+ .addAsResource(new StringAsset("server.host=localhost\n"), "application.properties"));
+
+ @Inject
+ Config config;
+
+ @Test
+ void validator() {
+ assertThrows(ConfigValidationException.class,
+ () -> config.unwrap(SmallRyeConfig.class).getConfigMapping(Server.class),
+ "server.host must be less than or equal to 3");
+ }
+
+ @ConfigMapping(prefix = "server")
+ public interface Server {
+ @Max(3)
+ String host();
+ }
+}
diff --git a/extensions/hibernate-validator/runtime/pom.xml b/extensions/hibernate-validator/runtime/pom.xml
index faa299a71afca9..a07425441a1e49 100644
--- a/extensions/hibernate-validator/runtime/pom.xml
+++ b/extensions/hibernate-validator/runtime/pom.xml
@@ -30,6 +30,10 @@
org.glassfish
jakarta.el
+
+ io.smallrye.config
+ smallrye-config-validator
+
org.jboss.resteasy
resteasy-core
diff --git a/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/HibernateBeanValidationConfigValidator.java b/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/HibernateBeanValidationConfigValidator.java
new file mode 100644
index 00000000000000..13aafb261b8c6c
--- /dev/null
+++ b/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/HibernateBeanValidationConfigValidator.java
@@ -0,0 +1,12 @@
+package io.quarkus.hibernate.validator.runtime;
+
+import javax.validation.Validator;
+
+import io.smallrye.config.validator.BeanValidationConfigValidator;
+
+public class HibernateBeanValidationConfigValidator implements BeanValidationConfigValidator {
+ @Override
+ public Validator getValidator() {
+ return ValidatorHolder.getValidator();
+ }
+}
diff --git a/extensions/hibernate-validator/runtime/src/main/resources/META-INF/services/io.smallrye.config.ConfigValidator b/extensions/hibernate-validator/runtime/src/main/resources/META-INF/services/io.smallrye.config.ConfigValidator
new file mode 100644
index 00000000000000..cf5de21457647e
--- /dev/null
+++ b/extensions/hibernate-validator/runtime/src/main/resources/META-INF/services/io.smallrye.config.ConfigValidator
@@ -0,0 +1,2 @@
+#io.smallrye.config.validator.BeanValidationConfigValidatorImpl
+io.quarkus.hibernate.validator.runtime.HibernateBeanValidationConfigValidator
diff --git a/integration-tests/smallrye-config/pom.xml b/integration-tests/smallrye-config/pom.xml
index 67d90fc4324aff..cf5a8c414e6895 100644
--- a/integration-tests/smallrye-config/pom.xml
+++ b/integration-tests/smallrye-config/pom.xml
@@ -15,6 +15,10 @@
Quarkus - Integration Tests - SmallRye Config
+
+ io.quarkus
+ quarkus-hibernate-validator
+
io.quarkus
quarkus-config-yaml
@@ -47,6 +51,19 @@
+
+ io.quarkus
+ quarkus-hibernate-validator-deployment
+ ${project.version}
+ pom
+ test
+
+
+ *
+ *
+
+
+
io.quarkus
quarkus-config-yaml-deployment
diff --git a/integration-tests/smallrye-config/src/main/java/io/quarkus/it/smallrye/config/Cloud.java b/integration-tests/smallrye-config/src/main/java/io/quarkus/it/smallrye/config/Cloud.java
new file mode 100644
index 00000000000000..bacd07dad75027
--- /dev/null
+++ b/integration-tests/smallrye-config/src/main/java/io/quarkus/it/smallrye/config/Cloud.java
@@ -0,0 +1,10 @@
+package io.quarkus.it.smallrye.config;
+
+import io.smallrye.config.ConfigMapping;
+import io.smallrye.config.WithParentName;
+
+@ConfigMapping(prefix = "cloud")
+public interface Cloud {
+ @WithParentName
+ Server server();
+}
diff --git a/integration-tests/smallrye-config/src/main/java/io/quarkus/it/smallrye/config/Server.java b/integration-tests/smallrye-config/src/main/java/io/quarkus/it/smallrye/config/Server.java
index b2a8e55f2f4322..a266a558e11e41 100644
--- a/integration-tests/smallrye-config/src/main/java/io/quarkus/it/smallrye/config/Server.java
+++ b/integration-tests/smallrye-config/src/main/java/io/quarkus/it/smallrye/config/Server.java
@@ -5,6 +5,11 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.OptionalInt;
+
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.Size;
import com.fasterxml.jackson.annotation.JsonProperty;
@@ -22,6 +27,7 @@ public interface Server {
String host();
@JsonProperty
+ @Min(8000)
int port();
@JsonProperty
@@ -48,6 +54,9 @@ public interface Server {
@JsonProperty
Log log();
+ @JsonProperty
+ Info info();
+
interface Form {
@JsonProperty
String loginPage();
@@ -77,6 +86,10 @@ interface Ssl {
interface Proxy {
@JsonProperty
boolean enable();
+
+ @JsonProperty
+ @Max(10)
+ int timeout();
}
interface Log {
@@ -99,6 +112,10 @@ interface Log {
@JsonProperty
Period period();
+ @JsonProperty
+ @Max(15)
+ int days();
+
@RegisterForReflection
enum Pattern {
COMMON,
@@ -113,14 +130,37 @@ interface Cors {
List origins();
@JsonProperty
- List methods();
+ List<@Size(min = 2) String> methods();
interface Origin {
@JsonProperty
+ @Size(min = 5)
String host();
@JsonProperty
+ @Min(8000)
int port();
}
}
+
+ interface Info {
+ @JsonProperty
+ Optional<@Size(max = 3) String> name();
+
+ @JsonProperty
+ @Max(3)
+ OptionalInt code();
+
+ @JsonProperty
+ Optional> alias();
+
+ @JsonProperty
+ Map admins();
+
+ interface Admin {
+ @JsonProperty
+ @Size(max = 3)
+ String username();
+ }
+ }
}
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 3bd44522eae7d7..5be4bf80738e02 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
@@ -1,14 +1,23 @@
package io.quarkus.it.smallrye.config;
import javax.inject.Inject;
+import javax.json.Json;
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonObjectBuilder;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
+import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.inject.ConfigProperties;
+import io.smallrye.config.ConfigValidationException;
+import io.smallrye.config.SmallRyeConfig;
+
@Path("/server")
public class ServerResource {
+ @Inject
+ Config config;
@Inject
Server server;
@Inject
@@ -25,4 +34,24 @@ public Response getServer() {
public Response getServerProperties() {
return Response.ok(serverProperties).build();
}
+
+ @GET
+ @Path("/validator/cloud")
+ public Response validator() {
+ try {
+ config.unwrap(SmallRyeConfig.class).getConfigMapping(Cloud.class, "cloud");
+ } catch (ConfigValidationException e) {
+ JsonArrayBuilder jsonArrayBuilder = Json.createArrayBuilder();
+ for (int i = 0; i < e.getProblemCount(); i++) {
+ jsonArrayBuilder.add(e.getProblem(i).getMessage());
+ }
+ JsonObjectBuilder jsonObjectBuilder = Json.createObjectBuilder();
+ jsonObjectBuilder.add("errors", jsonArrayBuilder);
+ return Response.ok().entity(jsonObjectBuilder.build().toString()).build();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return Response.serverError().build();
+ }
}
diff --git a/integration-tests/smallrye-config/src/main/resources/application.yaml b/integration-tests/smallrye-config/src/main/resources/application.yaml
index ee5a005d2cfeec..65a19799838e2b 100644
--- a/integration-tests/smallrye-config/src/main/resources/application.yaml
+++ b/integration-tests/smallrye-config/src/main/resources/application.yaml
@@ -25,6 +25,49 @@ server:
log:
period: P1D
+ days: 10
+
+cloud:
+ host: localhost
+ port: 8080
+ timeout: 60s
+ io-threads: 200
+
+ form:
+ login-page: login.html
+ error-page: error.html
+ landing-page: index.html
+
+ ssl:
+ port: 8443
+ certificate: certificate
+
+ cors:
+ origins:
+ - host: some-server
+ port: 9000
+ - host: localhost
+ port: 1
+ methods:
+ - GET
+ - POST
+
+ proxy:
+ enable: true
+ timeout: 20
+
+ log:
+ period: P1D
+ days: 20
+
+ info:
+ name: Bond
+ code: 007
+ alias:
+ - James
+ admins:
+ root:
+ username: root
profile:
main:
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 b2ea4c38ac4a5b..f8c230723c40f1 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
@@ -1,15 +1,33 @@
package io.quarkus.it.smallrye.config;
import static io.restassured.RestAssured.given;
+import static javax.ws.rs.core.HttpHeaders.ACCEPT;
+import static javax.ws.rs.core.HttpHeaders.CONTENT_TYPE;
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static javax.ws.rs.core.Response.Status.OK;
import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.hasSize;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import io.quarkus.test.junit.QuarkusTest;
+import io.restassured.RestAssured;
+import io.restassured.http.Header;
@QuarkusTest
class ServerResourceTest {
+ @BeforeAll
+ static void beforeAll() {
+ RestAssured.filters(
+ (requestSpec, responseSpec, ctx) -> {
+ requestSpec.header(new Header(CONTENT_TYPE, APPLICATION_JSON));
+ requestSpec.header(new Header(ACCEPT, APPLICATION_JSON));
+ return ctx.next(requestSpec, responseSpec);
+ });
+ }
+
@Test
void mapping() {
given()
@@ -46,4 +64,19 @@ void properties() {
.body("host", equalTo("localhost"))
.body("port", equalTo(8080));
}
+
+ @Test
+ void invalid() {
+ given().get("/server/validator/{prefix}", "cloud")
+ .then()
+ .statusCode(OK.getStatusCode())
+ .body("errors", hasSize(7))
+ .body("errors", hasItem("cloud.log.days must be less than or equal to 15"))
+ .body("errors", hasItem("cloud.cors.origins[1].port must be greater than or equal to 8000"))
+ .body("errors", hasItem("cloud.info.name size must be between 0 and 3"))
+ .body("errors", hasItem("cloud.info.code must be less than or equal to 3"))
+ .body("errors", hasItem("cloud.info.alias[0] size must be between 0 and 3"))
+ .body("errors", hasItem("cloud.info.admins.root.username size must be between 0 and 3"))
+ .body("errors", hasItem("cloud.proxy.timeout must be less than or equal to 10"));
+ }
}
diff --git a/tcks/microprofile-config/pom.xml b/tcks/microprofile-config/pom.xml
index 9d61669968eb8d..691d6e160d803d 100644
--- a/tcks/microprofile-config/pom.xml
+++ b/tcks/microprofile-config/pom.xml
@@ -32,7 +32,6 @@
Bob
- false
dummy
Tennis
120