Skip to content

Commit

Permalink
Support ConfigMappings in Extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
radcortez committed Jan 12, 2022
1 parent 34374c8 commit be5a242
Show file tree
Hide file tree
Showing 21 changed files with 843 additions and 284 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,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 @@ -165,6 +166,9 @@ public static Consumer<BuildChainBuilder> loadStepsFrom(ClassLoader classLoader,
builder.withSources(ds1, ds2, platformConfigSource, pcs);
}

for (ConfigClassWithPrefix mapping : reader.getBuildTimeVisibleMappings()) {
builder.withMapping(mapping.getKlass(), mapping.getPrefix());
}
final SmallRyeConfig src = builder.build();

// install globally
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,16 @@
* example config files and docs
*/
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;
private final String valueTypeName;
private final List<String> allowedValues;
private final ConfigPhase configPhase;

public ConfigDescriptionBuildItem(String propertyName, Class<?> type, String defaultValue, String docs,
String valueTypeName, List<String> allowedValues, ConfigPhase configPhase) {
public ConfigDescriptionBuildItem(String propertyName, String defaultValue, String docs, String valueTypeName,
List<String> allowedValues, ConfigPhase configPhase) {
this.propertyName = propertyName;
this.type = type;
this.defaultValue = defaultValue;
this.docs = docs;
this.valueTypeName = valueTypeName;
Expand All @@ -34,10 +31,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 @@ -21,6 +21,7 @@
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
import io.quarkus.deployment.builditem.ConfigClassBuildItem;
import io.quarkus.deployment.builditem.ConfigClassBuildItem.Kind;
import io.quarkus.deployment.builditem.GeneratedClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.smallrye.config.ConfigMapping;
Expand Down Expand Up @@ -50,42 +51,69 @@ public static void generateConfigClasses(

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

public static void processExtensionConfigMapping(
Class<?> configClass,
String prefix,
CombinedIndexBuildItem combinedIndex,
BuildProducer<GeneratedClassBuildItem> generatedClasses,
BuildProducer<ReflectiveClassBuildItem> reflectiveClasses,
BuildProducer<ConfigClassBuildItem> configClasses) {

processConfigClass(configClass, Kind.MAPPING, prefix, false, combinedIndex, 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)
.methods(true).build());

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

generatedClassesNames.add(mappingMetadata.getClassName());

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

// For implicit converters
for (ClassInfo classInfo : mappingsInfo) {
for (MethodInfo method : classInfo.methods()) {
reflectiveClasses.produce(new ReflectiveClassBuildItem(true, false, method.returnType().name().toString()));
}
static void processConfigClass(
Class<?> configClass,
Kind configClassKind,
String prefix,
boolean isApplicationClass,
CombinedIndexBuildItem combinedIndex,
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)
.methods(true).build());

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

generatedClassesNames.add(mappingMetadata.getClassName());

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

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

configClasses.produce(new ConfigClassBuildItem(configClass, collectTypes(combinedIndex, configClass),
generatedClassesNames, prefix, configClassKind));
}

private static Class<?> toClass(DotName dotName) {
Expand All @@ -97,11 +125,11 @@ private static Class<?> toClass(DotName dotName) {
}
}

private static ConfigClassBuildItem.Kind getConfigClassType(AnnotationInstance instance) {
private static Kind getConfigClassType(AnnotationInstance instance) {
if (instance.name().equals(CONFIG_MAPPING_NAME)) {
return ConfigClassBuildItem.Kind.MAPPING;
return Kind.MAPPING;
} else {
return ConfigClassBuildItem.Kind.PROPERTIES;
return Kind.PROPERTIES;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,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 @@ -189,6 +191,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 @@ -293,6 +298,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 @@ -335,6 +341,7 @@ public static final class GenerateOperation implements AutoCloseable {
this.liveReloadPossible = builder.liveReloadPossible;
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 @@ -372,11 +379,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 @@ -460,6 +467,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 @@ -835,17 +844,15 @@ public void run() {
// generate sweep for clinit
configSweepLoop(siParserBody, clinit, clinitConfig, getRegisteredRoots(BUILD_AND_RUN_TIME_FIXED));

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

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

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 @@ -1693,16 +1700,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 be5a242

Please sign in to comment.