Skip to content

Commit c5d9f78

Browse files
authored
Merge pull request #48500 from radcortez/runtime-config-warning
Warn if Runtime configuration is used in BuildSteps and without a RuntimeValue in Recorders
2 parents 9f54ca9 + 2191f01 commit c5d9f78

File tree

155 files changed

+1102
-944
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

155 files changed

+1102
-944
lines changed

core/deployment/src/main/java/io/quarkus/deployment/ExtensionLoader.java

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.quarkus.deployment;
22

3+
import static io.quarkus.deployment.ExtensionLoaderConfig.ReportRuntimeConfigAtDeployment.warn;
34
import static io.quarkus.deployment.util.ReflectUtil.isBuildProducerOf;
45
import static io.quarkus.deployment.util.ReflectUtil.isConsumerOf;
56
import static io.quarkus.deployment.util.ReflectUtil.isListOf;
@@ -113,7 +114,6 @@ private ExtensionLoader() {
113114
}
114115

115116
private static final Logger loadLog = Logger.getLogger("io.quarkus.deployment");
116-
private static final Logger cfgLog = Logger.getLogger("io.quarkus.configuration");
117117
@SuppressWarnings("unchecked")
118118
private static final Class<? extends BooleanSupplier>[] EMPTY_BOOLEAN_SUPPLIER_CLASS_ARRAY = new Class[0];
119119

@@ -207,6 +207,7 @@ public boolean canHandleObject(final Object obj, final boolean staticInit) {
207207
}
208208
};
209209

