Skip to content

Commit

Permalink
Update MicroProfile Config to 3.1
Browse files Browse the repository at this point in the history
  • Loading branch information
radcortez committed Apr 23, 2024
1 parent 0efd81a commit 6625d56
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 47 deletions.
2 changes: 1 addition & 1 deletion bom/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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() {
Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}
}
Expand Down
6 changes: 1 addition & 5 deletions tcks/microprofile-config/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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 -->
Expand Down

0 comments on commit 6625d56

Please sign in to comment.