Skip to content

CAMEL-24320: Fix Kamelet route creation with virtual threads on JDK 25 - #25265

Open
atiaomar1978-hub wants to merge 3 commits into
apache:mainfrom
atiaomar1978-hub:CAMEL-24320-kamelet-virtual-threads-fix
Open

CAMEL-24320: Fix Kamelet route creation with virtual threads on JDK 25#25265
atiaomar1978-hub wants to merge 3 commits into
apache:mainfrom
atiaomar1978-hub:CAMEL-24320-kamelet-virtual-threads-fix

Conversation

@atiaomar1978-hub

Copy link
Copy Markdown
Contributor

Summary

AI-generated on behalf of atiaomar1978-hub

Fixes CAMEL-24320: with -Dcamel.threads.virtual.enabled=true on JDK 25+, Kamelet route creation failed during endpoint init with an NPE.

Root cause: the JDK 25 ScopedValue-backed ContextValue.orElse(null) delegated to ScopedValue.orElse, which rejects a null fallback via Objects.requireNonNull. DefaultCamelContextExtension.getCreateRoute() / getCreateProcessor() call orElse(null) outside any binding scope (e.g. from KameletComponent.doInit).

Fix: make ScopedValueContextValue.orElse null-tolerant — return the default when unbound instead of delegating to ScopedValue.orElse.

Tests

  • ScopedValueContextValueOrElseTest (JDK 25+): direct regression for null/default/bound orElse on the ScopedValue backend
  • CreateContextValueTest: getCreateRoute() / getCreateProcessor() return null outside scope
  • KameletVirtualThreadsRouteCreationTest (JDK 25+): Main starts a route consuming kamelet:vt-repro-source with virtual threads enabled

Test plan

  • mvn test -pl core/camel-util,core/camel-core,components/camel-kamelet -am -Dtest=ContextValueTest,ScopedValueContextValueOrElseTest,CreateContextValueTest,KameletVirtualThreadsRouteCreationTest
  • Bugbot review — no findings
  • Grok review — production fix approved; primary guard is ScopedValueContextValueOrElseTest

JIRA: https://issues.apache.org/jira/browse/CAMEL-24320

Made with Cursor

Make ScopedValue-backed ContextValue.orElse null-tolerant so
getCreateRoute()/getCreateProcessor() can return null outside a binding
scope. Add regression tests for the ScopedValue path and Kamelet startup.

Co-authored-by: Cursor <cursoragent@cursor.com>
@atiaomar1978-hub

Copy link
Copy Markdown
Contributor Author

AI-generated comment on behalf of atiaomar1978-hub

Review follow-up (Bugbot + Grok)

Independent reviews were run on branch CAMEL-24320-kamelet-virtual-threads-fix.

Bugbot

No bugs found.

Grok findings

Severity Issue Status
Production fix (isBound() ? get() : default) is correct and matches ContextValue contract Accepted
High Main-based Kamelet IT may not always select ScopedValue backend if ContextValueFactory loaded earlier in the same JVM Mitigated — ScopedValueContextValueOrElseTest is the primary JDK 25 guard (reflection on ScopedValueContextValue); Kamelet IT sets camel.threads.virtual.enabled in a static initializer and asserts route exists after start
Low Document null-tolerant orElse on ContextValue javadoc Follow-up

Reproduction confirmed per JIRA: NPE at ScopedValue.orElseObjects.requireNonNull when getCreateRoute() is called from KameletComponent.doInit outside binding scope.

Ready for review.

@github-actions

Copy link
Copy Markdown
Contributor

🌟 Thank you for your contribution to the Apache Camel project! 🌟
🤖 CI automation will test this PR automatically.

🐫 Apache Camel Committers, please review the following items:

  • First-time contributors require MANUAL approval for the GitHub Actions to run
  • You can use the command /component-test (camel-)component-name1 (camel-)component-name2.. to request a test from the test bot although they are normally detected and executed by CI.
  • You can label PRs using skip-tests and test-dependents to fine-tune the checks executed by this PR.
  • Build and test logs are available in the summary page. Only Apache Camel committers have access to the summary.

⚠️ Be careful when sharing logs. Review their contents before sharing them publicly.

