This is an automated email from the ASF dual-hosted git repository. gnodet pushed a commit to branch context-value-scoped-value-support in repository https://gitbox.apache.org/repos/asf/camel.git
commit 5eb137717f8f1154f20028fc2410eb4ef6d00337 Author: Guillaume Nodet <[email protected]> AuthorDate: Thu Feb 19 13:18:56 2026 +0100 Fix ContextValue scoping issues with KameletProcessor and JDK 25 compilation The ContextValue abstraction properly scopes createRoute/createProcessor ThreadLocals, but KameletEndpoint.doInit() relied on a leaked ThreadLocal value from a bug in the old createRoute(null) which cleared isSetupRoutes instead of isCreateRoute. Fix by capturing the route/processor context in KameletReifier (within the createProcessor scope) and passing them to KameletProcessor, which restores them during doInit() so the endpoint can inherit error handlers correctly. Also fix JDK 25 ScopedValue.Carrier API: use call() instead of get(). Co-Authored-By: Claude Opus 4.6 <[email protected]> --- .../camel/component/kamelet/KameletProcessor.java | 29 +++++++++++++++++++++- .../camel/component/kamelet/KameletReifier.java | 8 +++++- .../camel/util/concurrent/ContextValueFactory.java | 3 +-- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletProcessor.java b/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletProcessor.java index 80cd3fe9b53f..0f4012e7c7c6 100644 --- a/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletProcessor.java +++ b/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletProcessor.java @@ -43,6 +43,8 @@ public class KameletProcessor extends BaseProcessorSupport private final String name; private final AsyncProcessor processor; + private final String parentRouteId; + private final String parentProcessorId; private KameletProducer producer; private KameletComponent component; private CamelContext camelContext; @@ -50,9 +52,16 @@ public class KameletProcessor extends BaseProcessorSupport private String routeId; public KameletProcessor(CamelContext camelContext, String name, Processor processor) throws Exception { + this(camelContext, name, processor, null, null); + } + + public KameletProcessor(CamelContext camelContext, String name, Processor processor, + String parentRouteId, String parentProcessorId) throws Exception { this.camelContext = camelContext; this.name = name; this.processor = AsyncProcessorConverterHelper.convert(processor); + this.parentRouteId = parentRouteId; + this.parentProcessorId = parentProcessorId; } @ManagedAttribute(description = "Kamelet name (templateId/routeId?options)") @@ -118,7 +127,25 @@ public class KameletProcessor extends BaseProcessorSupport @Override protected void doInit() throws Exception { this.component = camelContext.getComponent("kamelet", KameletComponent.class); - this.producer = (KameletProducer) camelContext.getEndpoint("kamelet://" + name).createAsyncProducer(); + + // set the route/processor context so the KameletEndpoint.doInit() can pick them up + // these were captured during construction (within the createProcessor scope) + if (parentRouteId != null) { + camelContext.getCamelContextExtension().createRoute(parentRouteId); + } + if (parentProcessorId != null) { + camelContext.getCamelContextExtension().createProcessor(parentProcessorId); + } + try { + this.producer = (KameletProducer) camelContext.getEndpoint("kamelet://" + name).createAsyncProducer(); + } finally { + if (parentRouteId != null) { + camelContext.getCamelContextExtension().createRoute(null); + } + if (parentProcessorId != null) { + camelContext.getCamelContextExtension().createProcessor(null); + } + } ServiceHelper.initService(processor, producer); diff --git a/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletReifier.java b/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletReifier.java index dd544eacc95c..587b2e144d87 100644 --- a/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletReifier.java +++ b/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletReifier.java @@ -40,8 +40,14 @@ public class KameletReifier extends ProcessorReifier<KameletDefinition> { // wrap in uow String outputId = definition.idOrCreate(camelContext.getCamelContextExtension().getContextPlugin(NodeIdFactory.class)); final Processor childProcessor = processor; + // capture route/processor context now (within createProcessor scope) for later use by KameletProcessor + final String parentRouteId = camelContext.getCamelContextExtension().getCreateRoute(); return camelContext.getCamelContextExtension().createProcessor(outputId, () -> { - Processor answer = new KameletProcessor(camelContext, parseString(definition.getName()), childProcessor); + final String parentProcessorId = camelContext.getCamelContextExtension().getCreateProcessor(); + Processor answer + = new KameletProcessor( + camelContext, parseString(definition.getName()), childProcessor, + parentRouteId, parentProcessorId); if (answer instanceof DisabledAware da) { da.setDisabled(isDisabled(camelContext, definition)); } diff --git a/core/camel-util/src/main/java25/org/apache/camel/util/concurrent/ContextValueFactory.java b/core/camel-util/src/main/java25/org/apache/camel/util/concurrent/ContextValueFactory.java index d6ce66128e38..8575a1f91705 100644 --- a/core/camel-util/src/main/java25/org/apache/camel/util/concurrent/ContextValueFactory.java +++ b/core/camel-util/src/main/java25/org/apache/camel/util/concurrent/ContextValueFactory.java @@ -81,8 +81,7 @@ class ContextValueFactory { */ static <T, R> R where(ContextValue<T> key, T value, Supplier<R> operation) { if (key instanceof ScopedValueContextValue<T> svKey) { - // In JDK 25+, ScopedValue.where() returns a Carrier that has get() method - return ScopedValue.where(svKey.scopedValue, value).get(operation); + return ScopedValue.where(svKey.scopedValue, value).call(operation::get); } else if (key instanceof ThreadLocalContextValue<T> tlKey) { T oldValue = tlKey.get(); try {
