Skip to content

Commit

Permalink
Support @ConfigMapping in Extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
radcortez committed Aug 4, 2021
1 parent a2adbd0 commit 4934c1d
Show file tree
Hide file tree
Showing 21 changed files with 794 additions and 275 deletions.
2 changes: 1 addition & 1 deletion bom/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
<microprofile-rest-client.version>2.0</microprofile-rest-client.version>
<microprofile-jwt.version>1.2</microprofile-jwt.version>
<smallrye-common.version>1.6.0</smallrye-common.version>
<smallrye-config.version>2.4.2</smallrye-config.version>
<smallrye-config.version>2.4.3-SNAPSHOT</smallrye-config.version>
<smallrye-health.version>3.1.1</smallrye-health.version>
<smallrye-metrics.version>3.0.1</smallrye-metrics.version>
<smallrye-open-api.version>2.1.9</smallrye-open-api.version>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
import io.quarkus.runtime.annotations.Recorder;
import io.quarkus.runtime.configuration.ConfigUtils;
import io.quarkus.runtime.configuration.QuarkusConfigFactory;
import io.smallrye.config.ConfigMappings.ConfigClassWithPrefix;
import io.smallrye.config.KeyMap;
import io.smallrye.config.KeyMapBackedConfigSource;
import io.smallrye.config.NameIterator;
Expand Down Expand Up @@ -164,6 +165,10 @@ public static Consumer<BuildChainBuilder> loadStepsFrom(ClassLoader classLoader,
builder.withSources(ds1, ds2, platformConfigSource, pcs);
}

for (ConfigClassWithPrefix mapping : reader.getBuildTimeVisibleMappings()) {
builder.withMapping(mapping.getKlass(), mapping.getPrefix());
}

