mirror of
https://github.com/jlengrand/quarkus.git
synced 2026-03-10 08:41:22 +00:00
Add first version of bootstrap config support
This commit is contained in:
@@ -363,12 +363,13 @@ stages:
|
||||
parameters:
|
||||
poolSettings: ${{parameters.poolSettings}}
|
||||
expectUseVMs: ${{parameters.expectUseVMs}}
|
||||
timeoutInMinutes: 30
|
||||
timeoutInMinutes: 35
|
||||
modules:
|
||||
- tika
|
||||
- hibernate-validator
|
||||
- test-extension
|
||||
- logging-gelf
|
||||
- bootstrap-config
|
||||
name: misc_2
|
||||
|
||||
- template: native-build-steps.yaml
|
||||
|
||||
@@ -66,8 +66,10 @@ import io.quarkus.deployment.builditem.BytecodeRecorderObjectLoaderBuildItem;
|
||||
import io.quarkus.deployment.builditem.CapabilityBuildItem;
|
||||
import io.quarkus.deployment.builditem.ConfigurationBuildItem;
|
||||
import io.quarkus.deployment.builditem.DeploymentClassLoaderBuildItem;
|
||||
import io.quarkus.deployment.builditem.MainBootstrapConfigBytecodeRecorderBuildItem;
|
||||
import io.quarkus.deployment.builditem.MainBytecodeRecorderBuildItem;
|
||||
import io.quarkus.deployment.builditem.RunTimeConfigurationProxyBuildItem;
|
||||
import io.quarkus.deployment.builditem.RunTimeConfigurationSourceValueBuildItem;
|
||||
import io.quarkus.deployment.builditem.StaticBytecodeRecorderBuildItem;
|
||||
import io.quarkus.deployment.configuration.BuildTimeConfigurationReader;
|
||||
import io.quarkus.deployment.configuration.DefaultValuesConfigurationSource;
|
||||
@@ -352,6 +354,8 @@ public final class ExtensionLoader {
|
||||
if (phase == ConfigPhase.BUILD_AND_RUN_TIME_FIXED) {
|
||||
runTimeProxies.computeIfAbsent(parameterClass, readResult::requireRootObjectForClass);
|
||||
}
|
||||
} else if (phase == ConfigPhase.BOOTSTRAP) {
|
||||
throw reportError(parameter, "Bootstrap configuration cannot be consumed here");
|
||||
} else if (phase == ConfigPhase.RUN_TIME) {
|
||||
throw reportError(parameter, "Run time configuration cannot be consumed here");
|
||||
} else {
|
||||
@@ -467,6 +471,8 @@ public final class ExtensionLoader {
|
||||
if (phase == ConfigPhase.BUILD_AND_RUN_TIME_FIXED) {
|
||||
runTimeProxies.computeIfAbsent(fieldClass, readResult::requireRootObjectForClass);
|
||||
}
|
||||
} else if (phase == ConfigPhase.BOOTSTRAP) {
|
||||
throw reportError(field, "Bootstrap configuration cannot be consumed here");
|
||||
} else if (phase == ConfigPhase.RUN_TIME) {
|
||||
throw reportError(field, "Run time configuration cannot be consumed here");
|
||||
} else {
|
||||
@@ -539,6 +545,8 @@ public final class ExtensionLoader {
|
||||
final ConfigPhase phase = annotation.phase();
|
||||
if (phase.isAvailableAtBuild()) {
|
||||
paramSuppList.add(() -> readResult.requireRootObjectForClass(parameterClass));
|
||||
} else if (phase == ConfigPhase.BOOTSTRAP) {
|
||||
throw reportError(parameter, "Bootstrap configuration cannot be consumed here");
|
||||
} else if (phase == ConfigPhase.RUN_TIME) {
|
||||
throw reportError(parameter, "Run time configuration cannot be consumed here");
|
||||
} else {
|
||||
@@ -572,6 +580,8 @@ public final class ExtensionLoader {
|
||||
if (phase.isAvailableAtBuild()) {
|
||||
setup = setup.andThen(o -> ReflectUtil.setFieldVal(field, o,
|
||||
readResult.requireRootObjectForClass(fieldClass)));
|
||||
} else if (phase == ConfigPhase.BOOTSTRAP) {
|
||||
throw reportError(field, "Bootstrap configuration cannot be consumed here");
|
||||
} else if (phase == ConfigPhase.RUN_TIME) {
|
||||
throw reportError(field, "Run time configuration cannot be consumed here");
|
||||
} else {
|
||||
@@ -634,9 +644,10 @@ public final class ExtensionLoader {
|
||||
assert recordAnnotation != null;
|
||||
final ExecutionTime executionTime = recordAnnotation.value();
|
||||
final boolean optional = recordAnnotation.optional();
|
||||
|
||||
methodStepConfig = methodStepConfig.andThen(bsb -> bsb.produces(
|
||||
executionTime == ExecutionTime.STATIC_INIT ? StaticBytecodeRecorderBuildItem.class
|
||||
: MainBytecodeRecorderBuildItem.class,
|
||||
: determineMainRecorderBuildItemType(method),
|
||||
optional ? ProduceFlags.of(ProduceFlag.WEAK) : ProduceFlags.NONE));
|
||||
}
|
||||
EnumSet<ConfigPhase> methodConsumingConfigPhases = consumingConfigPhases.clone();
|
||||
@@ -733,8 +744,14 @@ public final class ExtensionLoader {
|
||||
if (isRecorder && phase == ConfigPhase.BUILD_AND_RUN_TIME_FIXED) {
|
||||
runTimeProxies.computeIfAbsent(parameterClass, readResult::requireRootObjectForClass);
|
||||
}
|
||||
} else if (phase == ConfigPhase.RUN_TIME) {
|
||||
} else if (phase == ConfigPhase.BOOTSTRAP || phase == ConfigPhase.RUN_TIME) {
|
||||
if (isRecorder) {
|
||||
if ((phase == ConfigPhase.BOOTSTRAP)
|
||||
&& !method.getReturnType().equals(RunTimeConfigurationSourceValueBuildItem.class)) {
|
||||
throw reportError(parameter,
|
||||
"Bootstrap configuration can only be used in a Build step that returns "
|
||||
+ RunTimeConfigurationSourceValueBuildItem.class.getSimpleName());
|
||||
}
|
||||
methodParamFns.add((bc, bri) -> {
|
||||
final RunTimeConfigurationProxyBuildItem proxies = bc
|
||||
.consume(RunTimeConfigurationProxyBuildItem.class);
|
||||
@@ -743,7 +760,9 @@ public final class ExtensionLoader {
|
||||
runTimeProxies.computeIfAbsent(parameterClass, ReflectUtil::newInstance);
|
||||
} else {
|
||||
throw reportError(parameter,
|
||||
"Run time configuration cannot be consumed here unless the method is a @Recorder");
|
||||
String.format(
|
||||
"%s configuration cannot be consumed here unless the method is a @Recorder",
|
||||
phase == ConfigPhase.RUN_TIME ? "Run time" : "Bootstrap"));
|
||||
}
|
||||
} else {
|
||||
throw reportError(parameterClass, "Unknown value for ConfigPhase");
|
||||
@@ -778,6 +797,12 @@ public final class ExtensionLoader {
|
||||
resultConsumer = Functions.discardingBiConsumer();
|
||||
} else if (rawTypeExtends(returnType, BuildItem.class)) {
|
||||
final Class<? extends BuildItem> type = method.getReturnType().asSubclass(BuildItem.class);
|
||||
if (type.equals(RunTimeConfigurationSourceValueBuildItem.class)
|
||||
&& (!isRecorder || recordAnnotation.value() != ExecutionTime.RUNTIME_INIT)) {
|
||||
throw reportError(method,
|
||||
"A Build step that returns " + RunTimeConfigurationSourceValueBuildItem.class.getSimpleName()
|
||||
+ " must also be annotated with @Record(ExecutionTime.RUNTIME_INIT)");
|
||||
}
|
||||
if (overridable) {
|
||||
if (weak) {
|
||||
methodStepConfig = methodStepConfig
|
||||
@@ -837,7 +862,8 @@ public final class ExtensionLoader {
|
||||
throw reportError(method, "Unsupported method return type " + returnType);
|
||||
}
|
||||
|
||||
if (methodConsumingConfigPhases.contains(ConfigPhase.RUN_TIME)) {
|
||||
if (methodConsumingConfigPhases.contains(ConfigPhase.BOOTSTRAP)
|
||||
|| methodConsumingConfigPhases.contains(ConfigPhase.RUN_TIME)) {
|
||||
if (isRecorder && recordAnnotation.value() == ExecutionTime.STATIC_INIT) {
|
||||
throw reportError(method,
|
||||
"Bytecode recorder is static but an injected config object is declared as run time");
|
||||
@@ -845,6 +871,11 @@ public final class ExtensionLoader {
|
||||
methodStepConfig = methodStepConfig
|
||||
.andThen(bsb -> bsb.consumes(RunTimeConfigurationProxyBuildItem.class));
|
||||
}
|
||||
if (methodConsumingConfigPhases.contains(ConfigPhase.BOOTSTRAP)
|
||||
&& methodConsumingConfigPhases.contains(ConfigPhase.RUN_TIME)) {
|
||||
throw reportError(method,
|
||||
"Bootstrap configuration cannot be used together in a build step with run time configuration");
|
||||
}
|
||||
if (methodConsumingConfigPhases.contains(ConfigPhase.BUILD_AND_RUN_TIME_FIXED)
|
||||
|| methodConsumingConfigPhases.contains(ConfigPhase.BUILD_TIME)) {
|
||||
methodStepConfig = methodStepConfig
|
||||
@@ -934,9 +965,14 @@ public final class ExtensionLoader {
|
||||
if (recordAnnotation.value() == ExecutionTime.STATIC_INIT) {
|
||||
bc.produce(new StaticBytecodeRecorderBuildItem(bri));
|
||||
} else {
|
||||
bc.produce(new MainBytecodeRecorderBuildItem(bri));
|
||||
Class<? extends BuildItem> buildItemClass = determineMainRecorderBuildItemType(method);
|
||||
if (buildItemClass.equals(MainBytecodeRecorderBuildItem.class)) {
|
||||
bc.produce(new MainBytecodeRecorderBuildItem(bri));
|
||||
} else {
|
||||
assert buildItemClass == MainBootstrapConfigBytecodeRecorderBuildItem.class;
|
||||
bc.produce(new MainBootstrapConfigBytecodeRecorderBuildItem(bri));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -953,6 +989,12 @@ public final class ExtensionLoader {
|
||||
return chainConfig;
|
||||
}
|
||||
|
||||
private static Class<? extends BuildItem> determineMainRecorderBuildItemType(Method method) {
|
||||
return method.getReturnType().equals(RunTimeConfigurationSourceValueBuildItem.class)
|
||||
? MainBootstrapConfigBytecodeRecorderBuildItem.class
|
||||
: MainBytecodeRecorderBuildItem.class;
|
||||
}
|
||||
|
||||
private static BooleanSupplier and(BooleanSupplier a, BooleanSupplier b) {
|
||||
return () -> a.getAsBoolean() && b.getAsBoolean();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package io.quarkus.deployment.builditem;
|
||||
|
||||
import io.quarkus.builder.item.MultiBuildItem;
|
||||
import io.quarkus.deployment.recording.BytecodeRecorderImpl;
|
||||
|
||||
/**
|
||||
* This build item will be used to write bytecode that supports the Bootstrap phase of the configuration
|
||||
* That code essentially uses part of the configuration system to pass configuration data to
|
||||
* recorders that then use the configuration to create new configuration sources.
|
||||
* These sources are then used to create the final runtime configuration which then passed on
|
||||
* to all the other runtime recorders
|
||||
*/
|
||||
public final class MainBootstrapConfigBytecodeRecorderBuildItem extends MultiBuildItem {
|
||||
|
||||
private final BytecodeRecorderImpl bytecodeRecorder;
|
||||
|
||||
public MainBootstrapConfigBytecodeRecorderBuildItem(BytecodeRecorderImpl bytecodeRecorder) {
|
||||
this.bytecodeRecorder = bytecodeRecorder;
|
||||
}
|
||||
|
||||
public BytecodeRecorderImpl getBytecodeRecorder() {
|
||||
return bytecodeRecorder;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package io.quarkus.deployment.builditem;
|
||||
|
||||
import org.eclipse.microprofile.config.spi.ConfigSourceProvider;
|
||||
|
||||
import io.quarkus.builder.item.MultiBuildItem;
|
||||
import io.quarkus.runtime.RuntimeValue;
|
||||
|
||||
/**
|
||||
* This is a special build item that is intended to be used only to support bootstrap configuration in the following manner:
|
||||
*
|
||||
* A build step returns this build item (this is a limitation compared to other build items that can also be used with
|
||||
* BuildProducer)
|
||||
* containing a {@code RuntimeValue<ConfigSourceProvider>} that is obtained by calling a ({@code RUNTIME_INIT}) recorder.
|
||||
* The build step can optionally use a configuration object that uses the {@code BOOTSTRAP} config phase and pass this
|
||||
* configuration
|
||||
* to the recorder to allow the recorder at runtime to customize its behavior
|
||||
*/
|
||||
public final class RunTimeConfigurationSourceValueBuildItem extends MultiBuildItem {
|
||||
|
||||
private final RuntimeValue<ConfigSourceProvider> configSourcesValue;
|
||||
|
||||
public RunTimeConfigurationSourceValueBuildItem(RuntimeValue<ConfigSourceProvider> configSourcesValue) {
|
||||
this.configSourcesValue = configSourcesValue;
|
||||
}
|
||||
|
||||
public RuntimeValue<ConfigSourceProvider> getConfigSourcesValue() {
|
||||
return configSourcesValue;
|
||||
}
|
||||
}
|
||||
@@ -64,11 +64,14 @@ public final class BuildTimeConfigurationReader {
|
||||
|
||||
final ConfigPatternMap<Container> buildTimePatternMap;
|
||||
final ConfigPatternMap<Container> buildTimeRunTimePatternMap;
|
||||
final ConfigPatternMap<Container> bootstrapPatternMap;
|
||||
final ConfigPatternMap<Container> runTimePatternMap;
|
||||
|
||||
final List<RootDefinition> buildTimeVisibleRoots;
|
||||
final List<RootDefinition> allRoots;
|
||||
|
||||
final boolean bootstrapRootsEmpty;
|
||||
|
||||
/**
|
||||
* Construct a new instance.
|
||||
*
|
||||
@@ -77,6 +80,7 @@ public final class BuildTimeConfigurationReader {
|
||||
public BuildTimeConfigurationReader(final List<Class<?>> configRoots) {
|
||||
Assert.checkNotNullParam("configRoots", configRoots);
|
||||
|
||||
List<RootDefinition> bootstrapRoots = new ArrayList<>();
|
||||
List<RootDefinition> runTimeRoots = new ArrayList<>();
|
||||
List<RootDefinition> buildTimeRunTimeRoots = new ArrayList<>();
|
||||
List<RootDefinition> buildTimeRoots = new ArrayList<>();
|
||||
@@ -98,12 +102,15 @@ public final class BuildTimeConfigurationReader {
|
||||
buildTimeRoots.add(definition);
|
||||
} else if (phase == ConfigPhase.BUILD_AND_RUN_TIME_FIXED) {
|
||||
buildTimeRunTimeRoots.add(definition);
|
||||
} else if (phase == ConfigPhase.BOOTSTRAP) {
|
||||
bootstrapRoots.add(definition);
|
||||
} else {
|
||||
assert phase == ConfigPhase.RUN_TIME;
|
||||
runTimeRoots.add(definition);
|
||||
}
|
||||
}
|
||||
|
||||
bootstrapPatternMap = PatternMapBuilder.makePatterns(bootstrapRoots);
|
||||
runTimePatternMap = PatternMapBuilder.makePatterns(runTimeRoots);
|
||||
buildTimeRunTimePatternMap = PatternMapBuilder.makePatterns(buildTimeRunTimeRoots);
|
||||
buildTimePatternMap = PatternMapBuilder.makePatterns(buildTimeRoots);
|
||||
@@ -112,8 +119,12 @@ public final class BuildTimeConfigurationReader {
|
||||
buildTimeVisibleRoots.addAll(buildTimeRoots);
|
||||
buildTimeVisibleRoots.addAll(buildTimeRunTimeRoots);
|
||||
|
||||
List<RootDefinition> allRoots = new ArrayList<>(buildTimeVisibleRoots.size() + runTimeRoots.size());
|
||||
bootstrapRootsEmpty = bootstrapRoots.isEmpty();
|
||||
|
||||
List<RootDefinition> allRoots = new ArrayList<>(
|
||||
buildTimeVisibleRoots.size() + bootstrapRoots.size() + runTimeRoots.size());
|
||||
allRoots.addAll(buildTimeVisibleRoots);
|
||||
allRoots.addAll(bootstrapRoots);
|
||||
allRoots.addAll(runTimeRoots);
|
||||
|
||||
this.allRoots = allRoots;
|
||||
@@ -332,6 +343,21 @@ public final class BuildTimeConfigurationReader {
|
||||
ni.goToStart();
|
||||
ni.next();
|
||||
matched = runTimePatternMap.match(ni);
|
||||
if (matched != null) {
|
||||
// it's a specified run-time default (record for later)
|
||||
boolean old = ExpandingConfigSource.setExpanding(false);
|
||||
try {
|
||||
specifiedRunTimeDefaultValues.put(propertyName,
|
||||
config.getOptionalValue(propertyName, String.class).orElse(""));
|
||||
} finally {
|
||||
ExpandingConfigSource.setExpanding(old);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// 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)
|
||||
boolean old = ExpandingConfigSource.setExpanding(false);
|
||||
@@ -354,7 +380,8 @@ public final class BuildTimeConfigurationReader {
|
||||
}
|
||||
}
|
||||
return new ReadResult(objectsByRootClass, specifiedRunTimeDefaultValues, buildTimeRunTimeVisibleValues,
|
||||
buildTimePatternMap, buildTimeRunTimePatternMap, runTimePatternMap, allRoots);
|
||||
buildTimePatternMap, buildTimeRunTimePatternMap, bootstrapPatternMap, runTimePatternMap, allRoots,
|
||||
bootstrapRootsEmpty);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -689,22 +716,28 @@ public final class BuildTimeConfigurationReader {
|
||||
final Map<String, String> buildTimeRunTimeVisibleValues;
|
||||
final ConfigPatternMap<Container> buildTimePatternMap;
|
||||
final ConfigPatternMap<Container> buildTimeRunTimePatternMap;
|
||||
final ConfigPatternMap<Container> bootstrapPatternMap;
|
||||
final ConfigPatternMap<Container> runTimePatternMap;
|
||||
final Map<Class<?>, RootDefinition> runTimeRootsByClass;
|
||||
final List<RootDefinition> allRoots;
|
||||
final boolean bootstrapRootsEmpty;
|
||||
|
||||
ReadResult(final Map<Class<?>, Object> objectsByRootClass, final Map<String, String> specifiedRunTimeDefaultValues,
|
||||
final Map<String, String> buildTimeRunTimeVisibleValues,
|
||||
final ConfigPatternMap<Container> buildTimePatternMap,
|
||||
final ConfigPatternMap<Container> buildTimeRunTimePatternMap,
|
||||
final ConfigPatternMap<Container> runTimePatternMap, final List<RootDefinition> allRoots) {
|
||||
final ConfigPatternMap<Container> bootstrapPatternMap,
|
||||
final ConfigPatternMap<Container> runTimePatternMap, final List<RootDefinition> 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;
|
||||
Map<Class<?>, RootDefinition> map = new HashMap<>();
|
||||
for (RootDefinition root : allRoots) {
|
||||
map.put(root.getConfigurationClass(), root);
|
||||
@@ -740,6 +773,10 @@ public final class BuildTimeConfigurationReader {
|
||||
return buildTimeRunTimePatternMap;
|
||||
}
|
||||
|
||||
public ConfigPatternMap<Container> getBootstrapPatternMap() {
|
||||
return bootstrapPatternMap;
|
||||
}
|
||||
|
||||
public ConfigPatternMap<Container> getRunTimePatternMap() {
|
||||
return runTimePatternMap;
|
||||
}
|
||||
@@ -748,6 +785,10 @@ public final class BuildTimeConfigurationReader {
|
||||
return allRoots;
|
||||
}
|
||||
|
||||
public boolean isBootstrapRootsEmpty() {
|
||||
return bootstrapRootsEmpty;
|
||||
}
|
||||
|
||||
public RootDefinition requireRootDefinitionForClass(Class<?> clazz) {
|
||||
final RootDefinition def = runTimeRootsByClass.get(clazz);
|
||||
if (def == null) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package io.quarkus.deployment.configuration;
|
||||
import static io.quarkus.deployment.util.ReflectUtil.reportError;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -73,6 +74,7 @@ import io.smallrye.config.SmallRyeConfigBuilder;
|
||||
public final class RunTimeConfigurationGenerator {
|
||||
|
||||
public static final String CONFIG_CLASS_NAME = "io.quarkus.runtime.generated.Config";
|
||||
static final String BSDVCS_CLASS_NAME = "io.quarkus.runtime.generated.BootstrapDefaultValuesConfigSource";
|
||||
static final String RTDVCS_CLASS_NAME = "io.quarkus.runtime.generated.RunTimeDefaultValuesConfigSource";
|
||||
static final String BTRTDVCS_CLASS_NAME = "io.quarkus.runtime.generated.BuildTimeRunTimeDefaultValuesConfigSource";
|
||||
|
||||
@@ -84,13 +86,18 @@ public final class RunTimeConfigurationGenerator {
|
||||
ConfigSource.class);
|
||||
static final FieldDescriptor C_BUILD_TIME_RUN_TIME_DEFAULTS_CONFIG_SOURCE = FieldDescriptor.of(CONFIG_CLASS_NAME,
|
||||
"buildTimeRunTimeDefaultsConfigSource", ConfigSource.class);
|
||||
public static final MethodDescriptor C_CREATE_RUN_TIME_CONFIG = MethodDescriptor.ofMethod(CONFIG_CLASS_NAME,
|
||||
"createRunTimeConfig", void.class);
|
||||
public static final MethodDescriptor C_CREATE_BOOTSTRAP_CONFIG = MethodDescriptor.ofMethod(CONFIG_CLASS_NAME,
|
||||
"createBootstrapConfig", CONFIG_CLASS_NAME);
|
||||
public static final MethodDescriptor C_ENSURE_INITIALIZED = MethodDescriptor.ofMethod(CONFIG_CLASS_NAME,
|
||||
"ensureInitialized", void.class);
|
||||
static final FieldDescriptor C_BOOTSTRAP_DEFAULTS_CONFIG_SOURCE = FieldDescriptor.of(CONFIG_CLASS_NAME,
|
||||
"bootstrapDefaultsConfigSource", ConfigSource.class);
|
||||
static final FieldDescriptor C_RUN_TIME_DEFAULTS_CONFIG_SOURCE = FieldDescriptor.of(CONFIG_CLASS_NAME,
|
||||
"runTimeDefaultsConfigSource", ConfigSource.class);
|
||||
static final MethodDescriptor C_READ_CONFIG = MethodDescriptor.ofMethod(CONFIG_CLASS_NAME, "readConfig", void.class);
|
||||
static final MethodDescriptor C_BOOTSTRAP_CONFIG = MethodDescriptor.ofMethod(CONFIG_CLASS_NAME, "readBootstrapConfig",
|
||||
void.class);
|
||||
public static final MethodDescriptor C_READ_CONFIG = MethodDescriptor.ofMethod(CONFIG_CLASS_NAME, "readConfig", void.class,
|
||||
List.class);
|
||||
static final FieldDescriptor C_SPECIFIED_RUN_TIME_CONFIG_SOURCE = FieldDescriptor.of(CONFIG_CLASS_NAME,
|
||||
"specifiedRunTimeConfigSource",
|
||||
ConfigSource.class);
|
||||
@@ -138,8 +145,13 @@ public final class RunTimeConfigurationGenerator {
|
||||
IntFunction.class);
|
||||
static final MethodDescriptor CU_CONFIG_BUILDER = MethodDescriptor.ofMethod(ConfigUtils.class, "configBuilder",
|
||||
SmallRyeConfigBuilder.class, boolean.class);
|
||||
static final MethodDescriptor CU_CONFIG_BUILDER_WITH_ADD_DISCOVERED = MethodDescriptor.ofMethod(ConfigUtils.class,
|
||||
"configBuilder",
|
||||
SmallRyeConfigBuilder.class, boolean.class, boolean.class);
|
||||
static final MethodDescriptor CU_ADD_SOURCE_PROVIDER = MethodDescriptor.ofMethod(ConfigUtils.class, "addSourceProvider",
|
||||
void.class, SmallRyeConfigBuilder.class, ConfigSourceProvider.class);
|
||||
static final MethodDescriptor CU_ADD_SOURCE_PROVIDERS = MethodDescriptor.ofMethod(ConfigUtils.class, "addSourceProviders",
|
||||
void.class, SmallRyeConfigBuilder.class, Collection.class);
|
||||
|
||||
static final MethodDescriptor HM_NEW = MethodDescriptor.ofConstructor(HashMap.class);
|
||||
static final MethodDescriptor HM_PUT = MethodDescriptor.ofMethod(HashMap.class, "put", Object.class, Object.class,
|
||||
@@ -195,6 +207,7 @@ public final class RunTimeConfigurationGenerator {
|
||||
static final MethodDescriptor QCF_SET_CONFIG = MethodDescriptor.ofMethod(QuarkusConfigFactory.class, "setConfig",
|
||||
void.class, SmallRyeConfig.class);
|
||||
|
||||
static final MethodDescriptor BSDVCS_NEW = MethodDescriptor.ofConstructor(BSDVCS_CLASS_NAME);
|
||||
static final MethodDescriptor RTDVCS_NEW = MethodDescriptor.ofConstructor(RTDVCS_CLASS_NAME);
|
||||
|
||||
static final MethodDescriptor SRC_GET_CONVERTER = MethodDescriptor.ofMethod(SmallRyeConfig.class, "getConverter",
|
||||
@@ -229,11 +242,12 @@ public final class RunTimeConfigurationGenerator {
|
||||
final ClassCreator cc;
|
||||
final MethodCreator clinit;
|
||||
final BytecodeCreator converterSetup;
|
||||
final MethodCreator readBootstrapConfig;
|
||||
final ResultHandle readBootstrapConfigNameBuilder;
|
||||
final MethodCreator readConfig;
|
||||
final ResultHandle readConfigNameBuilder;
|
||||
final ResultHandle clinitNameBuilder;
|
||||
final BuildTimeConfigurationReader.ReadResult buildTimeConfigResult;
|
||||
final ConfigPatternMap<Container> runTimePatternMap;
|
||||
final List<RootDefinition> roots;
|
||||
// default values given in the build configuration
|
||||
final Map<String, String> specifiedRunTimeDefaultValues;
|
||||
@@ -309,6 +323,13 @@ public final class RunTimeConfigurationGenerator {
|
||||
final ResultHandle buildTimeRunTimeDefaultValuesConfigSource = clinit.newInstance(BTRTDVCS_NEW);
|
||||
clinit.writeStaticField(C_BUILD_TIME_RUN_TIME_DEFAULTS_CONFIG_SOURCE, buildTimeRunTimeDefaultValuesConfigSource);
|
||||
|
||||
// the bootstrap default values config source
|
||||
if (!buildTimeReadResult.isBootstrapRootsEmpty()) {
|
||||
cc.getFieldCreator(C_BOOTSTRAP_DEFAULTS_CONFIG_SOURCE)
|
||||
.setModifiers(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL);
|
||||
clinit.writeStaticField(C_BOOTSTRAP_DEFAULTS_CONFIG_SOURCE, clinit.newInstance(BSDVCS_NEW));
|
||||
}
|
||||
|
||||
// the run time default values config source
|
||||
cc.getFieldCreator(C_RUN_TIME_DEFAULTS_CONFIG_SOURCE)
|
||||
.setModifiers(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL);
|
||||
@@ -327,29 +348,37 @@ public final class RunTimeConfigurationGenerator {
|
||||
|
||||
// block for converter setup
|
||||
converterSetup = clinit.createScope();
|
||||
|
||||
// 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);
|
||||
if (buildTimeReadResult.isBootstrapRootsEmpty()) {
|
||||
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"));
|
||||
runTimePatternMap = buildTimeReadResult.getRunTimePatternMap();
|
||||
|
||||
accessorFinder = new AccessorFinder();
|
||||
}
|
||||
|
||||
// meant to be called in outside the constructor
|
||||
private boolean bootstrapConfigSetupNeeded() {
|
||||
return readBootstrapConfigNameBuilder != null;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
// in clinit, load the build-time config
|
||||
// make the build time config global until we read the run time config -
|
||||
// at run time (when we're ready) we update the factory and then release the build time config
|
||||
clinit.invokeStaticMethod(QCF_SET_CONFIG, clinitConfig);
|
||||
// release any previous configuration
|
||||
final ResultHandle clinitCpr = clinit.invokeStaticMethod(CPR_INSTANCE);
|
||||
try (TryBlock getConfigTry = clinit.tryBlock()) {
|
||||
final ResultHandle initialConfigHandle = getConfigTry.invokeVirtualMethod(CPR_GET_CONFIG,
|
||||
clinitCpr);
|
||||
getConfigTry.invokeVirtualMethod(CPR_RELEASE_CONFIG, clinitCpr, initialConfigHandle);
|
||||
// ignore
|
||||
getConfigTry.addCatch(IllegalStateException.class);
|
||||
}
|
||||
installConfiguration(clinitConfig, clinit);
|
||||
|
||||
// fill roots map
|
||||
for (RootDefinition root : roots) {
|
||||
@@ -361,19 +390,29 @@ public final class RunTimeConfigurationGenerator {
|
||||
final ConfigPatternMap<Container> buildTimePatternMap = buildTimeConfigResult.getBuildTimePatternMap();
|
||||
final ConfigPatternMap<Container> buildTimeRunTimePatternMap = buildTimeConfigResult
|
||||
.getBuildTimeRunTimePatternMap();
|
||||
final ConfigPatternMap<Container> bootstrapPatternMap = buildTimeConfigResult.getBootstrapPatternMap();
|
||||
final ConfigPatternMap<Container> runTimePatternMap = buildTimeConfigResult.getRunTimePatternMap();
|
||||
|
||||
final BiFunction<Container, Container, Container> combinator = (a, b) -> a == null ? b : a;
|
||||
final ConfigPatternMap<Container> buildTimeRunTimeIgnored = ConfigPatternMap.merge(buildTimePatternMap,
|
||||
runTimePatternMap, combinator);
|
||||
final ConfigPatternMap<Container> runTimeIgnored = ConfigPatternMap.merge(buildTimePatternMap,
|
||||
buildTimeRunTimePatternMap, combinator);
|
||||
final ConfigPatternMap<Container> buildTimeRunTimeIgnored = ConfigPatternMap
|
||||
.merge(ConfigPatternMap.merge(buildTimePatternMap,
|
||||
runTimePatternMap, combinator), bootstrapPatternMap, combinator);
|
||||
final ConfigPatternMap<Container> runTimeIgnored = ConfigPatternMap
|
||||
.merge(ConfigPatternMap.merge(buildTimePatternMap,
|
||||
buildTimeRunTimePatternMap, combinator), bootstrapPatternMap, combinator);
|
||||
|
||||
final MethodDescriptor siParserBody = generateParserBody(buildTimeRunTimePatternMap, buildTimeRunTimeIgnored,
|
||||
new StringBuilder("siParseKey"), false, false);
|
||||
final MethodDescriptor rtParserBody = generateParserBody(runTimePatternMap, runTimeIgnored,
|
||||
new StringBuilder("rtParseKey"), false, true);
|
||||
|
||||
// create the bootstrap config if necessary
|
||||
ResultHandle bootstrapBuilder = null;
|
||||
if (bootstrapConfigSetupNeeded()) {
|
||||
bootstrapBuilder = readBootstrapConfig.invokeStaticMethod(CU_CONFIG_BUILDER_WITH_ADD_DISCOVERED,
|
||||
readBootstrapConfig.load(false), readBootstrapConfig.load(false));
|
||||
}
|
||||
|
||||
// create the run time config
|
||||
final ResultHandle runTimeBuilder = readConfig.invokeStaticMethod(CU_CONFIG_BUILDER, readConfig.load(true));
|
||||
|
||||
@@ -399,16 +438,43 @@ public final class RunTimeConfigurationGenerator {
|
||||
cc.getFieldCreator(C_SPECIFIED_RUN_TIME_CONFIG_SOURCE).setModifiers(Opcodes.ACC_STATIC | Opcodes.ACC_FINAL);
|
||||
clinit.writeStaticField(C_SPECIFIED_RUN_TIME_CONFIG_SOURCE, specifiedRunTimeSource);
|
||||
|
||||
// add in the custom sources that bootstrap config needs
|
||||
ResultHandle bootstrapConfigSourcesArray = null;
|
||||
if (bootstrapConfigSetupNeeded()) {
|
||||
bootstrapConfigSourcesArray = readBootstrapConfig.newArray(ConfigSource[].class, 4);
|
||||
// build time config (expanded values)
|
||||
readBootstrapConfig.writeArrayValue(bootstrapConfigSourcesArray, 0,
|
||||
readBootstrapConfig.readStaticField(C_BUILD_TIME_CONFIG_SOURCE));
|
||||
// specified run time config default values
|
||||
readBootstrapConfig.writeArrayValue(bootstrapConfigSourcesArray, 1,
|
||||
readBootstrapConfig.readStaticField(C_SPECIFIED_RUN_TIME_CONFIG_SOURCE));
|
||||
// build time run time visible default config source
|
||||
readBootstrapConfig.writeArrayValue(bootstrapConfigSourcesArray, 2,
|
||||
readBootstrapConfig.readStaticField(C_BUILD_TIME_RUN_TIME_DEFAULTS_CONFIG_SOURCE));
|
||||
// bootstrap config default values
|
||||
readBootstrapConfig.writeArrayValue(bootstrapConfigSourcesArray, 3,
|
||||
readBootstrapConfig.readStaticField(C_BOOTSTRAP_DEFAULTS_CONFIG_SOURCE));
|
||||
}
|
||||
|
||||
// add in our custom sources
|
||||
final ResultHandle array = readConfig.newArray(ConfigSource[].class, 4);
|
||||
final ResultHandle runtimeConfigSourcesArray = readConfig.newArray(ConfigSource[].class,
|
||||
bootstrapConfigSetupNeeded() ? 5 : 4);
|
||||
// build time config (expanded values)
|
||||
readConfig.writeArrayValue(array, 0, readConfig.readStaticField(C_BUILD_TIME_CONFIG_SOURCE));
|
||||
readConfig.writeArrayValue(runtimeConfigSourcesArray, 0, readConfig.readStaticField(C_BUILD_TIME_CONFIG_SOURCE));
|
||||
// specified run time config default values
|
||||
readConfig.writeArrayValue(array, 1, readConfig.readStaticField(C_SPECIFIED_RUN_TIME_CONFIG_SOURCE));
|
||||
readConfig.writeArrayValue(runtimeConfigSourcesArray, 1,
|
||||
readConfig.readStaticField(C_SPECIFIED_RUN_TIME_CONFIG_SOURCE));
|
||||
// run time config default values
|
||||
readConfig.writeArrayValue(array, 2, readConfig.readStaticField(C_RUN_TIME_DEFAULTS_CONFIG_SOURCE));
|
||||
readConfig.writeArrayValue(runtimeConfigSourcesArray, 2,
|
||||
readConfig.readStaticField(C_RUN_TIME_DEFAULTS_CONFIG_SOURCE));
|
||||
// build time run time visible default config source
|
||||
readConfig.writeArrayValue(array, 3, readConfig.readStaticField(C_BUILD_TIME_RUN_TIME_DEFAULTS_CONFIG_SOURCE));
|
||||
readConfig.writeArrayValue(runtimeConfigSourcesArray, 3,
|
||||
readConfig.readStaticField(C_BUILD_TIME_RUN_TIME_DEFAULTS_CONFIG_SOURCE));
|
||||
if (bootstrapConfigSetupNeeded()) {
|
||||
// bootstrap config default values
|
||||
readConfig.writeArrayValue(runtimeConfigSourcesArray, 4,
|
||||
readConfig.readStaticField(C_BOOTSTRAP_DEFAULTS_CONFIG_SOURCE));
|
||||
}
|
||||
|
||||
// add in known converters
|
||||
for (Class<?> additionalType : additionalTypes) {
|
||||
@@ -430,28 +496,41 @@ public final class RunTimeConfigurationGenerator {
|
||||
for (Map.Entry<FieldDescriptor, Class<?>> entry : convertersToRegister.entrySet()) {
|
||||
final FieldDescriptor descriptor = entry.getKey();
|
||||
final Class<?> type = entry.getValue();
|
||||
if (bootstrapConfigSetupNeeded()) {
|
||||
readBootstrapConfig.invokeVirtualMethod(SRCB_WITH_CONVERTER, bootstrapBuilder,
|
||||
readBootstrapConfig.loadClass(type),
|
||||
readBootstrapConfig.load(100), readBootstrapConfig.readStaticField(descriptor));
|
||||
}
|
||||
readConfig.invokeVirtualMethod(SRCB_WITH_CONVERTER, runTimeBuilder, readConfig.loadClass(type),
|
||||
readConfig.load(100), readConfig.readStaticField(descriptor));
|
||||
}
|
||||
}
|
||||
|
||||
// put them in the builder
|
||||
readConfig.invokeVirtualMethod(SRCB_WITH_SOURCES, runTimeBuilder, array);
|
||||
// put sources in the bootstrap builder
|
||||
if (bootstrapConfigSetupNeeded()) {
|
||||
readBootstrapConfig.invokeVirtualMethod(SRCB_WITH_SOURCES, bootstrapBuilder, bootstrapConfigSourcesArray);
|
||||
}
|
||||
// put sources in the builder
|
||||
readConfig.invokeVirtualMethod(SRCB_WITH_SOURCES, runTimeBuilder, runtimeConfigSourcesArray);
|
||||
|
||||
final ResultHandle runTimeConfig = readConfig.invokeVirtualMethod(SRCB_BUILD, runTimeBuilder);
|
||||
// install run time config
|
||||
readConfig.invokeStaticMethod(QCF_SET_CONFIG, runTimeConfig);
|
||||
// now invalidate the cached config, so the next one to load the config gets the new one
|
||||
final ResultHandle configProviderResolver = readConfig.invokeStaticMethod(CPR_INSTANCE);
|
||||
try (TryBlock getConfigTry = readConfig.tryBlock()) {
|
||||
final ResultHandle initialConfigHandle = getConfigTry.invokeVirtualMethod(CPR_GET_CONFIG,
|
||||
configProviderResolver);
|
||||
getConfigTry.invokeVirtualMethod(CPR_RELEASE_CONFIG, configProviderResolver, initialConfigHandle);
|
||||
// ignore
|
||||
getConfigTry.addCatch(IllegalStateException.class);
|
||||
// add the ConfigSourceProvider List passed as the readConfig method param
|
||||
// (which were generated by the bootstrap config phase - an empty list is passed when there is no bootstrap phase)
|
||||
readConfig.invokeStaticMethod(CU_ADD_SOURCE_PROVIDERS, runTimeBuilder, readConfig.getMethodParam(0));
|
||||
|
||||
ResultHandle bootstrapConfig = null;
|
||||
if (bootstrapConfigSetupNeeded()) {
|
||||
bootstrapConfig = readBootstrapConfig.invokeVirtualMethod(SRCB_BUILD, bootstrapBuilder);
|
||||
installConfiguration(bootstrapConfig, readBootstrapConfig);
|
||||
}
|
||||
|
||||
final ResultHandle runTimeConfig = readConfig.invokeVirtualMethod(SRCB_BUILD, runTimeBuilder);
|
||||
installConfiguration(runTimeConfig, readConfig);
|
||||
|
||||
final ResultHandle clInitOldLen = clinit.invokeVirtualMethod(SB_LENGTH, clinitNameBuilder);
|
||||
ResultHandle bcOldLen = null;
|
||||
if (bootstrapConfigSetupNeeded()) {
|
||||
bcOldLen = readBootstrapConfig.invokeVirtualMethod(SB_LENGTH, readBootstrapConfigNameBuilder);
|
||||
}
|
||||
final ResultHandle rcOldLen = readConfig.invokeVirtualMethod(SB_LENGTH, readConfigNameBuilder);
|
||||
|
||||
// generate eager config read (both build and run time at once)
|
||||
@@ -487,6 +566,25 @@ public final class RunTimeConfigurationGenerator {
|
||||
}
|
||||
clinit.invokeStaticMethod(initGroup, clinitConfig, clinitNameBuilder, instance);
|
||||
clinit.invokeVirtualMethod(SB_SET_LENGTH, clinitNameBuilder, clInitOldLen);
|
||||
} else if (root.getConfigPhase() == ConfigPhase.BOOTSTRAP) {
|
||||
if (bootstrapConfigSetupNeeded()) {
|
||||
// config root field is volatile; we initialize and read config from the readBootstrapConfig method
|
||||
cc.getFieldCreator(rootFieldDescriptor)
|
||||
.setModifiers(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_VOLATILE);
|
||||
// construct instance in readBootstrapConfig
|
||||
final ResultHandle instance = readBootstrapConfig.invokeStaticMethod(ctor);
|
||||
// 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.invokeStaticMethod(initGroup, bootstrapConfig, readBootstrapConfigNameBuilder,
|
||||
instance);
|
||||
readBootstrapConfig.invokeVirtualMethod(SB_SET_LENGTH, readBootstrapConfigNameBuilder, bcOldLen);
|
||||
}
|
||||
} else if (root.getConfigPhase() == ConfigPhase.RUN_TIME) {
|
||||
// config root field is volatile; we initialize and read config from the readConfig method
|
||||
cc.getFieldCreator(rootFieldDescriptor)
|
||||
@@ -569,12 +667,12 @@ public final class RunTimeConfigurationGenerator {
|
||||
mc.returnValue(null);
|
||||
}
|
||||
|
||||
// generate run time entry point
|
||||
try (MethodCreator mc = cc.getMethodCreator(C_CREATE_RUN_TIME_CONFIG)) {
|
||||
// generate bootstrap config entry point
|
||||
try (MethodCreator mc = cc.getMethodCreator(C_CREATE_BOOTSTRAP_CONFIG)) {
|
||||
mc.setModifiers(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC);
|
||||
ResultHandle instance = mc.newInstance(MethodDescriptor.ofConstructor(CONFIG_CLASS_NAME));
|
||||
mc.invokeVirtualMethod(C_READ_CONFIG, instance);
|
||||
mc.returnValue(null);
|
||||
mc.invokeVirtualMethod(C_BOOTSTRAP_CONFIG, instance);
|
||||
mc.returnValue(instance);
|
||||
}
|
||||
|
||||
// wrap it up
|
||||
@@ -600,6 +698,9 @@ public final class RunTimeConfigurationGenerator {
|
||||
configurationException, emptyStackTraceElement);
|
||||
isError.throwException(configurationException);
|
||||
|
||||
readBootstrapConfig.returnValue(null);
|
||||
readBootstrapConfig.close();
|
||||
|
||||
readConfig.returnValue(null);
|
||||
readConfig.close();
|
||||
|
||||
@@ -607,40 +708,39 @@ public final class RunTimeConfigurationGenerator {
|
||||
clinit.close();
|
||||
cc.close();
|
||||
|
||||
// generate run time default values config source class
|
||||
try (ClassCreator dvcc = ClassCreator.builder().classOutput(classOutput).className(RTDVCS_CLASS_NAME)
|
||||
.superClass(AbstractRawDefaultConfigSource.class).setFinal(true).build()) {
|
||||
// implements abstract method AbstractRawDefaultConfigSource#getValue(NameIterator)
|
||||
try (MethodCreator mc = dvcc.getMethodCreator("getValue", String.class, NameIterator.class)) {
|
||||
final ResultHandle keyIter = mc.getMethodParam(0);
|
||||
final MethodDescriptor md = generateDefaultValueParse(dvcc, runTimePatternMap,
|
||||
new StringBuilder("getDefaultFor"));
|
||||
if (md != null) {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mc.returnValue(mc.loadNull());
|
||||
}
|
||||
if (bootstrapConfigSetupNeeded()) {
|
||||
// generate bootstrap default values config source class
|
||||
generateDefaultValuesConfigSourceClass(bootstrapPatternMap, BSDVCS_CLASS_NAME);
|
||||
}
|
||||
|
||||
// generate run time default values config source class
|
||||
generateDefaultValuesConfigSourceClass(runTimePatternMap, RTDVCS_CLASS_NAME);
|
||||
|
||||
// generate build time run time visible default values config source class
|
||||
try (ClassCreator dvcc = ClassCreator.builder().classOutput(classOutput).className(BTRTDVCS_CLASS_NAME)
|
||||
generateDefaultValuesConfigSourceClass(buildTimeRunTimePatternMap, BTRTDVCS_CLASS_NAME);
|
||||
}
|
||||
|
||||
private void installConfiguration(ResultHandle config, MethodCreator methodCreator) {
|
||||
// install config
|
||||
methodCreator.invokeStaticMethod(QCF_SET_CONFIG, config);
|
||||
// now invalidate the cached config, so the next one to load the config gets the new one
|
||||
final ResultHandle configProviderResolver = methodCreator.invokeStaticMethod(CPR_INSTANCE);
|
||||
try (TryBlock getConfigTry = methodCreator.tryBlock()) {
|
||||
final ResultHandle initialConfigHandle = getConfigTry.invokeVirtualMethod(CPR_GET_CONFIG,
|
||||
configProviderResolver);
|
||||
getConfigTry.invokeVirtualMethod(CPR_RELEASE_CONFIG, configProviderResolver, initialConfigHandle);
|
||||
// ignore
|
||||
getConfigTry.addCatch(IllegalStateException.class);
|
||||
}
|
||||
}
|
||||
|
||||
private void generateDefaultValuesConfigSourceClass(ConfigPatternMap<Container> patternMap, String className) {
|
||||
try (ClassCreator dvcc = ClassCreator.builder().classOutput(classOutput).className(className)
|
||||
.superClass(AbstractRawDefaultConfigSource.class).setFinal(true).build()) {
|
||||
// implements abstract method AbstractRawDefaultConfigSource#getValue(NameIterator)
|
||||
try (MethodCreator mc = dvcc.getMethodCreator("getValue", String.class, NameIterator.class)) {
|
||||
final ResultHandle keyIter = mc.getMethodParam(0);
|
||||
final MethodDescriptor md = generateDefaultValueParse(dvcc, buildTimeRunTimePatternMap,
|
||||
final MethodDescriptor md = generateDefaultValueParse(dvcc, patternMap,
|
||||
new StringBuilder("getDefaultFor"));
|
||||
if (md != null) {
|
||||
// there is at least one default value
|
||||
|
||||
@@ -40,6 +40,16 @@ public final class RootDefinition extends ClassDefinition {
|
||||
"Run", "Time", "Config"),
|
||||
"Configuration"),
|
||||
"Config");
|
||||
} else if (configPhase == ConfigPhase.BOOTSTRAP) {
|
||||
trimmedSegments = withoutSuffix(
|
||||
withoutSuffix(
|
||||
withoutSuffix(
|
||||
withoutSuffix(
|
||||
segments,
|
||||
"Bootstrap", "Configuration"),
|
||||
"Bootstrap", "Config"),
|
||||
"Configuration"),
|
||||
"Config");
|
||||
} else {
|
||||
trimmedSegments = withoutSuffix(
|
||||
withoutSuffix(
|
||||
|
||||
@@ -34,6 +34,7 @@ public class ConfigDescriptionBuildStep {
|
||||
List<ConfigDescriptionBuildItem> ret = new ArrayList<>();
|
||||
processConfig(config.getReadResult().getBuildTimePatternMap(), ret, javadoc);
|
||||
processConfig(config.getReadResult().getBuildTimeRunTimePatternMap(), ret, javadoc);
|
||||
processConfig(config.getReadResult().getBootstrapPatternMap(), ret, javadoc);
|
||||
processConfig(config.getReadResult().getRunTimePatternMap(), ret, javadoc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -5,12 +5,15 @@ import static io.quarkus.gizmo.MethodDescriptor.ofMethod;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.microprofile.config.spi.ConfigSourceProvider;
|
||||
import org.graalvm.nativeimage.ImageInfo;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
@@ -27,6 +30,7 @@ import io.quarkus.deployment.builditem.FeatureBuildItem;
|
||||
import io.quarkus.deployment.builditem.GeneratedClassBuildItem;
|
||||
import io.quarkus.deployment.builditem.JavaLibraryPathAdditionalPathBuildItem;
|
||||
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
|
||||
import io.quarkus.deployment.builditem.MainBootstrapConfigBytecodeRecorderBuildItem;
|
||||
import io.quarkus.deployment.builditem.MainBytecodeRecorderBuildItem;
|
||||
import io.quarkus.deployment.builditem.MainClassBuildItem;
|
||||
import io.quarkus.deployment.builditem.ObjectSubstitutionBuildItem;
|
||||
@@ -50,6 +54,7 @@ import io.quarkus.gizmo.TryBlock;
|
||||
import io.quarkus.runtime.Application;
|
||||
import io.quarkus.runtime.LaunchMode;
|
||||
import io.quarkus.runtime.NativeImageRuntimePropertiesRecorder;
|
||||
import io.quarkus.runtime.RuntimeValue;
|
||||
import io.quarkus.runtime.StartupContext;
|
||||
import io.quarkus.runtime.StartupTask;
|
||||
import io.quarkus.runtime.Timing;
|
||||
@@ -68,6 +73,7 @@ class MainClassBuildStep {
|
||||
MainClassBuildItem build(List<StaticBytecodeRecorderBuildItem> staticInitTasks,
|
||||
List<ObjectSubstitutionBuildItem> substitutions,
|
||||
List<MainBytecodeRecorderBuildItem> mainMethod,
|
||||
List<MainBootstrapConfigBytecodeRecorderBuildItem> mainBootstrapConfig,
|
||||
List<SystemPropertyBuildItem> properties,
|
||||
List<JavaLibraryPathAdditionalPathBuildItem> javaLibraryPathAdditionalPaths,
|
||||
Optional<SslTrustStoreSystemPropertyBuildItem> sslTrustStoreSystemProperty,
|
||||
@@ -139,19 +145,7 @@ class MainClassBuildStep {
|
||||
for (StaticBytecodeRecorderBuildItem holder : staticInitTasks) {
|
||||
final BytecodeRecorderImpl recorder = holder.getBytecodeRecorder();
|
||||
if (!recorder.isEmpty()) {
|
||||
// Register substitutions in all recorders
|
||||
for (ObjectSubstitutionBuildItem sub : substitutions) {
|
||||
ObjectSubstitutionBuildItem.Holder holder1 = sub.holder;
|
||||
recorder.registerSubstitution(holder1.from, holder1.to, holder1.substitution);
|
||||
}
|
||||
for (BytecodeRecorderObjectLoaderBuildItem item : loaders) {
|
||||
recorder.registerObjectLoader(item.getObjectLoader());
|
||||
}
|
||||
recorder.writeBytecode(gizmoOutput);
|
||||
|
||||
ResultHandle dup = tryBlock.newInstance(ofConstructor(recorder.getClassName()));
|
||||
tryBlock.invokeInterfaceMethod(ofMethod(StartupTask.class, "deploy", void.class, StartupContext.class), dup,
|
||||
startupContext);
|
||||
writeRecordedBytecode(recorder, substitutions, loaders, gizmoOutput, startupContext, tryBlock);
|
||||
}
|
||||
}
|
||||
tryBlock.returnValue(null);
|
||||
@@ -213,24 +207,61 @@ class MainClassBuildStep {
|
||||
|
||||
tryBlock = mv.tryBlock();
|
||||
|
||||
// Load the run time configuration
|
||||
tryBlock.invokeStaticMethod(RunTimeConfigurationGenerator.C_CREATE_RUN_TIME_CONFIG);
|
||||
// Load the bootstrap configuration
|
||||
ResultHandle generatedConfig = tryBlock.invokeStaticMethod(RunTimeConfigurationGenerator.C_CREATE_BOOTSTRAP_CONFIG);
|
||||
|
||||
if (mainBootstrapConfig.isEmpty()) {
|
||||
tryBlock.invokeVirtualMethod(RunTimeConfigurationGenerator.C_READ_CONFIG, generatedConfig,
|
||||
tryBlock.invokeStaticMethod(ofMethod(Collections.class, "emptyList", List.class)));
|
||||
} else {
|
||||
/*
|
||||
* This is not that great, since it is no by no means a general way of executing things before the main bytecode
|
||||
* stuff executes.
|
||||
* It's tailored made to support the bootstrap configuration stuff and would need to be rethought if we need
|
||||
* a general mechanism of executing things before the main bytecode stuff
|
||||
* (which would likely involve a new runtime phase)
|
||||
*
|
||||
* What this loop does is go through the MainBootstrapConfigBytecodeRecorderBuildItem objects which are guaranteed
|
||||
* to be constructed from build steps that return RunTimeConfigurationSourceValueBuildItem - thus ensuring that
|
||||
* the generated StartupTask will write its result (which is a RuntimeValue<ConfigSourceProvider>
|
||||
* configSourcesValue) into the StartupContext.
|
||||
* This value is then pulled out of the StartupContext by using the getLastValue method. All the
|
||||
* ConfigSourceProvider objects are then collected and passed to Config.readConfig()
|
||||
*/
|
||||
ResultHandle configSourcesProvidersList = tryBlock.newInstance(ofConstructor(ArrayList.class, int.class),
|
||||
tryBlock.load(mainBootstrapConfig.size()));
|
||||
for (MainBootstrapConfigBytecodeRecorderBuildItem holder : mainBootstrapConfig) {
|
||||
final BytecodeRecorderImpl recorder = holder.getBytecodeRecorder();
|
||||
if (!recorder.isEmpty()) {
|
||||
writeRecordedBytecode(recorder, substitutions, loaders, gizmoOutput, startupContext, tryBlock);
|
||||
|
||||
// we now get the result of the recorder invocation
|
||||
ResultHandle isLastValueSet = tryBlock.invokeVirtualMethod(
|
||||
ofMethod(StartupContext.class, "isLastValueSet", boolean.class), startupContext);
|
||||
BytecodeCreator isLastValueSetTrue = tryBlock.ifNonZero(isLastValueSet).trueBranch();
|
||||
ResultHandle getLastValue = isLastValueSetTrue
|
||||
.invokeVirtualMethod(ofMethod(StartupContext.class, "getLastValue", Object.class), startupContext);
|
||||
BytecodeCreator lastValueNotNull = isLastValueSetTrue.ifNull(getLastValue).falseBranch();
|
||||
ResultHandle runtimeValue = lastValueNotNull.checkCast(getLastValue, RuntimeValue.class);
|
||||
ResultHandle getValue = lastValueNotNull
|
||||
.invokeVirtualMethod(MethodDescriptor.ofMethod(RuntimeValue.class, "getValue", Object.class),
|
||||
runtimeValue);
|
||||
ResultHandle configSourceProvider = lastValueNotNull.checkCast(getValue, ConfigSourceProvider.class);
|
||||
lastValueNotNull.invokeVirtualMethod(
|
||||
MethodDescriptor.ofMethod(ArrayList.class, "add", boolean.class, Object.class),
|
||||
configSourcesProvidersList, configSourceProvider);
|
||||
lastValueNotNull.breakScope();
|
||||
isLastValueSetTrue.breakScope();
|
||||
}
|
||||
}
|
||||
tryBlock.invokeVirtualMethod(RunTimeConfigurationGenerator.C_READ_CONFIG, generatedConfig,
|
||||
configSourcesProvidersList);
|
||||
}
|
||||
|
||||
for (MainBytecodeRecorderBuildItem holder : mainMethod) {
|
||||
final BytecodeRecorderImpl recorder = holder.getBytecodeRecorder();
|
||||
if (!recorder.isEmpty()) {
|
||||
// Register substitutions in all recorders
|
||||
for (ObjectSubstitutionBuildItem sub : substitutions) {
|
||||
ObjectSubstitutionBuildItem.Holder holder1 = sub.holder;
|
||||
recorder.registerSubstitution(holder1.from, holder1.to, holder1.substitution);
|
||||
}
|
||||
for (BytecodeRecorderObjectLoaderBuildItem item : loaders) {
|
||||
recorder.registerObjectLoader(item.getObjectLoader());
|
||||
}
|
||||
recorder.writeBytecode(gizmoOutput);
|
||||
ResultHandle dup = tryBlock.newInstance(ofConstructor(recorder.getClassName()));
|
||||
tryBlock.invokeInterfaceMethod(ofMethod(StartupTask.class, "deploy", void.class, StartupContext.class), dup,
|
||||
startupContext);
|
||||
writeRecordedBytecode(recorder, substitutions, loaders, gizmoOutput, startupContext, tryBlock);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,4 +325,20 @@ class MainClassBuildStep {
|
||||
return new MainClassBuildItem(MAIN_CLASS);
|
||||
}
|
||||
|
||||
private void writeRecordedBytecode(BytecodeRecorderImpl recorder, List<ObjectSubstitutionBuildItem> substitutions,
|
||||
List<BytecodeRecorderObjectLoaderBuildItem> loaders, GeneratedClassGizmoAdaptor gizmoOutput,
|
||||
ResultHandle startupContext, BytecodeCreator bytecodeCreator) {
|
||||
for (ObjectSubstitutionBuildItem sub : substitutions) {
|
||||
ObjectSubstitutionBuildItem.Holder holder1 = sub.holder;
|
||||
recorder.registerSubstitution(holder1.from, holder1.to, holder1.substitution);
|
||||
}
|
||||
for (BytecodeRecorderObjectLoaderBuildItem item : loaders) {
|
||||
recorder.registerObjectLoader(item.getObjectLoader());
|
||||
}
|
||||
recorder.writeBytecode(gizmoOutput);
|
||||
ResultHandle dup = bytecodeCreator.newInstance(ofConstructor(recorder.getClassName()));
|
||||
bytecodeCreator.invokeInterfaceMethod(ofMethod(StartupTask.class, "deploy", void.class, StartupContext.class), dup,
|
||||
startupContext);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ public class StartupActionImpl implements StartupAction {
|
||||
try {
|
||||
final Class<?> configClass = Class.forName(RunTimeConfigurationGenerator.CONFIG_CLASS_NAME, true,
|
||||
runtimeClassLoader);
|
||||
configClass.getDeclaredMethod(RunTimeConfigurationGenerator.C_CREATE_RUN_TIME_CONFIG.getName())
|
||||
configClass.getDeclaredMethod(RunTimeConfigurationGenerator.C_CREATE_BOOTSTRAP_CONFIG.getName())
|
||||
.invoke(null);
|
||||
} catch (Throwable t2) {
|
||||
t.addSuppressed(t2);
|
||||
|
||||
@@ -74,6 +74,7 @@ final public class Constants {
|
||||
Constants.MEMORY_SIZE_NOTE_ANCHOR, "MemorySize");
|
||||
|
||||
public static final String CONFIG_PHASE_RUNTIME_ILLUSTRATION = "icon:cogs[title=Overridable at runtime]";
|
||||
public static final String CONFIG_PHASE_BOOTSTRAP_ILLUSTRATION = "icon:cogs[title=Bootstrap - Overridable at runtime]";
|
||||
public static final String CONFIG_PHASE_BUILD_TIME_ILLUSTRATION = "icon:archive[title=Fixed at build time]";
|
||||
public static final String CONFIG_PHASE_LEGEND = String.format(
|
||||
"%n%s Configuration property fixed at build time - %s️ Configuration property overridable at runtime %n",
|
||||
|
||||
@@ -6,6 +6,8 @@ import io.quarkus.annotation.processor.Constants;
|
||||
|
||||
public enum ConfigPhase implements Comparable<ConfigPhase> {
|
||||
RUN_TIME("The configuration is overridable at runtime", Constants.CONFIG_PHASE_RUNTIME_ILLUSTRATION, "RunTime"),
|
||||
BOOTSTRAP("The configuration is used to bootstrap runtime Config Sources and is overridable at runtime",
|
||||
Constants.CONFIG_PHASE_BOOTSTRAP_ILLUSTRATION, "Bootstrap"),
|
||||
BUILD_TIME("The configuration is not overridable at runtime", Constants.CONFIG_PHASE_BUILD_TIME_ILLUSTRATION, "BuildTime"),
|
||||
BUILD_AND_RUN_TIME_FIXED("The configuration is not overridable at runtime", Constants.CONFIG_PHASE_BUILD_TIME_ILLUSTRATION,
|
||||
"BuildTime");
|
||||
@@ -37,6 +39,20 @@ public enum ConfigPhase implements Comparable<ConfigPhase> {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
case BOOTSTRAP: {
|
||||
switch (secondPhase) {
|
||||
case BUILD_TIME:
|
||||
return 1;
|
||||
case BUILD_AND_RUN_TIME_FIXED:
|
||||
return 1;
|
||||
case BOOTSTRAP:
|
||||
return 0;
|
||||
case RUN_TIME:
|
||||
return 1;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
case RUN_TIME: {
|
||||
switch (secondPhase) {
|
||||
case RUN_TIME:
|
||||
|
||||
@@ -14,6 +14,9 @@ public class StartupContext implements Closeable {
|
||||
private static final Logger LOG = Logger.getLogger(StartupContext.class);
|
||||
|
||||
private final Map<String, Object> values = new HashMap<>();
|
||||
private Object lastValue;
|
||||
// this is done to distinguish between the value never having been set and having been set as null
|
||||
private boolean lastValueSet = false;
|
||||
private final List<Runnable> shutdownTasks = new ArrayList<>();
|
||||
private final List<Runnable> lastShutdownTasks = new ArrayList<>();
|
||||
private final ShutdownContext shutdownContext = new ShutdownContext() {
|
||||
@@ -34,12 +37,22 @@ public class StartupContext implements Closeable {
|
||||
|
||||
public void putValue(String name, Object value) {
|
||||
values.put(name, value);
|
||||
lastValueSet = true;
|
||||
this.lastValue = value;
|
||||
}
|
||||
|
||||
public Object getValue(String name) {
|
||||
return values.get(name);
|
||||
}
|
||||
|
||||
public Object getLastValue() {
|
||||
return lastValue;
|
||||
}
|
||||
|
||||
public boolean isLastValueSet() {
|
||||
return lastValueSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
runAllInReverseOrder(shutdownTasks);
|
||||
|
||||
@@ -9,6 +9,13 @@ public enum ConfigPhase {
|
||||
* Values are read and available for usage at build time, and available on a read-only basis at run time.
|
||||
*/
|
||||
BUILD_AND_RUN_TIME_FIXED(true, true, false),
|
||||
|
||||
/**
|
||||
* Values are read and available for usage at run time and are re-read on each program execution. These values
|
||||
* are used to configure ConfigSourceProvider implementations
|
||||
*/
|
||||
BOOTSTRAP(false, true, true),
|
||||
|
||||
/**
|
||||
* Values are read and available for usage at run time and are re-read on each program execution.
|
||||
*/
|
||||
|
||||
@@ -10,6 +10,7 @@ import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
@@ -52,13 +53,18 @@ public final class ConfigUtils {
|
||||
return size -> new TreeSet<>();
|
||||
}
|
||||
|
||||
public static SmallRyeConfigBuilder configBuilder(final boolean runTime) {
|
||||
return configBuilder(runTime, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the basic configuration builder.
|
||||
*
|
||||
* @param runTime {@code true} if the configuration is run time, {@code false} if build time
|
||||
* @param addDiscovered {@code true} if the ConfigSource and Converter objects should be auto-discovered
|
||||
* @return the configuration builder
|
||||
*/
|
||||
public static SmallRyeConfigBuilder configBuilder(final boolean runTime) {
|
||||
public static SmallRyeConfigBuilder configBuilder(final boolean runTime, final boolean addDiscovered) {
|
||||
final SmallRyeConfigBuilder builder = new SmallRyeConfigBuilder();
|
||||
final ApplicationPropertiesConfigSource.InFileSystem inFileSystem = new ApplicationPropertiesConfigSource.InFileSystem();
|
||||
final ApplicationPropertiesConfigSource.InJar inJar = new ApplicationPropertiesConfigSource.InJar();
|
||||
@@ -83,8 +89,10 @@ public final class ConfigUtils {
|
||||
sources.add(new SysPropConfigSource());
|
||||
builder.withSources(sources.toArray(new ConfigSource[0]));
|
||||
}
|
||||
builder.addDiscoveredSources();
|
||||
builder.addDiscoveredConverters();
|
||||
if (addDiscovered) {
|
||||
builder.addDiscoveredSources();
|
||||
builder.addDiscoveredConverters();
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
@@ -101,6 +109,18 @@ public final class ConfigUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a configuration source providers to the builder.
|
||||
*
|
||||
* @param builder the builder
|
||||
* @param providers the providers to add
|
||||
*/
|
||||
public static void addSourceProviders(SmallRyeConfigBuilder builder, Collection<ConfigSourceProvider> providers) {
|
||||
for (ConfigSourceProvider provider : providers) {
|
||||
addSourceProvider(builder, provider);
|
||||
}
|
||||
}
|
||||
|
||||
static class EnvConfigSource implements ConfigSource {
|
||||
static final Pattern REP_PATTERN = Pattern.compile("[^a-zA-Z0-9_]");
|
||||
|
||||
|
||||
100
integration-tests/bootstrap-config/application/pom.xml
Normal file
100
integration-tests/bootstrap-config/application/pom.xml
Normal file
@@ -0,0 +1,100 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-integration-test-bootstrap-config</artifactId>
|
||||
<version>999-SNAPSHOT</version>
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>quarkus-integration-test-bootstrap-config-application</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-resteasy</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-integration-test-bootstrap-config-extension</artifactId>
|
||||
<version>999-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-junit5</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
<artifactId>rest-assured</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>build</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>${compiler-plugin.version}</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<systemProperties>
|
||||
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
|
||||
</systemProperties>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>native</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>native</name>
|
||||
</property>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>integration-test</goal>
|
||||
<goal>verify</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<systemProperties>
|
||||
<native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
|
||||
</systemProperties>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<properties>
|
||||
<quarkus.package.type>native</quarkus.package.type>
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
@@ -0,0 +1,18 @@
|
||||
package io.quarkus.it.bootstrap.config;
|
||||
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
|
||||
@Path("/greeting")
|
||||
public class GreetingResource {
|
||||
|
||||
@ConfigProperty(name = "foo.key.i2")
|
||||
String propertyFromBootstrapConfig;
|
||||
|
||||
@GET
|
||||
public String greet() {
|
||||
return "hello " + propertyFromBootstrapConfig;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
quarkus.dummy.name=foo
|
||||
quarkus.application.name=dummy
|
||||
@@ -0,0 +1,23 @@
|
||||
package io.quarkus.it.bootstrap.config;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
|
||||
@QuarkusTest
|
||||
class GreetingResourceTest {
|
||||
|
||||
@Test
|
||||
void testEndpoint() {
|
||||
given()
|
||||
.when().get("/greeting")
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.body(containsString("hello"))
|
||||
.body(containsString("dummy2"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package io.quarkus.it.bootstrap.config;
|
||||
|
||||
import io.quarkus.test.junit.NativeImageTest;
|
||||
|
||||
@NativeImageTest
|
||||
class GreetingResourceTestIT extends GreetingResourceTest {
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-integration-test-bootstrap-config-extension-parent</artifactId>
|
||||
<version>999-SNAPSHOT</version>
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>quarkus-integration-test-bootstrap-config-extension-deployment</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-core-deployment</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-integration-test-bootstrap-config-extension</artifactId>
|
||||
<version>999-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-extension-processor</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,26 @@
|
||||
package io.quarkus.it.bootstrap.config.extension.deployment;
|
||||
|
||||
import io.quarkus.deployment.annotations.BuildStep;
|
||||
import io.quarkus.deployment.annotations.ExecutionTime;
|
||||
import io.quarkus.deployment.annotations.Record;
|
||||
import io.quarkus.deployment.builditem.RunTimeConfigurationSourceValueBuildItem;
|
||||
import io.quarkus.it.bootstrap.config.extension.DummyBootstrapRecorder;
|
||||
import io.quarkus.it.bootstrap.config.extension.DummyBootstrapRecorder2;
|
||||
import io.quarkus.it.bootstrap.config.extension.DummyConfig;
|
||||
import io.quarkus.runtime.ApplicationConfig;
|
||||
|
||||
public class DummyBootstrapConfigBuildStep {
|
||||
|
||||
@BuildStep
|
||||
@Record(ExecutionTime.RUNTIME_INIT)
|
||||
public RunTimeConfigurationSourceValueBuildItem dummyRecorder(DummyBootstrapRecorder recorder, DummyConfig dummyConfig,
|
||||
ApplicationConfig applicationConfig) {
|
||||
return new RunTimeConfigurationSourceValueBuildItem(recorder.create(dummyConfig, applicationConfig));
|
||||
}
|
||||
|
||||
@BuildStep
|
||||
@Record(ExecutionTime.RUNTIME_INIT)
|
||||
public RunTimeConfigurationSourceValueBuildItem dummyRecorder2(DummyBootstrapRecorder2 recorder) {
|
||||
return new RunTimeConfigurationSourceValueBuildItem(recorder.create());
|
||||
}
|
||||
}
|
||||
23
integration-tests/bootstrap-config/extension/pom.xml
Normal file
23
integration-tests/bootstrap-config/extension/pom.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-build-parent</artifactId>
|
||||
<version>999-SNAPSHOT</version>
|
||||
<relativePath>../../../build-parent/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>quarkus-integration-test-bootstrap-config-extension-parent</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<modules>
|
||||
<module>deployment</module>
|
||||
<module>runtime</module>
|
||||
</modules>
|
||||
|
||||
|
||||
</project>
|
||||
43
integration-tests/bootstrap-config/extension/runtime/pom.xml
Normal file
43
integration-tests/bootstrap-config/extension/runtime/pom.xml
Normal file
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-integration-test-bootstrap-config-extension-parent</artifactId>
|
||||
<version>999-SNAPSHOT</version>
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>quarkus-integration-test-bootstrap-config-extension</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-core</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-bootstrap-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-extension-processor</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,15 @@
|
||||
package io.quarkus.it.bootstrap.config.extension;
|
||||
|
||||
import org.eclipse.microprofile.config.spi.ConfigSourceProvider;
|
||||
|
||||
import io.quarkus.runtime.ApplicationConfig;
|
||||
import io.quarkus.runtime.RuntimeValue;
|
||||
import io.quarkus.runtime.annotations.Recorder;
|
||||
|
||||
@Recorder
|
||||
public class DummyBootstrapRecorder {
|
||||
|
||||
public RuntimeValue<ConfigSourceProvider> create(DummyConfig dummyConfig, ApplicationConfig applicationConfig) {
|
||||
return new RuntimeValue<>(new DummyConfigSourceProvider(dummyConfig, applicationConfig));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package io.quarkus.it.bootstrap.config.extension;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.eclipse.microprofile.config.spi.ConfigSource;
|
||||
import org.eclipse.microprofile.config.spi.ConfigSourceProvider;
|
||||
|
||||
import io.quarkus.runtime.RuntimeValue;
|
||||
import io.quarkus.runtime.annotations.Recorder;
|
||||
|
||||
@Recorder
|
||||
public class DummyBootstrapRecorder2 {
|
||||
|
||||
public RuntimeValue<ConfigSourceProvider> create() {
|
||||
return new RuntimeValue<>(new ConfigSourceProvider() {
|
||||
@Override
|
||||
public Iterable<ConfigSource> getConfigSources(ClassLoader forClassLoader) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package io.quarkus.it.bootstrap.config.extension;
|
||||
|
||||
import io.quarkus.runtime.annotations.ConfigItem;
|
||||
import io.quarkus.runtime.annotations.ConfigPhase;
|
||||
import io.quarkus.runtime.annotations.ConfigRoot;
|
||||
|
||||
@ConfigRoot(phase = ConfigPhase.BOOTSTRAP)
|
||||
public class DummyConfig {
|
||||
|
||||
/**
|
||||
* dummy name
|
||||
*/
|
||||
public String name;
|
||||
|
||||
/**
|
||||
* dummy times
|
||||
*/
|
||||
@ConfigItem(defaultValue = "2")
|
||||
public Integer times;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package io.quarkus.it.bootstrap.config.extension;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.microprofile.config.spi.ConfigSource;
|
||||
import org.eclipse.microprofile.config.spi.ConfigSourceProvider;
|
||||
|
||||
import io.quarkus.runtime.ApplicationConfig;
|
||||
|
||||
/**
|
||||
* A dummy provider that returns a single ConfigSource which contains
|
||||
* as many properties as DummyConfig.times indicates and where the
|
||||
* key depends on DummyConfig.name
|
||||
*/
|
||||
public class DummyConfigSourceProvider implements ConfigSourceProvider {
|
||||
|
||||
private final DummyConfig dummyConfig;
|
||||
private final ApplicationConfig applicationConfig;
|
||||
|
||||
public DummyConfigSourceProvider(DummyConfig dummyConfig, ApplicationConfig applicationConfig) {
|
||||
this.dummyConfig = dummyConfig;
|
||||
this.applicationConfig = applicationConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<ConfigSource> getConfigSources(ClassLoader forClassLoader) {
|
||||
InMemoryConfigSource configSource = new InMemoryConfigSource(Integer.MIN_VALUE, "dummy config source");
|
||||
for (int i = 0; i < dummyConfig.times; i++) {
|
||||
configSource.add(dummyConfig.name + ".key.i" + (i + 1), applicationConfig.name.get() + (i + 1));
|
||||
}
|
||||
return Collections.singletonList(configSource);
|
||||
}
|
||||
|
||||
private static final class InMemoryConfigSource implements ConfigSource {
|
||||
|
||||
private final Map<String, String> values = new HashMap<>();
|
||||
private final int ordinal;
|
||||
private final String name;
|
||||
|
||||
private InMemoryConfigSource(int ordinal, String name) {
|
||||
this.ordinal = ordinal;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void add(String key, String value) {
|
||||
values.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getProperties() {
|
||||
return values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getPropertyNames() {
|
||||
return values.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrdinal() {
|
||||
return ordinal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue(String propertyName) {
|
||||
return values.get(propertyName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
23
integration-tests/bootstrap-config/pom.xml
Normal file
23
integration-tests/bootstrap-config/pom.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-integration-tests-parent</artifactId>
|
||||
<version>999-SNAPSHOT</version>
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>quarkus-integration-test-bootstrap-config</artifactId>
|
||||
<name>Quarkus - Integration Tests - Bootstrap Config</name>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<modules>
|
||||
<module>extension</module>
|
||||
<module>application</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
@@ -94,6 +94,7 @@
|
||||
<module>logging-gelf</module>
|
||||
<module>cache</module>
|
||||
<module>qute</module>
|
||||
<module>bootstrap-config</module>
|
||||
</modules>
|
||||
|
||||
<build>
|
||||
|
||||
Reference in New Issue
Block a user