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 57abae96f853c..fb0e50785eca2 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 java.util.stream.Collectors.toSet; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -55,11 +56,13 @@ import io.quarkus.runtime.configuration.ConfigurationException; import io.quarkus.runtime.configuration.HyphenateEnumConverter; import io.quarkus.runtime.configuration.NameIterator; +import io.quarkus.runtime.configuration.PropertiesUtil; import io.smallrye.config.Converters; import io.smallrye.config.EnvConfigSource; import io.smallrye.config.Expressions; import io.smallrye.config.SmallRyeConfig; import io.smallrye.config.SmallRyeConfigBuilder; +import io.smallrye.config.SysPropConfigSource; /** * A configuration reader. @@ -91,14 +94,17 @@ public BuildTimeConfigurationReader(final List<Class<?>> configRoots) { List<RootDefinition> buildTimeRoots = new ArrayList<>(); Map<Class<?>, GroupDefinition> groups = new HashMap<>(); for (Class<?> configRoot : configRoots) { + String prefix = "quarkus"; String name = ConfigItem.HYPHENATED_ELEMENT_NAME; ConfigPhase phase = ConfigPhase.BUILD_TIME; ConfigRoot annotation = configRoot.getAnnotation(ConfigRoot.class); if (annotation != null) { + prefix = annotation.prefix(); name = annotation.name(); phase = annotation.phase(); } RootDefinition.Builder defBuilder = new RootDefinition.Builder(); + defBuilder.setPrefix(prefix); defBuilder.setConfigPhase(phase); defBuilder.setRootName(name); processClass(defBuilder, configRoot, groups); @@ -272,9 +278,7 @@ final class ReadOperation { ReadResult run() { final StringBuilder nameBuilder; - nameBuilder = new StringBuilder().append("quarkus"); - // eager init first - int len = nameBuilder.length(); + nameBuilder = new StringBuilder(); for (RootDefinition root : buildTimeVisibleRoots) { Class<?> clazz = root.getConfigurationClass(); Object instance; @@ -292,23 +296,21 @@ ReadResult run() { throw toError(e); } objectsByRootClass.put(clazz, instance); - String rootName = root.getRootName(); - if (!rootName.isEmpty()) { - nameBuilder.append('.').append(rootName); - } + nameBuilder.append(root.getName()); readConfigGroup(root, instance, nameBuilder); - nameBuilder.setLength(len); + nameBuilder.setLength(0); } + + Set<String> registeredRoots = allRoots.stream().map(RootDefinition::getPrefix).collect(toSet()); // sweep-up SmallRyeConfig runtimeDefaultsConfig = getConfigForRuntimeDefaults(); - for (String propertyName : getAllProperties()) { + for (String propertyName : getAllProperties(registeredRoots)) { if (propertyName.equals(ConfigSource.CONFIG_ORDINAL)) { continue; } NameIterator ni = new NameIterator(propertyName); - if (ni.hasNext() && ni.nextSegmentEquals("quarkus")) { - ni.next(); + if (ni.hasNext() && PropertiesUtil.isPropertyInRoot(registeredRoots, ni)) { // build time patterns Container matched = buildTimePatternMap.match(ni); if (matched instanceof FieldContainer) { @@ -337,7 +339,6 @@ ReadResult run() { } // build time (run time visible) patterns ni.goToStart(); - ni.next(); matched = buildTimeRunTimePatternMap.match(ni); if (matched instanceof FieldContainer) { ni.goToEnd(); @@ -369,7 +370,6 @@ ReadResult run() { } // run time patterns ni.goToStart(); - ni.next(); matched = runTimePatternMap.match(ni); if (matched != null) { // it's a specified run-time default (record for later) @@ -380,7 +380,6 @@ ReadResult run() { } // also check for the bootstrap properties since those need to be added to specifiedRunTimeDefaultValues as well ni.goToStart(); - ni.next(); matched = bootstrapPatternMap.match(ni); if (matched != null) { // it's a specified run-time default (record for later) @@ -732,11 +731,23 @@ private Converter<?> getConverter(SmallRyeConfig config, Field field, ConverterT * We collect all properties from ConfigSources, because Config#getPropertyNames exclude the active profiled * properties, meaning that the property is written in the default config source unprofiled. This may cause * issues if we run with a different profile and fallback to defaults. + * + * We also filter the properties coming from the System with the registered roots, because we don't want to + * record properties set by the compiling JVM (or other properties set that are only related to the build). */ - private Set<String> getAllProperties() { + private Set<String> getAllProperties(final Set<String> registeredRoots) { Set<String> properties = new HashSet<>(); for (ConfigSource configSource : config.getConfigSources()) { - properties.addAll(configSource.getPropertyNames()); + if (configSource instanceof SysPropConfigSource) { + for (String propertyName : configSource.getProperties().keySet()) { + NameIterator ni = new NameIterator(propertyName); + if (ni.hasNext() && PropertiesUtil.isPropertyInRoot(registeredRoots, ni)) { + properties.add(propertyName); + } + } + } else { + properties.addAll(configSource.getPropertyNames()); + } } for (String propertyName : config.getPropertyNames()) { properties.add(propertyName); diff --git a/core/deployment/src/main/java/io/quarkus/deployment/configuration/DefaultValuesConfigurationSource.java b/core/deployment/src/main/java/io/quarkus/deployment/configuration/DefaultValuesConfigurationSource.java index 59df69b94ed5e..268b44949c0c7 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/configuration/DefaultValuesConfigurationSource.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/configuration/DefaultValuesConfigurationSource.java @@ -29,10 +29,7 @@ public Set<String> getPropertyNames() { } public String getValue(final String propertyName) { - if (!propertyName.startsWith("quarkus.")) { - return null; - } - final Container match = leafs.match(propertyName.substring(8)); + final Container match = leafs.match(propertyName); if (match == null) { return null; } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/configuration/PropertiesUtil.java b/core/deployment/src/main/java/io/quarkus/deployment/configuration/PropertiesUtil.java deleted file mode 100644 index c8eb215cdb1f6..0000000000000 --- a/core/deployment/src/main/java/io/quarkus/deployment/configuration/PropertiesUtil.java +++ /dev/null @@ -1,72 +0,0 @@ -package io.quarkus.deployment.configuration; - -public class PropertiesUtil { - private PropertiesUtil() { - } - - public static boolean needsEscape(int codePoint) { - return codePoint == '#' || codePoint == '!' || codePoint == '=' || codePoint == ':'; - } - - public static boolean needsEscapeForKey(int codePoint) { - return Character.isSpaceChar(codePoint) || needsEscape(codePoint); - } - - public static boolean needsEscapeForValueFirst(int codePoint) { - return needsEscapeForKey(codePoint); - } - - public static boolean needsEscapeForValueSubsequent(int codePoint) { - return needsEscape(codePoint); - } - - public static String quotePropertyName(String name) { - final int length = name.length(); - int cp; - for (int i = 0; i < length; i = name.offsetByCodePoints(i, 1)) { - cp = name.codePointAt(i); - if (needsEscapeForKey(cp)) { - final StringBuilder b = new StringBuilder(length + (length >> 2)); - // get leading section - b.append(name, 0, i); - // and continue with escaping as needed - b.append('\\').appendCodePoint(cp); - for (i = name.offsetByCodePoints(i, 1); i < length; i = name.offsetByCodePoints(i, 1)) { - cp = name.codePointAt(i); - if (needsEscapeForKey(cp)) { - b.append('\\'); - } - b.appendCodePoint(cp); - } - return b.toString(); - } - } - // no escaping needed - majority case - return name; - } - - public static String quotePropertyValue(String value) { - final int length = value.length(); - int cp; - for (int i = 0; i < length; i = value.offsetByCodePoints(i, 1)) { - cp = value.codePointAt(i); - if (i == 0 ? needsEscapeForValueFirst(cp) : needsEscapeForValueSubsequent(cp)) { - final StringBuilder b = new StringBuilder(length + (length >> 2)); - // get leading section - b.append(value, 0, i); - // and continue with escaping as needed - b.append('\\').appendCodePoint(cp); - for (i = value.offsetByCodePoints(i, 1); i < length; i = value.offsetByCodePoints(i, 1)) { - cp = value.codePointAt(i); - if (needsEscapeForValueSubsequent(cp)) { - b.append('\\'); - } - b.appendCodePoint(cp); - } - return b.toString(); - } - } - // no escaping needed - majority case - return value; - } -} 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 ee5ebd04d064d..1594f0ab9fc05 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 @@ -1,12 +1,16 @@ package io.quarkus.deployment.configuration; import static io.quarkus.deployment.util.ReflectUtil.reportError; +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.RUN_TIME; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -66,6 +70,7 @@ import io.quarkus.runtime.configuration.HyphenateEnumConverter; import io.quarkus.runtime.configuration.NameIterator; import io.quarkus.runtime.configuration.ProfileManager; +import io.quarkus.runtime.configuration.PropertiesUtil; import io.quarkus.runtime.configuration.QuarkusConfigFactory; import io.quarkus.runtime.configuration.RuntimeConfigSource; import io.quarkus.runtime.configuration.RuntimeConfigSourceFactory; @@ -206,8 +211,6 @@ public final class RunTimeConfigurationGenerator { boolean.class, String.class); static final MethodDescriptor NI_NEXT = MethodDescriptor.ofMethod(NameIterator.class, "next", void.class); static final MethodDescriptor NI_PREVIOUS = MethodDescriptor.ofMethod(NameIterator.class, "previous", void.class); - static final MethodDescriptor NI_PREVIOUS_EQUALS = MethodDescriptor.ofMethod(NameIterator.class, "previousSegmentEquals", - boolean.class, String.class); static final MethodDescriptor OBJ_TO_STRING = MethodDescriptor.ofMethod(Object.class, "toString", String.class); @@ -253,6 +256,11 @@ public final class RunTimeConfigurationGenerator { static final MethodDescriptor SRCB_BUILD = MethodDescriptor.ofMethod(SmallRyeConfigBuilder.class, "build", SmallRyeConfig.class); + static final MethodDescriptor PU_IS_PROPERTY_IN_ROOT = MethodDescriptor.ofMethod(PropertiesUtil.class, + "isPropertyInRoot", boolean.class, Set.class, NameIterator.class); + static final MethodDescriptor HS_NEW = MethodDescriptor.ofConstructor(HashSet.class); + static final MethodDescriptor HS_PUT = MethodDescriptor.ofMethod(HashSet.class, "add", boolean.class, Object.class); + // todo: more space-efficient sorted map impl static final MethodDescriptor TM_NEW = MethodDescriptor.ofConstructor(TreeMap.class); @@ -362,7 +370,6 @@ public static final class GenerateOperation implements AutoCloseable { clinit.invokeStaticMethod(PM_SET_RUNTIME_DEFAULT_PROFILE, clinit.load(ProfileManager.getActiveProfile())); clinitNameBuilder = clinit.newInstance(SB_NEW); - clinit.invokeVirtualMethod(SB_APPEND_STRING, clinitNameBuilder, clinit.load("quarkus")); // create the map for build time config source final ResultHandle buildTimeValues = clinit.newInstance(HM_NEW); @@ -437,15 +444,12 @@ public static final class GenerateOperation implements AutoCloseable { readBootstrapConfigNameBuilder = null; } else { readBootstrapConfigNameBuilder = readBootstrapConfig.newInstance(SB_NEW); - readBootstrapConfig.invokeVirtualMethod(SB_APPEND_STRING, readBootstrapConfigNameBuilder, - readBootstrapConfig.load("quarkus")); } // create readConfig readConfig = cc.getMethodCreator(C_READ_CONFIG); // the readConfig name builder readConfigNameBuilder = readConfig.newInstance(SB_NEW); - readConfig.invokeVirtualMethod(SB_APPEND_STRING, readConfigNameBuilder, readConfig.load("quarkus")); accessorFinder = new AccessorFinder(); } @@ -701,8 +705,7 @@ public void run() { } // specific actions based on config phase - String rootName = root.getRootName(); - if (root.getConfigPhase() == ConfigPhase.BUILD_AND_RUN_TIME_FIXED) { + if (root.getConfigPhase() == BUILD_AND_RUN_TIME_FIXED) { // config root field is volatile in dev mode, final otherwise; we initialize it from clinit, and readConfig in dev mode cc.getFieldCreator(rootFieldDescriptor) .setModifiers(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC @@ -720,19 +723,13 @@ public void run() { clinit.writeStaticField(rootFieldDescriptor, instance); instanceCache.put(rootFieldDescriptor, instance); // eager init as appropriate - if (!rootName.isEmpty()) { - clinit.invokeVirtualMethod(SB_APPEND_CHAR, clinitNameBuilder, clinit.load('.')); - clinit.invokeVirtualMethod(SB_APPEND_STRING, clinitNameBuilder, clinit.load(rootName)); - } + clinit.invokeVirtualMethod(SB_APPEND_STRING, clinitNameBuilder, clinit.load(root.getName())); clinit.invokeStaticMethod(initGroup, clinitConfig, clinitNameBuilder, instance); clinit.invokeVirtualMethod(SB_SET_LENGTH, clinitNameBuilder, clInitOldLen); if (devMode) { instance = readConfig.readStaticField(rootFieldDescriptor); - if (!rootName.isEmpty()) { - readConfig.invokeVirtualMethod(SB_APPEND_CHAR, readConfigNameBuilder, readConfig.load('.')); - readConfig.invokeVirtualMethod(SB_APPEND_STRING, readConfigNameBuilder, - readConfig.load(rootName)); - } + readConfig.invokeVirtualMethod(SB_APPEND_STRING, readConfigNameBuilder, + readConfig.load(root.getName())); readConfig.invokeStaticMethod(initGroup, runTimeConfig, readConfigNameBuilder, instance); readConfig.invokeVirtualMethod(SB_SET_LENGTH, readConfigNameBuilder, rcOldLen); } @@ -752,12 +749,8 @@ public void run() { // assign instance to field readBootstrapConfig.writeStaticField(rootFieldDescriptor, instance); - if (!rootName.isEmpty()) { - readBootstrapConfig.invokeVirtualMethod(SB_APPEND_CHAR, readBootstrapConfigNameBuilder, - readBootstrapConfig.load('.')); - readBootstrapConfig.invokeVirtualMethod(SB_APPEND_STRING, readBootstrapConfigNameBuilder, - readBootstrapConfig.load(rootName)); - } + readBootstrapConfig.invokeVirtualMethod(SB_APPEND_STRING, readBootstrapConfigNameBuilder, + readBootstrapConfig.load(root.getName())); readBootstrapConfig.invokeStaticMethod(initGroup, bootstrapConfig, readBootstrapConfigNameBuilder, instance); readBootstrapConfig.invokeVirtualMethod(SB_SET_LENGTH, readBootstrapConfigNameBuilder, bcOldLen); @@ -777,11 +770,7 @@ public void run() { // assign instance to field readConfig.writeStaticField(rootFieldDescriptor, instance); - if (!rootName.isEmpty()) { - readConfig.invokeVirtualMethod(SB_APPEND_CHAR, readConfigNameBuilder, readConfig.load('.')); - readConfig.invokeVirtualMethod(SB_APPEND_STRING, readConfigNameBuilder, - readConfig.load(rootName)); - } + readConfig.invokeVirtualMethod(SB_APPEND_STRING, readConfigNameBuilder, readConfig.load(root.getName())); readConfig.invokeStaticMethod(initGroup, runTimeConfig, readConfigNameBuilder, instance); readConfig.invokeVirtualMethod(SB_SET_LENGTH, readConfigNameBuilder, rcOldLen); } else { @@ -791,23 +780,23 @@ public void run() { } // generate sweep for clinit - configSweepLoop(siParserBody, clinit, clinitConfig); + configSweepLoop(siParserBody, clinit, clinitConfig, getRegisteredRoots(BUILD_AND_RUN_TIME_FIXED)); clinit.invokeStaticMethod(CD_UNKNOWN_PROPERTIES, clinit.readStaticField(FieldDescriptor.of(cc.getClassName(), "unused", List.class))); if (devMode) { - configSweepLoop(siParserBody, readConfig, runTimeConfig); + configSweepLoop(siParserBody, readConfig, runTimeConfig, getRegisteredRoots(RUN_TIME)); } // generate sweep for run time - configSweepLoop(rtParserBody, readConfig, runTimeConfig); + configSweepLoop(rtParserBody, readConfig, runTimeConfig, getRegisteredRoots(RUN_TIME)); readConfig.invokeStaticMethod(CD_UNKNOWN_PROPERTIES_RT, readConfig.readStaticField(FieldDescriptor.of(cc.getClassName(), "unusedRuntime", List.class))); if (bootstrapConfigSetupNeeded()) { // generate sweep for bootstrap config - configSweepLoop(bsParserBody, readBootstrapConfig, bootstrapConfig); + configSweepLoop(bsParserBody, readBootstrapConfig, bootstrapConfig, getRegisteredRoots(BOOTSTRAP)); } // generate ensure-initialized method @@ -874,9 +863,17 @@ public void run() { generateDefaultValuesConfigSourceClass(buildTimeRunTimePatternMap, BTRTDVCS_CLASS_NAME); } - private static void configSweepLoop(MethodDescriptor parserBody, MethodCreator method, ResultHandle config) { + private void configSweepLoop(MethodDescriptor parserBody, MethodCreator method, ResultHandle config, + Set<String> registeredRoots) { + ResultHandle rootSet; ResultHandle nameSet; ResultHandle iterator; + + rootSet = method.newInstance(HS_NEW); + for (String registeredRoot : registeredRoots) { + method.invokeVirtualMethod(HS_PUT, rootSet, method.load(registeredRoot)); + } + nameSet = method.invokeVirtualMethod(SRC_GET_PROPERTY_NAMES, config); iterator = method.invokeInterfaceMethod(ITRA_ITERATOR, nameSet); @@ -890,10 +887,8 @@ private static void configSweepLoop(MethodDescriptor parserBody, MethodCreator m // if (! keyIter.hasNext()) continue sweepLoop; hasNext.ifNonZero(hasNext.invokeVirtualMethod(NI_HAS_NEXT, keyIter)).falseBranch().continueScope(sweepLoop); // if (! keyIter.nextSegmentEquals("quarkus")) continue sweepLoop; - hasNext.ifNonZero(hasNext.invokeVirtualMethod(NI_NEXT_EQUALS, keyIter, hasNext.load("quarkus"))) - .falseBranch().continueScope(sweepLoop); - // keyIter.next(); // skip "quarkus" - hasNext.invokeVirtualMethod(NI_NEXT, keyIter); + hasNext.ifNonZero(hasNext.invokeStaticMethod(PU_IS_PROPERTY_IN_ROOT, rootSet, keyIter)).falseBranch() + .continueScope(sweepLoop); // parse(config, keyIter); hasNext.invokeStaticMethod(parserBody, config, keyIter); // continue sweepLoop; @@ -902,6 +897,16 @@ private static void configSweepLoop(MethodDescriptor parserBody, MethodCreator m } } + private Set<String> getRegisteredRoots(ConfigPhase configPhase) { + Set<String> registeredRoots = new HashSet<>(); + for (RootDefinition root : roots) { + if (root.getConfigPhase().equals(configPhase)) { + registeredRoots.add(root.getPrefix()); + } + } + return registeredRoots; + } + private void installConfiguration(ResultHandle config, MethodCreator methodCreator) { // install config methodCreator.invokeStaticMethod(QCF_SET_CONFIG, config); @@ -928,14 +933,8 @@ private void generateDefaultValuesConfigSourceClass(ConfigPatternMap<Container> // there is at least one default value final BranchResult if1 = mc.ifNonZero(mc.invokeVirtualMethod(NI_HAS_NEXT, keyIter)); try (BytecodeCreator true1 = if1.trueBranch()) { - true1.invokeVirtualMethod(NI_NEXT, keyIter); - final BranchResult if2 = true1 - .ifNonZero(true1.invokeVirtualMethod(NI_PREVIOUS_EQUALS, keyIter, true1.load("quarkus"))); - try (BytecodeCreator true2 = if2.trueBranch()) { - final ResultHandle result = true2.invokeVirtualMethod( - md, mc.getThis(), keyIter); - true2.returnValue(result); - } + final ResultHandle result = true1.invokeVirtualMethod(md, mc.getThis(), keyIter); + true1.returnValue(result); } } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/configuration/definition/RootDefinition.java b/core/deployment/src/main/java/io/quarkus/deployment/configuration/definition/RootDefinition.java index f8b8f9956488f..0a8c8c8559728 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/configuration/definition/RootDefinition.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/configuration/definition/RootDefinition.java @@ -19,12 +19,14 @@ * */ public final class RootDefinition extends ClassDefinition { + private final String prefix; private final ConfigPhase configPhase; private final String rootName; private final FieldDescriptor descriptor; RootDefinition(final Builder builder) { super(builder); + this.prefix = builder.prefix; this.configPhase = builder.configPhase; String rootName = builder.rootName; final Class<?> configClass = getConfigurationClass(); @@ -76,6 +78,10 @@ public final class RootDefinition extends ClassDefinition { this.descriptor = FieldDescriptor.of(CONFIG_CLASS_NAME, String.join("", segments), configClass); } + public String getPrefix() { + return prefix; + } + public ConfigPhase getConfigPhase() { return configPhase; } @@ -84,17 +90,38 @@ public String getRootName() { return rootName; } + public String getName() { + if (prefix != null && !prefix.isEmpty()) { + if (rootName != null && !rootName.isEmpty()) { + return prefix + "." + rootName; + } else { + return prefix; + } + } else { + return rootName; + } + } + public FieldDescriptor getDescriptor() { return descriptor; } public static final class Builder extends ClassDefinition.Builder { + private String prefix = "quarkus"; private ConfigPhase configPhase = ConfigPhase.BUILD_TIME; private String rootName = ConfigItem.HYPHENATED_ELEMENT_NAME; public Builder() { } + public String getPrefix() { + return prefix; + } + + public void setPrefix(final String prefix) { + this.prefix = prefix; + } + public ConfigPhase getConfigPhase() { return configPhase; } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/configuration/matching/PatternMapBuilder.java b/core/deployment/src/main/java/io/quarkus/deployment/configuration/matching/PatternMapBuilder.java index d614f7a49caae..8d1c8a16e35cd 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/configuration/matching/PatternMapBuilder.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/configuration/matching/PatternMapBuilder.java @@ -17,21 +17,18 @@ private PatternMapBuilder() { public static ConfigPatternMap<Container> makePatterns(List<RootDefinition> rootDefinitions) { ConfigPatternMap<Container> patternMap = new ConfigPatternMap<>(); for (RootDefinition rootDefinition : rootDefinitions) { - final String rootName = rootDefinition.getRootName(); ConfigPatternMap<Container> addTo = patternMap, child; - if (!rootName.isEmpty()) { - NameIterator ni = new NameIterator(rootName); - assert ni.hasNext(); - do { - final String seg = ni.getNextSegment(); - child = addTo.getChild(seg); - ni.next(); - if (child == null) { - addTo.addChild(seg, child = new ConfigPatternMap<>()); - } - addTo = child; - } while (ni.hasNext()); - } + NameIterator ni = new NameIterator(rootDefinition.getName()); + assert ni.hasNext(); + do { + final String seg = ni.getNextSegment(); + child = addTo.getChild(seg); + ni.next(); + if (child == null) { + addTo.addChild(seg, child = new ConfigPatternMap<>()); + } + addTo = child; + } while (ni.hasNext()); addGroup(addTo, rootDefinition, null); } return patternMap; 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 a576fc64c7ad5..7df55f688507f 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 @@ -48,49 +48,65 @@ List<ConfigDescriptionBuildItem> createConfigDescriptions( return ret; } - private void processConfig(ConfigPatternMap<Container> patterns, List<ConfigDescriptionBuildItem> ret, + private void processConfig(ConfigPatternMap<Container> patterns, List<ConfigDescriptionBuildItem> ret, Properties javadoc, + ConfigPhase configPhase) { + for (String childName : patterns.childNames()) { + ConfigPatternMap<Container> child = patterns.getChild(childName); + processConfigChild(childName, child, ret, javadoc, configPhase); + } + } + + private void processConfigChild(String name, ConfigPatternMap<Container> patterns, List<ConfigDescriptionBuildItem> ret, Properties javadoc, ConfigPhase configPhase) { - patterns.forEach(new Consumer<Container>() { - @Override - public void accept(Container node) { - Field field = node.findField(); - ConfigItem configItem = field.getAnnotation(ConfigItem.class); - final ConfigProperty configProperty = field.getAnnotation(ConfigProperty.class); - String defaultDefault; - final Class<?> valueClass = field.getType(); - - EffectiveConfigTypeAndValues effectiveConfigTypeAndValues = getTypeName(field); - - if (valueClass == boolean.class) { - defaultDefault = "false"; - } else if (valueClass.isPrimitive() && valueClass != char.class) { - defaultDefault = "0"; - } else { - defaultDefault = null; - } - String defVal = defaultDefault; - if (configItem != null) { - final String itemDefVal = configItem.defaultValue(); - if (!itemDefVal.equals(ConfigItem.NO_DEFAULT)) { - defVal = itemDefVal; + Iterable<String> childNames = patterns.childNames(); + if (childNames.iterator().hasNext()) { + for (String childName : childNames) { + ConfigPatternMap<Container> child = patterns.getChild(childName); + processConfigChild(name + "." + childName, child, ret, javadoc, configPhase); + } + } else { + patterns.forEach(new Consumer<Container>() { + @Override + public void accept(Container node) { + Field field = node.findField(); + ConfigItem configItem = field.getAnnotation(ConfigItem.class); + final ConfigProperty configProperty = field.getAnnotation(ConfigProperty.class); + String defaultDefault; + final Class<?> valueClass = field.getType(); + + EffectiveConfigTypeAndValues effectiveConfigTypeAndValues = getTypeName(field); + + if (valueClass == boolean.class) { + defaultDefault = "false"; + } else if (valueClass.isPrimitive() && valueClass != char.class) { + defaultDefault = "0"; + } else { + defaultDefault = null; } - } else if (configProperty != null) { - final String propDefVal = configProperty.defaultValue(); - if (!propDefVal.equals(ConfigProperty.UNCONFIGURED_VALUE)) { - defVal = propDefVal; + String defVal = defaultDefault; + if (configItem != null) { + final String itemDefVal = configItem.defaultValue(); + if (!itemDefVal.equals(ConfigItem.NO_DEFAULT)) { + defVal = itemDefVal; + } + } else if (configProperty != null) { + final String propDefVal = configProperty.defaultValue(); + if (!propDefVal.equals(ConfigProperty.UNCONFIGURED_VALUE)) { + defVal = propDefVal; + } } + String javadocKey = field.getDeclaringClass().getName().replace('$', '.') + '.' + field.getName(); + ret.add(new ConfigDescriptionBuildItem(name, + node.findEnclosingClass().getConfigurationClass(), + defVal, + javadoc.getProperty(javadocKey), + effectiveConfigTypeAndValues.getTypeName(), + effectiveConfigTypeAndValues.getAllowedValues(), + configPhase)); } - String javadocKey = field.getDeclaringClass().getName().replace('$', '.') + '.' + field.getName(); - ret.add(new ConfigDescriptionBuildItem("quarkus." + node.getPropertyName(), - node.findEnclosingClass().getConfigurationClass(), - defVal, - javadoc.getProperty(javadocKey), - effectiveConfigTypeAndValues.getTypeName(), - effectiveConfigTypeAndValues.getAllowedValues(), - configPhase)); - } - }); + }); + } } private EffectiveConfigTypeAndValues getTypeName(Field field) { 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 0eacb06801099..90675727b9619 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 @@ -147,8 +147,12 @@ private List<String> getAdditionalBootstrapConfigSourceProviders( } @BuildStep - public SuppressNonRuntimeConfigChangedWarningBuildItem ignoreQuarkusProfileChange() { - return new SuppressNonRuntimeConfigChangedWarningBuildItem("quarkus.profile"); + public void suppressNonRuntimeConfigChanged( + BuildProducer<SuppressNonRuntimeConfigChangedWarningBuildItem> suppressNonRuntimeConfigChanged) { + suppressNonRuntimeConfigChanged.produce(new SuppressNonRuntimeConfigChangedWarningBuildItem("quarkus.profile")); + suppressNonRuntimeConfigChanged.produce(new SuppressNonRuntimeConfigChangedWarningBuildItem("quarkus.uuid")); + suppressNonRuntimeConfigChanged.produce(new SuppressNonRuntimeConfigChangedWarningBuildItem("quarkus.default-locale")); + suppressNonRuntimeConfigChanged.produce(new SuppressNonRuntimeConfigChangedWarningBuildItem("quarkus.locales")); } /** @@ -174,7 +178,7 @@ public void checkForBuildTimeConfigChange( root.getConfigPhase() == ConfigPhase.BUILD_TIME) { Iterable<ClassDefinition.ClassMember> members = root.getMembers(); - handleMembers(config, values, members, "quarkus." + root.getRootName() + ".", excludedConfigKeys); + handleMembers(config, values, members, root.getName() + ".", excludedConfigKeys); } } recorder.handleConfigChange(configurationConfig, values); 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 f0cf97b161be1..86c83f3b5f519 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 @@ -2,7 +2,7 @@ import static io.quarkus.annotation.processor.generate_doc.DocGeneratorUtil.computeConfigGroupDocFileName; import static io.quarkus.annotation.processor.generate_doc.DocGeneratorUtil.computeConfigRootDocFileName; -import static io.quarkus.annotation.processor.generate_doc.DocGeneratorUtil.deriveConfigRootName; +import static io.quarkus.annotation.processor.generate_doc.DocGeneratorUtil.getName; import java.io.IOException; import java.util.ArrayList; @@ -64,6 +64,7 @@ public void addConfigRoot(final PackageElement pkg, TypeElement clazz) { return; } + String prefix = Constants.QUARKUS; ConfigPhase configPhase = ConfigPhase.BUILD_TIME; for (AnnotationMirror annotationMirror : clazz.getAnnotationMirrors()) { @@ -71,20 +72,21 @@ public void addConfigRoot(final PackageElement pkg, TypeElement clazz) { if (annotationName.equals(Constants.ANNOTATION_CONFIG_ROOT)) { final Map<? extends ExecutableElement, ? extends AnnotationValue> elementValues = annotationMirror .getElementValues(); - String name = Constants.EMPTY; + String name = Constants.HYPHENATED_ELEMENT_NAME; for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : elementValues.entrySet()) { final String key = entry.getKey().toString(); final String value = entry.getValue().getValue().toString(); if ("name()".equals(key)) { - name = Constants.QUARKUS + Constants.DOT + value; + name = value; } else if ("phase()".equals(key)) { configPhase = ConfigPhase.valueOf(value); + } else if ("prefix()".equals(key)) { + prefix = value; } } - if (name.isEmpty()) { - name = deriveConfigRootName(clazz.getSimpleName().toString(), configPhase); - } else if (name.endsWith(Constants.DOT + Constants.PARENT)) { + 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 name = name.replace(Constants.DOT + Constants.PARENT, ""); } diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/DocGeneratorUtil.java b/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/DocGeneratorUtil.java index f4672bd4fa50d..7b4fc3fb99e45 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/DocGeneratorUtil.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/DocGeneratorUtil.java @@ -448,22 +448,39 @@ private static String typeSimpleName(TypeMirror typeMirror) { return type.substring(1 + type.lastIndexOf(Constants.DOT)); } - static String deriveConfigRootName(String simpleClassName, ConfigPhase configPhase) { + static String getName(String prefix, String name, String simpleClassName, ConfigPhase configPhase) { + if (name.equals(Constants.HYPHENATED_ELEMENT_NAME)) { + return deriveConfigRootName(simpleClassName, prefix, configPhase); + } + + if (!prefix.isEmpty()) { + if (!name.isEmpty()) { + return prefix + Constants.DOT + name; + } else { + return prefix; + } + } else { + return name; + } + } + + static String deriveConfigRootName(String simpleClassName, String prefix, ConfigPhase configPhase) { String simpleNameInLowerCase = simpleClassName.toLowerCase(); int length = simpleNameInLowerCase.length(); if (simpleNameInLowerCase.endsWith(CONFIG.toLowerCase())) { String sanitized = simpleClassName.substring(0, length - CONFIG.length()); - return deriveConfigRootName(sanitized, configPhase); + return deriveConfigRootName(sanitized, prefix, configPhase); } else if (simpleNameInLowerCase.endsWith(CONFIGURATION.toLowerCase())) { String sanitized = simpleClassName.substring(0, length - CONFIGURATION.length()); - return deriveConfigRootName(sanitized, configPhase); + return deriveConfigRootName(sanitized, prefix, configPhase); } else if (simpleNameInLowerCase.endsWith(configPhase.getConfigSuffix().toLowerCase())) { String sanitized = simpleClassName.substring(0, length - configPhase.getConfigSuffix().length()); - return deriveConfigRootName(sanitized, configPhase); + return deriveConfigRootName(sanitized, prefix, configPhase); } - return Constants.QUARKUS + Constants.DOT + hyphenate(simpleClassName); + return !prefix.isEmpty() ? prefix + Constants.DOT + hyphenate(simpleClassName) + : Constants.QUARKUS + Constants.DOT + hyphenate(simpleClassName); } /** diff --git a/core/processor/src/test/java/io/quarkus/annotation/processor/generate_doc/DocGeneratorUtilTest.java b/core/processor/src/test/java/io/quarkus/annotation/processor/generate_doc/DocGeneratorUtilTest.java index 5266429f28734..46a201d6deb5f 100644 --- a/core/processor/src/test/java/io/quarkus/annotation/processor/generate_doc/DocGeneratorUtilTest.java +++ b/core/processor/src/test/java/io/quarkus/annotation/processor/generate_doc/DocGeneratorUtilTest.java @@ -9,6 +9,7 @@ import static io.quarkus.annotation.processor.generate_doc.DocGeneratorUtil.computeExtensionDocFileName; import static io.quarkus.annotation.processor.generate_doc.DocGeneratorUtil.deriveConfigRootName; import static io.quarkus.annotation.processor.generate_doc.DocGeneratorUtil.getJavaDocSiteLink; +import static io.quarkus.annotation.processor.generate_doc.DocGeneratorUtil.getName; import static io.quarkus.annotation.processor.generate_doc.DocGeneratorUtil.normalizeDurationValue; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -317,43 +318,90 @@ public void shouldDeepAppendConfigSectionConfigItemsIntoExistingConfigItemsOfCon assertEquals(configItem, deepSection.getConfigDocItems().get(1)); } + @Test + void getNameTest() { + String prefix = Constants.QUARKUS; + String name = Constants.HYPHENATED_ELEMENT_NAME; + String simpleClassName = "MyConfig"; + String actual = getName(prefix, name, simpleClassName, ConfigPhase.RUN_TIME); + assertEquals("quarkus.my", actual); + + prefix = "my.prefix"; + name = ""; + simpleClassName = "MyPrefix"; + actual = getName(prefix, name, simpleClassName, ConfigPhase.RUN_TIME); + assertEquals("my.prefix", actual); + + prefix = ""; + name = "my.prefix"; + simpleClassName = "MyPrefix"; + actual = getName(prefix, name, simpleClassName, ConfigPhase.RUN_TIME); + assertEquals("my.prefix", actual); + + prefix = "my"; + name = "prefix"; + simpleClassName = "MyPrefix"; + actual = getName(prefix, name, simpleClassName, ConfigPhase.RUN_TIME); + assertEquals("my.prefix", actual); + + prefix = Constants.QUARKUS; + name = "prefix"; + simpleClassName = "SomethingElse"; + actual = getName(prefix, name, simpleClassName, ConfigPhase.RUN_TIME); + assertEquals("quarkus.prefix", actual); + + prefix = ""; + name = Constants.HYPHENATED_ELEMENT_NAME; + simpleClassName = "SomethingElse"; + actual = getName(prefix, name, simpleClassName, ConfigPhase.RUN_TIME); + assertEquals("quarkus.something-else", actual); + } + @Test public void derivingConfigRootNameTestCase() { // should hyphenate class name String simpleClassName = "RootName"; - String actual = deriveConfigRootName(simpleClassName, ConfigPhase.RUN_TIME); + String actual = deriveConfigRootName(simpleClassName, "", ConfigPhase.RUN_TIME); assertEquals("quarkus.root-name", actual); // should hyphenate class name after removing Config(uration) suffix simpleClassName = "RootNameConfig"; - actual = deriveConfigRootName(simpleClassName, ConfigPhase.BUILD_TIME); + actual = deriveConfigRootName(simpleClassName, "", ConfigPhase.BUILD_TIME); assertEquals("quarkus.root-name", actual); simpleClassName = "RootNameConfiguration"; - actual = deriveConfigRootName(simpleClassName, ConfigPhase.BUILD_AND_RUN_TIME_FIXED); + actual = deriveConfigRootName(simpleClassName, "", ConfigPhase.BUILD_AND_RUN_TIME_FIXED); assertEquals("quarkus.root-name", actual); // should hyphenate class name after removing RunTimeConfig(uration) suffix simpleClassName = "RootNameRunTimeConfig"; - actual = deriveConfigRootName(simpleClassName, ConfigPhase.RUN_TIME); + actual = deriveConfigRootName(simpleClassName, "", ConfigPhase.RUN_TIME); assertEquals("quarkus.root-name", actual); simpleClassName = "RootNameRuntimeConfig"; - actual = deriveConfigRootName(simpleClassName, ConfigPhase.RUN_TIME); + actual = deriveConfigRootName(simpleClassName, "", ConfigPhase.RUN_TIME); assertEquals("quarkus.root-name", actual); simpleClassName = "RootNameRunTimeConfiguration"; - actual = deriveConfigRootName(simpleClassName, ConfigPhase.RUN_TIME); + actual = deriveConfigRootName(simpleClassName, "", ConfigPhase.RUN_TIME); assertEquals("quarkus.root-name", actual); // should hyphenate class name after removing BuildTimeConfig(uration) suffix simpleClassName = "RootNameBuildTimeConfig"; - actual = deriveConfigRootName(simpleClassName, ConfigPhase.BUILD_AND_RUN_TIME_FIXED); + actual = deriveConfigRootName(simpleClassName, "", ConfigPhase.BUILD_AND_RUN_TIME_FIXED); assertEquals("quarkus.root-name", actual); simpleClassName = "RootNameBuildTimeConfiguration"; - actual = deriveConfigRootName(simpleClassName, ConfigPhase.BUILD_TIME); + actual = deriveConfigRootName(simpleClassName, "", ConfigPhase.BUILD_TIME); assertEquals("quarkus.root-name", actual); + + simpleClassName = "RootName"; + actual = deriveConfigRootName(simpleClassName, "prefix", ConfigPhase.RUN_TIME); + assertEquals("prefix.root-name", actual); + + simpleClassName = "RootName"; + actual = deriveConfigRootName(simpleClassName, "my.prefix", ConfigPhase.RUN_TIME); + assertEquals("my.prefix.root-name", actual); } @Test diff --git a/core/runtime/src/main/java/io/quarkus/runtime/annotations/ConfigRoot.java b/core/runtime/src/main/java/io/quarkus/runtime/annotations/ConfigRoot.java index a04b6fb81d12d..5124cb4d812ad 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/annotations/ConfigRoot.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/annotations/ConfigRoot.java @@ -17,6 +17,13 @@ @Target(TYPE) @Documented public @interface ConfigRoot { + /** + * Determine the prefix key of the configuration root. + * + * @return the prefix key name + */ + String prefix() default "quarkus"; + /** * Determine the phase of this configuration root. * diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigUtils.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigUtils.java index c9d2c35e3088c..315ebae2d7c17 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigUtils.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigUtils.java @@ -11,18 +11,16 @@ import java.net.URL; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.OptionalInt; import java.util.Set; import java.util.SortedSet; -import java.util.TreeMap; import java.util.TreeSet; import java.util.UUID; -import java.util.function.BiConsumer; import java.util.function.IntFunction; import org.eclipse.microprofile.config.ConfigProvider; @@ -223,7 +221,7 @@ static class BuildTimeEnvConfigSource extends EnvConfigSource { @Override public Set<String> getPropertyNames() { - return new HashSet<>(); + return Collections.emptySet(); } @Override @@ -257,28 +255,18 @@ public String getName() { /** * We only want to include properties in the quarkus namespace. + * + * We removed the filter on the quarkus namespace due to the any prefix support for ConfigRoot. Filtering is now + * done in io.quarkus.deployment.configuration.BuildTimeConfigurationReader.ReadOperation#getAllProperties. */ static class BuildTimeSysPropConfigSource extends SysPropConfigSource { - public Map<String, String> getProperties() { - BuildTimeSysPropMapProducer buildTimeSysPropMapProducer = new BuildTimeSysPropMapProducer(); - System.getProperties().forEach(buildTimeSysPropMapProducer); - return buildTimeSysPropMapProducer.output; - } - public String getName() { return "System properties"; } - } - - private static class BuildTimeSysPropMapProducer implements BiConsumer<Object, Object> { - final Map<String, String> output = new TreeMap<>(); @Override - public void accept(Object k, Object v) { - String key = (String) k; - if (key.startsWith("quarkus.")) { - output.put(key, v.toString()); - } + public Set<String> getPropertyNames() { + return Collections.emptySet(); } } } diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/PropertiesUtil.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/PropertiesUtil.java new file mode 100644 index 0000000000000..4af3b4e9e276c --- /dev/null +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/PropertiesUtil.java @@ -0,0 +1,50 @@ +package io.quarkus.runtime.configuration; + +import java.util.Set; + +public class PropertiesUtil { + private PropertiesUtil() { + } + + public static boolean isPropertyInRoot(Set<String> roots, NameIterator propertyName) { + for (String root : roots) { + // match everything + if (root.length() == 0) { + return true; + } + + // A sub property from a namespace is always bigger in length + if (propertyName.getName().length() <= root.length()) { + continue; + } + + final NameIterator rootNi = new NameIterator(root); + // compare segments + while (rootNi.hasNext()) { + String segment = rootNi.getNextSegment(); + if (!propertyName.hasNext()) { + propertyName.goToStart(); + break; + } + + final String nextSegment = propertyName.getNextSegment(); + if (!segment.equals(nextSegment)) { + propertyName.goToStart(); + break; + } + + rootNi.next(); + propertyName.next(); + + // root has no more segments and we reached this far so everything matched. + // on top, property still has more segments to do the mapping. + if (!rootNi.hasNext() && propertyName.hasNext()) { + propertyName.goToStart(); + return true; + } + } + } + + return false; + } +} diff --git a/core/test-extension/deployment/src/main/java/io/quarkus/extest/deployment/PrefixConfigTestProcessor.java b/core/test-extension/deployment/src/main/java/io/quarkus/extest/deployment/PrefixConfigTestProcessor.java new file mode 100644 index 0000000000000..5d733d5c9a24f --- /dev/null +++ b/core/test-extension/deployment/src/main/java/io/quarkus/extest/deployment/PrefixConfigTestProcessor.java @@ -0,0 +1,32 @@ +package io.quarkus.extest.deployment; + +import java.util.List; + +import io.quarkus.arc.deployment.ConfigPropertyBuildItem; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.ConfigDescriptionBuildItem; +import io.quarkus.extest.runtime.config.PrefixBuildTimeConfig; +import io.quarkus.extest.runtime.config.PrefixConfig; + +public class PrefixConfigTestProcessor { + @BuildStep + void validateBuildTime(BuildProducer<ConfigPropertyBuildItem> configProperties, + PrefixBuildTimeConfig prefixBuildTimeConfig) { + assert prefixBuildTimeConfig.prop.equals("1234"); + assert prefixBuildTimeConfig.map.get("prop").equals("1234"); + } + + @BuildStep + void validateRuntime(BuildProducer<ConfigPropertyBuildItem> configProperties, + List<ConfigDescriptionBuildItem> configDescriptionBuildItems, + PrefixConfig prefixConfig) { + assert prefixConfig.prop.equals("1234"); + assert prefixConfig.map.get("prop").equals("1234"); + assert prefixConfig.nested.nestedValue.equals("nested-1234"); + assert prefixConfig.nested.oov.getPart1().equals("nested-1234"); + assert prefixConfig.nested.oov.getPart2().equals("nested-5678"); + assert configDescriptionBuildItems.stream().map(ConfigDescriptionBuildItem::getPropertyName) + .anyMatch("my.prefix.prop"::equals); + } +} 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 7c2aa916918cd..964b48a7f560c 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 @@ -60,14 +60,17 @@ import io.quarkus.extest.runtime.TestRecorder; import io.quarkus.extest.runtime.beans.CommandServlet; 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.ObjectOfValue; import io.quarkus.extest.runtime.config.ObjectValueOf; +import io.quarkus.extest.runtime.config.PrefixConfig; 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.TestRunTimeConfig; import io.quarkus.extest.runtime.config.XmlConfig; +import io.quarkus.extest.runtime.config.named.PrefixNamedConfig; import io.quarkus.extest.runtime.logging.AdditionalLogHandlerValueFactory; import io.quarkus.extest.runtime.runtimeinitializedpackage.RuntimeInitializedClass; import io.quarkus.extest.runtime.subst.DSAPublicKeyObjectSubstitution; @@ -383,11 +386,13 @@ void scanForBeans(TestRecorder recorder, BeanArchiveIndexBuildItem beanArchiveIn @Record(RUNTIME_INIT) void configureBeans(TestRecorder recorder, List<TestBeanBuildItem> testBeans, BeanContainerBuildItem beanContainer, - TestRunTimeConfig runTimeConfig, FooRuntimeConfig fooRuntimeConfig) { + TestRunTimeConfig runTimeConfig, FooRuntimeConfig fooRuntimeConfig, PrefixConfig prefixConfig, + PrefixNamedConfig prefixNamedConfig, + AnotherPrefixConfig anotherPrefixConfig) { for (TestBeanBuildItem testBeanBuildItem : testBeans) { Class<IConfigConsumer> beanClass = testBeanBuildItem.getConfigConsumer(); recorder.configureBeans(beanContainer.getValue(), beanClass, buildAndRunTimeConfig, runTimeConfig, - fooRuntimeConfig); + fooRuntimeConfig, prefixConfig, prefixNamedConfig, anotherPrefixConfig); } } diff --git a/core/test-extension/deployment/src/main/resources/application.properties b/core/test-extension/deployment/src/main/resources/application.properties index 2786b4c2b8c1b..5826f053699c2 100644 --- a/core/test-extension/deployment/src/main/resources/application.properties +++ b/core/test-extension/deployment/src/main/resources/application.properties @@ -151,3 +151,20 @@ quarkus.arc.unremovable-types[0]=foo ### Do not record env values in build time bt.do.not.record=properties + +### prefix +my.prefix.prop=1234 +my.prefix.map.prop=1234 +my.prefix.nested.nested-value=nested-1234 +my.prefix.nested.oov=nested-1234+nested-5678 +my.prefix.named.prop=1234 +my.prefix.named.map.prop=1234 +my.prefix.named.nested.nested-value=nested-1234 +my.prefix.named.nested.oov=nested-1234+nested-5678 + +my.prefix.bt.prop=1234 +my.prefix.bt.nested.nested-value=nested-1234 +my.prefix.bt.nested.oov=nested-1234+nested-5678 + +another.another-prefix.prop=5678 +another.another-prefix.map.prop=5678 diff --git a/core/test-extension/deployment/src/test/java/io/quarkus/extest/ConfiguredBean.java b/core/test-extension/deployment/src/test/java/io/quarkus/extest/ConfiguredBean.java index b5c34882e94a6..50f29b7b7e287 100644 --- a/core/test-extension/deployment/src/test/java/io/quarkus/extest/ConfiguredBean.java +++ b/core/test-extension/deployment/src/test/java/io/quarkus/extest/ConfiguredBean.java @@ -1,14 +1,13 @@ package io.quarkus.extest; -import javax.enterprise.event.Observes; - import io.quarkus.extest.runtime.IConfigConsumer; import io.quarkus.extest.runtime.TestAnnotation; +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.runtime.ShutdownEvent; -import io.quarkus.runtime.StartupEvent; +import io.quarkus.extest.runtime.config.named.PrefixNamedConfig; /** * A sample bean @@ -18,10 +17,9 @@ public class ConfiguredBean implements IConfigConsumer { volatile TestRunTimeConfig runTimeConfig; volatile TestBuildAndRunTimeConfig buildTimeConfig; volatile FooRuntimeConfig fooRuntimeConfig; - - public ConfiguredBean() { - System.out.printf("ConfiguredBean.ctor, %s%n", super.toString()); - } + volatile PrefixConfig prefixConfig; + volatile PrefixNamedConfig prefixNamedConfig; + volatile AnotherPrefixConfig anotherPrefixConfig; /** * Called by runtime with the runtime config object @@ -30,25 +28,14 @@ public ConfiguredBean() { */ @Override public void loadConfig(TestBuildAndRunTimeConfig buildTimeConfig, TestRunTimeConfig runTimeConfig, - FooRuntimeConfig fooRuntimeConfig) { - System.out.printf("loadConfig, buildTimeConfig=%s, runTimeConfig=%s, fooRuntimeConfig=%s%n", buildTimeConfig, - runTimeConfig, fooRuntimeConfig); + FooRuntimeConfig fooRuntimeConfig, PrefixConfig prefixConfig, PrefixNamedConfig prefixNamedConfig, + AnotherPrefixConfig anotherPrefixConfig) { this.buildTimeConfig = buildTimeConfig; this.runTimeConfig = runTimeConfig; this.fooRuntimeConfig = fooRuntimeConfig; - } - - /** - * Called when the runtime has started - * - * @param event - */ - void onStart(@Observes StartupEvent event) { - System.out.printf("onStart, event=%s%n", event); - } - - void onStop(@Observes ShutdownEvent event) { - System.out.printf("onStop, event=%s%n", event); + this.prefixConfig = prefixConfig; + this.prefixNamedConfig = prefixNamedConfig; + this.anotherPrefixConfig = anotherPrefixConfig; } public TestRunTimeConfig getRunTimeConfig() { @@ -63,8 +50,15 @@ public FooRuntimeConfig getFooRuntimeConfig() { return fooRuntimeConfig; } - @Override - public String toString() { - return "ConfiguredBean{runTimeConfig=" + runTimeConfig + '}'; + public PrefixConfig getPrefixConfig() { + return prefixConfig; + } + + public PrefixNamedConfig getPrefixNamedConfig() { + return prefixNamedConfig; + } + + public AnotherPrefixConfig getAnotherPrefixConfig() { + return anotherPrefixConfig; } } 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 ddb88de43866a..1e5d7277733fb 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 @@ -2,7 +2,10 @@ import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.BufferedReader; import java.io.InputStreamReader; @@ -28,13 +31,16 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.quarkus.extest.runtime.config.AnotherPrefixConfig; import io.quarkus.extest.runtime.config.MyEnum; import io.quarkus.extest.runtime.config.NestedConfig; import io.quarkus.extest.runtime.config.ObjectOfValue; import io.quarkus.extest.runtime.config.ObjectValueOf; import io.quarkus.extest.runtime.config.OverrideBuildTimeConfigSource; +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.named.PrefixNamedConfig; import io.quarkus.test.QuarkusUnitTest; import io.restassured.RestAssured; @@ -359,4 +365,39 @@ public void testProfileDefaultValuesSource() { assertEquals("1234", defaultValues.getValue("%test.my.prop")); assertEquals("1234", config.getValue("my.prop", String.class)); } + + @Test + void prefixConfig() { + PrefixConfig prefixConfig = configuredBean.getPrefixConfig(); + assertNotNull(prefixConfig); + assertEquals("1234", prefixConfig.prop); + assertEquals("1234", prefixConfig.map.get("prop")); + assertEquals("nested-1234", prefixConfig.nested.nestedValue); + assertEquals("nested-1234", prefixConfig.nested.oov.getPart1()); + assertEquals("nested-5678", prefixConfig.nested.oov.getPart2()); + + PrefixNamedConfig prefixNamedConfig = configuredBean.getPrefixNamedConfig(); + assertNotNull(prefixNamedConfig); + assertEquals("1234", prefixNamedConfig.prop); + assertEquals("1234", prefixNamedConfig.map.get("prop")); + assertEquals("nested-1234", prefixNamedConfig.nested.nestedValue); + assertEquals("nested-1234", prefixNamedConfig.nested.oov.getPart1()); + assertEquals("nested-5678", prefixNamedConfig.nested.oov.getPart2()); + + AnotherPrefixConfig anotherPrefixConfig = configuredBean.getAnotherPrefixConfig(); + assertNotNull(anotherPrefixConfig); + assertEquals("5678", anotherPrefixConfig.prop); + assertEquals("5678", anotherPrefixConfig.map.get("prop")); + + ConfigSource defaultValues = null; + for (ConfigSource configSource : config.getConfigSources()) { + if (configSource.getName().contains("PropertiesConfigSource[source=Specified default values]")) { + defaultValues = configSource; + break; + } + } + assertNotNull(defaultValues); + // java.version should not be recorded + assertFalse(defaultValues.getPropertyNames().contains("java.version")); + } } diff --git a/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/IConfigConsumer.java b/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/IConfigConsumer.java index 7af418953384c..fa04b5d36543a 100644 --- a/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/IConfigConsumer.java +++ b/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/IConfigConsumer.java @@ -1,13 +1,17 @@ package io.quarkus.extest.runtime; +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.named.PrefixNamedConfig; /** * Interface used to pass the runtime configuration to an application bean for validation */ public interface IConfigConsumer { void loadConfig(TestBuildAndRunTimeConfig buildTimeConfig, TestRunTimeConfig runTimeConfig, - FooRuntimeConfig fooRuntimeConfig); + FooRuntimeConfig fooRuntimeConfig, PrefixConfig prefixConfig, PrefixNamedConfig prefixNamedConfig, + AnotherPrefixConfig anotherPrefixConfig); } 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 412c2ebb9aa01..5984d6b55e2ae 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,10 +8,13 @@ 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.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; @@ -31,14 +34,18 @@ public class TestRecorder { * @param beanClass - IConfigConsumer * @param buildTimeConfig - the extension TestBuildAndRunTimeConfig * @param runTimeConfig - the extension TestRunTimeConfig - * @see IConfigConsumer#loadConfig(TestBuildAndRunTimeConfig, TestRunTimeConfig) + * @see IConfigConsumer#loadConfig(TestBuildAndRunTimeConfig, TestRunTimeConfig, FooRuntimeConfig, PrefixConfig, + * PrefixNamedConfig, AnotherPrefixConfig) */ public void configureBeans(BeanContainer beanContainer, Class<IConfigConsumer> beanClass, TestBuildAndRunTimeConfig buildTimeConfig, - TestRunTimeConfig runTimeConfig, FooRuntimeConfig fooRuntimeConfig) { + 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); + instance.loadConfig(buildTimeConfig, runTimeConfig, fooRuntimeConfig, prefixConfig, prefixNamedConfig, + anotherPrefixConfig); log.infof("configureBeans, instance=%s\n", instance); } diff --git a/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/AnotherPrefixConfig.java b/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/AnotherPrefixConfig.java new file mode 100644 index 0000000000000..478c285b8c8d0 --- /dev/null +++ b/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/AnotherPrefixConfig.java @@ -0,0 +1,17 @@ +package io.quarkus.extest.runtime.config; + +import java.util.Map; + +import io.quarkus.runtime.annotations.ConfigItem; +import io.quarkus.runtime.annotations.ConfigPhase; +import io.quarkus.runtime.annotations.ConfigRoot; + +@ConfigRoot(prefix = "another", phase = ConfigPhase.BUILD_AND_RUN_TIME_FIXED) +public class AnotherPrefixConfig { + /** */ + @ConfigItem + public String prop; + /** */ + @ConfigItem + public Map<String, String> map; +} diff --git a/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/PrefixBuildTimeConfig.java b/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/PrefixBuildTimeConfig.java new file mode 100644 index 0000000000000..a7da5173518ea --- /dev/null +++ b/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/PrefixBuildTimeConfig.java @@ -0,0 +1,24 @@ +package io.quarkus.extest.runtime.config; + +import java.util.Map; + +import io.quarkus.runtime.annotations.ConfigItem; +import io.quarkus.runtime.annotations.ConfigPhase; +import io.quarkus.runtime.annotations.ConfigRoot; + +@ConfigRoot(prefix = "my.prefix.bt", phase = ConfigPhase.BUILD_TIME, name = "") +public class PrefixBuildTimeConfig { + /** */ + @ConfigItem + public String prop; + /** */ + @ConfigItem + public Map<String, String> map; + /** */ + @ConfigItem + public NestedConfig nested; + + static { + System.setProperty("my.prefix.bt.map.prop", "1234"); + } +} diff --git a/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/PrefixConfig.java b/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/PrefixConfig.java new file mode 100644 index 0000000000000..8cb25b5d241a3 --- /dev/null +++ b/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/PrefixConfig.java @@ -0,0 +1,20 @@ +package io.quarkus.extest.runtime.config; + +import java.util.Map; + +import io.quarkus.runtime.annotations.ConfigItem; +import io.quarkus.runtime.annotations.ConfigPhase; +import io.quarkus.runtime.annotations.ConfigRoot; + +@ConfigRoot(prefix = "my.prefix", phase = ConfigPhase.BUILD_AND_RUN_TIME_FIXED, name = "") +public class PrefixConfig { + /** */ + @ConfigItem + public String prop; + /** */ + @ConfigItem + public Map<String, String> map; + /** */ + @ConfigItem + public NestedConfig nested; +} diff --git a/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/named/PrefixNamedConfig.java b/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/named/PrefixNamedConfig.java new file mode 100644 index 0000000000000..bca9df5720ea2 --- /dev/null +++ b/core/test-extension/runtime/src/main/java/io/quarkus/extest/runtime/config/named/PrefixNamedConfig.java @@ -0,0 +1,21 @@ +package io.quarkus.extest.runtime.config.named; + +import java.util.Map; + +import io.quarkus.extest.runtime.config.NestedConfig; +import io.quarkus.runtime.annotations.ConfigItem; +import io.quarkus.runtime.annotations.ConfigPhase; +import io.quarkus.runtime.annotations.ConfigRoot; + +@ConfigRoot(prefix = "my.prefix", phase = ConfigPhase.BUILD_AND_RUN_TIME_FIXED, name = "named") +public class PrefixNamedConfig { + /** */ + @ConfigItem + public String prop; + /** */ + @ConfigItem + public Map<String, String> map; + /** */ + @ConfigItem + public NestedConfig nested; +} diff --git a/integration-tests/test-extension/src/main/java/io/quarkus/it/extension/NativeBean.java b/integration-tests/test-extension/src/main/java/io/quarkus/it/extension/NativeBean.java deleted file mode 100644 index 61f11ff16246e..0000000000000 --- a/integration-tests/test-extension/src/main/java/io/quarkus/it/extension/NativeBean.java +++ /dev/null @@ -1,51 +0,0 @@ -package io.quarkus.it.extension; - -import javax.enterprise.event.Observes; - -import io.quarkus.extest.runtime.IConfigConsumer; -import io.quarkus.extest.runtime.TestAnnotation; -import io.quarkus.extest.runtime.config.FooRuntimeConfig; -import io.quarkus.extest.runtime.config.TestBuildAndRunTimeConfig; -import io.quarkus.extest.runtime.config.TestRunTimeConfig; -import io.quarkus.runtime.ShutdownEvent; -import io.quarkus.runtime.StartupEvent; - -@TestAnnotation -public class NativeBean implements IConfigConsumer { - volatile TestRunTimeConfig runTimeConfig; - volatile TestBuildAndRunTimeConfig buildTimeConfig; - volatile FooRuntimeConfig fooRuntimeConfig; - - public NativeBean() { - System.out.printf("NativeBean.ctor, %s%n", super.toString()); - } - - /** - * Called by runtime with the runtime config object - * - * @param runTimeConfig - */ - @Override - public void loadConfig(TestBuildAndRunTimeConfig buildTimeConfig, TestRunTimeConfig runTimeConfig, - FooRuntimeConfig fooRuntimeConfig) { - System.out.printf("loadConfig, buildTimeConfig=%s, runTimeConfig=%s, fooRuntimeConfig=%s%n", buildTimeConfig, - runTimeConfig, fooRuntimeConfig); - this.buildTimeConfig = buildTimeConfig; - this.runTimeConfig = runTimeConfig; - this.fooRuntimeConfig = fooRuntimeConfig; - } - - /** - * Called when the runtime has started - * - * @param event - */ - void onStart(@Observes StartupEvent event) { - System.out.printf("onStart, event=%s%n", event); - } - - void onStop(@Observes ShutdownEvent event) { - System.out.printf("onStop, event=%s%n", event); - } - -}