if (configCustomizer != null) {
configCustomizer.accept(builder);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,11 @@
public final class ConfigDescriptionBuildItem extends MultiBuildItem implements Comparable<ConfigDescriptionBuildItem> {

private final String propertyName;
private final Class<?> type;
private final String defaultValue;
private final String docs;

public ConfigDescriptionBuildItem(String propertyName, Class<?> type, String defaultValue, String docs) {
public ConfigDescriptionBuildItem(String propertyName, String defaultValue, String docs) {
this.propertyName = propertyName;
this.type = type;
this.defaultValue = defaultValue;
this.docs = docs;
}
Expand All @@ -24,10 +22,6 @@ public String getPropertyName() {
return propertyName;
}

public Class<?> getType() {
return type;
}

public String getDefaultValue() {
return defaultValue;
}
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import org.jboss.jandex.MethodInfo;

import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
import io.quarkus.deployment.builditem.ConfigClassBuildItem;
import io.quarkus.deployment.builditem.GeneratedClassBuildItem;
Expand All @@ -38,23 +37,23 @@ public class ConfigMappingUtils {
private ConfigMappingUtils() {
}

@BuildStep
public static void generateConfigClasses(
CombinedIndexBuildItem combinedIndex,
BuildProducer<GeneratedClassBuildItem> generatedClasses,
BuildProducer<ReflectiveClassBuildItem> reflectiveClasses,
BuildProducer<ConfigClassBuildItem> configClasses,
DotName configAnnotation) {

boolean isMapping = configAnnotation.equals(CONFIG_MAPPING_NAME);
for (AnnotationInstance instance : combinedIndex.getIndex().getAnnotations(configAnnotation)) {
AnnotationTarget target = instance.target();
AnnotationValue annotationPrefix = instance.value("prefix");

if (target.kind().equals(FIELD)) {
if (annotationPrefix != null && !annotationPrefix.asString().equals(UNCONFIGURED_PREFIX)) {
configClasses.produce(
toConfigClassBuildItem(instance, toClass(target.asField().type().name()),
annotationPrefix.asString()));
toConfigClassBuildItem(toClass(target.asField().type().name()),
annotationPrefix.asString(), isMapping));
continue;
}
}
Expand All @@ -64,7 +63,7 @@ public static void generateConfigClasses(
ClassType classType = target.asMethodParameter().method().parameters()
.get(target.asMethodParameter().position()).asClassType();
configClasses
.produce(toConfigClassBuildItem(instance, toClass(classType.name()), annotationPrefix.asString()));
.produce(toConfigClassBuildItem(toClass(classType.name()), annotationPrefix.asString(), isMapping));
continue;
}
}
Expand All @@ -75,40 +74,54 @@ public static void generateConfigClasses(

Class<?> configClass = toClass(target.asClass().name());
String prefix = Optional.ofNullable(annotationPrefix).map(AnnotationValue::asString).orElse("");
processConfigClass(configClass, prefix, isMapping, combinedIndex, true, generatedClasses, reflectiveClasses,
configClasses);
}
}

List<ConfigMappingMetadata> configMappingsMetadata = ConfigMappingLoader.getConfigMappingsMetadata(configClass);
Set<String> generatedClassesNames = new HashSet<>();
Set<ClassInfo> mappingsInfo = new HashSet<>();
configMappingsMetadata.forEach(mappingMetadata -> {
generatedClasses.produce(
new GeneratedClassBuildItem(true, mappingMetadata.getClassName(), mappingMetadata.getClassBytes()));
reflectiveClasses
.produce(ReflectiveClassBuildItem.builder(mappingMetadata.getInterfaceType()).methods(true).build());
reflectiveClasses
.produce(ReflectiveClassBuildItem.builder(mappingMetadata.getClassName()).constructors(true).build());

for (Class<?> parent : getHierarchy(mappingMetadata.getInterfaceType())) {
reflectiveClasses.produce(ReflectiveClassBuildItem.builder(parent).methods(true).build());
}

generatedClassesNames.add(mappingMetadata.getClassName());
public static void processConfigClass(
Class<?> configClass,
String prefix,
boolean isMapping,
CombinedIndexBuildItem combinedIndex,
boolean isApplicationClass,
BuildProducer<GeneratedClassBuildItem> generatedClasses,
BuildProducer<ReflectiveClassBuildItem> reflectiveClasses,
BuildProducer<ConfigClassBuildItem> configClasses) {

List<ConfigMappingMetadata> configMappingsMetadata = ConfigMappingLoader.getConfigMappingsMetadata(configClass);
Set<String> generatedClassesNames = new HashSet<>();
Set<ClassInfo> mappingsInfo = new HashSet<>();
configMappingsMetadata.forEach(mappingMetadata -> {
generatedClasses.produce(
new GeneratedClassBuildItem(isApplicationClass, mappingMetadata.getClassName(),
mappingMetadata.getClassBytes()));
reflectiveClasses
.produce(ReflectiveClassBuildItem.builder(mappingMetadata.getInterfaceType()).methods(true).build());
reflectiveClasses
.produce(ReflectiveClassBuildItem.builder(mappingMetadata.getClassName()).constructors(true).build());

for (Class<?> parent : getHierarchy(mappingMetadata.getInterfaceType())) {
reflectiveClasses.produce(ReflectiveClassBuildItem.builder(parent).methods(true).build());
}

ClassInfo mappingInfo = combinedIndex.getIndex()
.getClassByName(DotName.createSimple(mappingMetadata.getInterfaceType().getName()));
if (mappingInfo != null) {
mappingsInfo.add(mappingInfo);
}
});
generatedClassesNames.add(mappingMetadata.getClassName());

// For implicit converters
for (ClassInfo classInfo : mappingsInfo) {
for (MethodInfo method : classInfo.methods()) {
reflectiveClasses.produce(new ReflectiveClassBuildItem(true, false, method.returnType().name().toString()));
}
ClassInfo mappingInfo = combinedIndex.getIndex()
.getClassByName(DotName.createSimple(mappingMetadata.getInterfaceType().getName()));
if (mappingInfo != null) {
mappingsInfo.add(mappingInfo);
}
});

configClasses.produce(toConfigClassBuildItem(instance, configClass, generatedClassesNames, prefix));
// For implicit converters
for (ClassInfo classInfo : mappingsInfo) {
for (MethodInfo method : classInfo.methods()) {
reflectiveClasses.produce(new ReflectiveClassBuildItem(true, false, method.returnType().name().toString()));
}
}

configClasses.produce(toConfigClassBuildItem(configClass, prefix, isMapping, generatedClassesNames));
}

private static Class<?> toClass(DotName dotName) {
Expand All @@ -121,22 +134,18 @@ private static Class<?> toClass(DotName dotName) {
}

private static ConfigClassBuildItem toConfigClassBuildItem(
AnnotationInstance instance,
Class<?> configClass,
String prefix) {
return toConfigClassBuildItem(instance, configClass, emptySet(), prefix);
String prefix,
boolean isMapping) {
return toConfigClassBuildItem(configClass, prefix, isMapping, emptySet());
}

private static ConfigClassBuildItem toConfigClassBuildItem(
AnnotationInstance instance,
Class<?> configClass,
Set<String> generatedClasses,
String prefix) {
if (instance.name().equals(CONFIG_MAPPING_NAME)) {
return new ConfigClassBuildItem(configClass, generatedClasses, prefix, MAPPING);
} else {
return new ConfigClassBuildItem(configClass, generatedClasses, prefix, PROPERTIES);
}
String prefix,
boolean isMapping,
Set<String> generatedClasses) {
return new ConfigClassBuildItem(configClass, generatedClasses, prefix, isMapping ? MAPPING : PROPERTIES);
}

private static List<Class<?>> getHierarchy(Class<?> mapping) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,10 @@ public final class RunTimeConfigurationGenerator {
static final FieldDescriptor C_SPECIFIED_RUN_TIME_CONFIG_SOURCE = FieldDescriptor.of(CONFIG_CLASS_NAME,
"specifiedRunTimeConfigSource",
ConfigSource.class);
static final FieldDescriptor C_UNUSED = FieldDescriptor.of(CONFIG_CLASS_NAME, "unused", List.class);
static final FieldDescriptor C_UNUSED_RUNTIME = FieldDescriptor.of(CONFIG_CLASS_NAME, "unusedRuntime", List.class);
static final FieldDescriptor C_UNKNOWN = FieldDescriptor.of(CONFIG_CLASS_NAME, "unknown", List.class);
static final FieldDescriptor C_UNKNOWN_RUNTIME = FieldDescriptor.of(CONFIG_CLASS_NAME, "unknownRuntime", List.class);
static final MethodDescriptor C_REPORT_UNKNOWN = MethodDescriptor.ofMethod(CONFIG_CLASS_NAME, "reportUnknown", void.class,
String.class, List.class);

static final MethodDescriptor CD_INVALID_VALUE = MethodDescriptor.ofMethod(ConfigDiagnostic.class, "invalidValue",
void.class, String.class, IllegalArgumentException.class);
Expand Down Expand Up @@ -182,6 +184,9 @@ public final class RunTimeConfigurationGenerator {
static final MethodDescriptor RCSP_NEW = MethodDescriptor.ofConstructor(RuntimeConfigSourceProvider.class, String.class);
static final MethodDescriptor RCSF_NEW = MethodDescriptor.ofConstructor(RuntimeConfigSourceFactory.class, String.class);

static final MethodDescriptor CS_GET_VALUE = MethodDescriptor.ofMethod(ConfigSource.class, "getValue", String.class,
String.class);

static final MethodDescriptor AL_NEW = MethodDescriptor.ofConstructor(ArrayList.class);
static final MethodDescriptor AL_ADD = MethodDescriptor.ofMethod(ArrayList.class, "add", boolean.class, Object.class);

Expand Down Expand Up @@ -283,6 +288,7 @@ public static final class GenerateOperation implements AutoCloseable {
final ResultHandle clinitNameBuilder;
final BuildTimeConfigurationReader.ReadResult buildTimeConfigResult;
final List<RootDefinition> roots;
final Map<String, String> allBuildTimeValues;
// default values given in the build configuration
final Map<String, String> specifiedRunTimeDefaultValues;
final Map<String, String> buildTimeRunTimeVisibleValues;
Expand Down Expand Up @@ -324,6 +330,7 @@ public static final class GenerateOperation implements AutoCloseable {
this.devMode = builder.launchMode == LaunchMode.DEVELOPMENT;
final BuildTimeConfigurationReader.ReadResult buildTimeReadResult = builder.buildTimeReadResult;
buildTimeConfigResult = Assert.checkNotNullParam("buildTimeReadResult", buildTimeReadResult);
allBuildTimeValues = Assert.checkNotNullParam("allBuildTimeValues", buildTimeReadResult.getAllBuildTimeValues());
specifiedRunTimeDefaultValues = Assert.checkNotNullParam("specifiedRunTimeDefaultValues",
buildTimeReadResult.getSpecifiedRunTimeDefaultValues());
buildTimeRunTimeVisibleValues = Assert.checkNotNullParam("buildTimeRunTimeVisibleValues",
Expand Down Expand Up @@ -359,11 +366,11 @@ public static final class GenerateOperation implements AutoCloseable {
clinit = cc.getMethodCreator(MethodDescriptor.ofMethod(CONFIG_CLASS_NAME, "<clinit>", void.class));
clinit.setModifiers(Opcodes.ACC_STATIC);

cc.getFieldCreator(C_UNUSED).setModifiers(Opcodes.ACC_STATIC | Opcodes.ACC_FINAL);
clinit.writeStaticField(C_UNUSED, clinit.newInstance(AL_NEW));
cc.getFieldCreator(C_UNKNOWN).setModifiers(Opcodes.ACC_STATIC | Opcodes.ACC_FINAL);
clinit.writeStaticField(C_UNKNOWN, clinit.newInstance(AL_NEW));

cc.getFieldCreator(C_UNUSED_RUNTIME).setModifiers(Opcodes.ACC_STATIC | Opcodes.ACC_FINAL);
clinit.writeStaticField(C_UNUSED_RUNTIME, clinit.newInstance(AL_NEW));
cc.getFieldCreator(C_UNKNOWN_RUNTIME).setModifiers(Opcodes.ACC_STATIC | Opcodes.ACC_FINAL);
clinit.writeStaticField(C_UNKNOWN_RUNTIME, clinit.newInstance(AL_NEW));

clinit.invokeStaticMethod(PM_SET_RUNTIME_DEFAULT_PROFILE, clinit.load(ProfileManager.getActiveProfile()));
clinitNameBuilder = clinit.newInstance(SB_NEW);
Expand Down Expand Up @@ -440,6 +447,8 @@ public static final class GenerateOperation implements AutoCloseable {
// block for converter setup
converterSetup = clinit.createScope();

reportUnknown(cc.getMethodCreator(C_REPORT_UNKNOWN));

// create readBootstrapConfig method - this will always exist whether or not it contains a method body
// the method body will be empty when there are no bootstrap configuration roots
readBootstrapConfig = cc.getMethodCreator(C_BOOTSTRAP_CONFIG);
Expand Down Expand Up @@ -809,17 +818,15 @@ public void run() {
// generate sweep for clinit
configSweepLoop(siParserBody, clinit, clinitConfig);

clinit.invokeStaticMethod(CD_UNKNOWN_PROPERTIES,
clinit.readStaticField(FieldDescriptor.of(cc.getClassName(), "unused", List.class)));
clinit.invokeStaticMethod(CD_UNKNOWN_PROPERTIES, clinit.readStaticField(C_UNKNOWN));

if (devMode) {
configSweepLoop(siParserBody, readConfig, runTimeConfig);
}
// generate sweep for run time
configSweepLoop(rtParserBody, readConfig, runTimeConfig);

readConfig.invokeStaticMethod(CD_UNKNOWN_PROPERTIES_RT,
readConfig.readStaticField(FieldDescriptor.of(cc.getClassName(), "unusedRuntime", List.class)));
readConfig.invokeStaticMethod(CD_UNKNOWN_PROPERTIES_RT, readConfig.readStaticField(C_UNKNOWN_RUNTIME));

if (bootstrapConfigSetupNeeded()) {
// generate sweep for bootstrap config
Expand Down Expand Up @@ -1657,16 +1664,56 @@ private FieldDescriptor getOrCreateConverterInstance(Field field, ConverterType
return fd;
}

private void reportUnknown(BytecodeCreator methodCreator, ResultHandle unusedProperty) {
ResultHandle unused = methodCreator.readStaticField(C_UNUSED);
methodCreator.invokeVirtualMethod(AL_ADD, unused,
methodCreator.invokeVirtualMethod(NI_GET_NAME, unusedProperty));
private void reportUnknown(final MethodCreator mc) {
mc.setModifiers(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC);

ResultHandle unknownProperty = mc.getMethodParam(0);

// Ignore all build property names. This is to ignore any properties mapped with @ConfigMapping, because
// these do not fall into the ignore ConfigPattern.
for (String buildTimeProperty : allBuildTimeValues.keySet()) {
ResultHandle equalsResult = mc.invokeVirtualMethod(
MethodDescriptor.ofMethod(Object.class, "equals", boolean.class, Object.class), unknownProperty,
mc.load(buildTimeProperty));
mc.ifTrue(equalsResult).trueBranch().returnValue(null);
}

ResultHandle unknown = mc.getMethodParam(1);

// Ignore recorded properties. If properties are present here, it means that they were recorded at build
// time as being mapped in a @ConfigMapping
ResultHandle buildTimeRunTimeSource = mc.readStaticField(C_BUILD_TIME_CONFIG_SOURCE);
ResultHandle buildTimeRunTimeValue = mc.invokeInterfaceMethod(CS_GET_VALUE, buildTimeRunTimeSource,
unknownProperty);
mc.ifNotNull(buildTimeRunTimeValue).trueBranch().returnValue(null);

ResultHandle buildTimeRunTimeDefaultsSource = mc.readStaticField(C_BUILD_TIME_RUN_TIME_DEFAULTS_CONFIG_SOURCE);
ResultHandle buildTimeRunTimeDefaultValue = mc.invokeInterfaceMethod(CS_GET_VALUE, buildTimeRunTimeDefaultsSource,
unknownProperty);
mc.ifNotNull(buildTimeRunTimeDefaultValue).trueBranch().returnValue(null);

ResultHandle runtimeSpecifiedSource = mc.readStaticField(C_SPECIFIED_RUN_TIME_CONFIG_SOURCE);
ResultHandle runtimeSpecifiedValue = mc.invokeInterfaceMethod(CS_GET_VALUE, runtimeSpecifiedSource,
unknownProperty);
mc.ifNotNull(runtimeSpecifiedValue).trueBranch().returnValue(null);

// Add the property as unknown only if all checks fail
mc.invokeVirtualMethod(AL_ADD, unknown, unknownProperty);

mc.returnValue(null);
mc.close();
}

private void reportUnknown(BytecodeCreator bc, ResultHandle unknownProperty) {
ResultHandle property = bc.invokeVirtualMethod(NI_GET_NAME, unknownProperty);
ResultHandle unknown = bc.readStaticField(C_UNKNOWN);
bc.invokeStaticMethod(C_REPORT_UNKNOWN, property, unknown);
}

private void reportUnknownRuntime(BytecodeCreator methodCreator, ResultHandle unusedProperty) {
ResultHandle unused = methodCreator.readStaticField(C_UNUSED_RUNTIME);
methodCreator.invokeVirtualMethod(AL_ADD, unused,
methodCreator.invokeVirtualMethod(NI_GET_NAME, unusedProperty));
private void reportUnknownRuntime(BytecodeCreator bc, ResultHandle unknownProperty) {
ResultHandle property = bc.invokeVirtualMethod(NI_GET_NAME, unknownProperty);
ResultHandle unknown = bc.readStaticField(C_UNKNOWN_RUNTIME);
bc.invokeStaticMethod(C_REPORT_UNKNOWN, property, unknown);
}

public void close() {
Expand Down
Loading

0 comments on commit 4934c1d

Please sign in to comment.