diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/MainClassBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/MainClassBuildStep.java index 439210c22e07b..de47b02e92602 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/steps/MainClassBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/MainClassBuildStep.java @@ -77,6 +77,7 @@ import io.quarkus.gizmo.TryBlock; import io.quarkus.runtime.Application; import io.quarkus.runtime.ApplicationLifecycleManager; +import io.quarkus.runtime.ExecutionModeManager; import io.quarkus.runtime.LaunchMode; import io.quarkus.runtime.NativeImageRuntimePropertiesRecorder; import io.quarkus.runtime.PreventFurtherStepsException; @@ -106,6 +107,14 @@ public class MainClassBuildStep { void.class, StartupContext.class); public static final MethodDescriptor CONFIGURE_STEP_TIME_ENABLED = ofMethod(StepTiming.class.getName(), "configureEnabled", void.class); + public static final MethodDescriptor RUNTIME_EXECUTION_STATIC_INIT = ofMethod(ExecutionModeManager.class.getName(), + "staticInit", void.class); + public static final MethodDescriptor RUNTIME_EXECUTION_RUNTIME_INIT = ofMethod(ExecutionModeManager.class.getName(), + "runtimeInit", void.class); + public static final MethodDescriptor RUNTIME_EXECUTION_RUNNING = ofMethod(ExecutionModeManager.class.getName(), + "running", void.class); + public static final MethodDescriptor RUNTIME_EXECUTION_UNSET = ofMethod(ExecutionModeManager.class.getName(), + "unset", void.class); public static final MethodDescriptor CONFIGURE_STEP_TIME_START = ofMethod(StepTiming.class.getName(), "configureStart", void.class); private static final DotName QUARKUS_APPLICATION = DotName.createSimple(QuarkusApplication.class.getName()); @@ -170,6 +179,7 @@ void build(List staticInitTasks, lm); mv.invokeStaticMethod(CONFIGURE_STEP_TIME_ENABLED); + mv.invokeStaticMethod(RUNTIME_EXECUTION_STATIC_INIT); mv.invokeStaticMethod(ofMethod(Timing.class, "staticInitStarted", void.class, boolean.class), mv.load(launchMode.isAuxiliaryApplication())); @@ -227,6 +237,7 @@ void build(List staticInitTasks, mv.load(i.getKey()), mv.load(i.getValue())); } mv.invokeStaticMethod(ofMethod(NativeImageRuntimePropertiesRecorder.class, "doRuntime", void.class)); + mv.invokeStaticMethod(RUNTIME_EXECUTION_RUNTIME_INIT); // Set the SSL system properties if (!javaLibraryPathAdditionalPaths.isEmpty()) { @@ -268,6 +279,8 @@ void build(List staticInitTasks, loaders, constants, gizmoOutput, startupContext, tryBlock); } + tryBlock.invokeStaticMethod(RUNTIME_EXECUTION_RUNNING); + // Startup log messages List featureNames = new ArrayList<>(); for (FeatureBuildItem feature : features) { @@ -324,6 +337,7 @@ void build(List staticInitTasks, mv = file.getMethodCreator("doStop", void.class); mv.setModifiers(Modifier.PROTECTED | Modifier.FINAL); + mv.invokeStaticMethod(RUNTIME_EXECUTION_UNSET); startupContext = mv.readStaticField(scField.getFieldDescriptor()); mv.invokeVirtualMethod(ofMethod(StartupContext.class, "close", void.class), startupContext); mv.returnValue(null); diff --git a/core/runtime/src/main/java/io/quarkus/runtime/ExecutionMode.java b/core/runtime/src/main/java/io/quarkus/runtime/ExecutionMode.java new file mode 100644 index 0000000000000..5dfd5bb68d2d1 --- /dev/null +++ b/core/runtime/src/main/java/io/quarkus/runtime/ExecutionMode.java @@ -0,0 +1,30 @@ +package io.quarkus.runtime; + +/** + * The runtime execution mode. + */ +public enum ExecutionMode { + + /** + * Static initializiation. + */ + STATIC_INIT, + + /** + * Runtime initialization. + */ + RUNTIME_INIT, + + /** + * The application is running. + */ + RUNNING, + + UNSET, + ; + + public static ExecutionMode current() { + return ExecutionModeManager.getExecutionMode(); + } + +} diff --git a/core/runtime/src/main/java/io/quarkus/runtime/ExecutionModeManager.java b/core/runtime/src/main/java/io/quarkus/runtime/ExecutionModeManager.java new file mode 100644 index 0000000000000..18bf97a8c49b2 --- /dev/null +++ b/core/runtime/src/main/java/io/quarkus/runtime/ExecutionModeManager.java @@ -0,0 +1,26 @@ +package io.quarkus.runtime; + +public final class ExecutionModeManager { + + private static volatile ExecutionMode executionMode = ExecutionMode.UNSET; + + public static void staticInit() { + executionMode = ExecutionMode.STATIC_INIT; + } + + public static void runtimeInit() { + executionMode = ExecutionMode.RUNTIME_INIT; + } + + public static void running() { + executionMode = ExecutionMode.RUNNING; + } + + public static void unset() { + executionMode = ExecutionMode.UNSET; + } + + public static ExecutionMode getExecutionMode() { + return executionMode; + } +} diff --git a/core/runtime/src/main/java/io/quarkus/runtime/annotations/StaticInitSafe.java b/core/runtime/src/main/java/io/quarkus/runtime/annotations/StaticInitSafe.java index 9e0eef1d411e3..1e7e956c84743 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/annotations/StaticInitSafe.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/annotations/StaticInitSafe.java @@ -1,5 +1,7 @@ package io.quarkus.runtime.annotations; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; @@ -7,10 +9,16 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; +import org.eclipse.microprofile.config.inject.ConfigProperty; + /** - * Used to mark a {@link org.eclipse.microprofile.config.spi.ConfigSource}, - * {@link org.eclipse.microprofile.config.spi.ConfigSourceProvider} or {@link io.smallrye.config.ConfigSourceFactory} - * as safe to be initialized during STATIC INIT. + * Used to mark a configuration object as safe to be initialized during the STATIC INIT phase. + *

