diff --git a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/OidcBuildStep.java b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/OidcBuildStep.java index b4827b8306bf4..9bc2cae847574 100644 --- a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/OidcBuildStep.java +++ b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/OidcBuildStep.java @@ -65,6 +65,7 @@ import io.quarkus.oidc.runtime.TenantConfigBean; import io.quarkus.oidc.runtime.providers.AzureAccessTokenCustomizer; import io.quarkus.runtime.TlsConfig; +import io.quarkus.smallrye.context.deployment.ContextPropagationInitializedBuildItem; import io.quarkus.vertx.core.deployment.CoreVertxBuildItem; import io.quarkus.vertx.http.deployment.EagerSecurityInterceptorBindingBuildItem; import io.quarkus.vertx.http.deployment.HttpAuthMechanismAnnotationBuildItem; @@ -220,7 +221,9 @@ public SyntheticBeanBuildItem setup( OidcConfig config, OidcRecorder recorder, CoreVertxBuildItem vertxBuildItem, - TlsConfig tlsConfig) { + TlsConfig tlsConfig, + // this is required for setup ordering: we need CP set up + ContextPropagationInitializedBuildItem cpInitializedBuildItem) { return SyntheticBeanBuildItem.configure(TenantConfigBean.class).unremovable().types(TenantConfigBean.class) .supplier(recorder.setup(config, vertxBuildItem.getVertx(), tlsConfig)) .destroyer(TenantConfigBean.Destroyer.class) diff --git a/extensions/smallrye-context-propagation/deployment/src/main/java/io/quarkus/smallrye/context/deployment/ContextPropagationInitializedBuildItem.java b/extensions/smallrye-context-propagation/deployment/src/main/java/io/quarkus/smallrye/context/deployment/ContextPropagationInitializedBuildItem.java new file mode 100644 index 0000000000000..534494892a3a4 --- /dev/null +++ b/extensions/smallrye-context-propagation/deployment/src/main/java/io/quarkus/smallrye/context/deployment/ContextPropagationInitializedBuildItem.java @@ -0,0 +1,11 @@ +package io.quarkus.smallrye.context.deployment; + +import io.quarkus.builder.item.SimpleBuildItem; + +/** + * Marker build item for build ordering. Signifies that CP is set up + * and ready for use. + */ +public final class ContextPropagationInitializedBuildItem extends SimpleBuildItem { + +} diff --git a/extensions/smallrye-context-propagation/deployment/src/main/java/io/quarkus/smallrye/context/deployment/SmallRyeContextPropagationProcessor.java b/extensions/smallrye-context-propagation/deployment/src/main/java/io/quarkus/smallrye/context/deployment/SmallRyeContextPropagationProcessor.java index f923fc6dcaf70..341986949891a 100644 --- a/extensions/smallrye-context-propagation/deployment/src/main/java/io/quarkus/smallrye/context/deployment/SmallRyeContextPropagationProcessor.java +++ b/extensions/smallrye-context-propagation/deployment/src/main/java/io/quarkus/smallrye/context/deployment/SmallRyeContextPropagationProcessor.java @@ -96,6 +96,7 @@ void buildStatic(SmallRyeContextPropagationRecorder recorder, List cpInitializedBuildItem, BuildProducer feature, BuildProducer syntheticBeans) { feature.produce(new FeatureBuildItem(Feature.SMALLRYE_CONTEXT_PROPAGATION)); @@ -111,6 +112,8 @@ void build(SmallRyeContextPropagationRecorder recorder, .unremovable() .supplier(recorder.initializeManagedExecutor(executorBuildItem.getExecutorProxy())) .setRuntimeInit().done()); + + cpInitializedBuildItem.produce(new ContextPropagationInitializedBuildItem()); } // transform IPs for ManagedExecutor/ThreadContext that use config annotation and don't yet have @NamedInstance diff --git a/extensions/smallrye-context-propagation/runtime/src/main/java/io/quarkus/smallrye/context/runtime/QuarkusContextManagerProvider.java b/extensions/smallrye-context-propagation/runtime/src/main/java/io/quarkus/smallrye/context/runtime/QuarkusContextManagerProvider.java new file mode 100644 index 0000000000000..9f334e8c3fc7c --- /dev/null +++ b/extensions/smallrye-context-propagation/runtime/src/main/java/io/quarkus/smallrye/context/runtime/QuarkusContextManagerProvider.java @@ -0,0 +1,42 @@ +package io.quarkus.smallrye.context.runtime; + +import org.eclipse.microprofile.context.spi.ContextManager; + +import io.smallrye.context.SmallRyeContextManager; +import io.smallrye.context.SmallRyeContextManagerProvider; + +/** + * Quarkus doesn't need one manager per CL, we only have the one + */ +public class QuarkusContextManagerProvider extends SmallRyeContextManagerProvider { + + private SmallRyeContextManager contextManager; + + @Override + public SmallRyeContextManager getContextManager(ClassLoader classLoader) { + return contextManager; + } + + @Override + public SmallRyeContextManager getContextManager() { + return contextManager; + } + + @Override + public ContextManager findContextManager(ClassLoader classLoader) { + return contextManager; + } + + @Override + public void registerContextManager(ContextManager manager, ClassLoader classLoader) { + if (manager instanceof SmallRyeContextManager == false) { + throw new IllegalArgumentException("Only instances of SmallRyeContextManager are supported: " + manager); + } + contextManager = (SmallRyeContextManager) manager; + } + + @Override + public void releaseContextManager(ContextManager manager) { + contextManager = null; + } +} diff --git a/extensions/smallrye-context-propagation/runtime/src/main/java/io/quarkus/smallrye/context/runtime/SmallRyeContextPropagationRecorder.java b/extensions/smallrye-context-propagation/runtime/src/main/java/io/quarkus/smallrye/context/runtime/SmallRyeContextPropagationRecorder.java index b1a40ce4a00c7..4955a2bbf54b7 100644 --- a/extensions/smallrye-context-propagation/runtime/src/main/java/io/quarkus/smallrye/context/runtime/SmallRyeContextPropagationRecorder.java +++ b/extensions/smallrye-context-propagation/runtime/src/main/java/io/quarkus/smallrye/context/runtime/SmallRyeContextPropagationRecorder.java @@ -1,11 +1,18 @@ package io.quarkus.smallrye.context.runtime; +import java.util.Collection; import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.function.Supplier; import org.eclipse.microprofile.context.ManagedExecutor; import org.eclipse.microprofile.context.ThreadContext; +import org.eclipse.microprofile.context.spi.ContextManager.Builder; import org.eclipse.microprofile.context.spi.ContextManagerExtension; import org.eclipse.microprofile.context.spi.ContextManagerProvider; import org.eclipse.microprofile.context.spi.ThreadContextProvider; @@ -14,7 +21,6 @@ import io.quarkus.runtime.ShutdownContext; import io.quarkus.runtime.annotations.Recorder; import io.smallrye.context.SmallRyeContextManager; -import io.smallrye.context.SmallRyeContextManagerProvider; import io.smallrye.context.SmallRyeManagedExecutor; import io.smallrye.context.SmallRyeThreadContext; @@ -24,6 +30,92 @@ @Recorder public class SmallRyeContextPropagationRecorder { + private static final ExecutorService NOPE_EXECUTOR_SERVICE = new ExecutorService() { + + @Override + public void execute(Runnable command) { + nope(); + } + + @Override + public void shutdown() { + nope(); + } + + @Override + public List shutdownNow() { + nope(); + return null; + } + + @Override + public boolean isShutdown() { + nope(); + return false; + } + + @Override + public boolean isTerminated() { + nope(); + return false; + } + + @Override + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + nope(); + return false; + } + + @Override + public Future submit(Callable task) { + nope(); + return null; + } + + @Override + public Future submit(Runnable task, T result) { + nope(); + return null; + } + + @Override + public Future submit(Runnable task) { + nope(); + return null; + } + + @Override + public List> invokeAll(Collection> tasks) throws InterruptedException { + nope(); + return null; + } + + @Override + public List> invokeAll(Collection> tasks, long timeout, TimeUnit unit) + throws InterruptedException { + nope(); + return null; + } + + @Override + public T invokeAny(Collection> tasks) + throws InterruptedException, ExecutionException { + nope(); + return null; + } + + @Override + public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + nope(); + return null; + } + + private void nope() { + throw new RuntimeException( + "Trying to invoke ContextPropagation on a partially-configured ContextManager instance. You should wait until runtime init is done. You can do that by consuming the ContextPropagationBuildItem."); + } + }; private static SmallRyeContextManager.Builder builder; public void configureStaticInit(List discoveredProviders, @@ -31,7 +123,7 @@ public void configureStaticInit(List discoveredProviders, // build the manager at static init time // in the live-reload mode, the provider instance may be already set in the previous start if (ContextManagerProvider.INSTANCE.get() == null) { - ContextManagerProvider contextManagerProvider = new SmallRyeContextManagerProvider(); + ContextManagerProvider contextManagerProvider = new QuarkusContextManagerProvider(); ContextManagerProvider.register(contextManagerProvider); } @@ -40,6 +132,16 @@ public void configureStaticInit(List discoveredProviders, .getContextManagerBuilder(); builder.withThreadContextProviders(discoveredProviders.toArray(new ThreadContextProvider[0])); builder.withContextManagerExtensions(discoveredExtensions.toArray(new ContextManagerExtension[0])); + + // During boot, if anyone is using CP, they will get no propagation and an error if they try to use + // the executor. This is (so far) only for spring-cloud-config-client which uses Vert.x via Mutiny + // to load config before we're ready for runtime init + SmallRyeContextManager.Builder noContextBuilder = (SmallRyeContextManager.Builder) ContextManagerProvider.instance() + .getContextManagerBuilder(); + noContextBuilder.withThreadContextProviders(new ThreadContextProvider[0]); + noContextBuilder.withContextManagerExtensions(new ContextManagerExtension[0]); + noContextBuilder.withDefaultExecutorService(NOPE_EXECUTOR_SERVICE); + ContextManagerProvider.instance().registerContextManager(noContextBuilder.build(), null /* not used */); } public void configureRuntime(ExecutorService executorService, ShutdownContext shutdownContext) { @@ -59,7 +161,7 @@ public void run() { } }); //Avoid leaking the classloader: - this.builder = null; + SmallRyeContextPropagationRecorder.builder = null; } public Supplier initializeManagedExecutor(ExecutorService executorService) { diff --git a/extensions/smallrye-fault-tolerance/deployment/src/main/java/io/quarkus/smallrye/faulttolerance/deployment/SmallRyeFaultToleranceProcessor.java b/extensions/smallrye-fault-tolerance/deployment/src/main/java/io/quarkus/smallrye/faulttolerance/deployment/SmallRyeFaultToleranceProcessor.java index 345c8808dc85f..e15fb3ad077d0 100644 --- a/extensions/smallrye-fault-tolerance/deployment/src/main/java/io/quarkus/smallrye/faulttolerance/deployment/SmallRyeFaultToleranceProcessor.java +++ b/extensions/smallrye-fault-tolerance/deployment/src/main/java/io/quarkus/smallrye/faulttolerance/deployment/SmallRyeFaultToleranceProcessor.java @@ -50,6 +50,7 @@ import io.quarkus.deployment.builditem.SystemPropertyBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveMethodBuildItem; +import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem; import io.quarkus.deployment.metrics.MetricsCapabilityBuildItem; import io.quarkus.deployment.recording.RecorderContext; @@ -87,7 +88,8 @@ public void build(BuildProducer annotationsTran CombinedIndexBuildItem combinedIndexBuildItem, BuildProducer reflectiveClass, BuildProducer reflectiveMethod, - BuildProducer config) { + BuildProducer config, + BuildProducer runtimeInitializedClassBuildItems) { feature.produce(new FeatureBuildItem(Feature.SMALLRYE_FAULT_TOLERANCE)); @@ -95,6 +97,8 @@ public void build(BuildProducer annotationsTran ContextPropagationRequestContextControllerProvider.class.getName())); serviceProvider.produce(new ServiceProviderBuildItem(RunnableWrapper.class.getName(), ContextPropagationRunnableWrapper.class.getName())); + // make sure this is initialised at runtime, otherwise it will get a non-initialised ContextPropagationManager + runtimeInitializedClassBuildItems.produce(new RuntimeInitializedClassBuildItem(RunnableWrapper.class.getName())); IndexView index = combinedIndexBuildItem.getIndex();