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 provider; + + public ThreadContextProviderBuildItem(Class provider) { + this.provider = provider; + } + + public Class getProvider() { + return provider; + } + +}