+ * The target configuration objects include {@link org.eclipse.microprofile.config.spi.ConfigSource}, + * {@link org.eclipse.microprofile.config.spi.ConfigSourceProvider}, {@link io.smallrye.config.ConfigSourceFactory} and + * {@link io.smallrye.config.ConfigMapping}. Moreover, this annotation can be used for + * {@link org.eclipse.microprofile.config.inject.ConfigProperty} injection points. + *

* * When a Quarkus application is starting up, Quarkus will execute first a static init method which contains some * extensions actions and configurations. Example: @@ -36,7 +44,7 @@ * previous code example and a ConfigSource that requires database access. In this case, it is impossible to properly * initialize such ConfigSource, because the database services are not yet available so the ConfigSource in unusable. */ -@Target(TYPE) +@Target({ TYPE, FIELD, PARAMETER }) @Retention(RUNTIME) @Documented public @interface StaticInitSafe { diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigBuildStep.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigBuildStep.java index af7ad71af9a81..f84a334724aa7 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigBuildStep.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigBuildStep.java @@ -83,7 +83,7 @@ public class ConfigBuildStep { private static final Logger LOGGER = Logger.getLogger(ConfigBuildStep.class.getName()); private static final DotName MP_CONFIG = DotName.createSimple(Config.class.getName()); - private static final DotName MP_CONFIG_PROPERTY_NAME = DotName.createSimple(ConfigProperty.class.getName()); + static final DotName MP_CONFIG_PROPERTY_NAME = DotName.createSimple(ConfigProperty.class.getName()); private static final DotName MP_CONFIG_PROPERTIES_NAME = DotName.createSimple(ConfigProperties.class.getName()); private static final DotName MP_CONFIG_VALUE_NAME = DotName.createSimple(ConfigValue.class.getName()); diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigStaticInitBuildSteps.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigStaticInitBuildSteps.java new file mode 100644 index 0000000000000..19bb844f1a9ae --- /dev/null +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ConfigStaticInitBuildSteps.java @@ -0,0 +1,35 @@ +package io.quarkus.arc.deployment; + +import org.jboss.jandex.DotName; + +import io.quarkus.arc.processor.AnnotationsTransformer; +import io.quarkus.arc.processor.DotNames; +import io.quarkus.arc.runtime.ConfigStaticInitCheck; +import io.quarkus.arc.runtime.ConfigStaticInitCheckInterceptor; +import io.quarkus.arc.runtime.ConfigStaticInitValues; +import io.quarkus.deployment.annotations.BuildStep; + +public class ConfigStaticInitBuildSteps { + + @BuildStep + AdditionalBeanBuildItem registerBeans() { + return AdditionalBeanBuildItem.builder() + .addBeanClasses(ConfigStaticInitCheckInterceptor.class, ConfigStaticInitValues.class, + ConfigStaticInitCheck.class) + .build(); + } + + @BuildStep + AnnotationsTransformerBuildItem transformConfigProducer() { + DotName configProducerName = DotName.createSimple("io.smallrye.config.inject.ConfigProducer"); + + return new AnnotationsTransformerBuildItem(AnnotationsTransformer.appliedToMethod().whenMethod(m -> { + // Apply to all producer methods declared on io.smallrye.config.inject.ConfigProducer + return m.declaringClass().name().equals(configProducerName) + && m.hasAnnotation(DotNames.PRODUCES) + && m.hasAnnotation(ConfigBuildStep.MP_CONFIG_PROPERTY_NAME); + }).thenTransform(t -> { + t.add(ConfigStaticInitCheck.class); + })); + } +} diff --git a/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/staticinit/StaticInitBean.java b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/staticinit/StaticInitBean.java new file mode 100644 index 0000000000000..c9e48eee97d74 --- /dev/null +++ b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/staticinit/StaticInitBean.java @@ -0,0 +1,20 @@ +package io.quarkus.arc.test.config.staticinit; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.context.Initialized; +import jakarta.enterprise.event.Observes; +import jakarta.inject.Singleton; + +import org.eclipse.microprofile.config.inject.ConfigProperty; + +@Singleton +public class StaticInitBean { + + @ConfigProperty(name = "apfelstrudel") + String value; + + // bean is instantiated during STATIC_INIT + void onInit(@Observes @Initialized(ApplicationScoped.class) Object event) { + } + +} diff --git a/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/staticinit/StaticInitConfigInjectionFailureTest.java b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/staticinit/StaticInitConfigInjectionFailureTest.java new file mode 100644 index 0000000000000..fbcb1e352bdb2 --- /dev/null +++ b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/staticinit/StaticInitConfigInjectionFailureTest.java @@ -0,0 +1,35 @@ +package io.quarkus.arc.test.config.staticinit; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.fail; + +import org.eclipse.microprofile.config.spi.ConfigSource; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class StaticInitConfigInjectionFailureTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot(root -> root + .addClasses(StaticInitBean.class, StaticInitEagerBean.class, UnsafeConfigSource.class) + .addAsServiceProvider(ConfigSource.class, UnsafeConfigSource.class) + // the value from application.properties should be injected during STATIC_INIT + .addAsResource(new StringAsset("apfelstrudel=jandex"), "application.properties")) + .assertException(t -> { + assertThat(t).isInstanceOf(IllegalStateException.class) + .hasMessageContainingAll( + "A runtime config property value differs from the value that was injected during the static intialization phase", + "the runtime value of 'apfelstrudel' is [gizmo] but the value [jandex] was injected into io.quarkus.arc.test.config.staticinit.StaticInitBean#value", + "the runtime value of 'apfelstrudel' is [gizmo] but the value [jandex] was injected into io.quarkus.arc.test.config.staticinit.StaticInitEagerBean#value"); + }); + + @Test + public void test() { + fail(); + } + +} diff --git a/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/staticinit/StaticInitEagerBean.java b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/staticinit/StaticInitEagerBean.java new file mode 100644 index 0000000000000..dd15dd8399e8f --- /dev/null +++ b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/staticinit/StaticInitEagerBean.java @@ -0,0 +1,23 @@ +package io.quarkus.arc.test.config.staticinit; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.context.Initialized; +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Singleton; + +import org.eclipse.microprofile.config.inject.ConfigProperty; + +@Singleton +public class StaticInitEagerBean { + + @ConfigProperty(name = "apfelstrudel") + Instance value; + + // bean is instantiated during STATIC_INIT + void onInit(@Observes @Initialized(ApplicationScoped.class) Object event) { + // this should trigger the failure + value.get(); + } + +} diff --git a/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/staticinit/StaticInitLazyBean.java b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/staticinit/StaticInitLazyBean.java new file mode 100644 index 0000000000000..0a56799519d9a --- /dev/null +++ b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/staticinit/StaticInitLazyBean.java @@ -0,0 +1,22 @@ +package io.quarkus.arc.test.config.staticinit; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.context.Initialized; +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Singleton; + +import org.eclipse.microprofile.config.inject.ConfigProperty; + +@Singleton +public class StaticInitLazyBean { + + @ConfigProperty(name = "apfelstrudel") + Instance value; + + // bean is instantiated during STATIC_INIT + void onInit(@Observes @Initialized(ApplicationScoped.class) Object event) { + // value is not obtained... + } + +} diff --git a/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/staticinit/StaticInitSafeBean.java b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/staticinit/StaticInitSafeBean.java new file mode 100644 index 0000000000000..d8ef1a03fea38 --- /dev/null +++ b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/staticinit/StaticInitSafeBean.java @@ -0,0 +1,25 @@ +package io.quarkus.arc.test.config.staticinit; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.context.Initialized; +import jakarta.enterprise.event.Observes; +import jakarta.inject.Singleton; + +import org.eclipse.microprofile.config.inject.ConfigProperty; + +import io.quarkus.runtime.annotations.StaticInitSafe; + +@Singleton +public class StaticInitSafeBean { + + String value; + + public StaticInitSafeBean(@StaticInitSafe @ConfigProperty(name = "apfelstrudel") String value) { + this.value = value; + } + + // bean is instantiated during STATIC_INIT + void onInit(@Observes @Initialized(ApplicationScoped.class) Object event) { + } + +} diff --git a/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/staticinit/StaticInitSafeConfigInjectionTest.java b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/staticinit/StaticInitSafeConfigInjectionTest.java new file mode 100644 index 0000000000000..fa910806570a3 --- /dev/null +++ b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/staticinit/StaticInitSafeConfigInjectionTest.java @@ -0,0 +1,32 @@ +package io.quarkus.arc.test.config.staticinit; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import jakarta.inject.Inject; + +import org.eclipse.microprofile.config.spi.ConfigSource; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class StaticInitSafeConfigInjectionTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot(root -> root + .addClasses(StaticInitSafeBean.class, StaticInitLazyBean.class, UnsafeConfigSource.class) + .addAsServiceProvider(ConfigSource.class, UnsafeConfigSource.class) + // the value from application.properties should be injected during STATIC_INIT + .addAsResource(new StringAsset("apfelstrudel=jandex"), "application.properties")); + + @Inject + StaticInitSafeBean bean; + + @Test + public void test() { + assertEquals("jandex", bean.value); + } + +} diff --git a/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/staticinit/UnsafeConfigSource.java b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/staticinit/UnsafeConfigSource.java new file mode 100644 index 0000000000000..a8b5181610846 --- /dev/null +++ b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/config/staticinit/UnsafeConfigSource.java @@ -0,0 +1,30 @@ +package io.quarkus.arc.test.config.staticinit; + +import java.util.Set; + +import org.eclipse.microprofile.config.spi.ConfigSource; + +// Intentionally not annotated with @StaticInitSafe so that it's not considered durin the STATIC_INIT +public class UnsafeConfigSource implements ConfigSource { + + @Override + public Set getPropertyNames() { + return Set.of("apfelstrudel"); + } + + @Override + public String getValue(String propertyName) { + return propertyName.equals("apfelstrudel") ? "gizmo" : null; + } + + @Override + public String getName() { + return "Unsafe Test"; + } + + @Override + public int getOrdinal() { + return 500; + } + +} diff --git a/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ConfigBeanCreator.java b/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ConfigBeanCreator.java index 4d0fdaf4efaad..548d2afe985c2 100644 --- a/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ConfigBeanCreator.java +++ b/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ConfigBeanCreator.java @@ -32,6 +32,8 @@ public Object create(CreationalContext creationalContext, Map) injectionPoint.getType()).isPrimitive()) { + if (injectionPoint.getType() == char.class) { + return null; + } else if (injectionPoint.getType() == boolean.class) { + return "false"; + } else { + return "0"; + } + } + return null; + } + +} diff --git a/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ConfigStaticInitValues.java b/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ConfigStaticInitValues.java new file mode 100644 index 0000000000000..f464416e7603f --- /dev/null +++ b/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ConfigStaticInitValues.java @@ -0,0 +1,94 @@ +package io.quarkus.arc.runtime; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import jakarta.annotation.Priority; +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.inject.spi.Annotated; +import jakarta.enterprise.inject.spi.AnnotatedConstructor; +import jakarta.enterprise.inject.spi.AnnotatedField; +import jakarta.enterprise.inject.spi.AnnotatedParameter; +import jakarta.enterprise.inject.spi.InjectionPoint; +import jakarta.inject.Singleton; + +import org.eclipse.microprofile.config.ConfigProvider; +import org.eclipse.microprofile.config.ConfigValue; + +import io.quarkus.runtime.StartupEvent; +import io.smallrye.config.SmallRyeConfig; + +/** + * This is a store for all config values injected (directly or programmatically) during the static init phase. + *

+ * The values are then compared with the current values during the runtime init, i.e. when the application starts. If a mismatch + * is found the startup fails with an actionable error. + */ +@Singleton +public class ConfigStaticInitValues { + + private final List injectedValues = Collections.synchronizedList(new ArrayList<>()); + + void recordConfigValue(InjectionPoint injectionPoint, String name, String value) { + injectedValues.add(new InjectedValue(injectionPoint, name, value)); + } + + void onStart(@Observes @Priority(Integer.MIN_VALUE) StartupEvent event) { + if (injectedValues.isEmpty()) { + // No config values were recorded during static init phase + return; + } + SmallRyeConfig config = ConfigProvider.getConfig().unwrap(SmallRyeConfig.class); + List mismatches = new ArrayList<>(); + for (InjectedValue injectedValue : injectedValues) { + ConfigValue currentValue = config.getConfigValue(injectedValue.name); + if (currentValue.getValue() != null && !injectedValue.value.equals(currentValue.getValue())) { + mismatches.add( + " - the runtime value of '" + injectedValue.name + "' is [" + currentValue.getValue() + + "] but the value [" + injectedValue.value + + "] was injected into " + + injectedValue.injectionPointInfo); + } + } + injectedValues.clear(); + if (!mismatches.isEmpty()) { + throw new IllegalStateException( + "A runtime config property value differs from the value that was injected during the static intialization phase:\n" + + String.join("\n", mismatches) + + "\n\nIf that's intentional then annotate the injected field/parameter with @io.quarkus.runtime.annotations.StaticInitSafe to eliminate the false positive."); + } + } + + private static class InjectedValue { + + private final String injectionPointInfo; + private final String name; + private final String value; + + private InjectedValue(InjectionPoint injectionPoint, String name, String value) { + this.injectionPointInfo = injectionPointToString(injectionPoint); + this.name = name; + this.value = value; + } + + } + + private static String injectionPointToString(InjectionPoint injectionPoint) { + Annotated annotated = injectionPoint.getAnnotated(); + if (annotated instanceof AnnotatedField) { + AnnotatedField field = (AnnotatedField) annotated; + return field.getDeclaringType().getJavaClass().getName() + "#" + field.getJavaMember().getName(); + } else if (annotated instanceof AnnotatedParameter) { + AnnotatedParameter param = (AnnotatedParameter) annotated; + if (param.getDeclaringCallable() instanceof AnnotatedConstructor) { + return param.getDeclaringCallable().getDeclaringType().getJavaClass().getName() + "()"; + } else { + return param.getDeclaringCallable().getDeclaringType().getJavaClass().getName() + "#" + + param.getDeclaringCallable().getJavaMember().getName() + "()"; + } + } + return injectionPoint.toString(); + } + +} diff --git a/integration-tests/resteasy-reactive-kotlin/standard/src/main/kotlin/io/quarkus/it/resteasy/reactive/kotlin/RegisterCustomModuleCustomizer.kt b/integration-tests/resteasy-reactive-kotlin/standard/src/main/kotlin/io/quarkus/it/resteasy/reactive/kotlin/RegisterCustomModuleCustomizer.kt index 1600498fb6ad6..41605c9d645e7 100644 --- a/integration-tests/resteasy-reactive-kotlin/standard/src/main/kotlin/io/quarkus/it/resteasy/reactive/kotlin/RegisterCustomModuleCustomizer.kt +++ b/integration-tests/resteasy-reactive-kotlin/standard/src/main/kotlin/io/quarkus/it/resteasy/reactive/kotlin/RegisterCustomModuleCustomizer.kt @@ -4,10 +4,11 @@ import com.fasterxml.jackson.databind.ObjectMapper import io.quarkus.jackson.ObjectMapperCustomizer import jakarta.inject.Singleton import org.eclipse.microprofile.config.inject.ConfigProperty +import io.quarkus.runtime.annotations.StaticInitSafe @Singleton class RegisterCustomModuleCustomizer : ObjectMapperCustomizer { - @ConfigProperty(name = "test.prop") lateinit var testProp: String + @StaticInitSafe @ConfigProperty(name = "test.prop") lateinit var testProp: String override fun customize(objectMapper: ObjectMapper) { GreetingResource.MY_PROPERTY.set(testProp)