diff --git a/bom/application/pom.xml b/bom/application/pom.xml
index d3467d2d5071b..d73d66ee698df 100644
--- a/bom/application/pom.xml
+++ b/bom/application/pom.xml
@@ -47,7 +47,7 @@
1.3.4
4.3.2
2.4.2
- 1.0.19
+ 1.1.0
1.0.13
1.4.0
2.7.1
@@ -3049,6 +3049,11 @@
smallrye-context-propagation-jta
${smallrye-context-propagation.version}
+
+ io.smallrye
+ smallrye-context-propagation-storage
+ ${smallrye-context-propagation.version}
+
io.smallrye
smallrye-jwt
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/StorageReadyBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/StorageReadyBuildItem.java
new file mode 100644
index 0000000000000..b2ac03831caf4
--- /dev/null
+++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/StorageReadyBuildItem.java
@@ -0,0 +1,7 @@
+package io.quarkus.deployment.builditem;
+
+import io.quarkus.builder.item.SimpleBuildItem;
+
+public final class StorageReadyBuildItem extends SimpleBuildItem {
+
+}
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/storage/StorageBuildProcessor.java b/core/deployment/src/main/java/io/quarkus/deployment/storage/StorageBuildProcessor.java
new file mode 100644
index 0000000000000..778a2cb7200ad
--- /dev/null
+++ b/core/deployment/src/main/java/io/quarkus/deployment/storage/StorageBuildProcessor.java
@@ -0,0 +1,235 @@
+package io.quarkus.deployment.storage;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.jboss.jandex.ClassInfo;
+import org.jboss.jandex.DotName;
+import org.jboss.jandex.ParameterizedType;
+import org.jboss.jandex.Type;
+import org.jboss.jandex.Type.Kind;
+import org.objectweb.asm.Opcodes;
+
+import io.quarkus.deployment.GeneratedClassGizmoAdaptor;
+import io.quarkus.deployment.annotations.BuildProducer;
+import io.quarkus.deployment.annotations.BuildStep;
+import io.quarkus.deployment.annotations.ExecutionTime;
+import io.quarkus.deployment.annotations.Record;
+import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
+import io.quarkus.deployment.builditem.GeneratedClassBuildItem;
+import io.quarkus.deployment.builditem.StorageReadyBuildItem;
+import io.quarkus.deployment.recording.RecorderContext;
+import io.quarkus.deployment.util.AsmUtil;
+import io.quarkus.gizmo.AssignableResultHandle;
+import io.quarkus.gizmo.BranchResult;
+import io.quarkus.gizmo.BytecodeCreator;
+import io.quarkus.gizmo.ClassCreator;
+import io.quarkus.gizmo.ClassOutput;
+import io.quarkus.gizmo.MethodCreator;
+import io.quarkus.gizmo.MethodDescriptor;
+import io.quarkus.gizmo.ResultHandle;
+import io.quarkus.runtime.RuntimeValue;
+import io.quarkus.runtime.storage.QuarkusThread;
+import io.quarkus.runtime.storage.StorageRecorder;
+import io.smallrye.context.storage.spi.StorageDeclaration;
+
+public class StorageBuildProcessor {
+
+ public final class StorageFieldInfo {
+ public final String typeSignature;
+ public final String rawType;
+ public final int index;
+ public final Type type;
+ public final ClassInfo threadLocalStorageClassInfo;
+
+ public StorageFieldInfo(int index, Type type, ClassInfo threadLocalStorageClassInfo) {
+ this.index = index;
+ this.type = type;
+ this.threadLocalStorageClassInfo = threadLocalStorageClassInfo;
+ // type.name() is the raw type
+ rawType = type.name().toString('.');
+ typeSignature = AsmUtil.getSignature(type, v -> null);
+
+ }
+ }
+
+ private static final String QUARKUS_STORAGE_IMPL_CLASS_NAME_PREFIX = "io.quarkus.deployment.storage.QuarkusStorageImpl__";
+ private static final DotName DOTNAME_STORAGE_DECLARATION = DotName.createSimple(StorageDeclaration.class.getName());
+
+ @BuildStep
+ @Record(ExecutionTime.STATIC_INIT)
+ public StorageReadyBuildItem setupStorage(StorageRecorder recorder,
+ RecorderContext recorderContext,
+ CombinedIndexBuildItem combinedIndex,
+ BuildProducer generatedClass)
+ throws ClassNotFoundException, IOException {
+
+ List fields = new ArrayList<>();
+ int index = 0;
+
+ for (ClassInfo threadLocalStorageClassInfo : combinedIndex.getIndex()
+ .getAllKnownImplementors(DOTNAME_STORAGE_DECLARATION)) {
+ Type storageType = null;
+ for (Type superClassType : threadLocalStorageClassInfo.interfaceTypes()) {
+ if (superClassType.kind() != Kind.PARAMETERIZED_TYPE)
+ continue;
+ ParameterizedType parameterizedType = superClassType.asParameterizedType();
+ if (!parameterizedType.name().equals(DOTNAME_STORAGE_DECLARATION))
+ continue;
+ if (parameterizedType.arguments().size() != 1)
+ throw storageValidation(threadLocalStorageClassInfo);
+ storageType = parameterizedType.arguments().get(0);
+ break;
+ }
+ if (storageType == null)
+ throw storageValidation(threadLocalStorageClassInfo);
+ System.err.println("Adding context element for " + threadLocalStorageClassInfo + " -> " + index);
+ fields.add(new StorageFieldInfo(index++,
+ storageType,
+ threadLocalStorageClassInfo));
+ }
+
+ ClassOutput classOutput = new GeneratedClassGizmoAdaptor(generatedClass, true);
+
+ Map>> storageMappings = new HashMap<>();
+
+ // now create our ThreadLocal per field
+ for (StorageFieldInfo storageFieldInfo : fields) {
+ String className = QUARKUS_STORAGE_IMPL_CLASS_NAME_PREFIX + storageFieldInfo.index;
+ System.err.println("Producing " + className + " for index " + storageFieldInfo.index);
+ try (ClassCreator clazz = ClassCreator.builder().classOutput(classOutput)
+ .className(className)
+ .superClass(ThreadLocal.class)
+ .build()) {
+ // Ljava/lang/ThreadLocal;Ljava/lang/Object;>;>;>;
+ clazz.setSignature("L" + ThreadLocal.class.getName().replace('.', '/') + "<"
+ + storageFieldInfo.typeSignature + ">;");
+
+ try (MethodCreator method = clazz.getMethodCreator("get", storageFieldInfo.rawType)) {
+ method.setModifiers(Opcodes.ACC_PUBLIC);
+ // signature: ()Ljava/util/List;Ljava/lang/Object;>;>;
+ method.setSignature("()" + storageFieldInfo.typeSignature);
+ // GENERATED:
+ // public List
+
+ io.smallrye
+ smallrye-context-propagation-storage
+
org.jboss.logging
jboss-logging
diff --git a/core/runtime/src/main/java/io/quarkus/runtime/storage/QuarkusStorageManager.java b/core/runtime/src/main/java/io/quarkus/runtime/storage/QuarkusStorageManager.java
new file mode 100644
index 0000000000000..bac72773d99fa
--- /dev/null
+++ b/core/runtime/src/main/java/io/quarkus/runtime/storage/QuarkusStorageManager.java
@@ -0,0 +1,34 @@
+package io.quarkus.runtime.storage;
+
+import java.util.Map;
+
+import io.smallrye.context.storage.spi.StorageDeclaration;
+import io.smallrye.context.storage.spi.StorageManager;
+
+public class QuarkusStorageManager implements StorageManager {
+
+ private Map> declaredStorages;
+ private int contextCount;
+
+ QuarkusStorageManager(Map> declaredStorages) {
+ this.contextCount = declaredStorages.size();
+ this.declaredStorages = declaredStorages;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public , X> ThreadLocal getThreadLocal(Class klass) {
+ ThreadLocal> storage = declaredStorages.get(klass.getName());
+ if (storage != null)
+ return (ThreadLocal) storage;
+ throw new IllegalArgumentException("Storage user nor registered: " + klass);
+ }
+
+ public static QuarkusStorageManager instance() {
+ return (QuarkusStorageManager) StorageManager.instance();
+ }
+
+ public Object[] newContext() {
+ return new Object[contextCount];
+ }
+}
diff --git a/core/runtime/src/main/java/io/quarkus/runtime/storage/QuarkusStorageManagerProvider.java b/core/runtime/src/main/java/io/quarkus/runtime/storage/QuarkusStorageManagerProvider.java
new file mode 100644
index 0000000000000..165a92d87de48
--- /dev/null
+++ b/core/runtime/src/main/java/io/quarkus/runtime/storage/QuarkusStorageManagerProvider.java
@@ -0,0 +1,24 @@
+package io.quarkus.runtime.storage;
+
+import java.util.Map;
+
+import io.smallrye.context.storage.spi.StorageManager;
+import io.smallrye.context.storage.spi.StorageManagerProvider;
+
+public class QuarkusStorageManagerProvider implements StorageManagerProvider {
+
+ private final QuarkusStorageManager storageManager;
+
+ public QuarkusStorageManagerProvider(Map> declaredStorages) {
+ storageManager = new QuarkusStorageManager(declaredStorages);
+ }
+
+ @Override
+ public StorageManager getStorageManager(ClassLoader classloader) {
+ return storageManager;
+ }
+
+ public QuarkusStorageManagerProvider instance() {
+ return (QuarkusStorageManagerProvider) StorageManagerProvider.instance();
+ }
+}
diff --git a/core/runtime/src/main/java/io/quarkus/runtime/storage/QuarkusThread.java b/core/runtime/src/main/java/io/quarkus/runtime/storage/QuarkusThread.java
new file mode 100644
index 0000000000000..f98b5b8ef69ca
--- /dev/null
+++ b/core/runtime/src/main/java/io/quarkus/runtime/storage/QuarkusThread.java
@@ -0,0 +1,6 @@
+package io.quarkus.runtime.storage;
+
+public interface QuarkusThread {
+
+ Object[] getQuarkusThreadContext();
+}
diff --git a/core/runtime/src/main/java/io/quarkus/runtime/storage/StorageRecorder.java b/core/runtime/src/main/java/io/quarkus/runtime/storage/StorageRecorder.java
new file mode 100644
index 0000000000000..9fad1222097c1
--- /dev/null
+++ b/core/runtime/src/main/java/io/quarkus/runtime/storage/StorageRecorder.java
@@ -0,0 +1,24 @@
+package io.quarkus.runtime.storage;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import io.quarkus.runtime.RuntimeValue;
+import io.quarkus.runtime.annotations.Recorder;
+import io.smallrye.context.storage.spi.StorageManagerProvider;
+
+@Recorder
+public class StorageRecorder {
+
+ public void configureStaticInit(Map>> storageMappings) {
+ System.err.println("Configuring storages for: " + storageMappings);
+ Map> declaredStorages = new HashMap<>();
+ for (Entry>> entry : storageMappings.entrySet()) {
+ declaredStorages.put(entry.getKey(), entry.getValue().getValue());
+ }
+ QuarkusStorageManagerProvider provider = new QuarkusStorageManagerProvider(declaredStorages);
+ StorageManagerProvider.register(provider);
+ }
+
+}
diff --git a/extensions/smallrye-context-propagation/deployment/pom.xml b/extensions/smallrye-context-propagation/deployment/pom.xml
index 1cc9af0cfcb62..d5405429cb258 100644
--- a/extensions/smallrye-context-propagation/deployment/pom.xml
+++ b/extensions/smallrye-context-propagation/deployment/pom.xml
@@ -24,6 +24,16 @@
io.quarkus
quarkus-smallrye-context-propagation
+
+ io.quarkus
+ quarkus-junit5-internal
+ test
+
+
+ io.quarkus
+ quarkus-vertx-http-deployment
+ test
+
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 209400682545e..7e10bb2552a72 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
@@ -70,6 +70,7 @@ void build(SmallRyeContextPropagationRecorder recorder,
ExecutorBuildItem executorBuildItem,
BuildProducer feature,
BuildProducer managedExecutorInitialized,
+ BuildProducer marker,
BuildProducer syntheticBeans) {
feature.produce(new FeatureBuildItem(Feature.SMALLRYE_CONTEXT_PROPAGATION));
@@ -87,5 +88,6 @@ void build(SmallRyeContextPropagationRecorder recorder,
// This should be removed at some point after Quarkus 1.7
managedExecutorInitialized.produce(new ManagedExecutorInitializedBuildItem());
+ marker.produce(new SmallRyeContextPropagationRuntimeInitialisedBuildItem());
}
}
diff --git a/extensions/smallrye-context-propagation/deployment/src/main/java/io/quarkus/smallrye/context/deployment/SmallRyeContextPropagationRuntimeInitialisedBuildItem.java b/extensions/smallrye-context-propagation/deployment/src/main/java/io/quarkus/smallrye/context/deployment/SmallRyeContextPropagationRuntimeInitialisedBuildItem.java
new file mode 100644
index 0000000000000..55beecb5f7245
--- /dev/null
+++ b/extensions/smallrye-context-propagation/deployment/src/main/java/io/quarkus/smallrye/context/deployment/SmallRyeContextPropagationRuntimeInitialisedBuildItem.java
@@ -0,0 +1,10 @@
+package io.quarkus.smallrye.context.deployment;
+
+import io.quarkus.builder.item.SimpleBuildItem;
+
+/**
+ * Marker build item to be able to run after SR-CP has had time to configure itself at runtime.
+ */
+public final class SmallRyeContextPropagationRuntimeInitialisedBuildItem extends SimpleBuildItem {
+
+}
diff --git a/extensions/smallrye-context-propagation/deployment/src/test/java/io/quarkus/smallrye/context/test/CPTest.java b/extensions/smallrye-context-propagation/deployment/src/test/java/io/quarkus/smallrye/context/test/CPTest.java
new file mode 100644
index 0000000000000..e1e43b935d795
--- /dev/null
+++ b/extensions/smallrye-context-propagation/deployment/src/test/java/io/quarkus/smallrye/context/test/CPTest.java
@@ -0,0 +1,67 @@
+package io.quarkus.smallrye.context.test;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.function.Supplier;
+
+import javax.inject.Inject;
+
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import io.quarkus.arc.Arc;
+import io.quarkus.runtime.storage.QuarkusThread;
+import io.quarkus.test.QuarkusUnitTest;
+import io.smallrye.context.SmallRyeThreadContext;
+import io.vertx.core.Vertx;
+
+public class CPTest {
+
+ @RegisterExtension
+ static QuarkusUnitTest test = new QuarkusUnitTest()
+ .overrideConfigKey("quarkus.vertx.storage", "false")
+ .setArchiveProducer(new Supplier() {
+ @Override
+ public JavaArchive get() {
+ return ShrinkWrap.create(JavaArchive.class)
+ .addClasses();
+ }
+ });
+
+ @Inject
+ SmallRyeThreadContext tc;
+
+ @Inject
+ Vertx vertx;
+
+ @Test
+ public void test() throws InterruptedException, ExecutionException {
+ System.err.println("Current thread: " + Thread.currentThread().getClass());
+ System.err.println("Plan: " + tc.getPlan().propagatedProviders);
+ Arc.container().requestContext().activate();
+ System.err.println("Request context: " + Arc.container().requestContext().isActive());
+
+ Runnable runner = tc.contextualRunnable(() -> {
+ System.err.println("Runner thread supports storage: " + (Thread.currentThread() instanceof QuarkusThread));
+ System.err.println("Runner request context: " + Arc.container().requestContext().isActive());
+ System.err.println("Runner called with context");
+ });
+
+ CompletableFuture