[ 
https://issues.apache.org/jira/browse/CAMEL-24320?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Omar Atie reassigned CAMEL-24320:
---------------------------------

    Assignee: Omar Atie

> camel-core - Kamelet route creation fails with virtual threads enabled on JDK 
> 25
> --------------------------------------------------------------------------------
>
>                 Key: CAMEL-24320
>                 URL: https://issues.apache.org/jira/browse/CAMEL-24320
>             Project: Camel
>          Issue Type: Bug
>          Components: camel-core, camel-kamelet
>    Affects Versions: 4.21.0
>         Environment: Apache Camel Main 4.21.0; Eclipse Temurin 25.0.3; 
> camel.threads.virtual.enabled=true; reproduced in Docker. Control: the 
> identical route starts and consumes timer ticks with virtual threads disabled.
>            Reporter: Serdar Gökay
>            Assignee: Omar Atie
>            Priority: Major
>         Attachments: camel-kamelet-vt-jdk25-reproducer.zip
>
>
> h3. What happens
> With {{-Dcamel.threads.virtual.enabled=true}} on JDK 25, a route that
> consumes a Kamelet fails during route creation with a
> {{NullPointerException}}. The same route starts and runs normally with
> virtual threads disabled.
> h3. Root cause
> # On JDK 25 with virtual threads enabled, the multi-release
> {{ContextValueFactory}} ({{core/camel-util}}, Java 25 variant) selects the
> {{ScopedValue}}-backed {{ScopedValueContextValue}} because
> {{ThreadType.current()}} reports {{VIRTUAL}}.
> # {{DefaultCamelContextExtension.getCreateRoute()}} and
> {{getCreateProcessor()}} are implemented as {{isCreateRoute.orElse(null)}}
> and {{isCreateProcessor.orElse(null)}}
> ({{DefaultCamelContextExtension.java:330,335}}).
> # {{ScopedValueContextValue.orElse}} ({{ContextValueFactory.java:141}})
> delegates to {{java.lang.ScopedValue.orElse}}, which on JDK 25 executes
> {{Objects.requireNonNull(other)}} before any binding lookup — a null
> fallback therefore throws unconditionally, even where a binding exists.
> # {{KameletComponent}} reads {{getCreateRoute()}} while initializing its
> endpoint service ({{KameletComponent.java:184}}), outside any binding scope,
> so every Kamelet route creation throws NPE before the route exists.
> There is no configuration that keeps virtual threads enabled while forcing
> the {{ThreadLocal}}-backed {{ContextValue}}; the implementation is selected
> solely from {{ThreadType.current()}}. The defect is still present on {{main}}
> as of 2026-07-31 (call sites and implementation unchanged).
> h3. Stack trace (camel 4.21.0, Temurin 25.0.3)
> {code}
> Exception in thread "main" org.apache.camel.FailedToCreateRouteException: 
> Failed to create route: vt-kamelet-repro (source: file:route.camel.yaml): 
> Route(vt-kamelet-repro)[From[kamelet:vt-repro-source] -> [Lo... because: 
> Failed to resolve endpoint: kamelet://vt-repro-source due to: null
>       at 
> org.apache.camel.reifier.RouteReifier.createRoute(RouteReifier.java:96)
>       ...
> Caused by: org.apache.camel.ResolveEndpointFailedException: Failed to resolve 
> endpoint: kamelet://vt-repro-source due to: null
>       at 
> org.apache.camel.impl.engine.AbstractCamelContext.doGetEndpoint(AbstractCamelContext.java:891)
>       ...
> Caused by: java.lang.NullPointerException
>       at java.base/java.util.Objects.requireNonNull(Unknown Source)
>       at java.base/java.lang.ScopedValue.orElse(Unknown Source)
>       at 
> org.apache.camel.util.concurrent.ContextValueFactory$ScopedValueContextValue.orElse(ContextValueFactory.java:141)
>       at 
> org.apache.camel.impl.engine.DefaultCamelContextExtension.getCreateRoute(DefaultCamelContextExtension.java:330)
>       at 
> org.apache.camel.component.kamelet.KameletComponent$1.doInit(KameletComponent.java:184)
> {code}
> h3. Reproducer (plain Camel Main, two small files)
> {{route.camel.yaml}}:
> {code}
> - route:
>     id: vt-kamelet-repro
>     from:
>       uri: kamelet:vt-repro-source
>       steps:
>         - log: "consumed ${body}"
> {code}
> {{kamelets/vt-repro-source.kamelet.yaml}}:
> {code}
> apiVersion: camel.apache.org/v1
> kind: Kamelet
> metadata:
>   name: vt-repro-source
>   labels:
>     camel.apache.org/kamelet.type: source
> spec:
>   definition:
>     title: VT repro source
>   template:
>     from:
>       uri: timer:tick
>       parameters:
>         period: "1000"
>       steps:
>         - setBody:
>             constant: hello
>         - to:
>             uri: kamelet:sink
> {code}
> Run with any JDK 25 and camel-main 4.21.0 (kamelets/ on the classpath):
> {code}
> java -cp <camel-main-4.21.0-classpath>:. \
>   -Dcamel.threads.virtual.enabled=true \
>   -Dcamel.main.routesIncludePattern=file:route.camel.yaml \
>   org.apache.camel.main.Main
> {code}
> → {{FailedToCreateRouteException}} with the NPE above at startup.
> With {{-Dcamel.threads.virtual.enabled=false}} the identical files start and
> consume timer ticks.
> h3. Suggested fix direction
> Make the {{ScopedValue}}-backed {{orElse}} null-tolerant — e.g. return the
> default when unbound instead of delegating a null fallback
> ({{scopedValue.isBound() ? scopedValue.get() : defaultValue}}) — or change
> the two call sites to a non-null fallback. A regression test that creates a
> Kamelet route with virtual threads enabled on JDK 25 would cover the path;
> note that {{getCreateProcessor()}} carries the same defect for any component
> reading it during endpoint creation.
> Related: CAMEL-20199 (virtual threads umbrella).



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to