Skip to content

Commit

Permalink
Add first version of bootstrap config support
Browse files Browse the repository at this point in the history
  • Loading branch information
geoand committed Feb 10, 2020
1 parent 2a3b019 commit 4a680de
Show file tree
Hide file tree
Showing 30 changed files with 905 additions and 108 deletions.
3 changes: 2 additions & 1 deletion ci-templates/stages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,10 @@
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;
Expand Down Expand Up @@ -352,6 +354,8 @@ public static Consumer<BuildChainBuilder> loadStepsFrom(Class<?> clazz, BuildTim
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 {
Expand Down Expand Up @@ -467,6 +471,8 @@ public static Consumer<BuildChainBuilder> loadStepsFrom(Class<?> clazz, BuildTim
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 {
Expand Down Expand Up @@ -539,6 +545,8 @@ public static Consumer<BuildChainBuilder> loadStepsFrom(Class<?> clazz, BuildTim
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 {
Expand Down Expand Up @@ -572,6 +580,8 @@ public static Consumer<BuildChainBuilder> loadStepsFrom(Class<?> clazz, BuildTim
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 {
Expand Down Expand Up @@ -634,9 +644,10 @@ public static Consumer<BuildChainBuilder> loadStepsFrom(Class<?> clazz, BuildTim
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();
Expand Down Expand Up @@ -733,8 +744,14 @@ public static Consumer<BuildChainBuilder> loadStepsFrom(Class<?> clazz, BuildTim
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);
Expand All @@ -743,7 +760,9 @@ public static Consumer<BuildChainBuilder> loadStepsFrom(Class<?> clazz, BuildTim
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");
Expand Down Expand Up @@ -778,6 +797,12 @@ public static Consumer<BuildChainBuilder> loadStepsFrom(Class<?> clazz, BuildTim
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
Expand Down Expand Up @@ -837,14 +862,20 @@ public static Consumer<BuildChainBuilder> loadStepsFrom(Class<?> clazz, BuildTim
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");
}
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
Expand Down Expand Up @@ -934,9 +965,14 @@ public void execute(final BuildContext bc) {
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));
}
}

}
}

Expand All @@ -953,6 +989,12 @@ public String toString() {
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();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Loading

0 comments on commit 4a680de

Please sign in to comment.