diff --git a/core/deployment/src/main/java/io/quarkus/deployment/BooleanSupplierFactoryBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/BooleanSupplierFactoryBuildItem.java index 583c808a98146..c46014862e14d 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/BooleanSupplierFactoryBuildItem.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/BooleanSupplierFactoryBuildItem.java @@ -49,7 +49,7 @@ protected BooleanSupplier computeValue(Class type) { final ConfigRoot annotation = parameterClass.getAnnotation(ConfigRoot.class); final ConfigPhase phase = annotation.phase(); if (phase.isAvailableAtBuild()) { - paramSuppList.add(() -> readResult.requireRootObjectForClass(parameterClass)); + paramSuppList.add(() -> readResult.requireObjectForClass(parameterClass)); } else if (phase.isReadAtMain()) { throw reportError(parameter, phase + " configuration cannot be consumed here"); } else { @@ -82,7 +82,7 @@ protected BooleanSupplier computeValue(Class type) { final ConfigPhase phase = annotation.phase(); if (phase.isAvailableAtBuild()) { setup = setup.andThen(o -> ReflectUtil.setFieldVal(field, o, - readResult.requireRootObjectForClass(fieldClass))); + readResult.requireObjectForClass(fieldClass))); } else if (phase.isReadAtMain()) { throw reportError(field, phase + " configuration cannot be consumed here"); } else { diff --git a/core/deployment/src/main/java/io/quarkus/deployment/ExtensionLoader.java b/core/deployment/src/main/java/io/quarkus/deployment/ExtensionLoader.java index c630201a2e2f4..f9837df503a4e 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/ExtensionLoader.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/ExtensionLoader.java @@ -42,6 +42,8 @@ import javax.inject.Inject; +import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.config.ConfigProvider; import org.eclipse.microprofile.config.spi.ConfigProviderResolver; import org.jboss.logging.Logger; import org.wildfly.common.function.Functions; @@ -74,6 +76,7 @@ import io.quarkus.deployment.builditem.RuntimeConfigSetupCompleteBuildItem; import io.quarkus.deployment.builditem.StaticBytecodeRecorderBuildItem; import io.quarkus.deployment.configuration.BuildTimeConfigurationReader; +import io.quarkus.deployment.configuration.ConfigMappingUtils; import io.quarkus.deployment.configuration.DefaultValuesConfigurationSource; import io.quarkus.deployment.configuration.definition.RootDefinition; import io.quarkus.deployment.recording.BytecodeRecorderImpl; @@ -84,6 +87,7 @@ import io.quarkus.dev.spi.DevModeType; import io.quarkus.gizmo.BytecodeCreator; import io.quarkus.gizmo.FieldDescriptor; +import io.quarkus.gizmo.MethodDescriptor; import io.quarkus.gizmo.ResultHandle; import io.quarkus.runtime.LaunchMode; import io.quarkus.runtime.RuntimeValue; @@ -92,6 +96,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; @@ -165,6 +170,9 @@ public static Consumer 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 @@ -196,24 +204,60 @@ public static Consumer loadStepsFrom(ClassLoader classLoader, } // this has to be an identity hash map else the recorder will get angry - Map proxyFields = new IdentityHashMap<>(); + Map rootFields = new IdentityHashMap<>(); + Map mappingClasses = new IdentityHashMap<>(); for (Map.Entry, Object> entry : proxies.entrySet()) { - final RootDefinition def = readResult.requireRootDefinitionForClass(entry.getKey()); - proxyFields.put(entry.getValue(), def.getDescriptor()); + // ConfigRoot + RootDefinition root = readResult.getAllRootsByClass().get(entry.getKey()); + if (root != null) { + rootFields.put(entry.getValue(), root.getDescriptor()); + continue; + } + + // ConfigMapping + ConfigClassWithPrefix mapping = readResult.getAllMappings().get(entry.getKey()); + if (mapping != null) { + mappingClasses.put(entry.getValue(), mapping); + continue; + } + + throw new IllegalStateException("No config found for " + entry.getKey()); } result = result.andThen(bcb -> bcb.addBuildStep(bc -> { bc.produce(new ConfigurationBuildItem(readResult)); bc.produce(new RunTimeConfigurationProxyBuildItem(proxies)); - final ObjectLoader loader = new ObjectLoader() { + + ObjectLoader rootLoader = new ObjectLoader() { public ResultHandle load(final BytecodeCreator body, final Object obj, final boolean staticInit) { - return body.readStaticField(proxyFields.get(obj)); + return body.readStaticField(rootFields.get(obj)); } public boolean canHandleObject(final Object obj, final boolean staticInit) { - return proxyFields.containsKey(obj); + return rootFields.containsKey(obj); } }; - bc.produce(new BytecodeRecorderObjectLoaderBuildItem(loader)); + + ObjectLoader mappingLoader = new ObjectLoader() { + @Override + public ResultHandle load(final BytecodeCreator body, final Object obj, final boolean staticInit) { + ConfigClassWithPrefix mapping = mappingClasses.get(obj); + MethodDescriptor getConfig = MethodDescriptor.ofMethod(ConfigProvider.class, "getConfig", Config.class); + ResultHandle config = body.invokeStaticMethod(getConfig); + MethodDescriptor getMapping = MethodDescriptor.ofMethod(SmallRyeConfig.class, "getConfigMapping", + Object.class, Class.class, String.class); + return body.invokeVirtualMethod(getMapping, config, body.loadClass(mapping.getKlass()), + body.load(mapping.getPrefix())); + } + + @Override + public boolean canHandleObject(final Object obj, final boolean staticInit) { + return mappingClasses.containsKey(obj); + } + }; + + bc.produce(new BytecodeRecorderObjectLoaderBuildItem(rootLoader)); + bc.produce(new BytecodeRecorderObjectLoaderBuildItem(mappingLoader)); + }).produces(ConfigurationBuildItem.class) .produces(RunTimeConfigurationProxyBuildItem.class) .produces(BytecodeRecorderObjectLoaderBuildItem.class) @@ -337,9 +381,9 @@ private static Consumer loadStepsFromClass(Class clazz, if (phase.isAvailableAtBuild()) { ctorParamFns.add(bc -> bc.consume(ConfigurationBuildItem.class).getReadResult() - .requireRootObjectForClass(parameterClass)); + .requireObjectForClass(parameterClass)); if (phase == ConfigPhase.BUILD_AND_RUN_TIME_FIXED) { - runTimeProxies.computeIfAbsent(parameterClass, readResult::requireRootObjectForClass); + runTimeProxies.computeIfAbsent(parameterClass, readResult::requireObjectForClass); } } else if (phase.isReadAtMain()) { throw reportError(parameter, phase + " configuration cannot be consumed here"); @@ -453,10 +497,10 @@ private static Consumer loadStepsFromClass(Class clazz, final ConfigurationBuildItem configurationBuildItem = bc .consume(ConfigurationBuildItem.class); ReflectUtil.setFieldVal(field, o, - configurationBuildItem.getReadResult().requireRootObjectForClass(fieldClass)); + configurationBuildItem.getReadResult().requireObjectForClass(fieldClass)); }); if (phase == ConfigPhase.BUILD_AND_RUN_TIME_FIXED) { - runTimeProxies.computeIfAbsent(fieldClass, readResult::requireRootObjectForClass); + runTimeProxies.computeIfAbsent(fieldClass, readResult::requireObjectForClass); } } else if (phase.isReadAtMain()) { throw reportError(field, phase + " configuration cannot be consumed here"); @@ -618,10 +662,10 @@ private static Consumer loadStepsFromClass(Class clazz, methodParamFns.add((bc, bri) -> { final ConfigurationBuildItem configurationBuildItem = bc .consume(ConfigurationBuildItem.class); - return configurationBuildItem.getReadResult().requireRootObjectForClass(parameterClass); + return configurationBuildItem.getReadResult().requireObjectForClass(parameterClass); }); if (isRecorder && phase == ConfigPhase.BUILD_AND_RUN_TIME_FIXED) { - runTimeProxies.computeIfAbsent(parameterClass, readResult::requireRootObjectForClass); + runTimeProxies.computeIfAbsent(parameterClass, readResult::requireObjectForClass); } } else if (phase.isReadAtMain()) { if (isRecorder) { @@ -630,7 +674,7 @@ private static Consumer loadStepsFromClass(Class clazz, .consume(RunTimeConfigurationProxyBuildItem.class); return proxies.getProxyObjectFor(parameterClass); }); - runTimeProxies.computeIfAbsent(parameterClass, ReflectUtil::newInstance); + runTimeProxies.computeIfAbsent(parameterClass, ConfigMappingUtils::newInstance); } else { throw reportError(parameter, phase + " configuration cannot be consumed here unless the method is a @Recorder"); @@ -673,10 +717,9 @@ private static Consumer loadStepsFromClass(Class clazz, methodConsumingConfigPhases.add(annotation.phase()); } if (annotation.phase().isReadAtMain()) { - runTimeProxies.computeIfAbsent(theType, ReflectUtil::newInstance); + runTimeProxies.computeIfAbsent(theType, ConfigMappingUtils::newInstance); } else { - runTimeProxies.computeIfAbsent(theType, - readResult::requireRootObjectForClass); + runTimeProxies.computeIfAbsent(theType, readResult::requireObjectForClass); } } } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/ConfigDescriptionBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/ConfigDescriptionBuildItem.java index ad2059b87c604..0f4a85265d051 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/builditem/ConfigDescriptionBuildItem.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/ConfigDescriptionBuildItem.java @@ -10,19 +10,16 @@ * example config files and docs */ public final class ConfigDescriptionBuildItem extends MultiBuildItem implements Comparable { - private final String propertyName; - private final Class type; private final String defaultValue; private final String docs; private final String valueTypeName; private final List allowedValues; private final ConfigPhase configPhase; - public ConfigDescriptionBuildItem(String propertyName, Class type, String defaultValue, String docs, - String valueTypeName, List allowedValues, ConfigPhase configPhase) { + public ConfigDescriptionBuildItem(String propertyName, String defaultValue, String docs, String valueTypeName, + List allowedValues, ConfigPhase configPhase) { this.propertyName = propertyName; - this.type = type; this.defaultValue = defaultValue; this.docs = docs; this.valueTypeName = valueTypeName; @@ -34,10 +31,6 @@ public String getPropertyName() { return propertyName; } - public Class getType() { - return type; - } - public String getDefaultValue() { return defaultValue; } 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 31d9450b14845..e26f5b5ff6d47 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 @@ -6,6 +6,7 @@ import static io.quarkus.deployment.util.ReflectUtil.toError; import static io.quarkus.deployment.util.ReflectUtil.typeOfParameter; import static io.quarkus.deployment.util.ReflectUtil.unwrapInvocationTargetException; +import static io.smallrye.config.ConfigMappings.ConfigClassWithPrefix.configClassWithPrefix; import static io.smallrye.config.Expressions.withoutExpansion; import static java.util.stream.Collectors.toSet; @@ -59,6 +60,9 @@ import io.quarkus.runtime.configuration.NameIterator; import io.quarkus.runtime.configuration.ProfileManager; import io.quarkus.runtime.configuration.PropertiesUtil; +import io.smallrye.config.ConfigMapping; +import io.smallrye.config.ConfigMappings; +import io.smallrye.config.ConfigMappings.ConfigClassWithPrefix; import io.smallrye.config.ConfigValue; import io.smallrye.config.Converters; import io.smallrye.config.EnvConfigSource; @@ -74,14 +78,19 @@ public final class BuildTimeConfigurationReader { final ConfigPatternMap buildTimePatternMap; final ConfigPatternMap buildTimeRunTimePatternMap; - final ConfigPatternMap bootstrapPatternMap; final ConfigPatternMap runTimePatternMap; + final ConfigPatternMap bootstrapPatternMap; - final List buildTimeVisibleRoots; final List allRoots; + final List buildTimeVisibleRoots; final boolean bootstrapRootsEmpty; + final List buildTimeMappings; + final List buildTimeRunTimeMappings; + final List runTimeMappings; + final List buildTimeVisibleMappings; + /** * Construct a new instance. * @@ -90,12 +99,47 @@ public final class BuildTimeConfigurationReader { public BuildTimeConfigurationReader(final List> configRoots) { Assert.checkNotNullParam("configRoots", configRoots); - List bootstrapRoots = new ArrayList<>(); - List runTimeRoots = new ArrayList<>(); - List buildTimeRunTimeRoots = new ArrayList<>(); List buildTimeRoots = new ArrayList<>(); + List buildTimeRunTimeRoots = new ArrayList<>(); + List runTimeRoots = new ArrayList<>(); + List bootstrapRoots = new ArrayList<>(); + + buildTimeMappings = new ArrayList<>(); + buildTimeRunTimeMappings = new ArrayList<>(); + runTimeMappings = new ArrayList<>(); + Map, GroupDefinition> groups = new HashMap<>(); for (Class configRoot : configRoots) { + boolean isMapping = configRoot.isAnnotationPresent(ConfigMapping.class); + if (isMapping) { + ConfigPhase phase = ConfigPhase.BUILD_TIME; + // To retrieve config phase + ConfigRoot annotation = configRoot.getAnnotation(ConfigRoot.class); + if (annotation != null) { + if (!annotation.prefix().equals("quarkus")) { + throw reportError(configRoot, + "@ConfigRoot.prefix() is not allowed in combination with @ConfigMapping, please use @ConfigMapping.prefix()"); + } + + if (!annotation.name().equals(ConfigItem.HYPHENATED_ELEMENT_NAME)) { + throw reportError(configRoot, "@ConfigRoot.name() is not allowed in combination with @ConfigMapping"); + } + + phase = annotation.phase(); + } + + ConfigClassWithPrefix mapping = configClassWithPrefix(configRoot); + if (phase.equals(ConfigPhase.BUILD_TIME)) { + buildTimeMappings.add(mapping); + } else if (phase.equals(ConfigPhase.BUILD_AND_RUN_TIME_FIXED)) { + buildTimeRunTimeMappings.add(mapping); + } else if (phase.equals(ConfigPhase.RUN_TIME)) { + runTimeMappings.add(mapping); + } + + continue; + } + String prefix = "quarkus"; String name = ConfigItem.HYPHENATED_ELEMENT_NAME; ConfigPhase phase = ConfigPhase.BUILD_TIME; @@ -123,10 +167,11 @@ public BuildTimeConfigurationReader(final List> configRoots) { } } - bootstrapPatternMap = PatternMapBuilder.makePatterns(bootstrapRoots); - runTimePatternMap = PatternMapBuilder.makePatterns(runTimeRoots); - buildTimeRunTimePatternMap = PatternMapBuilder.makePatterns(buildTimeRunTimeRoots); + // ConfigRoots buildTimePatternMap = PatternMapBuilder.makePatterns(buildTimeRoots); + buildTimeRunTimePatternMap = PatternMapBuilder.makePatterns(buildTimeRunTimeRoots); + runTimePatternMap = PatternMapBuilder.makePatterns(runTimeRoots); + bootstrapPatternMap = PatternMapBuilder.makePatterns(bootstrapRoots); buildTimeVisibleRoots = new ArrayList<>(buildTimeRoots.size() + buildTimeRunTimeRoots.size()); buildTimeVisibleRoots.addAll(buildTimeRoots); @@ -134,13 +179,15 @@ public BuildTimeConfigurationReader(final List> configRoots) { bootstrapRootsEmpty = bootstrapRoots.isEmpty(); - List allRoots = new ArrayList<>( - buildTimeVisibleRoots.size() + bootstrapRoots.size() + runTimeRoots.size()); + allRoots = new ArrayList<>(buildTimeVisibleRoots.size() + bootstrapRoots.size() + runTimeRoots.size()); allRoots.addAll(buildTimeVisibleRoots); allRoots.addAll(bootstrapRoots); allRoots.addAll(runTimeRoots); - this.allRoots = allRoots; + // ConfigMappings + buildTimeVisibleMappings = new ArrayList<>(buildTimeMappings.size() + buildTimeRunTimeMappings.size()); + buildTimeVisibleMappings.addAll(buildTimeMappings); + buildTimeVisibleMappings.addAll(buildTimeRunTimeMappings); } private static void processClass(ClassDefinition.Builder builder, Class clazz, @@ -259,6 +306,18 @@ public List getAllRoots() { return allRoots; } + public List getBuildTimeMappings() { + return buildTimeMappings; + } + + public List getBuildTimeRunTimeMappings() { + return buildTimeRunTimeMappings; + } + + public List getBuildTimeVisibleMappings() { + return buildTimeVisibleMappings; + } + public ReadResult readConfiguration(final SmallRyeConfig config) { return new ReadOperation(config).run(); } @@ -267,7 +326,7 @@ final class ReadOperation { final SmallRyeConfig config; final Set processedNames = new HashSet<>(); - final Map, Object> objectsByRootClass = new HashMap<>(); + final Map, Object> objectsByClass = new HashMap<>(); final Map specifiedRunTimeDefaultValues = new TreeMap<>(); final Map buildTimeRunTimeVisibleValues = new TreeMap<>(); final Map allBuildTimeValues = new TreeMap<>(); @@ -297,7 +356,7 @@ ReadResult run() { } catch (NoSuchMethodException e) { throw toError(e); } - objectsByRootClass.put(clazz, instance); + objectsByClass.put(clazz, instance); nameBuilder.append(root.getName()); readConfigGroup(root, instance, nameBuilder); nameBuilder.setLength(0); @@ -306,7 +365,8 @@ ReadResult run() { Set registeredRoots = allRoots.stream().map(RootDefinition::getPrefix).collect(toSet()); // sweep-up SmallRyeConfig runtimeDefaultsConfig = getConfigForRuntimeDefaults(); - for (String propertyName : getAllProperties(registeredRoots)) { + final Set allProperties = getAllProperties(registeredRoots); + for (String propertyName : allProperties) { if (propertyName.equals(ConfigSource.CONFIG_ORDINAL)) { continue; } @@ -409,13 +469,59 @@ ReadResult run() { } } - filterActiveProfileProperties(buildTimeRunTimeVisibleValues); - filterActiveProfileProperties(specifiedRunTimeDefaultValues); + // ConfigMappings + for (ConfigClassWithPrefix mapping : buildTimeVisibleMappings) { + objectsByClass.put(mapping.getKlass(), config.getConfigMapping(mapping.getKlass(), mapping.getPrefix())); + } + + // Build Time Values Recording + for (ConfigClassWithPrefix mapping : buildTimeMappings) { + Set mappedProperties = ConfigMappings.mappedProperties(mapping, allProperties); + for (String property : mappedProperties) { + ConfigValue value = config.getConfigValue(property); + if (value != null && value.getRawValue() != null) { + allBuildTimeValues.put(property, value.getRawValue()); + } + } + } + + // Build Time and Run Time Values Recording + for (ConfigClassWithPrefix mapping : buildTimeRunTimeMappings) { + Set mappedProperties = ConfigMappings.mappedProperties(mapping, allProperties); + for (String property : mappedProperties) { + ConfigValue value = config.getConfigValue(property); + if (value != null && value.getRawValue() != null) { + allBuildTimeValues.put(property, value.getRawValue()); + buildTimeRunTimeVisibleValues.put(property, value.getRawValue()); + } + } + } + + // Run Time Values Recording + for (ConfigClassWithPrefix mapping : runTimeMappings) { + Set mappedProperties = ConfigMappings.mappedProperties(mapping, allProperties); + for (String property : mappedProperties) { + ConfigValue value = config.getConfigValue(property); + if (value != null && value.getRawValue() != null) { + specifiedRunTimeDefaultValues.put(property, value.getRawValue()); + } + } + } - return new ReadResult(objectsByRootClass, specifiedRunTimeDefaultValues, buildTimeRunTimeVisibleValues, - allBuildTimeValues, - buildTimePatternMap, buildTimeRunTimePatternMap, bootstrapPatternMap, runTimePatternMap, allRoots, - bootstrapRootsEmpty); + return new ReadResult.Builder().setObjectsByClass(objectsByClass) + .setAllBuildTimeValues(allBuildTimeValues) + .setBuildTimeRunTimeVisibleValues(filterActiveProfileProperties(buildTimeRunTimeVisibleValues)) + .setSpecifiedRunTimeDefaultValues(filterActiveProfileProperties(specifiedRunTimeDefaultValues)) + .setBuildTimePatternMap(buildTimePatternMap) + .setBuildTimeRunTimePatternMap(buildTimeRunTimePatternMap) + .setBootstrapPatternMap(bootstrapPatternMap) + .setRunTimePatternMap(runTimePatternMap) + .setAllRoots(allRoots) + .setBootstrapRootsEmpty(bootstrapRootsEmpty) + .setBuildTimeMappings(buildTimeMappings) + .setBuildTimeRunTimeMappings(buildTimeRunTimeMappings) + .setRunTimeMappings(runTimeMappings) + .createReadResult(); } /** @@ -431,7 +537,7 @@ private Object getGroup(FieldContainer matched, NameIterator ni) { Class configurationClass = definition.getConfigurationClass(); if (definition instanceof RootDefinition) { // found the root - return objectsByRootClass.get(configurationClass); + return objectsByClass.get(configurationClass); } Container parent = matched.getParent(); final boolean consume = !classMember.getPropertyName().isEmpty(); @@ -799,7 +905,7 @@ private SmallRyeConfig getConfigForRuntimeDefaults() { return builder.build(); } - private void filterActiveProfileProperties(final Map properties) { + private Map filterActiveProfileProperties(final Map properties) { Set propertiesToRemove = new HashSet<>(); for (String property : properties.keySet()) { String profiledProperty = "%" + ProfileManager.getActiveProfile() + "." + property; @@ -808,67 +914,93 @@ private void filterActiveProfileProperties(final Map properties) } } properties.keySet().removeAll(propertiesToRemove); + return properties; } } public static final class ReadResult { - final Map, Object> objectsByRootClass; - final Map specifiedRunTimeDefaultValues; - final Map buildTimeRunTimeVisibleValues; + final Map, Object> objectsByClass; + final Map allBuildTimeValues; + final Map buildTimeRunTimeVisibleValues; + final Map specifiedRunTimeDefaultValues; + final ConfigPatternMap buildTimePatternMap; final ConfigPatternMap buildTimeRunTimePatternMap; final ConfigPatternMap bootstrapPatternMap; final ConfigPatternMap runTimePatternMap; - final Map, RootDefinition> runTimeRootsByClass; - final List allRoots; + final boolean bootstrapRootsEmpty; - ReadResult(final Map, Object> objectsByRootClass, final Map specifiedRunTimeDefaultValues, - final Map buildTimeRunTimeVisibleValues, - Map allBuildTimeValues, - final ConfigPatternMap buildTimePatternMap, - final ConfigPatternMap buildTimeRunTimePatternMap, - final ConfigPatternMap bootstrapPatternMap, - final ConfigPatternMap runTimePatternMap, final List allRoots, - boolean bootstrapRootsEmpty) { - this.objectsByRootClass = objectsByRootClass; - this.specifiedRunTimeDefaultValues = specifiedRunTimeDefaultValues; - this.buildTimeRunTimeVisibleValues = buildTimeRunTimeVisibleValues; - this.buildTimePatternMap = buildTimePatternMap; - this.buildTimeRunTimePatternMap = buildTimeRunTimePatternMap; - this.bootstrapPatternMap = bootstrapPatternMap; - this.runTimePatternMap = runTimePatternMap; - this.allRoots = allRoots; - this.bootstrapRootsEmpty = bootstrapRootsEmpty; - this.allBuildTimeValues = allBuildTimeValues; + final List allRoots; + final Map, RootDefinition> allRootsByClass; + + final List buildTimeMappings; + final List buildTimeRunTimeMappings; + final List runTimeMappings; + final Map, ConfigClassWithPrefix> allMappings; + + public ReadResult(final Builder builder) { + this.objectsByClass = builder.getObjectsByClass(); + + this.allBuildTimeValues = builder.getAllBuildTimeValues(); + this.buildTimeRunTimeVisibleValues = builder.getBuildTimeRunTimeVisibleValues(); + this.specifiedRunTimeDefaultValues = builder.getSpecifiedRunTimeDefaultValues(); + + this.buildTimePatternMap = builder.getBuildTimePatternMap(); + this.buildTimeRunTimePatternMap = builder.getBuildTimeRunTimePatternMap(); + this.bootstrapPatternMap = builder.getBootstrapPatternMap(); + this.runTimePatternMap = builder.getRunTimePatternMap(); + + this.bootstrapRootsEmpty = builder.isBootstrapRootsEmpty(); + + this.allRoots = builder.getAllRoots(); + this.allRootsByClass = rootsToMap(builder); + + this.buildTimeMappings = builder.getBuildTimeMappings(); + this.buildTimeRunTimeMappings = builder.getBuildTimeRunTimeMappings(); + this.runTimeMappings = builder.getRunTimeMappings(); + this.allMappings = mappingsToMap(builder); + } + + private static Map, RootDefinition> rootsToMap(Builder builder) { Map, RootDefinition> map = new HashMap<>(); - for (RootDefinition root : allRoots) { + for (RootDefinition root : builder.getAllRoots()) { map.put(root.getConfigurationClass(), root); } - runTimeRootsByClass = map; + return map; } - public Map, Object> getObjectsByRootClass() { - return objectsByRootClass; + private static Map, ConfigClassWithPrefix> mappingsToMap(Builder builder) { + Map, ConfigClassWithPrefix> map = new HashMap<>(); + for (ConfigClassWithPrefix mapping : builder.getBuildTimeMappings()) { + map.put(mapping.getKlass(), mapping); + } + for (ConfigClassWithPrefix mapping : builder.getBuildTimeRunTimeMappings()) { + map.put(mapping.getKlass(), mapping); + } + for (ConfigClassWithPrefix mapping : builder.getRunTimeMappings()) { + map.put(mapping.getKlass(), mapping); + } + return map; } - public Object requireRootObjectForClass(Class clazz) { - Object obj = objectsByRootClass.get(clazz); - if (obj == null) { - throw new IllegalStateException("No root found for " + clazz); - } - return obj; + public Map, Object> getObjectsByClass() { + return objectsByClass; } - public Map getSpecifiedRunTimeDefaultValues() { - return specifiedRunTimeDefaultValues; + public Map getAllBuildTimeValues() { + return allBuildTimeValues; } public Map getBuildTimeRunTimeVisibleValues() { return buildTimeRunTimeVisibleValues; } + public Map getSpecifiedRunTimeDefaultValues() { + return specifiedRunTimeDefaultValues; + } + public ConfigPatternMap getBuildTimePatternMap() { return buildTimePatternMap; } @@ -885,24 +1017,177 @@ public ConfigPatternMap getRunTimePatternMap() { return runTimePatternMap; } - public Map getAllBuildTimeValues() { - return allBuildTimeValues; + public boolean isBootstrapRootsEmpty() { + return bootstrapRootsEmpty; } public List getAllRoots() { return allRoots; } - public boolean isBootstrapRootsEmpty() { - return bootstrapRootsEmpty; + public Map, RootDefinition> getAllRootsByClass() { + return allRootsByClass; } - public RootDefinition requireRootDefinitionForClass(Class clazz) { - final RootDefinition def = runTimeRootsByClass.get(clazz); - if (def == null) { - throw new IllegalStateException("No root definition found for " + clazz); + public List getBuildTimeMappings() { + return buildTimeMappings; + } + + public List getBuildTimeRunTimeMappings() { + return buildTimeRunTimeMappings; + } + + public List getRunTimeMappings() { + return runTimeMappings; + } + + public Map, ConfigClassWithPrefix> getAllMappings() { + return allMappings; + } + + public Object requireObjectForClass(Class clazz) { + Object obj = objectsByClass.get(clazz); + if (obj == null) { + throw new IllegalStateException("No config found for " + clazz); + } + return obj; + } + + static class Builder { + private Map, Object> objectsByClass; + private Map allBuildTimeValues; + private Map buildTimeRunTimeVisibleValues; + private Map specifiedRunTimeDefaultValues; + private ConfigPatternMap buildTimePatternMap; + private ConfigPatternMap buildTimeRunTimePatternMap; + private ConfigPatternMap bootstrapPatternMap; + private ConfigPatternMap runTimePatternMap; + private List allRoots; + private boolean bootstrapRootsEmpty; + private List buildTimeMappings; + private List buildTimeRunTimeMappings; + private List runTimeMappings; + + Map, Object> getObjectsByClass() { + return objectsByClass; + } + + Builder setObjectsByClass(final Map, Object> objectsByClass) { + this.objectsByClass = objectsByClass; + return this; + } + + Map getAllBuildTimeValues() { + return allBuildTimeValues; + } + + Builder setAllBuildTimeValues(final Map allBuildTimeValues) { + this.allBuildTimeValues = allBuildTimeValues; + return this; + } + + Map getBuildTimeRunTimeVisibleValues() { + return buildTimeRunTimeVisibleValues; + } + + Builder setBuildTimeRunTimeVisibleValues(final Map buildTimeRunTimeVisibleValues) { + this.buildTimeRunTimeVisibleValues = buildTimeRunTimeVisibleValues; + return this; + } + + Map getSpecifiedRunTimeDefaultValues() { + return specifiedRunTimeDefaultValues; + } + + Builder setSpecifiedRunTimeDefaultValues(final Map specifiedRunTimeDefaultValues) { + this.specifiedRunTimeDefaultValues = specifiedRunTimeDefaultValues; + return this; + } + + ConfigPatternMap getBuildTimePatternMap() { + return buildTimePatternMap; + } + + Builder setBuildTimePatternMap(final ConfigPatternMap buildTimePatternMap) { + this.buildTimePatternMap = buildTimePatternMap; + return this; + } + + ConfigPatternMap getBuildTimeRunTimePatternMap() { + return buildTimeRunTimePatternMap; + } + + Builder setBuildTimeRunTimePatternMap(final ConfigPatternMap buildTimeRunTimePatternMap) { + this.buildTimeRunTimePatternMap = buildTimeRunTimePatternMap; + return this; + } + + ConfigPatternMap getBootstrapPatternMap() { + return bootstrapPatternMap; + } + + Builder setBootstrapPatternMap(final ConfigPatternMap bootstrapPatternMap) { + this.bootstrapPatternMap = bootstrapPatternMap; + return this; + } + + ConfigPatternMap getRunTimePatternMap() { + return runTimePatternMap; + } + + Builder setRunTimePatternMap(final ConfigPatternMap runTimePatternMap) { + this.runTimePatternMap = runTimePatternMap; + return this; + } + + List getAllRoots() { + return allRoots; + } + + Builder setAllRoots(final List allRoots) { + this.allRoots = allRoots; + return this; + } + + boolean isBootstrapRootsEmpty() { + return bootstrapRootsEmpty; + } + + Builder setBootstrapRootsEmpty(final boolean bootstrapRootsEmpty) { + this.bootstrapRootsEmpty = bootstrapRootsEmpty; + return this; + } + + List getBuildTimeMappings() { + return buildTimeMappings; + } + + Builder setBuildTimeMappings(final List buildTimeMappings) { + this.buildTimeMappings = buildTimeMappings; + return this; + } + + List getBuildTimeRunTimeMappings() { + return buildTimeRunTimeMappings; + } + + Builder setBuildTimeRunTimeMappings(final List buildTimeRunTimeMappings) { + this.buildTimeRunTimeMappings = buildTimeRunTimeMappings; + return this; + } + + List getRunTimeMappings() { + return runTimeMappings; + } + + Builder setRunTimeMappings(final List runTimeMappings) { + this.runTimeMappings = runTimeMappings; + return this; + } + + ReadResult createReadResult() { + return new ReadResult(this); } - return def; } } } 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 9a801af4687da..bf63f67f5aca3 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 @@ -21,8 +21,10 @@ 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.quarkus.deployment.util.ReflectUtil; import io.smallrye.config.ConfigMapping; import io.smallrye.config.ConfigMappingLoader; import io.smallrye.config.ConfigMappingMetadata; @@ -50,41 +52,76 @@ 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 generatedClasses, + BuildProducer reflectiveClasses, + BuildProducer configClasses) { + + processConfigClass(configClass, Kind.MAPPING, prefix, false, combinedIndex, generatedClasses, reflectiveClasses, + configClasses); + } + + static void processConfigClass( + Class configClass, + Kind configClassKind, + String prefix, + boolean isApplicationClass, + CombinedIndexBuildItem combinedIndex, + BuildProducer generatedClasses, + BuildProducer reflectiveClasses, + BuildProducer configClasses) { + + List configMappingsMetadata = ConfigMappingLoader.getConfigMappingsMetadata(configClass); + Set generatedClassesNames = new HashSet<>(); + Set 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); + } + }); - List configMappingsMetadata = ConfigMappingLoader.getConfigMappingsMetadata(configClass); - Set generatedClassesNames = new HashSet<>(); - Set 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())); - } + // 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, getConfigClassType(instance))); + configClasses.produce(new ConfigClassBuildItem(configClass, collectTypes(combinedIndex, configClass), + generatedClassesNames, prefix, configClassKind)); + } + + public static Object newInstance(Class configClass) { + if (configClass.isAnnotationPresent(ConfigMapping.class)) { + return ReflectUtil.newInstance(ConfigMappingLoader.getImplementationClass(configClass)); + } else { + return ReflectUtil.newInstance(configClass); } } @@ -97,11 +134,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; } } 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 9afb121558e8a..4c3dbbb39bdf9 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 @@ -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); @@ -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); @@ -293,6 +298,7 @@ public static final class GenerateOperation implements AutoCloseable { final ResultHandle clinitNameBuilder; final BuildTimeConfigurationReader.ReadResult buildTimeConfigResult; final List roots; + final Map allBuildTimeValues; // default values given in the build configuration final Map specifiedRunTimeDefaultValues; final Map buildTimeRunTimeVisibleValues; @@ -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", @@ -372,11 +379,11 @@ 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_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); @@ -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); @@ -835,8 +844,7 @@ 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)); @@ -844,8 +852,7 @@ public void run() { // 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 @@ -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() { diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigDescriptionBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigDescriptionBuildStep.java index 7df55f688507f..67173dd316b95 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigDescriptionBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigDescriptionBuildStep.java @@ -1,11 +1,18 @@ package io.quarkus.deployment.steps; +import static io.quarkus.runtime.annotations.ConfigPhase.BOOTSTRAP; +import static io.quarkus.runtime.annotations.ConfigPhase.BUILD_AND_RUN_TIME_FIXED; +import static io.quarkus.runtime.annotations.ConfigPhase.BUILD_TIME; +import static io.quarkus.runtime.annotations.ConfigPhase.RUN_TIME; + import java.io.IOException; import java.io.UncheckedIOException; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.OptionalDouble; import java.util.OptionalInt; @@ -25,6 +32,11 @@ import io.quarkus.runtime.annotations.ConfigItem; import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.util.ClassPathUtils; +import io.smallrye.config.ConfigMappingInterface.LeafProperty; +import io.smallrye.config.ConfigMappingInterface.PrimitiveProperty; +import io.smallrye.config.ConfigMappingInterface.Property; +import io.smallrye.config.ConfigMappings; +import io.smallrye.config.ConfigMappings.ConfigClassWithPrefix; public class ConfigDescriptionBuildStep { @@ -40,11 +52,13 @@ List createConfigDescriptions( } }); List ret = new ArrayList<>(); - processConfig(config.getReadResult().getBuildTimePatternMap(), ret, javadoc, ConfigPhase.BUILD_TIME); - processConfig(config.getReadResult().getBuildTimeRunTimePatternMap(), ret, javadoc, - ConfigPhase.BUILD_AND_RUN_TIME_FIXED); - processConfig(config.getReadResult().getBootstrapPatternMap(), ret, javadoc, ConfigPhase.BOOTSTRAP); - processConfig(config.getReadResult().getRunTimePatternMap(), ret, javadoc, ConfigPhase.RUN_TIME); + processConfig(config.getReadResult().getBuildTimePatternMap(), ret, javadoc, BUILD_TIME); + processConfig(config.getReadResult().getBuildTimeRunTimePatternMap(), ret, javadoc, BUILD_AND_RUN_TIME_FIXED); + processConfig(config.getReadResult().getBootstrapPatternMap(), ret, javadoc, BOOTSTRAP); + processConfig(config.getReadResult().getRunTimePatternMap(), ret, javadoc, RUN_TIME); + processMappings(config.getReadResult().getBuildTimeMappings(), ret, javadoc, BUILD_TIME); + processMappings(config.getReadResult().getBuildTimeRunTimeMappings(), ret, javadoc, BUILD_AND_RUN_TIME_FIXED); + processMappings(config.getReadResult().getRunTimeMappings(), ret, javadoc, RUN_TIME); return ret; } @@ -98,7 +112,6 @@ public void accept(Container node) { } String javadocKey = field.getDeclaringClass().getName().replace('$', '.') + '.' + field.getName(); ret.add(new ConfigDescriptionBuildItem(name, - node.findEnclosingClass().getConfigurationClass(), defVal, javadoc.getProperty(javadocKey), effectiveConfigTypeAndValues.getTypeName(), @@ -109,6 +122,40 @@ public void accept(Container node) { } } + private void processMappings(List mappings, List descriptionBuildItems, + Properties javaDocProperties, ConfigPhase configPhase) { + for (ConfigClassWithPrefix mapping : mappings) { + Map properties = ConfigMappings.getProperties(mapping); + for (Map.Entry entry : properties.entrySet()) { + String propertyName = entry.getKey(); + Property property = entry.getValue(); + Method method = property.getMethod(); + + String defaultValue = null; + if (property instanceof PrimitiveProperty) { + PrimitiveProperty primitiveProperty = (PrimitiveProperty) property; + if (primitiveProperty.hasDefaultValue()) { + defaultValue = primitiveProperty.getDefaultValue(); + } else if (primitiveProperty.getPrimitiveType() == boolean.class) { + defaultValue = "false"; + } else if (primitiveProperty.getPrimitiveType() != char.class) { + defaultValue = "0"; + } + } else if (property instanceof LeafProperty) { + LeafProperty leafProperty = (LeafProperty) property; + if (leafProperty.hasDefaultValue()) { + defaultValue = leafProperty.getDefaultValue(); + } + } + + String javadocKey = method.getDeclaringClass().getName().replace('$', '.') + '.' + method.getName(); + // TODO - radcortez - Fix nulls + descriptionBuildItems.add(new ConfigDescriptionBuildItem(propertyName, defaultValue, + javaDocProperties.getProperty(javadocKey), null, null, configPhase)); + } + } + } + private EffectiveConfigTypeAndValues getTypeName(Field field) { final Class valueClass = field.getType(); return getTypeName(field, valueClass); @@ -194,7 +241,7 @@ private EffectiveConfigTypeAndValues getTypeName(Field field, Class valueClas return typeAndValues; } - class EffectiveConfigTypeAndValues { + static class EffectiveConfigTypeAndValues { private String typeName; private List allowedValues; @@ -234,5 +281,4 @@ public void addAllowedValue(String v) { allowedValues.add(v); } } - } 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 1177fb3df27ac..e4aaaf2a7b0ff 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 @@ -1,5 +1,6 @@ package io.quarkus.deployment.steps; +import static io.quarkus.deployment.configuration.ConfigMappingUtils.processExtensionConfigMapping; import static io.quarkus.deployment.steps.ConfigBuildSteps.SERVICES_PREFIX; import static io.quarkus.deployment.util.ServiceUtil.classNamesNamedIn; import static io.smallrye.config.ConfigMappings.ConfigClassWithPrefix.configClassWithPrefix; @@ -38,6 +39,8 @@ import io.quarkus.deployment.annotations.ExecutionTime; import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.builditem.AdditionalBootstrapConfigSourceProviderBuildItem; +import io.quarkus.deployment.builditem.CombinedIndexBuildItem; +import io.quarkus.deployment.builditem.ConfigClassBuildItem; import io.quarkus.deployment.builditem.ConfigMappingBuildItem; import io.quarkus.deployment.builditem.ConfigurationBuildItem; import io.quarkus.deployment.builditem.ConfigurationTypeBuildItem; @@ -98,6 +101,26 @@ GeneratedResourceBuildItem runtimeDefaultsConfig(List generatedClasses, + BuildProducer reflectiveClasses, + BuildProducer configClasses) { + + List buildTimeRunTimeMappings = configItem.getReadResult().getBuildTimeRunTimeMappings(); + for (ConfigClassWithPrefix buildTimeRunTimeMapping : buildTimeRunTimeMappings) { + processExtensionConfigMapping(buildTimeRunTimeMapping.getKlass(), buildTimeRunTimeMapping.getPrefix(), + combinedIndex, generatedClasses, reflectiveClasses, configClasses); + } + + final List runTimeMappings = configItem.getReadResult().getRunTimeMappings(); + for (ConfigClassWithPrefix runTimeMapping : runTimeMappings) { + processExtensionConfigMapping(runTimeMapping.getKlass(), runTimeMapping.getPrefix(), combinedIndex, + generatedClasses, reflectiveClasses, configClasses); + } + } + /** * Generate the Config class that instantiates MP Config and holds all the config objects */ @@ -134,6 +157,15 @@ void generateConfigClass( staticConfigSourceFactories.addAll(staticInitConfigSourceFactories.stream() .map(StaticInitConfigSourceFactoryBuildItem::getFactoryClassName).collect(Collectors.toSet())); + Set staticMappings = new HashSet<>(); + staticMappings.addAll(staticSafeConfigMappings(configMappings)); + staticMappings.addAll(configItem.getReadResult().getBuildTimeRunTimeMappings()); + + Set runtimeMappings = new HashSet<>(); + runtimeMappings.addAll(runtimeConfigMappings(configMappings)); + runtimeMappings.addAll(configItem.getReadResult().getBuildTimeRunTimeMappings()); + runtimeMappings.addAll(configItem.getReadResult().getRunTimeMappings()); + RunTimeConfigurationGenerator.GenerateOperation .builder() .setBuildTimeReadResult(configItem.getReadResult()) @@ -147,13 +179,13 @@ void generateConfigClass( .setStaticConfigSources(staticSafeServices(discoveredConfigSources)) .setStaticConfigSourceProviders(staticConfigSourceProviders) .setStaticConfigSourceFactories(staticConfigSourceFactories) - .setStaticConfigMappings(staticSafeConfigMappings(configMappings)) + .setStaticConfigMappings(staticMappings) .setStaticConfigBuilders(staticInitConfigBuilders.stream() .map(StaticInitConfigBuilderBuildItem::getBuilderClassName).collect(toSet())) .setRuntimeConfigSources(discoveredConfigSources) .setRuntimeConfigSourceProviders(discoveredConfigSourceProviders) .setRuntimeConfigSourceFactories(discoveredConfigSourceFactories) - .setRuntimeConfigMappings(runtimeConfigMappings(configMappings)) + .setRuntimeConfigMappings(runtimeMappings) .setRuntimeConfigBuilders( runTimeConfigBuilders.stream().map(RunTimeConfigBuilderBuildItem::getBuilderClassName).collect(toSet())) .build() diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/Constants.java b/core/processor/src/main/java/io/quarkus/annotation/processor/Constants.java index 526f172df3ce5..39e80c573252b 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/Constants.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/Constants.java @@ -52,6 +52,7 @@ final public class Constants { public static final String ANNOTATION_CONFIG_ITEM = "io.quarkus.runtime.annotations.ConfigItem"; public static final String ANNOTATION_BUILD_STEP = "io.quarkus.deployment.annotations.BuildStep"; public static final String ANNOTATION_CONFIG_ROOT = "io.quarkus.runtime.annotations.ConfigRoot"; + public static final String ANNOTATION_CONFIG_MAPPING = "io.smallrye.config.ConfigMapping"; public static final String ANNOTATION_DEFAULT_CONVERTER = "io.quarkus.runtime.annotations.DefaultConverter"; public static final String ANNOTATION_CONVERT_WITH = "io.quarkus.runtime.annotations.ConvertWith"; public static final String ANNOTATION_CONFIG_GROUP = "io.quarkus.runtime.annotations.ConfigGroup"; diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/ExtensionAnnotationProcessor.java b/core/processor/src/main/java/io/quarkus/annotation/processor/ExtensionAnnotationProcessor.java index 0bd0ef334f0fb..09a61691b41ab 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/ExtensionAnnotationProcessor.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/ExtensionAnnotationProcessor.java @@ -412,6 +412,37 @@ private void recordConfigJavadoc(TypeElement clazz) { default: } } + writeJavadocProperties(clazz, javadocProps); + } + + private void recordMappingJavadoc(TypeElement clazz) { + String className = clazz.getQualifiedName().toString(); + if (!generatedJavaDocs.add(className)) + return; + final Properties javadocProps = new Properties(); + recordMappingJavadoc(clazz, javadocProps); + writeJavadocProperties(clazz, javadocProps); + } + + private void recordMappingJavadoc(final TypeElement clazz, final Properties javadocProps) { + String className = clazz.getQualifiedName().toString(); + for (Element e : clazz.getEnclosedElements()) { + switch (e.getKind()) { + case INTERFACE: { + recordMappingJavadoc(((TypeElement) e), javadocProps); + break; + } + + case METHOD: { + processMethodConfigMapping((ExecutableElement) e, javadocProps, className); + break; + } + default: + } + } + } + + private void writeJavadocProperties(final TypeElement clazz, final Properties javadocProps) { if (javadocProps.isEmpty()) return; final PackageElement pkg = processingEnv.getElementUtils().getPackageOf(clazz); @@ -449,6 +480,11 @@ private void processMethodConfigItem(ExecutableElement method, Properties javado javadocProps.put(className + Constants.DOT + buf.toString(), docComment); } + private void processMethodConfigMapping(ExecutableElement method, Properties javadocProps, String className) { + final String docComment = getRequiredJavadoc(method); + javadocProps.put(className + Constants.DOT + method.getSimpleName().toString(), docComment); + } + private void processConfigGroup(RoundEnvironment roundEnv, TypeElement annotation) { final Set groupClassNames = new HashSet<>(); for (TypeElement i : typesIn(roundEnv.getElementsAnnotatedWith(annotation))) { @@ -480,8 +516,12 @@ private void processConfigRoot(RoundEnvironment roundEnv, TypeElement annotation final String binaryName = processingEnv.getElementUtils().getBinaryName(clazz).toString(); if (rootClassNames.add(binaryName)) { // new class - recordConfigJavadoc(clazz); - generateAccessor(clazz); + if (isAnnotationPresent(clazz, Constants.ANNOTATION_CONFIG_MAPPING)) { + recordMappingJavadoc(clazz); + } else if (isAnnotationPresent(clazz, Constants.ANNOTATION_CONFIG_ROOT)) { + recordConfigJavadoc(clazz); + generateAccessor(clazz); + } final StringBuilder rbn = getRelativeBinaryName(clazz, new StringBuilder()); try { final FileObject itemResource = processingEnv.getFiler().createResource( @@ -666,7 +706,8 @@ private String getRequiredJavadoc(Element e) { String docComment = processingEnv.getElementUtils().getDocComment(e); if (docComment == null) { - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Unable to find javadoc for config item " + e, e); + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, + "Unable to find javadoc for config item " + e.getEnclosingElement() + " " + e, e); return ""; } diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/ConfigDoItemFinder.java b/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/ConfigDoItemFinder.java index 97133943a7890..577e5774512cd 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/ConfigDoItemFinder.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/ConfigDoItemFinder.java @@ -40,6 +40,7 @@ import javax.lang.model.element.TypeElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.ExecutableType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; @@ -49,7 +50,7 @@ class ConfigDoItemFinder { - private static String COMMA = ","; + private static final String COMMA = ","; private static final String BACK_TICK = "`"; private static final String NAMED_MAP_CONFIG_ITEM_FORMAT = ".\"%s\""; private static final Set PRIMITIVE_TYPES = new HashSet<>( @@ -85,8 +86,7 @@ ScannedConfigDocsItemHolder findInMemoryConfigurationItems() throws IOException for (Map.Entry entry : configGroupQualifiedNameToTypeElementMap.entrySet()) { ConfigPhase buildTime = ConfigPhase.BUILD_TIME; final List configDocItems = recursivelyFindConfigItems( - entry.getValue(), EMPTY, EMPTY, buildTime, - false, 1, false); + entry.getValue(), EMPTY, EMPTY, buildTime, false, false, 1, false); allConfigurationGroups.put(entry.getKey(), OBJECT_MAPPER.writeValueAsString(configDocItems)); } @@ -95,8 +95,9 @@ ScannedConfigDocsItemHolder findInMemoryConfigurationItems() throws IOException final TypeElement element = configRootInfo.getClazz(); String rootName = configRootInfo.getName(); ConfigPhase configPhase = configRootInfo.getConfigPhase(); + boolean isMapping = configRootInfo.isMapping(); final List configDocItems = recursivelyFindConfigItems(element, rootName, rootName, configPhase, - false, sectionLevel, true); + isMapping, false, sectionLevel, true); holder.addConfigRootItems(configRootInfo, configDocItems); allConfigurationRoots.put(configRootInfo.getClazz().toString(), OBJECT_MAPPER.writeValueAsString(configDocItems)); } @@ -108,12 +109,14 @@ ScannedConfigDocsItemHolder findInMemoryConfigurationItems() throws IOException * Recursively find config item found in a config root or config group given as {@link Element} */ private List recursivelyFindConfigItems(Element element, String rootName, String parentName, - ConfigPhase configPhase, boolean withinAMap, int sectionLevel, boolean generateSeparateConfigGroupDocsFiles) + ConfigPhase configPhase, boolean isMapping, boolean withinAMap, int sectionLevel, + boolean generateSeparateConfigGroupDocsFiles) throws JsonProcessingException { List configDocItems = new ArrayList<>(); TypeElement asTypeElement = (TypeElement) element; TypeMirror superType = asTypeElement.getSuperclass(); - if (superType.getKind() != TypeKind.NONE) { + + if (superType.getKind() != TypeKind.NONE && !superType.toString().equals(Object.class.getName())) { String key = superType.toString(); String rawConfigItems = allConfigurationGroups.get(key); if (rawConfigItems == null) { @@ -123,17 +126,16 @@ private List recursivelyFindConfigItems(Element element, String r if (rawConfigItems == null) { // element not yet scanned Element superElement = ((DeclaredType) superType).asElement(); superTypeConfigItems = recursivelyFindConfigItems(superElement, rootName, parentName, - configPhase, withinAMap, sectionLevel, generateSeparateConfigGroupDocsFiles); + configPhase, isMapping, withinAMap, sectionLevel, generateSeparateConfigGroupDocsFiles); } else { superTypeConfigItems = OBJECT_MAPPER.readValue(rawConfigItems, LIST_OF_CONFIG_ITEMS_TYPE_REF); } configDocItems.addAll(superTypeConfigItems); - } for (Element enclosedElement : element.getEnclosedElements()) { - if (!enclosedElement.getKind().isField()) { + if (!enclosedElement.getKind().isField() && (!enclosedElement.getKind().equals(ElementKind.METHOD) || !isMapping)) { continue; } @@ -229,7 +231,7 @@ private List recursivelyFindConfigItems(Element element, String r if (isConfigGroup(type)) { List groupConfigItems = readConfigGroupItems(configPhase, rootName, name, type, - configSection, withinAMap, generateSeparateConfigGroupDocsFiles); + configSection, isMapping, withinAMap, generateSeparateConfigGroupDocsFiles); DocGeneratorUtil.appendConfigItemsIntoExistingOnes(configDocItems, groupConfigItems); } else { final ConfigDocKey configDocKey = new ConfigDocKey(); @@ -237,8 +239,24 @@ private List recursivelyFindConfigItems(Element element, String r boolean list = false; boolean optional = false; if (!typeMirror.getKind().isPrimitive()) { - DeclaredType declaredType = (DeclaredType) typeMirror; - TypeElement typeElement = (TypeElement) declaredType.asElement(); + TypeElement typeElement; + DeclaredType declaredType; + if (typeMirror instanceof DeclaredType) { + declaredType = (DeclaredType) typeMirror; + typeElement = (TypeElement) declaredType.asElement(); + } else if (typeMirror instanceof ExecutableType) { + ExecutableType executableType = (ExecutableType) typeMirror; + TypeMirror returnType = executableType.getReturnType(); + if (returnType instanceof DeclaredType) { + declaredType = ((DeclaredType) returnType); + typeElement = (TypeElement) declaredType.asElement(); + } else { + continue; + } + } else { + continue; + } + Name qualifiedName = typeElement.getQualifiedName(); optional = qualifiedName.toString().startsWith(Optional.class.getName()) || qualifiedName.contentEquals(Map.class.getName()); @@ -253,7 +271,7 @@ private List recursivelyFindConfigItems(Element element, String r if (isConfigGroup(type)) { name += String.format(NAMED_MAP_CONFIG_ITEM_FORMAT, configDocMapKey); List groupConfigItems = readConfigGroupItems(configPhase, rootName, name, type, - configSection, true, generateSeparateConfigGroupDocsFiles); + configSection, isMapping, true, generateSeparateConfigGroupDocsFiles); DocGeneratorUtil.appendConfigItemsIntoExistingOnes(configDocItems, groupConfigItems); continue; } else { @@ -279,14 +297,22 @@ private List recursivelyFindConfigItems(Element element, String r } configSection.setOptional(true); List groupConfigItems = readConfigGroupItems(configPhase, rootName, name, - typeInString, configSection, withinAMap, generateSeparateConfigGroupDocsFiles); + typeInString, configSection, isMapping, withinAMap, + generateSeparateConfigGroupDocsFiles); DocGeneratorUtil.appendConfigItemsIntoExistingOnes(configDocItems, groupConfigItems); continue; } else if ((typeInString.startsWith(List.class.getName()) || typeInString.startsWith(Set.class.getName()) || realTypeMirror.getKind() == TypeKind.ARRAY)) { list = true; - DeclaredType declaredRealType = (DeclaredType) typeMirror; + DeclaredType declaredRealType; + if (typeMirror instanceof DeclaredType) { + declaredRealType = (DeclaredType) typeMirror; + } else { + ExecutableType executableType = (ExecutableType) typeMirror; + TypeMirror returnType = executableType.getReturnType(); + declaredRealType = ((DeclaredType) returnType); + } typeArguments = declaredRealType.getTypeArguments(); if (!typeArguments.isEmpty()) { realTypeMirror = typeArguments.get(0); @@ -400,7 +426,7 @@ private boolean isDurationType(TypeMirror realTypeMirror) { * */ private List readConfigGroupItems(ConfigPhase configPhase, String topLevelRootName, String parentName, - String configGroup, ConfigDocSection configSection, boolean withinAMap, + String configGroup, ConfigDocSection configSection, boolean isMapping, boolean withinAMap, boolean generateSeparateConfigGroupDocs) throws JsonProcessingException { configSection.setConfigGroupType(configGroup); @@ -419,7 +445,7 @@ private List readConfigGroupItems(ConfigPhase configPhase, String groupConfigItems = OBJECT_MAPPER.readValue(property, LIST_OF_CONFIG_ITEMS_TYPE_REF); } else { TypeElement configGroupTypeElement = configGroupQualifiedNameToTypeElementMap.get(configGroup); - groupConfigItems = recursivelyFindConfigItems(configGroupTypeElement, EMPTY, EMPTY, configPhase, + groupConfigItems = recursivelyFindConfigItems(configGroupTypeElement, EMPTY, EMPTY, configPhase, isMapping, false, 1, generateSeparateConfigGroupDocs); allConfigurationGroups.put(configGroup, OBJECT_MAPPER.writeValueAsString(groupConfigItems)); } diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/ConfigDocItemScanner.java b/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/ConfigDocItemScanner.java index 86c83f3b5f519..50b08aea4ab67 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/ConfigDocItemScanner.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/ConfigDocItemScanner.java @@ -66,6 +66,7 @@ public void addConfigRoot(final PackageElement pkg, TypeElement clazz) { String prefix = Constants.QUARKUS; ConfigPhase configPhase = ConfigPhase.BUILD_TIME; + boolean isMapping = false; for (AnnotationMirror annotationMirror : clazz.getAnnotationMirrors()) { String annotationName = annotationMirror.getAnnotationType().toString(); @@ -85,6 +86,17 @@ public void addConfigRoot(final PackageElement pkg, TypeElement clazz) { } } + for (AnnotationMirror mirror : clazz.getAnnotationMirrors()) { + if (mirror.getAnnotationType().toString().equals(Constants.ANNOTATION_CONFIG_MAPPING)) { + for (Entry entry : mirror.getElementValues() + .entrySet()) { + if ("prefix()".equals(entry.getKey().toString())) { + prefix = entry.getValue().getValue().toString(); + } + } + } + } + name = getName(prefix, name, clazz.getSimpleName().toString(), configPhase); if (name.endsWith(Constants.DOT + Constants.PARENT)) { // take into account the root case which would contain characters that can't be used to create the final file @@ -99,7 +111,7 @@ public void addConfigRoot(final PackageElement pkg, TypeElement clazz) { fileName = name.replace(Constants.DOT, Constants.DASH.charAt(0)) + Constants.ADOC_EXTENSION; } - ConfigRootInfo configRootInfo = new ConfigRootInfo(name, clazz, configPhase, fileName); + ConfigRootInfo configRootInfo = new ConfigRootInfo(name, clazz, configPhase, isMapping, fileName); configRoots.add(configRootInfo); break; } diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/ConfigRootInfo.java b/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/ConfigRootInfo.java index 9f9d5759a8be6..4652502c984a0 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/ConfigRootInfo.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/ConfigRootInfo.java @@ -8,12 +8,17 @@ final public class ConfigRootInfo { private final String name; private final TypeElement clazz; private final ConfigPhase configPhase; + private final boolean mapping; private final String fileName; - public ConfigRootInfo(String name, TypeElement clazz, ConfigPhase visibility, String fileName) { + public ConfigRootInfo( + final String name, + final TypeElement clazz, + final ConfigPhase configPhase, final boolean mapping, final String fileName) { this.name = name; this.clazz = clazz; - this.configPhase = visibility; + this.configPhase = configPhase; + this.mapping = mapping; this.fileName = fileName; } @@ -22,21 +27,24 @@ public String getFileName() { } @Override - public boolean equals(Object o) { - if (this == o) + public boolean equals(final Object o) { + if (this == o) { return true; - if (o == null || getClass() != o.getClass()) + } + if (o == null || getClass() != o.getClass()) { return false; - ConfigRootInfo that = (ConfigRootInfo) o; - return Objects.equals(name, that.name) && - Objects.equals(clazz, that.clazz) && + } + final ConfigRootInfo that = (ConfigRootInfo) o; + return mapping == that.mapping && + name.equals(that.name) && + clazz.equals(that.clazz) && configPhase == that.configPhase && - Objects.equals(fileName, that.fileName); + fileName.equals(that.fileName); } @Override public int hashCode() { - return Objects.hash(name, clazz, configPhase, fileName); + return Objects.hash(name, clazz, configPhase, mapping, fileName); } @Override @@ -45,6 +53,7 @@ public String toString() { "name='" + name + '\'' + ", clazz=" + clazz + ", configPhase=" + configPhase + + ", mapping=" + mapping + ", fileName='" + fileName + '\'' + '}'; } @@ -60,4 +69,8 @@ public TypeElement getClazz() { public ConfigPhase getConfigPhase() { return configPhase; } + + public boolean isMapping() { + return mapping; + } } diff --git a/core/runtime/pom.xml b/core/runtime/pom.xml index 531af0d3e7060..907d8d0ce36ff 100644 --- a/core/runtime/pom.xml +++ b/core/runtime/pom.xml @@ -223,7 +223,6 @@ org.wildfly.common:wildfly-common io.smallrye.common:smallrye-common-io - io.smallrye:smallrye-config diff --git a/core/test-extension/deployment/src/main/java/io/quarkus/extest/deployment/TestProcessor.java b/core/test-extension/deployment/src/main/java/io/quarkus/extest/deployment/TestProcessor.java index 4d7f9c6533a62..e92e47c1533c4 100644 --- a/core/test-extension/deployment/src/main/java/io/quarkus/extest/deployment/TestProcessor.java +++ b/core/test-extension/deployment/src/main/java/io/quarkus/extest/deployment/TestProcessor.java @@ -18,6 +18,7 @@ import java.util.Arrays; import java.util.Base64; import java.util.HashSet; +import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.function.BooleanSupplier; @@ -30,9 +31,11 @@ import io.quarkus.arc.deployment.AdditionalBeanBuildItem; import io.quarkus.arc.deployment.BeanContainerBuildItem; +import io.quarkus.arc.deployment.ConfigPropertyBuildItem; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.annotations.Record; +import io.quarkus.deployment.builditem.ConfigurationBuildItem; import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.deployment.builditem.LaunchModeBuildItem; import io.quarkus.deployment.builditem.LogHandlerBuildItem; @@ -56,6 +59,9 @@ import io.quarkus.extest.runtime.config.TestBuildAndRunTimeConfig; import io.quarkus.extest.runtime.config.TestBuildTimeConfig; import io.quarkus.extest.runtime.config.TestConfigRoot; +import io.quarkus.extest.runtime.config.TestMappingBuildTime; +import io.quarkus.extest.runtime.config.TestMappingBuildTimeRunTime; +import io.quarkus.extest.runtime.config.TestMappingRunTime; import io.quarkus.extest.runtime.config.XmlConfig; import io.quarkus.extest.runtime.logging.AdditionalLogHandlerValueFactory; import io.quarkus.extest.runtime.runtimeinitializedpackage.RuntimeInitializedClass; @@ -323,6 +329,62 @@ void checkConfig(BuildProducer unused) { } } + @BuildStep + void configMapping(BuildProducer configProperties, + ConfigurationBuildItem configItem, + TestMappingBuildTime testMappingBuildTime, + TestMappingBuildTimeRunTime testMappingBuildTimeRunTime) { + Map buildTimeValues = configItem.getReadResult().getAllBuildTimeValues(); + Map buildTimeRunTimeValues = configItem.getReadResult().getBuildTimeRunTimeVisibleValues(); + + if (!testMappingBuildTime.value().equals("value") + || !buildTimeValues.getOrDefault("quarkus.mapping.bt.value", "").equals("value")) { + throw new IllegalStateException(); + } + + if (!testMappingBuildTime.group().value().equals("value") + || !buildTimeValues.getOrDefault("quarkus.mapping.bt.group.value", "").equals("value")) { + throw new IllegalStateException(); + } + + if (testMappingBuildTime.missing().isPresent()) { + throw new IllegalStateException(); + } + + if (testMappingBuildTime.present().isEmpty() || !testMappingBuildTime.present().get().value().equals("present") + || !buildTimeValues.getOrDefault("quarkus.mapping.bt.present.value", "").equals("present")) { + throw new IllegalStateException(); + } + + if (testMappingBuildTime.groups().isEmpty() + || !buildTimeValues.getOrDefault("quarkus.mapping.bt.groups[0].value", "").equals("first") + || !buildTimeValues.getOrDefault("quarkus.mapping.bt.groups[1].value", "").equals("second")) { + throw new IllegalStateException(); + } + + if (!testMappingBuildTimeRunTime.value().equals("value") + || !buildTimeRunTimeValues.getOrDefault("quarkus.mapping.btrt.value", "").equals("value")) { + throw new IllegalStateException(); + } + + if (!testMappingBuildTimeRunTime.group().value().equals("value") + || !buildTimeRunTimeValues.getOrDefault("quarkus.mapping.btrt.group.value", "").equals("value")) { + throw new IllegalStateException(); + } + } + + @BuildStep + @Record(STATIC_INIT) + void configMappingStatic(TestRecorder recorder, TestMappingBuildTimeRunTime buildTimeRunTime) { + recorder.configMappingStatic(buildTimeRunTime); + } + + @BuildStep + @Record(RUNTIME_INIT) + void configMappingRuntime(TestRecorder recorder, TestMappingBuildTimeRunTime buildTimeRunTime, TestMappingRunTime runTime) { + recorder.configMappingRuntime(buildTimeRunTime, runTime); + } + /** * Test for https://github.com/quarkusio/quarkus/issues/1633 * diff --git a/core/test-extension/deployment/src/main/resources/application.properties b/core/test-extension/deployment/src/main/resources/application.properties index 57c311df7ece8..57fada4f51cbf 100644 --- a/core/test-extension/deployment/src/main/resources/application.properties +++ b/core/test-extension/deployment/src/main/resources/application.properties @@ -155,6 +155,19 @@ quarkus.arc.unremovable-types[0]=foo bt.do.not.record=properties %test.bt.profile.record=properties +### mappings +quarkus.mapping.bt.value=value +quarkus.mapping.bt.group.value=value +quarkus.mapping.bt.present.value=present +quarkus.mapping.bt.groups[0].value=first +quarkus.mapping.bt.groups[1].value=second + +quarkus.mapping.btrt.value=value +quarkus.mapping.btrt.group.value=value + +quarkus.mapping.rt.value=value +quarkus.mapping.rt.group.value=value + ### prefix my.prefix.prop=1234 my.prefix.map.prop=1234 diff --git a/core/test-extension/deployment/src/test/java/io/quarkus/config/BuildTimeConfigTest.java b/core/test-extension/deployment/src/test/java/io/quarkus/config/BuildTimeConfigTest.java index 6eddbc3541a00..4bfc5b396c451 100644 --- a/core/test-extension/deployment/src/test/java/io/quarkus/config/BuildTimeConfigTest.java +++ b/core/test-extension/deployment/src/test/java/io/quarkus/config/BuildTimeConfigTest.java @@ -9,6 +9,8 @@ import java.nio.file.Paths; import org.eclipse.microprofile.config.ConfigProvider; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -30,6 +32,8 @@ public class BuildTimeConfigTest { @RegisterExtension static final QuarkusUnitTest config = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)) + .withConfigurationResource("application.properties") .overrideConfigKey("test-map-config", testFile.getAbsolutePath()); /** diff --git a/core/test-extension/deployment/src/test/java/io/quarkus/extest/ConfigMappingTest.java b/core/test-extension/deployment/src/test/java/io/quarkus/extest/ConfigMappingTest.java new file mode 100644 index 0000000000000..d67d0f2be9fc5 --- /dev/null +++ b/core/test-extension/deployment/src/test/java/io/quarkus/extest/ConfigMappingTest.java @@ -0,0 +1,40 @@ +package io.quarkus.extest; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import javax.inject.Inject; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.extest.runtime.config.TestMappingBuildTimeRunTime; +import io.quarkus.extest.runtime.config.TestMappingRunTime; +import io.quarkus.test.QuarkusUnitTest; +import io.smallrye.config.SmallRyeConfig; + +public class ConfigMappingTest { + @RegisterExtension + static final QuarkusUnitTest TEST = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addAsResource("application.properties")); + + @Inject + SmallRyeConfig config; + + @Inject + TestMappingBuildTimeRunTime mappingBuildTimeRunTime; + @Inject + TestMappingRunTime mappingRunTime; + + @Test + void mappingBuildTimeRunTime() { + assertEquals("value", mappingBuildTimeRunTime.value()); + } + + @Test + void mappingRunTime() { + assertEquals("value", mappingBuildTimeRunTime.value()); + } +} diff --git a/core/test-extension/deployment/src/test/java/io/quarkus/extest/ConfiguredBeanTest.java b/core/test-extension/deployment/src/test/java/io/quarkus/extest/ConfiguredBeanTest.java index 9b1e988faa1b0..0810ca4b017b1 100644 --- a/core/test-extension/deployment/src/test/java/io/quarkus/extest/ConfiguredBeanTest.java +++ b/core/test-extension/deployment/src/test/java/io/quarkus/extest/ConfiguredBeanTest.java @@ -67,7 +67,6 @@ public class ConfiguredBeanTest { */ @Test public void validateConfiguredBean() { - System.out.printf("validateConfiguredBean, %s%n", configuredBean); Assertions.assertNotNull(configuredBean); Assertions.assertNotNull(configuredBean.getBuildTimeConfig()); Assertions.assertNotNull(configuredBean.getRunTimeConfig()); diff --git a/core/test-extension/deployment/src/test/resources/build-time-config-test.properties b/core/test-extension/deployment/src/test/resources/build-time-config-test.properties deleted file mode 100644 index a07978c99967c..0000000000000 --- a/core/test-extension/deployment/src/test/resources/build-time-config-test.properties +++ /dev/null @@ -1,137 +0,0 @@ -# Log settings -# log level in lower case for testing -quarkus.log.level=info -quarkus.log.file.enable=true -quarkus.log.file.level=INFO -quarkus.log.file.format=%d{HH:mm:ss} %-5p [%c{2.}]] (%t) %s%e%n - -# Resource path to DSAPublicKey base64 encoded bytes -quarkus.root.dsa-key-location=/DSAPublicKey.encoded - -# Have the TestProcessor validate the build time configuration below -quarkus.root.validate-build-config=true - - -### Configuration settings for the TestBuildTimeConfig config root -quarkus.bt.bt-string-opt=btStringOptValue -quarkus.bt.bt-sbv=StringBasedValue -# This is not set so that we should get the @ConfigItem defaultValue -#quarkus.bt.bt-sbv-with-default=StringBasedValue -quarkus.bt.all-values.oov=configPart1+configPart2 -quarkus.bt.all-values.ovo=configPart1+configPart2 -# This is not set so that we should get the @ConfigItem defaultValue -#quarkus.bt.bt-oov-with-default=ObjectOfValue -quarkus.bt.all-values.long-primitive=1234567891 -quarkus.bt.all-values.double-primitive=3.1415926535897932384 -quarkus.bt.all-values.long-value=1234567892 -quarkus.bt.all-values.opt-long-value=1234567893 -quarkus.bt.all-values.opt-double-value=3.1415926535897932384 -quarkus.bt.all-values.optional-long-value=1234567894 -quarkus.bt.all-values.nested-config-map.key1.nested-value=value1 -quarkus.bt.all-values.nested-config-map.key1.oov=value1.1+value1.2 -quarkus.bt.all-values.nested-config-map.key2.nested-value=value2 -quarkus.bt.all-values.nested-config-map.key2.oov=value2.1+value2.2 -quarkus.bt.all-values.string-list=value1,value2 -quarkus.bt.all-values.long-list=1,2,3 - -### Duplicate settings for the TestBuildAndRunTimeConfig. May be able to drop if ConfigRoot inheritance is added -quarkus.btrt.bt-string-opt=btStringOptValue -quarkus.btrt.bt-sbv=StringBasedValue -quarkus.btrt.all-values.oov=configPart1+configPart2 -quarkus.btrt.all-values.ovo=configPart1+configPart2 -quarkus.btrt.all-values.long-primitive=1234567891 -quarkus.btrt.all-values.double-primitive=3.1415926535897932384 -quarkus.btrt.all-values.long-value=1234567892 -quarkus.btrt.all-values.opt-long-value=1234567893 -quarkus.btrt.all-values.opt-double-value=3.1415926535897932384 -quarkus.btrt.all-values.optional-long-value=1234567894 -quarkus.btrt.all-values.nested-config-map.key1.nested-value=value1 -quarkus.btrt.all-values.nested-config-map.key1.oov=value1.1+value1.2 -quarkus.btrt.all-values.nested-config-map.key2.nested-value=value2 -quarkus.btrt.all-values.nested-config-map.key2.oov=value2.1+value2.2 -quarkus.btrt.all-values.string-list=value1,value2 -quarkus.btrt.all-values.long-list=1,2,3 -# The expansion value is not available in runtime so we need to set it directly. -quarkus.btrt.all-values.expanded-default=1234 - -### Configuration settings for the TestRunTimeConfig config root -quarkus.rt.rt-string-opt=rtStringOptValue -quarkus.rt.rt-string-opt-with-default=rtStringOptWithDefaultValue -quarkus.rt.all-values.oov=configPart1+configPart2 -quarkus.rt.all-values.ovo=configPart1+configPart2 -quarkus.rt.all-values.long-primitive=12345678911 -quarkus.rt.all-values.double-primitive=3.1415926535897932384 -quarkus.rt.all-values.long-value=12345678921 -quarkus.rt.all-values.opt-long-value=12345678931 -quarkus.rt.all-values.opt-double-value=3.1415926535897932384 -quarkus.rt.all-values.optional-long-value=12345678941 -quarkus.rt.all-values.nested-config-map.key1.nested-value=value1 -quarkus.rt.all-values.nested-config-map.key1.oov=value1.1+value1.2 -quarkus.rt.all-values.nested-config-map.key2.nested-value=value2 -quarkus.rt.all-values.nested-config-map.key2.oov=value2.1+value2.2 -quarkus.rt.all-values.string-list=value1,value2 -quarkus.rt.all-values.long-list=1,2,3 -# A nested map of properties -quarkus.rt.all-values.string-map.key1=value1 -quarkus.rt.all-values.string-map.key2=value2 -quarkus.rt.all-values.string-map.key3=value3 -# And list form -quarkus.rt.all-values.string-list-map.key1=value1,value2,value3 -quarkus.rt.all-values.string-list-map.key2=value4,value5 -quarkus.rt.all-values.string-list-map.key3=value6 -# A root map of properties -quarkus.rt.string-map.key1=value1 -quarkus.rt.string-map.key2=value2 -quarkus.rt.string-map.key3=value3 -# And list form -quarkus.rt.string-list-map.key1=value1 -quarkus.rt.string-list-map.key2=value2,value3 -quarkus.rt.string-list-map.key3=value4,value5,value6 - -### run time configuration using enhanced converters -quarkus.rt.my-enum=enum-two -quarkus.rt.my-enums=enum-one,enum-two -quarkus.rt.my-optional-enums=optional -quarkus.rt.no-hyphenate-first-enum=ENUM_ONE -quarkus.rt.no-hyphenate-second-enum=Enum_Two -quarkus.rt.primitive-boolean=YES -quarkus.rt.object-boolean=NO -quarkus.rt.primitive-integer=two -quarkus.rt.object-integer=nine -quarkus.rt.one-to-nine=one,two,three,four,five,six,seven,eight,nine -quarkus.rt.map-of-numbers.key1=one -quarkus.rt.map-of-numbers.key2=two - -### map configurations -quarkus.rt.leaf-map.key.first=first-key-value -quarkus.rt.leaf-map.key.second=second-key-value -quarkus.rt.config-group-map.key.group.nested-value=value -quarkus.rt.config-group-map.key.group.oov=value2.1+value2.2 - -### build time and run time configuration using enhanced converters -quarkus.btrt.map-of-numbers.key1=one -quarkus.btrt.map-of-numbers.key2=two -quarkus.btrt.my-enum=optional -quarkus.btrt.my-enums=optional,enum-one,enum-two - -### anonymous root property -quarkus.test-property=foo - -### map of map of strings -quarkus.rt.map-map.outer-key.inner-key=1234 -quarkus.btrt.map-map.outer-key.inner-key=1234 -quarkus.bt.map-map.outer-key.inner-key=1234 - -# Test config root with "RuntimeConfig" suffix -quarkus.foo.bar=huhu - -### named map with profiles -quarkus.btrt.map-map.main-profile.property=1234 -%test.quarkus.btrt.map-map.test-profile.property=5678 - -### ordinal and default values source -config_ordinal=1000 -my.prop=1234 -%prod.my.prop=1234 -%dev.my.prop=5678 -%test.my.prop=1234 \ No newline at end of file diff --git a/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/TestRecorder.java b/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/TestRecorder.java index 5984d6b55e2ae..9e6c62f4c3b1a 100644 --- a/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/TestRecorder.java +++ b/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/TestRecorder.java @@ -8,13 +8,9 @@ import io.quarkus.arc.runtime.BeanContainer; import io.quarkus.extest.runtime.beans.PublicKeyProducer; -import io.quarkus.extest.runtime.config.AnotherPrefixConfig; -import io.quarkus.extest.runtime.config.FooRuntimeConfig; -import io.quarkus.extest.runtime.config.PrefixConfig; -import io.quarkus.extest.runtime.config.TestBuildAndRunTimeConfig; -import io.quarkus.extest.runtime.config.TestRunTimeConfig; +import io.quarkus.extest.runtime.config.TestMappingBuildTimeRunTime; +import io.quarkus.extest.runtime.config.TestMappingRunTime; import io.quarkus.extest.runtime.config.XmlConfig; -import io.quarkus.extest.runtime.config.named.PrefixNamedConfig; import io.quarkus.runtime.RuntimeValue; import io.quarkus.runtime.ShutdownContext; import io.quarkus.runtime.annotations.Recorder; @@ -26,29 +22,6 @@ public class TestRecorder { static final Logger log = Logger.getLogger(TestRecorder.class); - /** - * Instantiate the given class in the given BeanContainer and passes the TestBuildAndRunTimeConfig and TestRunTimeConfig to - * it - * - * @param beanContainer - CDI container - * @param beanClass - IConfigConsumer - * @param buildTimeConfig - the extension TestBuildAndRunTimeConfig - * @param runTimeConfig - the extension TestRunTimeConfig - * @see IConfigConsumer#loadConfig(TestBuildAndRunTimeConfig, TestRunTimeConfig, FooRuntimeConfig, PrefixConfig, - * PrefixNamedConfig, AnotherPrefixConfig) - */ - public void configureBeans(BeanContainer beanContainer, Class beanClass, - TestBuildAndRunTimeConfig buildTimeConfig, - TestRunTimeConfig runTimeConfig, FooRuntimeConfig fooRuntimeConfig, PrefixConfig prefixConfig, - PrefixNamedConfig prefixNamedConfig, - AnotherPrefixConfig anotherPrefixConfig) { - log.infof("Begin BeanContainerListener callback\n"); - IConfigConsumer instance = beanContainer.instance(beanClass); - instance.loadConfig(buildTimeConfig, runTimeConfig, fooRuntimeConfig, prefixConfig, prefixNamedConfig, - anotherPrefixConfig); - log.infof("configureBeans, instance=%s\n", instance); - } - /** * Create a non-CDI based RuntimeXmlConfigService from the XmlConfig * @@ -95,4 +68,32 @@ public void validateTypes(Set> typesSet) { log.debugf("Checking type: %s", type.getName()); } } + + public void configMappingStatic(TestMappingBuildTimeRunTime buildTimeRunTime) { + if (!buildTimeRunTime.value().equals("value")) { + throw new IllegalStateException(); + } + + if (!buildTimeRunTime.group().value().equals("value")) { + throw new IllegalStateException(); + } + } + + public void configMappingRuntime(TestMappingBuildTimeRunTime buildTimeRunTime, TestMappingRunTime runTime) { + if (!buildTimeRunTime.value().equals("value")) { + throw new IllegalStateException(); + } + + if (!buildTimeRunTime.group().value().equals("value")) { + throw new IllegalStateException(); + } + + if (!runTime.value().equals("value")) { + throw new IllegalStateException(); + } + + if (!runTime.group().value().equals("value")) { + throw new IllegalStateException(); + } + } } diff --git a/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/RunTimeConfigBuilder.java b/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/RunTimeConfigBuilder.java index f7acbd18693a7..9a1ee85c021a7 100644 --- a/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/RunTimeConfigBuilder.java +++ b/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/RunTimeConfigBuilder.java @@ -6,6 +6,7 @@ public class RunTimeConfigBuilder implements ConfigBuilder { @Override public SmallRyeConfigBuilder configBuilder(final SmallRyeConfigBuilder builder) { + builder.withDefaultValue("skip.build.sources", "true"); builder.withDefaultValue("additional.builder.property", "1234"); return builder; } diff --git a/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/StaticInitConfigBuilder.java b/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/StaticInitConfigBuilder.java index 292d4d62ac5c4..879d886ef85d5 100644 --- a/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/StaticInitConfigBuilder.java +++ b/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/StaticInitConfigBuilder.java @@ -14,6 +14,7 @@ public StaticInitConfigBuilder() { @Override public SmallRyeConfigBuilder configBuilder(final SmallRyeConfigBuilder builder) { + builder.withDefaultValue("skip.build.sources", "true"); return builder; } } diff --git a/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/TestMappingBuildTime.java b/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/TestMappingBuildTime.java new file mode 100644 index 0000000000000..2701764b02835 --- /dev/null +++ b/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/TestMappingBuildTime.java @@ -0,0 +1,49 @@ +package io.quarkus.extest.runtime.config; + +import java.util.List; +import java.util.Optional; + +import io.quarkus.runtime.annotations.ConfigPhase; +import io.quarkus.runtime.annotations.ConfigRoot; +import io.smallrye.config.ConfigMapping; + +@ConfigMapping(prefix = "quarkus.mapping.bt") +@ConfigRoot(phase = ConfigPhase.BUILD_TIME) +public interface TestMappingBuildTime { + /** + * A String value. + */ + String value(); + + /** + * A nested Group. + */ + Group group(); + + /** + * An Optional missing Group. + */ + Optional missing(); + + /** + * An Optional present Group. + */ + Optional present(); + + /** + * A List of Groups. + */ + List groups(); + + /** + * An Optional List of Groups. + */ + Optional> optionalGroups(); + + interface Group { + /** + * A Group value. + */ + String value(); + } +} diff --git a/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/TestMappingBuildTimeRunTime.java b/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/TestMappingBuildTimeRunTime.java new file mode 100644 index 0000000000000..781a017516036 --- /dev/null +++ b/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/TestMappingBuildTimeRunTime.java @@ -0,0 +1,26 @@ +package io.quarkus.extest.runtime.config; + +import io.quarkus.runtime.annotations.ConfigPhase; +import io.quarkus.runtime.annotations.ConfigRoot; +import io.smallrye.config.ConfigMapping; + +@ConfigMapping(prefix = "quarkus.mapping.btrt") +@ConfigRoot(phase = ConfigPhase.BUILD_AND_RUN_TIME_FIXED) +public interface TestMappingBuildTimeRunTime { + /** + * A String value + */ + String value(); + + /** + * A nested Group. + */ + Group group(); + + interface Group { + /** + * A Group value. + */ + String value(); + } +} diff --git a/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/TestMappingRunTime.java b/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/TestMappingRunTime.java new file mode 100644 index 0000000000000..39d3fdabccd78 --- /dev/null +++ b/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/TestMappingRunTime.java @@ -0,0 +1,26 @@ +package io.quarkus.extest.runtime.config; + +import io.quarkus.runtime.annotations.ConfigPhase; +import io.quarkus.runtime.annotations.ConfigRoot; +import io.smallrye.config.ConfigMapping; + +@ConfigMapping(prefix = "quarkus.mapping.rt") +@ConfigRoot(phase = ConfigPhase.RUN_TIME) +public interface TestMappingRunTime { + /** + * A String value. + */ + String value(); + + /** + * A nested Group. + */ + Group group(); + + interface Group { + /** + * A Group value. + */ + String value(); + } +} diff --git a/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/rename/RenameConfigSource.java b/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/rename/RenameConfigSource.java index 90b169ca37d05..0a99b6468b0da 100644 --- a/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/rename/RenameConfigSource.java +++ b/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/rename/RenameConfigSource.java @@ -1,27 +1,12 @@ package io.quarkus.extest.runtime.config.rename; -import static java.util.Collections.emptySet; - import java.util.HashMap; import java.util.Map; import java.util.Set; -import org.eclipse.microprofile.config.Config; -import org.eclipse.microprofile.config.ConfigProvider; -import org.eclipse.microprofile.config.spi.ConfigSource; - -import io.quarkus.runtime.annotations.StaticInitSafe; import io.smallrye.config.common.MapBackedConfigSource; -/** - * This simulates a build time only source to test the recording of configuration values. It is still discovered at - * runtime, but it doesn't return any configuration. - */ -@StaticInitSafe public class RenameConfigSource extends MapBackedConfigSource { - // Because getPropertyNames() is called during SmallRyeConfig init - private int propertyNamesCallCount = 0; - private static final Map FALLBACK_PROPERTIES = Map.of( "quarkus.rename.prop", "1234", "quarkus.rename.only-in-new", "only-in-new", @@ -36,30 +21,11 @@ public RenameConfigSource() { @Override public String getValue(final String propertyName) { - if (propertyName.startsWith("quarkus.rename") && isBuildTime()) { - return FALLBACK_PROPERTIES.get(propertyName); - } - return null; + return FALLBACK_PROPERTIES.get(propertyName); } @Override public Set getPropertyNames() { - if (propertyNamesCallCount > 0) { - return isBuildTime() ? FALLBACK_PROPERTIES.keySet() : emptySet(); - } else { - propertyNamesCallCount++; - return emptySet(); - } - } - - private static boolean isBuildTime() { - // We can only call this when the SmallRyeConfig is already initialized, or else we may get into a loop - Config config = ConfigProvider.getConfig(); - for (ConfigSource configSource : config.getConfigSources()) { - if (configSource.getClass().getSimpleName().equals("BuildTimeEnvConfigSource")) { - return true; - } - } - return false; + return FALLBACK_PROPERTIES.keySet(); } } diff --git a/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/rename/RenameConfigSourceFactory.java b/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/rename/RenameConfigSourceFactory.java new file mode 100644 index 0000000000000..e382034545a3d --- /dev/null +++ b/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/rename/RenameConfigSourceFactory.java @@ -0,0 +1,22 @@ +package io.quarkus.extest.runtime.config.rename; + +import java.util.Collections; +import java.util.List; + +import org.eclipse.microprofile.config.spi.ConfigSource; + +import io.smallrye.config.ConfigSourceContext; +import io.smallrye.config.ConfigSourceFactory; +import io.smallrye.config.ConfigValue; + +public class RenameConfigSourceFactory implements ConfigSourceFactory { + @Override + public Iterable getConfigSources(final ConfigSourceContext context) { + ConfigValue value = context.getValue("skip.build.sources"); + if (value.getValue() == null || value.getValue().equals("false")) { + return List.of(new RenameConfigSource()); + } else { + return Collections.emptyList(); + } + } +} diff --git a/core/test-extension/runtime/src/main/resources/META-INF/services/io.smallrye.config.ConfigSourceFactory b/core/test-extension/runtime/src/main/resources/META-INF/services/io.smallrye.config.ConfigSourceFactory new file mode 100644 index 0000000000000..5e56f7b2414ee --- /dev/null +++ b/core/test-extension/runtime/src/main/resources/META-INF/services/io.smallrye.config.ConfigSourceFactory @@ -0,0 +1 @@ +io.quarkus.extest.runtime.config.rename.RenameConfigSourceFactory diff --git a/core/test-extension/runtime/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource b/core/test-extension/runtime/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource deleted file mode 100644 index 59b7341c9dbd3..0000000000000 --- a/core/test-extension/runtime/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource +++ /dev/null @@ -1 +0,0 @@ -io.quarkus.extest.runtime.config.rename.RenameConfigSource diff --git a/extensions/container-image/deployment/src/test/java/io/quarkus/container/image/deployment/ContainerImageInfoTest.java b/extensions/container-image/deployment/src/test/java/io/quarkus/container/image/deployment/ContainerImageInfoTest.java index 89bedd0fb0fca..70d2591059e1b 100644 --- a/extensions/container-image/deployment/src/test/java/io/quarkus/container/image/deployment/ContainerImageInfoTest.java +++ b/extensions/container-image/deployment/src/test/java/io/quarkus/container/image/deployment/ContainerImageInfoTest.java @@ -116,7 +116,7 @@ private void whenPublishImageInfo() { SmallRyeConfig src = builder.build(); BuildTimeConfigurationReader.ReadResult readResult = reader.readConfiguration(src); ContainerImageConfig containerImageConfig = (ContainerImageConfig) readResult - .requireRootObjectForClass(ContainerImageConfig.class); + .requireObjectForClass(ContainerImageConfig.class); ApplicationInfoBuildItem app = new ApplicationInfoBuildItem(Optional.of(APP_NAME), Optional.of(APP_VERSION)); Capabilities capabilities = new Capabilities(Collections.emptySet()); diff --git a/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/WiringProcessor.java b/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/WiringProcessor.java index a928a1af6f574..64d3e0d1f9a9e 100644 --- a/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/WiringProcessor.java +++ b/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/WiringProcessor.java @@ -201,7 +201,7 @@ private void handleMethodAnnotationWithOutgoing(BuildProducer } if (outgoing != null) { configDescriptionBuildItemBuildProducer.produce(new ConfigDescriptionBuildItem( - "mp.messaging.outgoing." + outgoing.value().asString() + ".connector", String.class, null, + "mp.messaging.outgoing." + outgoing.value().asString() + ".connector", null, "The connector to use", null, null, ConfigPhase.BUILD_TIME)); produceOutgoingChannel(appChannels, outgoing.value().asString()); @@ -219,7 +219,7 @@ private void handleMethodAnnotationWithIncomings(BuildProducer new DeploymentException("Empty @Incoming annotation on method " + method))); } configDescriptionBuildItemBuildProducer.produce(new ConfigDescriptionBuildItem( - "mp.messaging.incoming." + instance.value().asString() + ".connector", String.class, null, + "mp.messaging.incoming." + instance.value().asString() + ".connector", null, "The connector to use", null, null, ConfigPhase.BUILD_TIME)); produceIncomingChannel(appChannels, instance.value().asString()); } @@ -236,7 +236,7 @@ private void handleMethodAnnotatedWithIncoming(BuildProducer a } if (incoming != null) { configDescriptionBuildItemBuildProducer.produce(new ConfigDescriptionBuildItem( - "mp.messaging.incoming." + incoming.value().asString() + ".connector", String.class, null, + "mp.messaging.incoming." + incoming.value().asString() + ".connector", null, "The connector to use", null, null, ConfigPhase.BUILD_TIME)); produceIncomingChannel(appChannels, incoming.value().asString()); } @@ -278,8 +278,7 @@ public void detectOrphanChannels(List channels, @BuildStep void generateDocumentationItem(BuildProducer config, - List channels, List connectors) - throws ClassNotFoundException { + List channels, List connectors) { Parser markdownParser = Parser.builder().build(); HtmlRenderer renderer = HtmlRenderer.builder().build(); for (ConnectorManagedChannelBuildItem channel : channels) { @@ -289,8 +288,8 @@ void generateDocumentationItem(BuildProducer config, + channel.getName() + "."; if (connector != null) { for (ConnectorAttribute attribute : connector.getAttributes()) { - ConfigDescriptionBuildItem cfg = new ConfigDescriptionBuildItem(prefix + attribute.name(), - WiringHelper.toType(attribute.type()), + ConfigDescriptionBuildItem cfg = new ConfigDescriptionBuildItem( + prefix + attribute.name(), attribute.defaultValue().equalsIgnoreCase(ConnectorAttribute.NO_VALUE) ? null : attribute.defaultValue(), renderer.render(markdownParser.parse(attribute.description())),