210+
// Load @ConfigMapping in recorded deployment code from Recorder
210211
ObjectLoader mappingLoader = new ObjectLoader() {
211212
@Override
212213
public ResultHandle load(final BytecodeCreator body, final Object obj, final boolean staticInit) {
@@ -268,6 +269,8 @@ private static Consumer<BuildChainBuilder> loadStepsFromClass(Class<?> clazz,
268269
throw reportError(clazz, "Build step classes must have exactly one constructor");
269270
}
270271

272+
ExtensionLoaderConfig extensionLoaderConfig = (ExtensionLoaderConfig) readResult.getObjectsByClass()
273+
.get(ExtensionLoaderConfig.class);
271274
EnumSet<ConfigPhase> consumingConfigPhases = EnumSet.noneOf(ConfigPhase.class);
272275

273276
final Constructor<?> constructor = constructors[0];
@@ -591,12 +594,24 @@ private static Consumer<BuildChainBuilder> loadStepsFromClass(Class<?> clazz,
591594
}
592595
} else if (phase.isReadAtMain()) {
593596
if (isRecorder) {
594-
methodParamFns.add((bc, bri) -> {
595-
final RunTimeConfigurationProxyBuildItem proxies = bc
596-
.consume(RunTimeConfigurationProxyBuildItem.class);
597-
return proxies.getProxyObjectFor(parameterClass);
598-
});
599-
runTimeProxies.computeIfAbsent(parameterClass, ConfigMappingUtils::newInstance);
597+
if (extensionLoaderConfig.reportRuntimeConfigAtDeployment().equals(warn)) {
598+
methodParamFns.add((bc, bri) -> {
599+
RunTimeConfigurationProxyBuildItem proxies = bc
600+
.consume(RunTimeConfigurationProxyBuildItem.class);
601+
return proxies.getProxyObjectFor(parameterClass);
602+
});
603+
loadLog.warn(reportError(parameter,
604+
phase + " configuration should not be consumed in Build Steps, use RuntimeValue<"
605+
+ parameter.getType().getTypeName()
606+
+ "> in a @Recorder constructor instead")
607+
.getMessage());
608+
runTimeProxies.computeIfAbsent(parameterClass, ConfigMappingUtils::newInstance);
609+
} else {
610+
throw reportError(parameter,
611+
phase + " configuration cannot be consumed in Build Steps, use RuntimeValue<"
612+
+ parameter.getType().getTypeName()
613+
+ "> in a @Recorder constructor instead");
614+
}
600615
} else {
601616
throw reportError(parameter,
602617
phase + " configuration cannot be consumed here unless the method is a @Recorder");
@@ -619,11 +634,13 @@ private static Consumer<BuildChainBuilder> loadStepsFromClass(Class<?> clazz,
619634
for (var ctor : ctors) {
620635
if (ctors.length == 1 || ctor.isAnnotationPresent(Inject.class)) {
621636
for (var type : ctor.getGenericParameterTypes()) {
622-
Class<?> theType = null;
637+
Class<?> theType;
638+
boolean isRuntimeValue = false;
623639
if (type instanceof ParameterizedType) {
624640
ParameterizedType pt = (ParameterizedType) type;
625641
if (pt.getRawType().equals(RuntimeValue.class)) {
626642
theType = (Class<?>) pt.getActualTypeArguments()[0];
643+
isRuntimeValue = true;
627644
} else {
628645
throw new RuntimeException("Unknown recorder constructor parameter: " + type
629646
+ " in recorder " + parameter.getType());
@@ -634,11 +651,26 @@ private static Consumer<BuildChainBuilder> loadStepsFromClass(Class<?> clazz,
634651
ConfigRoot annotation = theType.getAnnotation(ConfigRoot.class);
635652
if (annotation != null) {
636653
if (recordAnnotation.value() == ExecutionTime.STATIC_INIT) {
654+
// TODO - Check for runtime config is done in another place, we may want to make things more consistent. Rewrite once we disallow the injection of runtime objects in build steps
637655
methodConsumingConfigPhases.add(ConfigPhase.BUILD_AND_RUN_TIME_FIXED);
638656
} else {
639657
methodConsumingConfigPhases.add(annotation.phase());
658+
if (annotation.phase().isReadAtMain() && !isRuntimeValue) {
659+
if (extensionLoaderConfig.reportRuntimeConfigAtDeployment().equals(warn)) {
660+
loadLog.warn(reportError(parameter, annotation.phase() + " configuration "
661+
+ type.getTypeName()
662+
+ " should be injected in a @Recorder constructor as a RuntimeValue<"
663+
+ type.getTypeName() + ">").getMessage());
664+
} else {
665+
throw reportError(parameter, annotation.phase() + " configuration "
666+
+ type.getTypeName()
667+
+ " can only be injected in a @Recorder constructor as a RuntimeValue<"
668+
+ type.getTypeName() + ">");
669+
}
670+
}
640671
}
641672
if (annotation.phase().isReadAtMain()) {
673+
// TODO - Remove once we disallow the injection of runtime objects in build steps
642674
runTimeProxies.computeIfAbsent(theType, ConfigMappingUtils::newInstance);
643675
} else {
644676
runTimeProxies.computeIfAbsent(theType, readResult::requireObjectForClass);
@@ -835,6 +867,7 @@ public void execute(final BuildContext bc) {
835867
}
836868
return runTimeProxies.get(s);
837869
}
870+
// TODO - Remove once we disallow the injection of runtime objects in build steps
838871
if (s instanceof ParameterizedType) {
839872
ParameterizedType p = (ParameterizedType) s;
840873
if (p.getRawType() == RuntimeValue.class) {
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package io.quarkus.deployment;
2+
3+
import io.quarkus.runtime.annotations.ConfigRoot;
4+
import io.smallrye.config.ConfigMapping;
5+
import io.smallrye.config.WithDefault;
6+
7+
@ConfigMapping(prefix = "quarkus.extension-loader")
8+
@ConfigRoot
9+
public interface ExtensionLoaderConfig {
10+
/**
11+
* Report runtime Config objects used during deployment time.
12+
*/
13+
@WithDefault("warn")
14+
ReportRuntimeConfigAtDeployment reportRuntimeConfigAtDeployment();
15+
16+
enum ReportRuntimeConfigAtDeployment {
17+
warn,
18+
fail
19+
}
20+
}

core/deployment/src/main/java/io/quarkus/deployment/builditem/RunTimeConfigurationProxyBuildItem.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
/**
88
* A build item that carries all the "fake" run time config objects for use by recorders.
99
*/
10+
@Deprecated(forRemoval = true, since = "3.25")
1011
public final class RunTimeConfigurationProxyBuildItem extends SimpleBuildItem {
1112
private final Map<Class<?>, Object> objects;
1213

core/deployment/src/main/java/io/quarkus/deployment/configuration/ConfigMappingUtils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,9 +191,9 @@ private static void registerImplicitConverter(
191191
}
192192
}
193193

194+
@Deprecated(forRemoval = true, since = "3.25")
194195
public static Object newInstance(Class<?> configClass) {
195196
if (configClass.isAnnotationPresent(ConfigMapping.class)) {
196-
// TODO - radcortez - mapping classes cannot be initialized like this.
197197
return ReflectUtil.newInstance(ConfigMappingLoader.ensureLoaded(configClass).implementation());
198198
} else {
199199
return ReflectUtil.newInstance(configClass);

core/deployment/src/main/java/io/quarkus/deployment/logging/LoggingResourceProcessor.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -240,8 +240,6 @@ void miscSetup(
240240
LoggingSetupBuildItem setupLoggingRuntimeInit(
241241
final RecorderContext context,
242242
final LoggingSetupRecorder recorder,
243-
final LogRuntimeConfig logRuntimeConfig,
244-
final LogBuildTimeConfig logBuildTimeConfig,
245243
final CombinedIndexBuildItem combinedIndexBuildItem,
246244
final LogCategoryMinLevelDefaultsBuildItem categoryMinLevelDefaults,
247245
final Optional<StreamingLogHandlerBuildItem> streamingLogStreamHandlerBuildItem,
@@ -308,7 +306,7 @@ LoggingSetupBuildItem setupLoggingRuntimeInit(
308306
}
309307

310308
shutdownListenerBuildItemBuildProducer.produce(new ShutdownListenerBuildItem(
311-
recorder.initializeLogging(logRuntimeConfig, logBuildTimeConfig, discoveredLogComponents,
309+
recorder.initializeLogging(discoveredLogComponents,
312310
categoryMinLevelDefaults.content, alwaysEnableLogStream,
313311
streamingDevUiLogHandler, handlers, namedHandlers,
314312
possibleConsoleFormatters, possibleFileFormatters, possibleSyslogFormatters,
@@ -326,6 +324,7 @@ LoggingSetupBuildItem setupLoggingRuntimeInit(
326324
}
327325

328326
SmallRyeConfig config = ConfigProvider.getConfig().unwrap(SmallRyeConfig.class);
327+
LogBuildTimeConfig logBuildTimeConfig = config.getConfigMapping(LogBuildTimeConfig.class);
329328
LogRuntimeConfig logRuntimeConfigInBuild = config.getConfigMapping(LogRuntimeConfig.class);
330329
ConsoleRuntimeConfig consoleRuntimeConfig = config.getConfigMapping(ConsoleRuntimeConfig.class);
331330

core/runtime/src/main/java/io/quarkus/runtime/BannerRecorder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
@Recorder
99
public class BannerRecorder {
10-
final RuntimeValue<BannerRuntimeConfig> bannerRuntimeConfig;
10+
private final RuntimeValue<BannerRuntimeConfig> bannerRuntimeConfig;
1111

1212
public BannerRecorder(RuntimeValue<BannerRuntimeConfig> bannerRuntimeConfig) {
1313
this.bannerRuntimeConfig = bannerRuntimeConfig;

core/runtime/src/main/java/io/quarkus/runtime/ExecutorRecorder.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,19 @@
2424
*/
2525
@Recorder
2626
public class ExecutorRecorder {
27-
2827
private static final Logger log = Logger.getLogger("io.quarkus.thread-pool");
2928

3029
private static volatile Executor current;
3130

32-
final ThreadPoolConfig threadPoolConfig;
31+
private final RuntimeValue<ThreadPoolConfig> threadPoolConfig;
3332

34-
public ExecutorRecorder(ThreadPoolConfig threadPoolConfig) {
33+
public ExecutorRecorder(RuntimeValue<ThreadPoolConfig> threadPoolConfig) {
3534
this.threadPoolConfig = threadPoolConfig;
3635
}
3736

3837
public ScheduledExecutorService setupRunTime(ShutdownContext shutdownContext,
3938
LaunchMode launchMode, ThreadFactory threadFactory, ContextHandler<Object> contextHandler) {
40-
final EnhancedQueueExecutor underlying = createExecutor(threadPoolConfig, threadFactory, contextHandler);
39+
final EnhancedQueueExecutor underlying = createExecutor(threadPoolConfig.getValue(), threadFactory, contextHandler);
4140
if (launchMode == LaunchMode.DEVELOPMENT) {
4241
shutdownContext.addLastShutdownTask(new Runnable() {
4342
@Override
@@ -52,10 +51,10 @@ public void run() {
5251
}
5352
});
5453
} else {
55-
Runnable shutdownTask = createShutdownTask(threadPoolConfig, underlying);
54+
Runnable shutdownTask = createShutdownTask(threadPoolConfig.getValue(), underlying);
5655
shutdownContext.addLastShutdownTask(shutdownTask);
5756
}
58-
if (threadPoolConfig.prefill()) {
57+
if (threadPoolConfig.getValue().prefill()) {
5958
underlying.prestartAllCoreThreads();
6059
}
6160
ScheduledExecutorService managed = underlying;

core/runtime/src/main/java/io/quarkus/runtime/init/InitializationTaskRecorder.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,22 @@
66

77
import io.quarkus.runtime.PreventFurtherStepsException;
88
import io.quarkus.runtime.Quarkus;
9+
import io.quarkus.runtime.RuntimeValue;
910
import io.quarkus.runtime.annotations.Recorder;
1011

1112
/**
1213
* A {@link Recorder} that is used to check if the application should exit once all initialization tasks are completed.
1314
*/
1415
@Recorder
1516
public class InitializationTaskRecorder {
16-
private final InitRuntimeConfig initRuntimeConfig;
17+
private final RuntimeValue<InitRuntimeConfig> initRuntimeConfig;
1718

18-
public InitializationTaskRecorder(InitRuntimeConfig initRuntimeConfig) {
19+
public InitializationTaskRecorder(RuntimeValue<InitRuntimeConfig> initRuntimeConfig) {
1920
this.initRuntimeConfig = initRuntimeConfig;
2021
}
2122

2223
public void exitIfNeeded() {
23-
if (initRuntimeConfig.initAndExit()) {
24+
if (initRuntimeConfig.getValue().initAndExit()) {
2425
preventFurtherRecorderSteps(5, "Error attempting to gracefully shutdown after initialization",
2526
() -> new PreventFurtherStepsException("Gracefully exiting after initialization.", 0));
2627
}

core/runtime/src/main/java/io/quarkus/runtime/logging/LoggingSetupRecorder.java

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,18 @@
7373

7474
@Recorder
7575
public class LoggingSetupRecorder {
76-
7776
private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(LoggingSetupRecorder.class);
7877

79-
final RuntimeValue<ConsoleRuntimeConfig> consoleRuntimeConfig;
78+
private final LogBuildTimeConfig logBuildTimeConfig;
79+
private final RuntimeValue<LogRuntimeConfig> logRuntimeConfig;
80+
private final RuntimeValue<ConsoleRuntimeConfig> consoleRuntimeConfig;
8081

81-
public LoggingSetupRecorder(RuntimeValue<ConsoleRuntimeConfig> consoleRuntimeConfig) {
82+
public LoggingSetupRecorder(
83+
final LogBuildTimeConfig logBuildTimeConfig,
84+
final RuntimeValue<LogRuntimeConfig> logRuntimeConfig,
85+
final RuntimeValue<ConsoleRuntimeConfig> consoleRuntimeConfig) {
86+
this.logBuildTimeConfig = logBuildTimeConfig;
87+
this.logRuntimeConfig = logRuntimeConfig;
8288
this.consoleRuntimeConfig = consoleRuntimeConfig;
8389
}
8490

@@ -114,18 +120,17 @@ public String getName() {
114120
return "Logging Config";
115121
}
116122
}).build();
117-
LogRuntimeConfig logRuntimeConfig = loggingConfig.getConfigMapping(LogRuntimeConfig.class);
118123
LogBuildTimeConfig logBuildTimeConfig = loggingConfig.getConfigMapping(LogBuildTimeConfig.class);
124+
LogRuntimeConfig logRuntimeConfig = loggingConfig.getConfigMapping(LogRuntimeConfig.class);
119125
ConsoleRuntimeConfig consoleRuntimeConfig = loggingConfig.getConfigMapping(ConsoleRuntimeConfig.class);
120-
new LoggingSetupRecorder(new RuntimeValue<>(consoleRuntimeConfig)).initializeLogging(logRuntimeConfig,
121-
logBuildTimeConfig,
122-
DiscoveredLogComponents.ofEmpty(), emptyMap(), false, null, emptyList(), emptyList(), emptyList(), emptyList(),
123-
emptyList(), emptyList(), banner, LaunchMode.DEVELOPMENT, false);
126+
new LoggingSetupRecorder(logBuildTimeConfig, new RuntimeValue<>(logRuntimeConfig),
127+
new RuntimeValue<>(consoleRuntimeConfig)).initializeLogging(
128+
DiscoveredLogComponents.ofEmpty(), emptyMap(), false, null, emptyList(), emptyList(), emptyList(),
129+
emptyList(),
130+
emptyList(), emptyList(), banner, LaunchMode.DEVELOPMENT, false);
124131
}
125132

126133
public ShutdownListener initializeLogging(
127-
final LogRuntimeConfig config,
128-
final LogBuildTimeConfig buildConfig,
129134
final DiscoveredLogComponents discoveredLogComponents,
130135
final Map<String, InheritableLevel> categoryDefaultMinLevels,
131136
final boolean enableWebStream,
@@ -140,6 +145,9 @@ public ShutdownListener initializeLogging(
140145
final LaunchMode launchMode,
141146
final boolean includeFilters) {
142147

148+
LogBuildTimeConfig buildConfig = logBuildTimeConfig;
149+
LogRuntimeConfig config = logRuntimeConfig.getValue();
150+
143151
ShutdownNotifier shutdownNotifier = new ShutdownNotifier();
144152
Map<String, CategoryConfig> categories = config.categories();
145153
LogContext logContext = LogContext.getLogContext();

core/runtime/src/main/java/io/quarkus/runtime/shutdown/ShutdownRecorder.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import org.jboss.logging.Logger;
99

10+
import io.quarkus.runtime.RuntimeValue;
1011
import io.quarkus.runtime.annotations.Recorder;
1112

1213
@Recorder
@@ -15,10 +16,13 @@ public class ShutdownRecorder {
1516
private static final Logger log = Logger.getLogger(ShutdownRecorder.class);
1617

1718
private static volatile List<ShutdownListener> shutdownListeners;
18-
private static volatile ShutdownConfig shutdownConfig;
19+
private static volatile RuntimeValue<ShutdownConfig> shutdownConfig;
1920
private static volatile boolean delayEnabled;
2021

21-
public ShutdownRecorder(ShutdownConfig shutdownConfig) {
22+
/**
23+
* ShutdownRecorder is only called in <code>RUNTIME_INIT</code> build steps, so it is safe to set ShutdownConfig.
24+
*/
25+
public ShutdownRecorder(RuntimeValue<ShutdownConfig> shutdownConfig) {
2226
ShutdownRecorder.shutdownConfig = shutdownConfig;
2327
}
2428

@@ -50,9 +54,9 @@ private static void executePreShutdown() throws InterruptedException {
5054
}
5155

5256
private static void waitForDelay() {
53-
if (delayEnabled && shutdownConfig.isDelayEnabled()) {
57+
if (delayEnabled && shutdownConfig.getValue().isDelayEnabled()) {
5458
try {
55-
Thread.sleep(shutdownConfig.delay().get().toMillis());
59+
Thread.sleep(shutdownConfig.getValue().delay().get().toMillis());
5660
} catch (InterruptedException e) {
5761
log.error("Interrupted while waiting for delay, continuing to shutdown immediately");
5862
}
@@ -64,8 +68,8 @@ private static void executeShutdown() throws InterruptedException {
6468
for (ShutdownListener i : shutdownListeners) {
6569
i.shutdown(new LatchShutdownNotification(shutdown));
6670
}
67-
if (shutdownConfig.isTimeoutEnabled()
68-
&& !shutdown.await(shutdownConfig.timeout().get().toMillis(), TimeUnit.MILLISECONDS)) {
71+
if (shutdownConfig.getValue().isTimeoutEnabled()
72+
&& !shutdown.await(shutdownConfig.getValue().timeout().get().toMillis(), TimeUnit.MILLISECONDS)) {
6973
log.error("Timed out waiting for graceful shutdown, shutting down anyway.");
7074
}
7175
}

0 commit comments

Comments
 (0)