@apupier

apupier commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

need to format:

diff --git a/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletVirtualThreadsRouteCreationTest.java b/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletVirtualThreadsRouteCreationTest.java
index 13e93a86b737..d5f9f568d0a1 100644
--- a/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletVirtualThreadsRouteCreationTest.java
+++ b/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletVirtualThreadsRouteCreationTest.java
@@ -33,8 +33,8 @@ import static org.assertj.core.api.Assertions.assertThatCode;
 /**
  * CAMEL-24320: Kamelet route creation must not NPE when virtual threads are enabled on JDK 25+.
  * <p>
- * Virtual threads must be selected before the first {@code ContextValue} is created; the system property is set in
- * a static initializer so {@code ContextValueFactory} picks the ScopedValue backend when this class loads first in an
+ * Virtual threads must be selected before the first {@code ContextValue} is created; the system property is set in a
+ * static initializer so {@code ContextValueFactory} picks the ScopedValue backend when this class loads first in an
  * isolated fork. {@link org.apache.camel.util.concurrent.ScopedValueContextValueOrElseTest} is the primary unit guard.
  */
 @Isolated

Comment on lines +44 to +54
static {
System.setProperty("camel.threads.virtual.enabled", "true");
}

@AfterEach
void resetThreadType() throws Exception {
Field field = ThreadType.class.getDeclaredField("current");
field.setAccessible(true);
field.set(null, null);
System.clearProperty("camel.threads.virtual.enabled");
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to look for the value of the property before the test and reset it to thsi value after

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 58bc2359d1d@BeforeEach saves the previous camel.threads.virtual.enabled value and @AfterEach restores it (clear when previously unset).

Comment on lines +40 to +42
@Isolated
@EnabledForJreRange(min = JRE.JAVA_25)
class KameletVirtualThreadsRouteCreationTest {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should add a Resource lock like here

and maybe no need of @isolated in this case

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 58bc2359d1d — replaced @Isolated with @ResourceLock(Resources.SYSTEM_PROPERTIES) on the test class.

main.start();
assertThat(main.getCamelContext().getRoute("vt-kamelet-repro")).isNotNull();
main.stop();
}).doesNotThrowAnyException();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is already an assertion in the test, not sure it is necessary to use doesNotThorwAnyException. If an exception is thrown, it will be reorted as an error already by the test

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 58bc2359d1d — removed assertThatCode; the test now uses direct assertions and a try/finally around main.stop().

@atiaomar1978-hub

Copy link
Copy Markdown
Contributor Author

AI-generated comment on behalf of atiaomar1978-hub

Fixed the sourcecheck failure in run 30603239831 (Fail if there are uncommitted changes).

The build reformatted javadoc line wrapping in KameletVirtualThreadsRouteCreationTest.java. Applied formatter:format and committed the synced file.

Re-ran ContextValueTest, ScopedValueContextValueOrElseTest, CreateContextValueTest, and KameletVirtualThreadsRouteCreationTest — all green (JDK 25 tests skipped locally on JDK 21).

Please re-run or wait for CI on the latest commit.

Omar Atie and others added 2 commits July 31, 2026 09:08
Fix sourcecheck uncommitted-changes failure from javadoc line wrapping.

Co-authored-by: Cursor <cursoragent@cursor.com>
Save and restore camel.threads.virtual.enabled, use ResourceLock for system
properties instead of @isolated, and drop redundant assertThatCode wrapper.

Co-authored-by: Cursor <cursoragent@cursor.com>
@atiaomar1978-hub

Copy link
Copy Markdown
Contributor Author

AI-generated comment on behalf of atiaomar1978-hub

Addressed @apupier review in 58bc2359d1d:

  1. Save/restore camel.threads.virtual.enabled around each test instead of clearing unconditionally.
  2. Use @ResourceLock(Resources.SYSTEM_PROPERTIES) instead of @Isolated.
  3. Drop redundant assertThatCode wrapper.

Re-ran ContextValueTest, ScopedValueContextValueOrElseTest, CreateContextValueTest, and KameletVirtualThreadsRouteCreationTest — all green (JDK 25 tests skipped locally on JDK 21).

Ready for re-review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants