diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 71d5baec96ea3..53c0c1581d886 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -47,7 +47,7 @@ 2.0 4.0.2 2.9.0 - 3.10.2 + 3.11.1 4.1.1 4.0.0 4.0.6 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 5c8ef29854607..774ee4784759f 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 @@ -1212,7 +1212,7 @@ private static void getDefaults( .withName(propertyName.toString()) .withValue(defaultValue) .withRawValue(defaultValue) - .withConfigSourceName("DefaultValuesConfigSource") + .withConfigSourceName(DefaultValuesConfigSource.NAME) .withConfigSourceOrdinal(Integer.MIN_VALUE) .build()); } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/configuration/ConfigMappingUtils.java b/core/deployment/src/main/java/io/quarkus/deployment/configuration/ConfigMappingUtils.java index 32f567f0c38d7..cc32fafb9e737 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/configuration/ConfigMappingUtils.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/configuration/ConfigMappingUtils.java @@ -122,9 +122,7 @@ private static void processConfigClass( .reason(ConfigMappingUtils.class.getName()) .build()); reflectiveMethods.produce(new ReflectiveMethodBuildItem(ConfigMappingUtils.class.getName(), - mappingMetadata.getClassName(), "getDefaults", new String[0])); - reflectiveMethods.produce(new ReflectiveMethodBuildItem(ConfigMappingUtils.class.getName(), - mappingMetadata.getClassName(), "getNames", new String[0])); + mappingMetadata.getClassName(), "getProperties", new String[0])); configComponentInterfaces.add(mappingMetadata.getInterfaceType()); diff --git a/core/deployment/src/main/java/io/quarkus/deployment/configuration/RunTimeConfigurationGenerator.java b/core/deployment/src/main/java/io/quarkus/deployment/configuration/RunTimeConfigurationGenerator.java index 2fcfed8eea5f6..15b092b40d967 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/configuration/RunTimeConfigurationGenerator.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/configuration/RunTimeConfigurationGenerator.java @@ -6,7 +6,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import java.util.Collection; +import java.util.ArrayDeque; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -64,8 +64,6 @@ import io.quarkus.runtime.configuration.NameIterator; import io.quarkus.runtime.configuration.PropertiesUtil; import io.quarkus.runtime.configuration.QuarkusConfigFactory; -import io.smallrye.config.ConfigMappingInterface; -import io.smallrye.config.ConfigMappings; import io.smallrye.config.ConfigMappings.ConfigClass; import io.smallrye.config.Converters; import io.smallrye.config.PropertyName; @@ -89,7 +87,6 @@ public final class RunTimeConfigurationGenerator { void.class); public static final MethodDescriptor C_READ_CONFIG = MethodDescriptor.ofMethod(CONFIG_CLASS_NAME, "readConfig", void.class); - static final FieldDescriptor C_MAPPED_PROPERTIES = FieldDescriptor.of(CONFIG_CLASS_NAME, "mappedProperties", Set.class); static final FieldDescriptor C_UNKNOWN = FieldDescriptor.of(CONFIG_CLASS_NAME, "unknown", Set.class); static final FieldDescriptor C_UNKNOWN_RUNTIME = FieldDescriptor.of(CONFIG_CLASS_NAME, "unknownRuntime", Set.class); @@ -149,6 +146,7 @@ public final class RunTimeConfigurationGenerator { boolean.class, String.class); static final MethodDescriptor NI_NEXT = MethodDescriptor.ofMethod(NameIterator.class, "next", void.class); static final MethodDescriptor NI_PREVIOUS = MethodDescriptor.ofMethod(NameIterator.class, "previous", void.class); + static final MethodDescriptor NI_GO_TO_START = MethodDescriptor.ofMethod(NameIterator.class, "goToStart", void.class); static final MethodDescriptor OBJ_TO_STRING = MethodDescriptor.ofMethod(Object.class, "toString", String.class); @@ -186,18 +184,16 @@ public final class RunTimeConfigurationGenerator { static final MethodDescriptor SRCB_BUILD = MethodDescriptor.ofMethod(SmallRyeConfigBuilder.class, "build", SmallRyeConfig.class); + static final MethodDescriptor PU_IS_MAPPED = MethodDescriptor.ofMethod(PropertiesUtil.class, "isMapped", boolean.class, + NameIterator.class, String.class); static final MethodDescriptor PU_IS_PROPERTY_QUARKUS_COMPOUND_NAME = MethodDescriptor.ofMethod(PropertiesUtil.class, "isPropertyQuarkusCompoundName", boolean.class, NameIterator.class); static final MethodDescriptor PU_IS_PROPERTY_IN_ROOTS = MethodDescriptor.ofMethod(PropertiesUtil.class, "isPropertyInRoots", boolean.class, String.class, Set.class); + static final MethodDescriptor HS_NEW = MethodDescriptor.ofConstructor(HashSet.class); static final MethodDescriptor HS_NEW_SIZED = MethodDescriptor.ofConstructor(HashSet.class, int.class); static final MethodDescriptor HS_ADD = MethodDescriptor.ofMethod(HashSet.class, "add", boolean.class, Object.class); - static final MethodDescriptor HS_ADD_ALL = MethodDescriptor.ofMethod(HashSet.class, "addAll", boolean.class, - Collection.class); - static final MethodDescriptor HS_CONTAINS = MethodDescriptor.ofMethod(HashSet.class, "contains", boolean.class, - Object.class); - static final MethodDescriptor PN_NEW = MethodDescriptor.ofConstructor(PropertyName.class, String.class); // todo: more space-efficient sorted map impl static final MethodDescriptor TM_NEW = MethodDescriptor.ofConstructor(TreeMap.class); @@ -274,17 +270,13 @@ public static final class GenerateOperation implements AutoCloseable { clinit = cc.getMethodCreator(MethodDescriptor.ofMethod(CONFIG_CLASS_NAME, "", void.class)); clinit.setModifiers(Opcodes.ACC_STATIC); - cc.getFieldCreator(C_MAPPED_PROPERTIES).setModifiers(Opcodes.ACC_STATIC); - clinit.writeStaticField(C_MAPPED_PROPERTIES, clinit.newInstance(HS_NEW_SIZED, - clinit.load((int) ((float) buildTimeConfigResult.getAllMappingsNames().size() / 0.75f + 1.0f)))); - cc.getFieldCreator(C_UNKNOWN).setModifiers(Opcodes.ACC_STATIC); clinit.writeStaticField(C_UNKNOWN, clinit.newInstance(HS_NEW)); cc.getFieldCreator(C_UNKNOWN_RUNTIME).setModifiers(Opcodes.ACC_STATIC); clinit.writeStaticField(C_UNKNOWN_RUNTIME, clinit.newInstance(HS_NEW)); - generateMappedProperties(); + generateIsMapped(); clinitNameBuilder = clinit.newInstance(SB_NEW); @@ -530,14 +522,18 @@ private void configSweepLoop(MethodDescriptor parserBody, MethodCreator method, .trueBranch()) { ResultHandle key = hasNext.checkCast(hasNext.invokeInterfaceMethod(ITR_NEXT, iterator), String.class); - // !mappedProperties.contains(new PropertyName(key)) continue sweepLoop; - hasNext.ifNonZero( - hasNext.invokeVirtualMethod(HS_CONTAINS, hasNext.readStaticField(C_MAPPED_PROPERTIES), - hasNext.newInstance(PN_NEW, key))) - .trueBranch().continueScope(sweepLoop); - // NameIterator keyIter = new NameIterator(key); ResultHandle keyIter = hasNext.newInstance(NI_NEW_STRING, key); + // if (!isMappedProperty(keyIter)) + ResultHandle isMappedName = hasNext.invokeStaticMethod( + MethodDescriptor.ofMethod(CONFIG_CLASS_NAME, "isMapped", boolean.class, NameIterator.class), + keyIter); + try (BytecodeCreator isMappedPropertyTrue = hasNext.ifTrue(isMappedName).trueBranch()) { + isMappedPropertyTrue.continueScope(sweepLoop); + } + + // keyIter.goToStart() - reset the NameIterator so it can be used by the ConfigRoot code + hasNext.invokeVirtualMethod(NI_GO_TO_START, keyIter); // if (PropertiesUtil.isPropertyQuarkusCompoundName(keyIter)) BranchResult quarkusCompoundName = hasNext @@ -1186,33 +1182,74 @@ private FieldDescriptor getOrCreateConverterInstance(Field field, ConverterType return fd; } - private void generateMappedProperties() { - MethodDescriptor method = MethodDescriptor.ofMethod(CONFIG_CLASS_NAME, "addMappedProperties", void.class); - MethodCreator mc = cc.getMethodCreator(method); - mc.setModifiers(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC); - for (ConfigClass mapping : buildTimeConfigResult.getAllMappings()) { - mc.invokeStaticMethod(generateMappedProperties(mapping, buildTimeConfigResult.getAllMappingsNames())); + private void generateIsMapped() { + Map names = buildTimeConfigResult.getAllMappingsNames(); + ConfigPatternMap patterns = new ConfigPatternMap<>(); + for (Map.Entry entry : names.entrySet()) { + NameIterator name = new NameIterator(entry.getValue()); + ConfigPatternMap current = patterns; + while (name.hasNext()) { + String segment = name.getNextSegment(); + ConfigPatternMap child = current.getChild(segment); + if (child == null) { + child = new ConfigPatternMap<>(); + current.addChild(segment, child); + } + current = child; + name.next(); + } + current.setMatched(true); } - mc.returnVoid(); - mc.close(); - clinit.invokeStaticMethod(method); + + generateIsMapped("isMapped", patterns); } - private MethodDescriptor generateMappedProperties(final ConfigClass mapping, - final Map propertyNames) { - MethodDescriptor method = MethodDescriptor.ofMethod(CONFIG_CLASS_NAME, - "addMappedProperties$" + mapping.getKlass().getName().replace('.', '$'), void.class); + private void generateIsMapped(final String methodName, final ConfigPatternMap names) { + MethodDescriptor method = MethodDescriptor.ofMethod(CONFIG_CLASS_NAME, methodName, boolean.class, + NameIterator.class); MethodCreator mc = cc.getMethodCreator(method); mc.setModifiers(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC); - Map properties = ConfigMappings.getProperties(mapping); - ResultHandle set = mc.readStaticField(C_MAPPED_PROPERTIES); - for (String propertyName : properties.keySet()) { - String name = propertyNames.get(new PropertyName(propertyName)); - mc.invokeVirtualMethod(HS_ADD, set, mc.newInstance(PN_NEW, mc.load(name))); + + ResultHandle nameIterator = mc.getMethodParam(0); + BranchResult hasNext = mc.ifTrue(mc.invokeVirtualMethod(NI_HAS_NEXT, nameIterator)); + + try (BytecodeCreator hasNextTrue = hasNext.trueBranch()) { + ArrayDeque childNames = new ArrayDeque<>(); + // * matching has to come last + for (String childName : names.childNames()) { + if (childName.startsWith("*")) { + childNames.addLast(childName); + } else { + childNames.addFirst(childName); + } + } + + for (String childName : childNames) { + ConfigPatternMap child = names.getChild(childName); + BranchResult nextEquals = hasNextTrue + .ifTrue(hasNextTrue.invokeStaticMethod(PU_IS_MAPPED, nameIterator, hasNextTrue.load(childName))); + try (BytecodeCreator nextEqualsTrue = nextEquals.trueBranch()) { + String childMethodName = methodName + "$" + childName.replace("[*]", "-collection"); + if (child.getMatched() == null) { + generateIsMapped(childMethodName, child); + nextEqualsTrue.invokeVirtualMethod(NI_NEXT, nameIterator); + nextEqualsTrue + .returnValue(nextEqualsTrue.invokeStaticMethod(MethodDescriptor.ofMethod(CONFIG_CLASS_NAME, + childMethodName, boolean.class, NameIterator.class), nameIterator)); + } else { + nextEqualsTrue.returnBoolean(true); + } + } + } + hasNextTrue.returnBoolean(false); } - mc.returnVoid(); + + try (BytecodeCreator hasNextFalse = hasNext.falseBranch()) { + hasNextFalse.returnBoolean(false); + } + + mc.returnBoolean(false); mc.close(); - return method; } private void reportUnknown(BytecodeCreator bc, ResultHandle unknownProperty) { diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigGenerationBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigGenerationBuildStep.java index 24ab9fad7b140..a8d4b305e2fc4 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigGenerationBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigGenerationBuildStep.java @@ -227,7 +227,7 @@ void generateBuilders( SmallRyeConfig config = ConfigProvider.getConfig().unwrap(SmallRyeConfig.class); List profiles = config.getProfiles(); for (Map.Entry entry : configItem.getReadResult().getRunTimeValues().entrySet()) { - if ("DefaultValuesConfigSource".equals(entry.getValue().getConfigSourceName())) { + if (DefaultValuesConfigSource.NAME.equals(entry.getValue().getConfigSourceName())) { continue; } // Runtime values may contain active profiled names that override sames names in defaults diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/DeprecatedRuntimePropertiesRecorder.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/DeprecatedRuntimePropertiesRecorder.java index 994e2fbd5c0dc..e26aada4e53a9 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/DeprecatedRuntimePropertiesRecorder.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/DeprecatedRuntimePropertiesRecorder.java @@ -7,6 +7,7 @@ import org.jboss.logging.Logger; import io.quarkus.runtime.annotations.Recorder; +import io.smallrye.config.DefaultValuesConfigSource; import io.smallrye.config.SmallRyeConfig; @Recorder @@ -20,7 +21,7 @@ public void reportDeprecatedProperties(Set deprecatedRuntimeProperties) if (deprecatedRuntimeProperties.contains(property)) { String configSourceName = ((SmallRyeConfig) config).getConfigValue(property).getConfigSourceName(); // this condition can be removed when support of the @ConfigRoot annotation on classes is removed - if ("DefaultValuesConfigSource".equals(configSourceName)) { + if (DefaultValuesConfigSource.NAME.equals(configSourceName)) { continue; } log.warnf("The '%s' config property is deprecated and should not be used anymore", property); diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/PropertiesUtil.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/PropertiesUtil.java index 7c51a8707538e..74055336f70e8 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/PropertiesUtil.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/PropertiesUtil.java @@ -2,11 +2,19 @@ import java.util.Set; +import io.smallrye.config.PropertyName; + public class PropertiesUtil { private PropertiesUtil() { throw new IllegalStateException("Utility class"); } + public static boolean isMapped(final NameIterator name, final String mapped) { + int offset = name.getPosition() == -1 ? 0 : name.getPosition() + 1; + int len = name.getNextEnd() == 0 ? name.getName().length() : name.getNextEnd() - offset; + return PropertyName.equals(name.getName(), offset, len, mapped, 0, mapped.length()); + } + public static boolean isPropertyInRoots(final String property, final Set roots) { for (String root : roots) { if (isPropertyInRoot(property, root)) { diff --git a/devtools/gradle/gradle/libs.versions.toml b/devtools/gradle/gradle/libs.versions.toml index 45c9107126256..69f2aa9ec9b2c 100644 --- a/devtools/gradle/gradle/libs.versions.toml +++ b/devtools/gradle/gradle/libs.versions.toml @@ -3,7 +3,7 @@ plugin-publish = "1.3.0" # updating Kotlin here makes QuarkusPluginTest > shouldNotFailOnProjectDependenciesWithoutMain(Path) fail kotlin = "2.0.21" -smallrye-config = "3.10.2" +smallrye-config = "3.11.1" junit5 = "5.10.5" assertj = "3.27.2" diff --git a/extensions/hibernate-validator/deployment/src/test/java/io/quarkus/hibernate/validator/test/config/ConfigMappingInvalidTest.java b/extensions/hibernate-validator/deployment/src/test/java/io/quarkus/hibernate/validator/test/config/ConfigMappingInvalidTest.java index 696b7a2dc61f9..cedf4a2bc434c 100644 --- a/extensions/hibernate-validator/deployment/src/test/java/io/quarkus/hibernate/validator/test/config/ConfigMappingInvalidTest.java +++ b/extensions/hibernate-validator/deployment/src/test/java/io/quarkus/hibernate/validator/test/config/ConfigMappingInvalidTest.java @@ -1,56 +1,76 @@ package io.quarkus.hibernate.validator.test.config; -import static org.junit.jupiter.api.Assertions.assertThrows; - +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.time.Duration; +import java.time.Period; import java.util.List; - -import jakarta.inject.Inject; +import java.util.Map; +import java.util.Optional; +import java.util.OptionalInt; +import java.util.function.Consumer; + +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.Size; 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.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.arc.Unremovable; +import io.quarkus.runtime.configuration.DurationConverter; import io.quarkus.test.QuarkusUnitTest; import io.smallrye.config.ConfigMapping; import io.smallrye.config.ConfigValidationException; -import io.smallrye.config.SmallRyeConfig; +import io.smallrye.config.WithConverter; +import io.smallrye.config.WithDefault; +import io.smallrye.config.WithName; +import io.smallrye.config.WithParentName; public class ConfigMappingInvalidTest { @RegisterExtension - static final QuarkusUnitTest UNIT_TEST = new QuarkusUnitTest().setArchiveProducer( + static final QuarkusUnitTest TEST = new QuarkusUnitTest().setArchiveProducer( () -> ShrinkWrap.create(JavaArchive.class) - .addAsResource(new StringAsset("validator.server.host=localhost\n" + - "validator.server.services=redis,postgresql\n" + - "validator.hierarchy.number=1\n" + - "validator.repeatable.name=a"), "application.properties")); - - @Inject - SmallRyeConfig config; + .addAsResource("application-mappings-validation.properties", "application.properties")) + .assertException(new Consumer() { + @Override + public void accept(final Throwable throwable) { + // can't use instanceOf because of different ClassLoaders + assertEquals(ConfigValidationException.class.getName(), throwable.getClass().getName()); + String message = throwable.getMessage(); + assertTrue(message.contains("validator.server.host must be less than or equal to 3")); + assertTrue(message.contains("validator.hierarchy.number must be greater than or equal to 10")); + assertTrue(message.contains("validator.repeatable.name size must be between 2")); + + assertTrue(message.contains("cloud.port must be greater than or equal to 8000")); + assertTrue(message.contains("cloud.log.days must be less than or equal to 15")); + assertTrue(message.contains("cloud.cors.origins[1].port must be greater than or equal to 8000")); + assertTrue(message.contains("cloud.info.name size must be between 0 and 3")); + assertTrue(message.contains("cloud.info.code must be less than or equal to 3")); + assertTrue(message.contains("cloud.info.alias[0] size must be between 0 and 3")); + assertTrue(message.contains("cloud.info.admins.root[1].username size must be between 0 and 4")); + assertTrue(message.contains("cloud.info.firewall.accepted[1] size must be between 8 and 15")); + assertTrue(message.contains("cloud.proxy.timeout must be less than or equal to 10")); + assertTrue(message.contains("cloud server is not prod")); + } + }); @Test void invalid() { - assertThrows(ConfigValidationException.class, () -> config.getConfigMapping(Server.class), - "validator.server.host must be less than or equal to 3"); - } - - @Test - @Disabled("Requires https://github.com/smallrye/smallrye-config/pull/923") - void invalidHierarchy() { - assertThrows(ConfigValidationException.class, () -> config.getConfigMapping(Child.class), - "validator.hierarchy.number must be greater than or equal to 10"); - } - - @Test - void repeatable() { - assertThrows(ConfigValidationException.class, () -> config.getConfigMapping(Repeatable.class)); + fail(); } @Unremovable @@ -80,4 +100,138 @@ public interface Repeatable { @Size(min = 2) String name(); } + + @Unremovable + @ConfigMapping(prefix = "cloud") + @Prod + public interface Cloud { + String host(); + + @Min(8000) + int port(); + + @WithConverter(DurationConverter.class) + Duration timeout(); + + @WithName("io-threads") + int threads(); + + @WithParentName + Map form(); + + Optional ssl(); + + Optional proxy(); + + Optional cors(); + + Log log(); + + Info info(); + + interface Form { + String loginPage(); + + String errorPage(); + + String landingPage(); + + Optional cookie(); + + @WithDefault("1") + List positions(); + } + + interface Ssl { + int port(); + + String certificate(); + + @WithDefault("TLSv1.3,TLSv1.2") + List protocols(); + } + + interface Proxy { + boolean enable(); + + @Max(10) + int timeout(); + } + + interface Log { + @WithDefault("false") + boolean enabled(); + + @WithDefault(".log") + String suffix(); + + @WithDefault("true") + boolean rotate(); + + @WithDefault("COMMON") + Pattern pattern(); + + Period period(); + + @Max(15) + int days(); + + enum Pattern { + COMMON, + SHORT, + COMBINED, + LONG; + } + } + + interface Cors { + List origins(); + + List<@Size(min = 2) String> methods(); + + interface Origin { + @Size(min = 5) + String host(); + + @Min(8000) + int port(); + } + } + + interface Info { + Optional<@Size(max = 3) String> name(); + + @Max(3) + OptionalInt code(); + + Optional> alias(); + + Map> admins(); + + Map> firewall(); + + interface Admin { + @Size(max = 4) + String username(); + } + } + } + + @Target({ ElementType.TYPE_USE, ElementType.FIELD }) + @Retention(RetentionPolicy.RUNTIME) + @Constraint(validatedBy = Prod.Validator.class) + public @interface Prod { + String message() default "server is not prod"; + + Class[] groups() default {}; + + Class[] payload() default {}; + + class Validator implements ConstraintValidator { + @Override + public boolean isValid(final Cloud value, final ConstraintValidatorContext context) { + return value.host().equals("prod"); + } + } + } } diff --git a/extensions/hibernate-validator/deployment/src/test/resources/application-mappings-validation.properties b/extensions/hibernate-validator/deployment/src/test/resources/application-mappings-validation.properties new file mode 100644 index 0000000000000..a94e71916cc37 --- /dev/null +++ b/extensions/hibernate-validator/deployment/src/test/resources/application-mappings-validation.properties @@ -0,0 +1,34 @@ +validator.server.host=localhost +validator.server.services=redis,postgresql +validator.hierarchy.number=1 +validator.repeatable.name=a + +cloud.host=localhost +cloud.port=5000 +cloud.io-threads=200 +cloud.timeout=60s +cloud.form.error-page=error.html +cloud.form.landing-page=index.html +cloud.form.login-page=login.html +cloud.info.admins.root[0].username=root +cloud.info.admins.root[1].username=super +cloud.info.alias=James +cloud.info.alias[0]=James +cloud.info.code=007 +cloud.info.firewall.accepted=127.0.0.1,8.8.8 +cloud.info.firewall.accepted[0]=127.0.0.1 +cloud.info.firewall.accepted[1]=8.8.8 +cloud.info.name=Bond +cloud.log.days=20 +cloud.log.period=P1D +cloud.proxy.enable=true +cloud.proxy.timeout=20 +cloud.ssl.certificate=certificate +cloud.ssl.port=8443 +cloud.cors.methods=GET,POST +cloud.cors.methods[0]=GET +cloud.cors.methods[1]=POST +cloud.cors.origins[0].host=some-server +cloud.cors.origins[0].port=9000 +cloud.cors.origins[1].host=localhost +cloud.cors.origins[1].port=1 diff --git a/extensions/kubernetes-config/runtime/src/main/java/io/quarkus/kubernetes/config/runtime/KubernetesConfigSourceFactory.java b/extensions/kubernetes-config/runtime/src/main/java/io/quarkus/kubernetes/config/runtime/KubernetesConfigSourceFactory.java index f3a98ccec4ea5..39821839ce840 100644 --- a/extensions/kubernetes-config/runtime/src/main/java/io/quarkus/kubernetes/config/runtime/KubernetesConfigSourceFactory.java +++ b/extensions/kubernetes-config/runtime/src/main/java/io/quarkus/kubernetes/config/runtime/KubernetesConfigSourceFactory.java @@ -16,6 +16,7 @@ import io.smallrye.config.ConfigSourceFactory; import io.smallrye.config.ConfigValue; import io.smallrye.config.Converters; +import io.smallrye.config.DefaultValuesConfigSource; import io.smallrye.config.SmallRyeConfig; import io.smallrye.config.SmallRyeConfigBuilder; @@ -84,7 +85,7 @@ Iterable getConfigSources(final KubernetesConfigSourceConfig confi private boolean isExplicitlyDisabled(ConfigSourceContext context) { ConfigValue configValue = context.getValue("quarkus.kubernetes-config.enabled"); - if ("DefaultValuesConfigSource".equals(configValue.getConfigSourceName())) { + if (DefaultValuesConfigSource.NAME.equals(configValue.getConfigSourceName())) { return false; } if (configValue.getValue() != null) { diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/menu/DependenciesProcessor.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/menu/DependenciesProcessor.java index 43d11dc5fe48c..eb38bd492003e 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/menu/DependenciesProcessor.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/menu/DependenciesProcessor.java @@ -25,6 +25,7 @@ import io.quarkus.maven.dependency.ArtifactCoords; import io.quarkus.maven.dependency.ArtifactKey; import io.quarkus.maven.dependency.ResolvedDependency; +import io.smallrye.config.DefaultValuesConfigSource; public class DependenciesProcessor { @@ -84,7 +85,8 @@ void createBuildTimeActions(BuildProducer buildTimeAct private boolean isEnabled() { var value = ConfigProvider.getConfig().getConfigValue("quarkus.bootstrap.incubating-model-resolver"); // if it's not false and if it's false it doesn't come from the default value - return value == null || !"false".equals(value.getValue()) || "DefaultValuesConfigSource".equals(value.getSourceName()); + return value == null || !"false".equals(value.getValue()) + || DefaultValuesConfigSource.NAME.equals(value.getSourceName()); } private void buildTree(ApplicationModel model, Root root, Optional> allGavs, Optional toTarget) { diff --git a/integration-tests/hibernate-validator-resteasy-reactive/src/test/java/io/quarkus/it/hibernate/validator/ConfigMappingStartupValidatorTest.java b/integration-tests/hibernate-validator-resteasy-reactive/src/test/java/io/quarkus/it/hibernate/validator/ConfigMappingStartupValidatorTest.java deleted file mode 100644 index 16652a5a9c213..0000000000000 --- a/integration-tests/hibernate-validator-resteasy-reactive/src/test/java/io/quarkus/it/hibernate/validator/ConfigMappingStartupValidatorTest.java +++ /dev/null @@ -1,108 +0,0 @@ -package io.quarkus.it.hibernate.validator; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import jakarta.inject.Inject; -import jakarta.validation.constraints.Pattern; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import io.quarkus.hibernate.validator.runtime.HibernateBeanValidationConfigValidator; -import io.quarkus.runtime.configuration.QuarkusConfigFactory; -import io.quarkus.test.junit.QuarkusTest; -import io.quarkus.test.junit.QuarkusTestProfile; -import io.quarkus.test.junit.TestProfile; -import io.smallrye.config.ConfigMapping; -import io.smallrye.config.ConfigMappingLoader; -import io.smallrye.config.ConfigMappingMetadata; -import io.smallrye.config.ConfigValidationException; -import io.smallrye.config.ConfigValidationException.Problem; -import io.smallrye.config.ConfigValidator; -import io.smallrye.config.SmallRyeConfig; -import io.smallrye.config.SmallRyeConfigBuilder; -import io.smallrye.config.WithDefault; - -@QuarkusTest -@TestProfile(ConfigMappingStartupValidatorTest.Profile.class) -public class ConfigMappingStartupValidatorTest { - - @ConfigMapping(prefix = "config") - public interface ConfigWithValidation { - String CONFIG_WITH_VALIDATION_VALUE_MUST_BE_D_3 = "ConfigWithValidation.value() must be \"-\\d{3}\""; - - @WithDefault("invalid-value") - @Pattern(regexp = "-\\d{3}", message = CONFIG_WITH_VALIDATION_VALUE_MUST_BE_D_3) - String value(); - } - - public static class Profile implements QuarkusTestProfile { - @Override - public boolean disableGlobalTestResources() { - return false; - } - } - - private static SmallRyeConfig smallryeConfig; - private static Map, Exception> suppressedConfigValidatorExceptions; - - @Inject - ConfigWithValidation config; - - @BeforeAll - public static void doBefore() { - Set constraints = Set.of(Pattern.class.getName()); - Set> classesToBeValidated = new HashSet<>(); - for (ConfigMappingMetadata configMappingMetadata : ConfigMappingLoader - .getConfigMappingsMetadata(ConfigWithValidation.class)) { - try { - classesToBeValidated.add(Class.forName(configMappingMetadata.getClassName())); - } catch (ClassNotFoundException e) { - // Ignore - } - } - - SmallRyeConfigBuilder builder = new SmallRyeConfigBuilder(); - builder.withMapping(ConfigWithValidation.class).setAddDefaultSources(true) - .withValidator(new ConfigValidator() { - final ConfigValidator base = new HibernateBeanValidationConfigValidator(constraints, classesToBeValidated); - - @Override - public void validateMapping(Class mappingClass, String prefix, Object mappingObject) - throws ConfigValidationException { - try { - base.validateMapping(mappingClass, prefix, mappingObject); - } catch (Exception e) { - suppressedConfigValidatorExceptions.put(mappingClass, e); - } - } - }); - QuarkusConfigFactory.setConfig(smallryeConfig = builder.build()); - suppressedConfigValidatorExceptions = new HashMap<>(); - } - - @AfterAll - public static void doAfter() { - QuarkusConfigFactory.setConfig(null); - smallryeConfig = null; - suppressedConfigValidatorExceptions = null; - } - - @Test - public void test() { - assertEquals("invalid-value", config.value()); - ConfigValidationException ex = assertThrows(ConfigValidationException.class, () -> { - throw suppressedConfigValidatorExceptions.get(ConfigWithValidation.class); - }); - assertEquals(1, ex.getProblemCount()); - final Problem problem = ex.getProblem(0); - assertEquals("config.value " + ConfigWithValidation.CONFIG_WITH_VALIDATION_VALUE_MUST_BE_D_3, problem.getMessage()); - } -} 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 deleted file mode 100644 index 74fd78e03b5e5..0000000000000 --- a/integration-tests/smallrye-config/src/main/java/io/quarkus/it/smallrye/config/Cloud.java +++ /dev/null @@ -1,136 +0,0 @@ -package io.quarkus.it.smallrye.config; - -import java.time.Duration; -import java.time.Period; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.OptionalInt; - -import jakarta.validation.constraints.Max; -import jakarta.validation.constraints.Min; -import jakarta.validation.constraints.Size; - -import io.quarkus.arc.Unremovable; -import io.quarkus.runtime.configuration.DurationConverter; -import io.smallrye.config.ConfigMapping; -import io.smallrye.config.WithConverter; -import io.smallrye.config.WithDefault; -import io.smallrye.config.WithName; -import io.smallrye.config.WithParentName; - -@Unremovable -@ConfigMapping(prefix = "cloud") -@Prod -public interface Cloud { - String host(); - - @Min(8000) - int port(); - - @WithConverter(DurationConverter.class) - Duration timeout(); - - @WithName("io-threads") - int threads(); - - @WithParentName - Map form(); - - Optional ssl(); - - Optional proxy(); - - Optional cors(); - - Log log(); - - Info info(); - - interface Form { - String loginPage(); - - String errorPage(); - - String landingPage(); - - Optional cookie(); - - @WithDefault("1") - List positions(); - } - - interface Ssl { - int port(); - - String certificate(); - - @WithDefault("TLSv1.3,TLSv1.2") - List protocols(); - } - - interface Proxy { - boolean enable(); - - @Max(10) - int timeout(); - } - - interface Log { - @WithDefault("false") - boolean enabled(); - - @WithDefault(".log") - String suffix(); - - @WithDefault("true") - boolean rotate(); - - @WithDefault("COMMON") - Pattern pattern(); - - Period period(); - - @Max(15) - int days(); - - enum Pattern { - COMMON, - SHORT, - COMBINED, - LONG; - } - } - - interface Cors { - List origins(); - - List<@Size(min = 2) String> methods(); - - interface Origin { - @Size(min = 5) - String host(); - - @Min(8000) - int port(); - } - } - - interface Info { - Optional<@Size(max = 3) String> name(); - - @Max(3) - OptionalInt code(); - - Optional> alias(); - - Map> admins(); - - Map> firewall(); - - interface Admin { - @Size(max = 4) - String username(); - } - } -} diff --git a/integration-tests/smallrye-config/src/main/java/io/quarkus/it/smallrye/config/Prod.java b/integration-tests/smallrye-config/src/main/java/io/quarkus/it/smallrye/config/Prod.java deleted file mode 100644 index 0387cabd46b58..0000000000000 --- a/integration-tests/smallrye-config/src/main/java/io/quarkus/it/smallrye/config/Prod.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.quarkus.it.smallrye.config; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import jakarta.validation.Constraint; -import jakarta.validation.ConstraintValidator; -import jakarta.validation.ConstraintValidatorContext; -import jakarta.validation.Payload; - -@Target({ ElementType.TYPE_USE, ElementType.FIELD }) -@Retention(RetentionPolicy.RUNTIME) -@Constraint(validatedBy = Prod.Validator.class) -public @interface Prod { - String message() default "server is not prod"; - - Class[] groups() default {}; - - Class[] payload() default {}; - - class Validator implements ConstraintValidator { - @Override - public boolean isValid(final Cloud value, final ConstraintValidatorContext context) { - return value.host().equals("prod"); - } - } -} 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 69205f27a8553..ca507f61583c2 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 @@ -4,24 +4,15 @@ import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; -import jakarta.json.Json; -import jakarta.json.JsonArrayBuilder; -import jakarta.json.JsonObjectBuilder; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.core.Response; -import org.eclipse.microprofile.config.Config; import org.eclipse.microprofile.config.inject.ConfigProperties; import org.eclipse.microprofile.config.inject.ConfigProperty; -import io.smallrye.config.ConfigValidationException; -import io.smallrye.config.SmallRyeConfig; - @Path("/server") public class ServerResource { - @Inject - Config config; @Inject Server server; @Inject @@ -45,26 +36,6 @@ 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(); - } - @GET @Path("/info") public String info() { diff --git a/integration-tests/smallrye-config/src/main/resources/application.yaml b/integration-tests/smallrye-config/src/main/resources/application.yaml index 7da81f0bee2be..81e3e4e758693 100644 --- a/integration-tests/smallrye-config/src/main/resources/application.yaml +++ b/integration-tests/smallrye-config/src/main/resources/application.yaml @@ -34,55 +34,6 @@ http: period: P1D days: 10 -cloud: - host: localhost - port: 5000 - 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 - - - username: super - firewall: - accepted: - - 127.0.0.1 - - 8.8.8 - profile: main: yaml: 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 400df4f21f128..0951ad1e8f5f8 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,19 +1,13 @@ package io.quarkus.it.smallrye.config; import static io.restassured.RestAssured.given; -import static jakarta.ws.rs.core.HttpHeaders.ACCEPT; -import static jakarta.ws.rs.core.HttpHeaders.CONTENT_TYPE; -import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; import static jakarta.ws.rs.core.Response.Status.OK; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.hasItem; -import static org.hamcrest.Matchers.hasSize; import org.junit.jupiter.api.Test; import io.quarkus.test.junit.QuarkusTest; -import io.restassured.http.Header; @QuarkusTest class ServerResourceTest { @@ -75,25 +69,4 @@ void info() { .header("X-VERSION", "1.2.3.4") .body(containsString("My application info")); } - - @Test - void invalid() { - given() - .header(new Header(CONTENT_TYPE, APPLICATION_JSON)) - .header(new Header(ACCEPT, APPLICATION_JSON)) - .get("/server/validator/{prefix}", "cloud") - .then() - .statusCode(OK.getStatusCode()) - .body("errors", hasSize(10)) - .body("errors", hasItem("cloud.port must be greater than or equal to 8000")) - .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[1].username size must be between 0 and 4")) - .body("errors", hasItem("cloud.info.firewall.accepted[1] size must be between 8 and 15")) - .body("errors", hasItem("cloud.proxy.timeout must be less than or equal to 10")) - .body("errors", hasItem("cloud server is not prod")); - } }