Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
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.

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.

Good catch! Yes, the autoConfigurationSingleOption call for virtualThreadsEnabled is now redundant — configureVirtualThreadsEarly() covers all the same configuration sources and runs at the right moment (before camelContext.build()). Removed it in the follow-up commit.

Claude Code on behalf of Claus Ibsen


// ensure camel context is build
camelContext.build();
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,17 @@ public static ThreadType current() {
}
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);
}
}