diff --git a/bom/application/pom.xml b/bom/application/pom.xml
index dc604a1959d8c..39db65ad5a40a 100644
--- a/bom/application/pom.xml
+++ b/bom/application/pom.xml
@@ -1370,6 +1370,11 @@
quarkus-smallrye-context-propagation-deployment
${project.version}
+
+ io.quarkus
+ quarkus-smallrye-context-propagation-spi
+ ${project.version}
+
io.quarkus
quarkus-smallrye-reactive-streams-operators
diff --git a/extensions/arc/deployment/pom.xml b/extensions/arc/deployment/pom.xml
index 5eb18e81e26a5..74666d60cd6c4 100644
--- a/extensions/arc/deployment/pom.xml
+++ b/extensions/arc/deployment/pom.xml
@@ -17,6 +17,10 @@
io.quarkus
quarkus-core-deployment
+
+ io.quarkus
+ quarkus-smallrye-context-propagation-spi
+
io.quarkus
quarkus-vertx-http-dev-console-spi
diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcConfig.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcConfig.java
index 35159793b75f7..2eeaa0e2041d2 100644
--- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcConfig.java
+++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcConfig.java
@@ -203,6 +203,12 @@ public class ArcConfig {
@ConfigItem
public Optional> ignoredSplitPackages;
+ /**
+ * Context propagation configuration.
+ */
+ @ConfigItem
+ public ArcContextPropagationConfig contextPropagation;
+
public final boolean isRemoveUnusedBeansFieldValid() {
return ALLOWED_REMOVE_UNUSED_BEANS_VALUES.contains(removeUnusedBeans.toLowerCase());
}
diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcContextPropagationConfig.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcContextPropagationConfig.java
new file mode 100644
index 0000000000000..dee455eefed42
--- /dev/null
+++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcContextPropagationConfig.java
@@ -0,0 +1,16 @@
+package io.quarkus.arc.deployment;
+
+import io.quarkus.runtime.annotations.ConfigGroup;
+import io.quarkus.runtime.annotations.ConfigItem;
+
+@ConfigGroup
+public class ArcContextPropagationConfig {
+
+ /**
+ * If set to true and SmallRye Context Propagation extension is present then enable the context propagation for CDI
+ * contexts.
+ */
+ @ConfigItem(defaultValue = "true")
+ public boolean enabled;
+
+}
diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java
index f07140fff2de6..abd6e9b659086 100644
--- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java
+++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java
@@ -81,6 +81,7 @@
import io.quarkus.arc.runtime.BeanContainer;
import io.quarkus.arc.runtime.LaunchModeProducer;
import io.quarkus.arc.runtime.LoggerProducer;
+import io.quarkus.arc.runtime.context.ArcContextProvider;
import io.quarkus.arc.runtime.test.PreloadedTestApplicationClassPredicate;
import io.quarkus.bootstrap.BootstrapDebug;
import io.quarkus.deployment.Capabilities;
@@ -114,6 +115,7 @@
import io.quarkus.runtime.annotations.QuarkusMain;
import io.quarkus.runtime.test.TestApplicationClassPredicate;
import io.quarkus.runtime.util.HashUtil;
+import io.quarkus.smallrye.context.deployment.spi.ThreadContextProviderBuildItem;
/**
* This class contains build steps that trigger various phases of the bean processing.
@@ -813,6 +815,13 @@ void validateAsyncObserverExceptionHandlers(ValidationPhaseBuildItem validationP
}
}
+ @BuildStep
+ void registerContextPropagation(ArcConfig config, BuildProducer threadContextProvider) {
+ if (config.contextPropagation.enabled) {
+ threadContextProvider.produce(new ThreadContextProviderBuildItem(ArcContextProvider.class));
+ }
+ }
+
private void registerListInjectionPointsBeans(BeanRegistrationPhaseBuildItem beanRegistrationPhase,
List injectionPoints, BuildProducer reflectiveMethods,
BuildProducer reflectiveFields,
diff --git a/extensions/arc/runtime/src/main/resources/META-INF/services/org.eclipse.microprofile.context.spi.ThreadContextProvider b/extensions/arc/runtime/src/main/resources/META-INF/services/org.eclipse.microprofile.context.spi.ThreadContextProvider
deleted file mode 100644
index 45ab8e34c6250..0000000000000
--- a/extensions/arc/runtime/src/main/resources/META-INF/services/org.eclipse.microprofile.context.spi.ThreadContextProvider
+++ /dev/null
@@ -1 +0,0 @@
-io.quarkus.arc.runtime.context.ArcContextProvider
\ No newline at end of file
diff --git a/extensions/smallrye-context-propagation/deployment/pom.xml b/extensions/smallrye-context-propagation/deployment/pom.xml
index 2532ae391f4b1..9b9c5e71d32c0 100644
--- a/extensions/smallrye-context-propagation/deployment/pom.xml
+++ b/extensions/smallrye-context-propagation/deployment/pom.xml
@@ -20,6 +20,10 @@
io.quarkus
quarkus-arc-deployment
+
+ io.quarkus
+ quarkus-smallrye-context-propagation-spi
+
io.quarkus
quarkus-smallrye-context-propagation
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 ae5afbb49d579..8141b68e431f2 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
@@ -40,6 +40,7 @@
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.builditem.ShutdownContextBuildItem;
import io.quarkus.deployment.util.ServiceUtil;
+import io.quarkus.smallrye.context.deployment.spi.ThreadContextProviderBuildItem;
import io.quarkus.smallrye.context.runtime.SmallRyeContextPropagationProvider;
import io.quarkus.smallrye.context.runtime.SmallRyeContextPropagationRecorder;
import io.smallrye.context.SmallRyeManagedExecutor;
@@ -59,12 +60,15 @@ void registerBean(BuildProducer additionalBeans) {
@BuildStep
@Record(ExecutionTime.STATIC_INIT)
- void buildStatic(SmallRyeContextPropagationRecorder recorder)
+ void buildStatic(SmallRyeContextPropagationRecorder recorder, List threadContextProviders)
throws ClassNotFoundException, IOException {
List discoveredProviders = new ArrayList<>();
List discoveredExtensions = new ArrayList<>();
- for (Class> provider : ServiceUtil.classesNamedIn(Thread.currentThread().getContextClassLoader(),
- "META-INF/services/" + ThreadContextProvider.class.getName())) {
+ List> providers = threadContextProviders.stream().map(ThreadContextProviderBuildItem::getProvider)
+ .collect(Collectors.toCollection(ArrayList::new));
+ ServiceUtil.classesNamedIn(Thread.currentThread().getContextClassLoader(),
+ "META-INF/services/" + ThreadContextProvider.class.getName()).forEach(providers::add);
+ for (Class> provider : providers) {
try {
discoveredProviders.add((ThreadContextProvider) provider.getDeclaredConstructor().newInstance());
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
diff --git a/extensions/smallrye-context-propagation/deployment/src/test/java/io/quarkus/smallrye/context/deployment/test/cdi/ContextProviderDisabledTest.java b/extensions/smallrye-context-propagation/deployment/src/test/java/io/quarkus/smallrye/context/deployment/test/cdi/ContextProviderDisabledTest.java
new file mode 100644
index 0000000000000..6ba2ed04c67e5
--- /dev/null
+++ b/extensions/smallrye-context-propagation/deployment/src/test/java/io/quarkus/smallrye/context/deployment/test/cdi/ContextProviderDisabledTest.java
@@ -0,0 +1,75 @@
+package io.quarkus.smallrye.context.deployment.test.cdi;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import javax.annotation.PostConstruct;
+import javax.enterprise.context.ContextNotActiveException;
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+
+import org.eclipse.microprofile.context.ManagedExecutor;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import io.quarkus.arc.Arc;
+import io.quarkus.arc.ManagedContext;
+import io.quarkus.test.QuarkusUnitTest;
+
+public class ContextProviderDisabledTest {
+
+ @RegisterExtension
+ static final QuarkusUnitTest config = new QuarkusUnitTest().overrideConfigKey("quarkus.arc.context-propagation.enabled",
+ "false");
+
+ @Inject
+ ManagedExecutor all;
+
+ @Inject
+ MyRequestBean bean;
+
+ @Test
+ public void testPropagationDisabled() throws InterruptedException, ExecutionException, TimeoutException {
+ ManagedContext requestContext = Arc.container().requestContext();
+
+ requestContext.activate();
+ assertEquals("FOO", bean.getId());
+ try {
+ assertEquals("OK",
+ all.completedFuture("OK").thenApplyAsync(text -> {
+ // Assertion error would result in an ExecutionException thrown from the CompletableFuture.get()
+ assertFalse(requestContext.isActive());
+ try {
+ bean.getId();
+ fail();
+ } catch (ContextNotActiveException expected) {
+ }
+ return text;
+ }).toCompletableFuture().get(5, TimeUnit.SECONDS));
+ } finally {
+ requestContext.terminate();
+ }
+ }
+
+ @RequestScoped
+ public static class MyRequestBean {
+
+ String id;
+
+ @PostConstruct
+ void init() {
+ id = "FOO";
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ }
+
+}
diff --git a/extensions/smallrye-context-propagation/deployment/src/test/java/io/quarkus/smallrye/context/deployment/test/cdi/ContextProviderEnabledTest.java b/extensions/smallrye-context-propagation/deployment/src/test/java/io/quarkus/smallrye/context/deployment/test/cdi/ContextProviderEnabledTest.java
new file mode 100644
index 0000000000000..e94c8e67f941d
--- /dev/null
+++ b/extensions/smallrye-context-propagation/deployment/src/test/java/io/quarkus/smallrye/context/deployment/test/cdi/ContextProviderEnabledTest.java
@@ -0,0 +1,70 @@
+package io.quarkus.smallrye.context.deployment.test.cdi;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import javax.annotation.PostConstruct;
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+
+import org.eclipse.microprofile.context.ManagedExecutor;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import io.quarkus.arc.Arc;
+import io.quarkus.arc.ManagedContext;
+import io.quarkus.test.QuarkusUnitTest;
+
+public class ContextProviderEnabledTest {
+
+ @RegisterExtension
+ static final QuarkusUnitTest config = new QuarkusUnitTest().overrideConfigKey("quarkus.arc.context-propagation.enabled",
+ "true");
+
+ @Inject
+ ManagedExecutor all;
+
+ @Inject
+ MyRequestBean bean;
+
+ @Test
+ public void testPropagationEnabled() throws InterruptedException, ExecutionException, TimeoutException {
+ ManagedContext requestContext = Arc.container().requestContext();
+
+ requestContext.activate();
+ assertEquals("FOO", bean.getId());
+ try {
+ assertEquals("OK",
+ all.completedFuture("OK").thenApplyAsync(text -> {
+ // Assertion error would result in an ExecutionException thrown from the CompletableFuture.get()
+ assertTrue(requestContext.isActive());
+ assertEquals("FOO", bean.getId());
+ return text;
+ }).toCompletableFuture().get(5, TimeUnit.SECONDS));
+ ;
+ } finally {
+ requestContext.terminate();
+ }
+ }
+
+ @RequestScoped
+ public static class MyRequestBean {
+
+ String id;
+
+ @PostConstruct
+ void init() {
+ id = "FOO";
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ }
+
+}
diff --git a/extensions/smallrye-context-propagation/pom.xml b/extensions/smallrye-context-propagation/pom.xml
index 3206cecd3aa44..94f0d789ffcf7 100644
--- a/extensions/smallrye-context-propagation/pom.xml
+++ b/extensions/smallrye-context-propagation/pom.xml
@@ -17,5 +17,6 @@
deployment
runtime
+ spi
diff --git a/extensions/smallrye-context-propagation/spi/pom.xml b/extensions/smallrye-context-propagation/spi/pom.xml
new file mode 100644
index 0000000000000..3f2672224e5f1
--- /dev/null
+++ b/extensions/smallrye-context-propagation/spi/pom.xml
@@ -0,0 +1,27 @@
+
+
+
+ quarkus-smallrye-context-propagation-parent
+ io.quarkus
+ 999-SNAPSHOT
+ ../
+
+ 4.0.0
+
+ quarkus-smallrye-context-propagation-spi
+ Quarkus - SmallRye Context Propagation - SPI
+
+
+
+ org.eclipse.microprofile.context-propagation
+ microprofile-context-propagation-api
+
+
+ io.quarkus
+ quarkus-core-deployment
+
+
+
+
diff --git a/extensions/smallrye-context-propagation/spi/src/main/java/io/quarkus/smallrye/context/deployment/spi/ThreadContextProviderBuildItem.java b/extensions/smallrye-context-propagation/spi/src/main/java/io/quarkus/smallrye/context/deployment/spi/ThreadContextProviderBuildItem.java
new file mode 100644
index 0000000000000..4f2834ec8301d
--- /dev/null
+++ b/extensions/smallrye-context-propagation/spi/src/main/java/io/quarkus/smallrye/context/deployment/spi/ThreadContextProviderBuildItem.java
@@ -0,0 +1,22 @@
+package io.quarkus.smallrye.context.deployment.spi;
+
+import org.eclipse.microprofile.context.spi.ThreadContextProvider;
+
+import io.quarkus.builder.item.MultiBuildItem;
+
+/**
+ * This build item can be used to register a {@link ThreadContextProvider}.
+ */
+public final class ThreadContextProviderBuildItem extends MultiBuildItem {
+
+ private final Class extends ThreadContextProvider> provider;
+
+ public ThreadContextProviderBuildItem(Class extends ThreadContextProvider> provider) {
+ this.provider = provider;
+ }
+
+ public Class extends ThreadContextProvider> getProvider() {
+ return provider;
+ }
+
+}