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 edec9b123a514..df830d9473a9b 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.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 @@ protected void autoconfigure(CamelContext camelContext) throws Exception { 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 @@ protected void configureMainListener(CamelContext camelContext) throws Exception } } + 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 @@ protected void postProcessCamelContext(CamelContext camelContext) throws Excepti 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 0000000000000..c846adf97a6f4 --- /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 074048e3d9a52..1731fe943a0cd 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 static ThreadType current() { } return type; } + + /** + * Directly enables virtual threads by setting the cached type to {@code VIRTUAL}. + *

+ * 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); + } }