From 6625d56d1a7d1b7644e8dcfd9226a6da113a01b3 Mon Sep 17 00:00:00 2001 From: Roberto Cortez <radcortez@yahoo.com> Date: Wed, 6 Mar 2024 16:53:16 +0000 Subject: [PATCH] Update MicroProfile Config to 3.1 --- bom/application/pom.xml | 2 +- .../arc/deployment/ConfigBuildStep.java | 108 ++++++++++++------ .../deployment/ConfigPropertyBuildItem.java | 36 +++++- .../ClassConfigurationPropertiesUtil.java | 5 +- .../InterfaceConfigurationPropertiesUtil.java | 4 +- tcks/microprofile-config/pom.xml | 6 +- 6 files changed, 114 insertions(+), 47 deletions(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index f9e8c714810e3..d19b9565d1224 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -39,7 +39,7 @@ <hdrhistogram.version>2.1.12</hdrhistogram.version><!-- keep in sync with micrometer --> <google-auth.version>0.22.0</google-auth.version> <graphql-java.version>21.3</graphql-java.version> <!-- keep in sync with smallrye-graphql --> - <microprofile-config-api.version>3.0.3</microprofile-config-api.version> + <microprofile-config-api.version>3.1</microprofile-config-api.version> <microprofile-health-api.version>4.0.1</microprofile-health-api.version> <microprofile-metrics-api.version>4.0.1</microprofile-metrics-api.version> <microprofile-context-propagation.version>1.3</microprofile-context-propagation.version> 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 bffd54d9b832c..8e9dce3d6591d 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 @@ -2,6 +2,7 @@ import static io.quarkus.arc.processor.Annotations.getParameterAnnotations; import static io.quarkus.deployment.annotations.ExecutionTime.RUNTIME_INIT; +import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT; import static io.quarkus.deployment.builditem.ConfigClassBuildItem.Kind.MAPPING; import static io.quarkus.deployment.builditem.ConfigClassBuildItem.Kind.PROPERTIES; import static io.quarkus.deployment.configuration.ConfigMappingUtils.CONFIG_MAPPING_NAME; @@ -49,8 +50,10 @@ import io.quarkus.arc.processor.Annotations; import io.quarkus.arc.processor.AnnotationsTransformer; import io.quarkus.arc.processor.BeanConfigurator; +import io.quarkus.arc.processor.BuiltinScope; import io.quarkus.arc.processor.DotNames; import io.quarkus.arc.processor.InjectionPointInfo; +import io.quarkus.arc.processor.ObserverInfo; import io.quarkus.arc.runtime.ConfigBeanCreator; import io.quarkus.arc.runtime.ConfigMappingCreator; import io.quarkus.arc.runtime.ConfigRecorder; @@ -135,8 +138,23 @@ void registerCustomConfigBeanTypes(BeanDiscoveryFinishedBuildItem beanDiscovery, } @BuildStep - void validateConfigInjectionPoints(ValidationPhaseBuildItem validationPhase, - BuildProducer<ConfigPropertyBuildItem> configProperties) { + void configPropertyInjectionPoints( + ValidationPhaseBuildItem validationPhase, + BuildProducer<ConfigPropertyBuildItem> configProperties, + BuildProducer<ReflectiveClassBuildItem> reflectiveClass) { + + // @Observes @Initialized(ApplicationScoped.class) requires validation at static init + Set<MethodInfo> observerMethods = new HashSet<>(); + for (ObserverInfo observer : validationPhase.getBeanProcessor().getBeanDeployment().getObservers()) { + if (observer.isSynthetic()) { + continue; + } + AnnotationInstance instance = Annotations.getParameterAnnotation(observer.getObserverMethod(), + DotNames.INITIALIZED); + if (instance != null && instance.value().asClass().name().equals(BuiltinScope.APPLICATION.getName())) { + observerMethods.add(observer.getObserverMethod()); + } + } for (InjectionPointInfo injectionPoint : validationPhase.getContext().getInjectionPoints()) { if (injectionPoint.hasDefaultedQualifier()) { @@ -185,47 +203,43 @@ void validateConfigInjectionPoints(ValidationPhaseBuildItem validationPhase, propertyDefaultValue = defaultValue.asString(); } - configProperties.produce(new ConfigPropertyBuildItem(propertyName, injectedType, propertyDefaultValue)); + if (injectionPoint.getTarget().kind().equals(METHOD) + && observerMethods.contains(injectionPoint.getTarget().asMethod())) { + configProperties + .produce(ConfigPropertyBuildItem.staticInit(propertyName, injectedType, propertyDefaultValue)); + } + + configProperties.produce(ConfigPropertyBuildItem.runtimeInit(propertyName, injectedType, propertyDefaultValue)); } } } @BuildStep - @Record(RUNTIME_INIT) - void validateConfigValues(ConfigRecorder recorder, List<ConfigPropertyBuildItem> configProperties, - BeanContainerBuildItem beanContainer, BuildProducer<ReflectiveClassBuildItem> reflectiveClass) { - // IMPL NOTE: we do depend on BeanContainerBuildItem to make sure that the BeanDeploymentValidator finished its processing - - // the non-primitive types need to be registered for reflection since Class.forName is used at runtime to load the class - for (ConfigPropertyBuildItem item : configProperties) { - Type requiredType = item.getPropertyType(); - String propertyType = requiredType.name().toString(); - if (requiredType.kind() != Kind.PRIMITIVE) { - reflectiveClass.produce(ReflectiveClassBuildItem.builder(propertyType).build()); - } - } + @Record(STATIC_INIT) + void validateStaticInitConfigProperty( + ConfigRecorder recorder, + List<ConfigPropertyBuildItem> configProperties, + BuildProducer<ReflectiveClassBuildItem> reflectiveClass) { - Set<ConfigValidationMetadata> propertiesToValidate = new HashSet<>(); - for (ConfigPropertyBuildItem configProperty : configProperties) { - String rawTypeName = configProperty.getPropertyType().name().toString(); - List<String> actualTypeArgumentNames = Collections.emptyList(); - if (configProperty.getPropertyType().kind() == Kind.PARAMETERIZED_TYPE) { - List<Type> argumentTypes = configProperty.getPropertyType().asParameterizedType().arguments(); - actualTypeArgumentNames = new ArrayList<>(argumentTypes.size()); - for (Type argumentType : argumentTypes) { - actualTypeArgumentNames.add(argumentType.name().toString()); - if (argumentType.kind() != Kind.PRIMITIVE) { - reflectiveClass.produce(ReflectiveClassBuildItem.builder(argumentType.name().toString()) - .build()); - } - } + recorder.validateConfigProperties( + configProperties.stream() + .filter(ConfigPropertyBuildItem::isStaticInit) + .map(p -> configPropertyToConfigValidation(p, reflectiveClass)) + .collect(toSet())); + } - } - propertiesToValidate.add(new ConfigValidationMetadata(configProperty.getPropertyName(), - rawTypeName, actualTypeArgumentNames, configProperty.getDefaultValue())); - } + @BuildStep + @Record(RUNTIME_INIT) + void validateRuntimeConfigProperty( + ConfigRecorder recorder, + List<ConfigPropertyBuildItem> configProperties, + BuildProducer<ReflectiveClassBuildItem> reflectiveClass) { - recorder.validateConfigProperties(propertiesToValidate); + recorder.validateConfigProperties( + configProperties.stream() + .filter(ConfigPropertyBuildItem::isRuntimeInit) + .map(p -> configPropertyToConfigValidation(p, reflectiveClass)) + .collect(toSet())); } @BuildStep @@ -524,6 +538,30 @@ public static boolean isHandledByProducers(Type type) { MP_CONFIG_VALUE_NAME.equals(type.name()); } + private static ConfigValidationMetadata configPropertyToConfigValidation(ConfigPropertyBuildItem configProperty, + BuildProducer<ReflectiveClassBuildItem> reflectiveClass) { + String typeName = configProperty.getPropertyType().name().toString(); + List<String> typeArgumentNames = Collections.emptyList(); + + if (configProperty.getPropertyType().kind() != Kind.PRIMITIVE) { + reflectiveClass.produce(ReflectiveClassBuildItem.builder(typeName).build()); + } + + if (configProperty.getPropertyType().kind() == Kind.PARAMETERIZED_TYPE) { + List<Type> argumentTypes = configProperty.getPropertyType().asParameterizedType().arguments(); + typeArgumentNames = new ArrayList<>(argumentTypes.size()); + for (Type argumentType : argumentTypes) { + typeArgumentNames.add(argumentType.name().toString()); + if (argumentType.kind() != Kind.PRIMITIVE) { + reflectiveClass.produce(ReflectiveClassBuildItem.builder(argumentType.name().toString()).build()); + } + } + } + + return new ConfigValidationMetadata(configProperty.getPropertyName(), typeName, typeArgumentNames, + configProperty.getDefaultValue()); + } + private static Map<Type, ConfigClassBuildItem> configClassesToTypesMap(List<ConfigClassBuildItem> configClasses, ConfigClassBuildItem.Kind kind) { Map<Type, ConfigClassBuildItem> configClassesTypes = new HashMap<>(); 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 ad5fef88ab6cf..5e78261619882 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 @@ -3,6 +3,7 @@ import org.jboss.jandex.Type; import io.quarkus.builder.item.MultiBuildItem; +import io.quarkus.runtime.ExecutionMode; /** * Represents a mandatory config property that needs to be validated at runtime. @@ -11,11 +12,18 @@ public final class ConfigPropertyBuildItem extends MultiBuildItem { private final String propertyName; private final Type propertyType; private final String defaultValue; + private final ExecutionMode executionMode; + + private ConfigPropertyBuildItem( + final String propertyName, + final Type propertyType, + final String defaultValue, + final ExecutionMode executionMode) { - public ConfigPropertyBuildItem(final String propertyName, final Type propertyType, final String defaultValue) { this.propertyName = propertyName; this.propertyType = propertyType; this.defaultValue = defaultValue; + this.executionMode = executionMode; } public String getPropertyName() { @@ -29,4 +37,30 @@ public Type getPropertyType() { public String getDefaultValue() { return defaultValue; } + + public ExecutionMode getExecutionMode() { + return executionMode; + } + + public boolean isStaticInit() { + return executionMode.equals(ExecutionMode.STATIC_INIT); + } + + public boolean isRuntimeInit() { + return executionMode.equals(ExecutionMode.RUNTIME_INIT); + } + + public static ConfigPropertyBuildItem staticInit( + final String propertyName, + final Type propertyType, + final String defaultValue) { + return new ConfigPropertyBuildItem(propertyName, propertyType, defaultValue, ExecutionMode.STATIC_INIT); + } + + public static ConfigPropertyBuildItem runtimeInit( + final String propertyName, + final Type propertyType, + final String defaultValue) { + return new ConfigPropertyBuildItem(propertyName, propertyType, defaultValue, ExecutionMode.RUNTIME_INIT); + } } diff --git a/extensions/spring-boot-properties/deployment/src/main/java/io/quarkus/spring/boot/properties/deployment/ClassConfigurationPropertiesUtil.java b/extensions/spring-boot-properties/deployment/src/main/java/io/quarkus/spring/boot/properties/deployment/ClassConfigurationPropertiesUtil.java index a9082db669ce8..a8b14681e05b9 100644 --- a/extensions/spring-boot-properties/deployment/src/main/java/io/quarkus/spring/boot/properties/deployment/ClassConfigurationPropertiesUtil.java +++ b/extensions/spring-boot-properties/deployment/src/main/java/io/quarkus/spring/boot/properties/deployment/ClassConfigurationPropertiesUtil.java @@ -399,9 +399,8 @@ private ResultHandle populateConfigObject(ClassLoader classLoader, ClassInfo con } for (ConfigPropertyBuildItemCandidate candidate : configPropertyBuildItemCandidates) { - configProperties - .produce(new ConfigPropertyBuildItem(candidate.getConfigPropertyName(), candidate.getConfigPropertyType(), - null)); + configProperties.produce(ConfigPropertyBuildItem.runtimeInit(candidate.getConfigPropertyName(), + candidate.getConfigPropertyType(), null)); } return configObject; diff --git a/extensions/spring-boot-properties/deployment/src/main/java/io/quarkus/spring/boot/properties/deployment/InterfaceConfigurationPropertiesUtil.java b/extensions/spring-boot-properties/deployment/src/main/java/io/quarkus/spring/boot/properties/deployment/InterfaceConfigurationPropertiesUtil.java index 7b642060b1cd9..a1dec9502c709 100644 --- a/extensions/spring-boot-properties/deployment/src/main/java/io/quarkus/spring/boot/properties/deployment/InterfaceConfigurationPropertiesUtil.java +++ b/extensions/spring-boot-properties/deployment/src/main/java/io/quarkus/spring/boot/properties/deployment/InterfaceConfigurationPropertiesUtil.java @@ -259,8 +259,8 @@ private String generateImplementationForInterfaceConfigPropertiesRec(ClassInfo o method.declaringClass().name(), methodCreator, config); methodCreator.returnValue(value); if (defaultValueStr == null || ConfigProperty.UNCONFIGURED_VALUE.equals(defaultValueStr)) { - configProperties - .produce(new ConfigPropertyBuildItem(fullConfigName, returnType, defaultValueStr)); + configProperties.produce( + ConfigPropertyBuildItem.runtimeInit(fullConfigName, returnType, defaultValueStr)); } } } diff --git a/tcks/microprofile-config/pom.xml b/tcks/microprofile-config/pom.xml index f36b1ca04bfd5..2263e81879cb7 100644 --- a/tcks/microprofile-config/pom.xml +++ b/tcks/microprofile-config/pom.xml @@ -13,7 +13,7 @@ <name>Quarkus - TCK - MicroProfile Config</name> <properties> - <microprofile-config-tck.version>3.0.3</microprofile-config-tck.version> + <microprofile-config-tck.version>3.1</microprofile-config-tck.version> </properties> <build> @@ -43,10 +43,6 @@ <systemPropertyVariables> <!-- Disable quarkus optimization --> <quarkus.arc.remove-unused-beans>false</quarkus.arc.remove-unused-beans> - <!-- 1. There is a bug in TCK: --> - <!-- https://github.com/eclipse/microprofile-config/issues/543 --> - <!-- 2. After transformation we get a ClassCastException which is very likely caused by a class loading problem in our Arquillian adapter --> - <quarkus.arc.transform-unproxyable-classes>false</quarkus.arc.transform-unproxyable-classes> </systemPropertyVariables> <!-- This workaround allows us to run a single test using the "test" system property -->