This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new f6f300e87f47 CAMEL-23479: Fix camel.main.virtualThreadsEnabled not
working (#23307)
f6f300e87f47 is described below
commit f6f300e87f470ef4fbb9ddae2d02ac96980450c1
Author: Claus Ibsen <[email protected]>
AuthorDate: Tue May 19 09:50:03 2026 +0200
CAMEL-23479: Fix camel.main.virtualThreadsEnabled not working (#23307)
* CAMEL-23479: Fix camel.main.virtualThreadsEnabled not working
Two bugs fixed:
1. Race condition: autoconfigure() set the system property after
camelContext.build() could already have triggered ThreadType.current()
and permanently cached PLATFORM via double-checked locking.
2. Programmatic case broken:
main.configure().withVirtualThreadsEnabled(true)
set mainConfigurationProperties but never set the system property,
so ThreadType.current() always returned PLATFORM.
Fix: add configureVirtualThreadsEarly() called before camelContext.build()
that checks all config sources (programmatic, initialProperties,
PropertiesComponent,
ENV, JVM system props) and sets the system property early. Also adds
ThreadType.enable() to directly set the cached value, overriding any value
already cached before this point.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
* CAMEL-23479: Remove redundant virtualThreadsEnabled
autoConfigurationSingleOption
configureVirtualThreadsEarly() now covers all configuration sources and
runs before camelContext.build(), making the later
autoConfigurationSingleOption
call for virtualThreadsEnabled redundant.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
---------
Co-authored-by: Claude Sonnet 4.6 <[email protected]>
---
.../org/apache/camel/main/BaseMainSupport.java | 64 +++++++++++++++++++---
.../apache/camel/main/MainVirtualThreadsTest.java | 62 +++++++++++++++++++++
.../apache/camel/util/concurrent/ThreadType.java | 13 +++++
3 files changed, 130 insertions(+), 9 deletions(-)
diff --git
a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
index edec9b123a51..df830d9473a9 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
@@ -119,6 +119,7 @@ import org.apache.camel.util.OrderedProperties;
import org.apache.camel.util.SecurityUtils;
import org.apache.camel.util.SecurityViolation;
import org.apache.camel.util.StringHelper;
+import org.apache.camel.util.concurrent.ThreadType;
import org.apache.camel.vault.VaultConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -589,15 +590,6 @@ public abstract class BaseMainSupport extends BaseService {
autoConfigurationPropertiesComponent(camelContext,
autoConfiguredProperties);
recorder.endStep(step);
step = recorder.beginStep(BaseMainSupport.class,
"autoConfigurationSingleOption", "Auto Configure");
- autoConfigurationSingleOption(camelContext,
autoConfiguredProperties, "camel.main.virtualThreadsEnabled",
- value -> {
- boolean enabled = Boolean.parseBoolean(value);
-
mainConfigurationProperties.setVirtualThreadsEnabled(enabled);
- if (enabled) {
-
System.setProperty("camel.threads.virtual.enabled", "true");
- }
- return null;
- });
autoConfigurationSingleOption(camelContext,
autoConfiguredProperties, "camel.main.routesIncludePattern",
value -> {
mainConfigurationProperties.setRoutesIncludePattern(value);
@@ -795,6 +787,58 @@ public abstract class BaseMainSupport extends BaseService {
}
}
+ private void configureVirtualThreadsEarly(CamelContext camelContext) {
+ // Check programmatic configuration first
(main.configure().withVirtualThreadsEnabled(true))
+ boolean enabled =
mainConfigurationProperties.isVirtualThreadsEnabled();
+
+ if (!enabled &&
mainConfigurationProperties.isAutoConfigurationEnabled()) {
+ // Check initial properties (main.addInitialProperty /
main.setInitialProperties)
+ if (initialProperties != null) {
+ String val =
initialProperties.getProperty(optionKey("camel.main.virtualThreadsEnabled"));
+ if (val != null) {
+ enabled = Boolean.parseBoolean(val);
+ }
+ }
+
+ if (!enabled) {
+ // Load from PropertiesComponent (application.properties, etc.)
+ OrderedLocationProperties props = (OrderedLocationProperties)
camelContext.getPropertiesComponent()
+ .loadProperties(name -> name.startsWith("camel."),
MainHelper::optionKey);
+ String val =
props.getProperty(optionKey("camel.main.virtualThreadsEnabled"));
+ if (val != null) {
+ enabled = Boolean.parseBoolean(val);
+ }
+ }
+
+ if (!enabled &&
mainConfigurationProperties.isAutoConfigurationEnvironmentVariablesEnabled()) {
+ Properties envProps =
MainHelper.loadEnvironmentVariablesAsProperties(new String[] { "camel.main." });
+ String val =
envProps.getProperty(optionKey("camel.main.virtualThreadsEnabled"));
+ if (val != null) {
+ enabled = Boolean.parseBoolean(val);
+ }
+ }
+
+ if (!enabled &&
mainConfigurationProperties.isAutoConfigurationSystemPropertiesEnabled()) {
+ Properties sysProps =
MainHelper.loadJvmSystemPropertiesAsProperties(new String[] { "camel.main." });
+ String val =
sysProps.getProperty("camel.main.virtualThreadsEnabled");
+ if (val != null) {
+ enabled = Boolean.parseBoolean(val);
+ }
+ }
+
+ if (enabled) {
+ mainConfigurationProperties.setVirtualThreadsEnabled(true);
+ }
+ }
+
+ if (enabled) {
+ System.setProperty("camel.threads.virtual.enabled", "true");
+ // Directly set the cached value so even if ThreadType.current()
was already called
+ // and cached PLATFORM before this point, it is overridden to
VIRTUAL
+ ThreadType.enable();
+ }
+ }
+
protected void autoConfigurationStartupConditions(
CamelContext camelContext, OrderedLocationProperties
autoConfiguredProperties)
throws Exception {
@@ -958,6 +1002,8 @@ public abstract class BaseMainSupport extends BaseService {
configureRoutesLoader(camelContext);
// configure custom main listeners
configureMainListener(camelContext);
+ // configure virtual threads early before build() to avoid ThreadType
DCL race
+ configureVirtualThreadsEarly(camelContext);
// ensure camel context is build
camelContext.build();
diff --git
a/core/camel-main/src/test/java/org/apache/camel/main/MainVirtualThreadsTest.java
b/core/camel-main/src/test/java/org/apache/camel/main/MainVirtualThreadsTest.java
new file mode 100644
index 000000000000..c846adf97a6f
--- /dev/null
+++
b/core/camel-main/src/test/java/org/apache/camel/main/MainVirtualThreadsTest.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.main;
+
+import java.lang.reflect.Field;
+
+import org.apache.camel.util.concurrent.ThreadType;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class MainVirtualThreadsTest {
+
+ @BeforeEach
+ @AfterEach
+ void resetThreadType() throws Exception {
+ Field field = ThreadType.class.getDeclaredField("current");
+ field.setAccessible(true);
+ field.set(null, null);
+ System.clearProperty("camel.threads.virtual.enabled");
+ }
+
+ @Test
+ void testProgrammaticVirtualThreadsEnabled() {
+ Main main = new Main();
+ main.configure().withVirtualThreadsEnabled(true);
+ main.start();
+ try {
+ assertEquals(ThreadType.VIRTUAL, ThreadType.current());
+ } finally {
+ main.stop();
+ }
+ }
+
+ @Test
+ void testInitialPropertyVirtualThreadsEnabled() {
+ Main main = new Main();
+ main.addInitialProperty("camel.main.virtualThreadsEnabled", "true");
+ main.start();
+ try {
+ assertEquals(ThreadType.VIRTUAL, ThreadType.current());
+ } finally {
+ main.stop();
+ }
+ }
+}
diff --git
a/core/camel-util/src/main/java/org/apache/camel/util/concurrent/ThreadType.java
b/core/camel-util/src/main/java/org/apache/camel/util/concurrent/ThreadType.java
index 074048e3d9a5..1731fe943a0c 100644
---
a/core/camel-util/src/main/java/org/apache/camel/util/concurrent/ThreadType.java
+++
b/core/camel-util/src/main/java/org/apache/camel/util/concurrent/ThreadType.java
@@ -53,4 +53,17 @@ public enum ThreadType {
}
return type;
}
+
+ /**
+ * Directly enables virtual threads by setting the cached type to {@code
VIRTUAL}.
+ * <p>
+ * This must be called before any thread pools are created, ideally during
early bootstrap, to ensure the cached
+ * value reflects the configured intent regardless of the order in which
{@link #current()} was previously invoked.
+ */
+ public static void enable() {
+ synchronized (ThreadType.class) {
+ current = VIRTUAL;
+ }
+ LOG.info("The type of thread enabled is: {}", VIRTUAL);
+ }
}