This is an automated email from the ASF dual-hosted git repository.

gnodet 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 29e73b566a79 CAMEL-20199: Add JMX support for virtual thread executors 
(#21701)
29e73b566a79 is described below

commit 29e73b566a7936ccadb755ff137268c64dc1d044
Author: Guillaume Nodet <[email protected]>
AuthorDate: Wed Mar 4 23:57:21 2026 +0100

    CAMEL-20199: Add JMX support for virtual thread executors (#21701)
    
    Virtual thread executors (ThreadPerTaskExecutor) were silently skipped
    from JMX registration because LifecycleStrategy.onThreadPoolAdd only
    accepted ThreadPoolExecutor. This adds overloaded methods for
    ExecutorService to register non-ThreadPoolExecutor instances in JMX.
    
    New ManagedVirtualThreadExecutorMBean exposes basic attributes: id,
    sourceId, routeId, isVirtualThread, and isShutdown.
---
 .../org/apache/camel/spi/LifecycleStrategy.java    | 27 ++++++
 .../camel/spi/ManagementObjectNameStrategy.java    |  7 ++
 .../apache/camel/spi/ManagementObjectStrategy.java |  7 ++
 .../impl/engine/BaseExecutorServiceManager.java    | 10 +++
 .../mbean/ManagedVirtualThreadExecutorMBean.java   | 46 ++++++++++
 .../DefaultManagementObjectNameStrategy.java       | 17 ++++
 .../DefaultManagementObjectStrategy.java           | 12 +++
 .../management/JmxManagementLifecycleStrategy.java | 59 ++++++++++++-
 .../mbean/ManagedVirtualThreadExecutor.java        | 98 ++++++++++++++++++++++
 .../ManagedVirtualThreadExecutorTest.java          | 88 +++++++++++++++++++
 10 files changed, 370 insertions(+), 1 deletion(-)

diff --git 
a/core/camel-api/src/main/java/org/apache/camel/spi/LifecycleStrategy.java 
b/core/camel-api/src/main/java/org/apache/camel/spi/LifecycleStrategy.java
index 607ac8526805..c035c773be14 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/LifecycleStrategy.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/LifecycleStrategy.java
@@ -17,6 +17,7 @@
 package org.apache.camel.spi;
 
 import java.util.Collection;
+import java.util.concurrent.ExecutorService;
 import java.util.concurrent.ThreadPoolExecutor;
 
 import org.apache.camel.CamelContext;
@@ -209,4 +210,30 @@ public interface LifecycleStrategy {
      */
     void onThreadPoolRemove(CamelContext camelContext, ThreadPoolExecutor 
threadPool);
 
+    /**
+     * Notification on adding an executor service (such as a virtual thread 
executor) that is not a
+     * {@link ThreadPoolExecutor}.
+     *
+     * @param camelContext        the camel context
+     * @param executorService     the executor service
+     * @param id                  id of the thread pool (can be null in 
special cases)
+     * @param sourceId            id of the source creating the thread pool 
(can be null in special cases)
+     * @param routeId             id of the route for the source (is null if 
no source)
+     * @param threadPoolProfileId id of the thread pool profile, if used for 
creating this thread pool (can be null)
+     */
+    default void onThreadPoolAdd(
+            CamelContext camelContext, ExecutorService executorService, String 
id,
+            String sourceId, String routeId, String threadPoolProfileId) {
+    }
+
+    /**
+     * Notification on removing an executor service (such as a virtual thread 
executor) that is not a
+     * {@link ThreadPoolExecutor}.
+     *
+     * @param camelContext    the camel context
+     * @param executorService the executor service
+     */
+    default void onThreadPoolRemove(CamelContext camelContext, ExecutorService 
executorService) {
+    }
+
 }
diff --git 
a/core/camel-api/src/main/java/org/apache/camel/spi/ManagementObjectNameStrategy.java
 
b/core/camel-api/src/main/java/org/apache/camel/spi/ManagementObjectNameStrategy.java
index 5c3ca2ba3b51..64bd66b15e95 100644
--- 
a/core/camel-api/src/main/java/org/apache/camel/spi/ManagementObjectNameStrategy.java
+++ 
b/core/camel-api/src/main/java/org/apache/camel/spi/ManagementObjectNameStrategy.java
@@ -16,6 +16,7 @@
  */
 package org.apache.camel.spi;
 
+import java.util.concurrent.ExecutorService;
 import java.util.concurrent.ThreadPoolExecutor;
 
 import javax.management.MalformedObjectNameException;
@@ -78,6 +79,12 @@ public interface ManagementObjectNameStrategy {
     ObjectName getObjectNameForThreadPool(CamelContext context, 
ThreadPoolExecutor threadPool, String id, String sourceId)
             throws MalformedObjectNameException;
 
+    default ObjectName getObjectNameForThreadPool(
+            CamelContext context, ExecutorService executorService, String id, 
String sourceId)
+            throws MalformedObjectNameException {
+        return null;
+    }
+
     ObjectName getObjectNameForEventNotifier(CamelContext context, 
EventNotifier eventNotifier)
             throws MalformedObjectNameException;
 }
diff --git 
a/core/camel-api/src/main/java/org/apache/camel/spi/ManagementObjectStrategy.java
 
b/core/camel-api/src/main/java/org/apache/camel/spi/ManagementObjectStrategy.java
index 972ba4c9514a..1a44a9887c0f 100644
--- 
a/core/camel-api/src/main/java/org/apache/camel/spi/ManagementObjectStrategy.java
+++ 
b/core/camel-api/src/main/java/org/apache/camel/spi/ManagementObjectStrategy.java
@@ -16,6 +16,7 @@
  */
 package org.apache.camel.spi;
 
+import java.util.concurrent.ExecutorService;
 import java.util.concurrent.ThreadPoolExecutor;
 
 import org.apache.camel.CamelContext;
@@ -67,5 +68,11 @@ public interface ManagementObjectStrategy {
             CamelContext context, ThreadPoolExecutor threadPool,
             String id, String sourceId, String routeId, String 
threadPoolProfileId);
 
+    default Object getManagedObjectForThreadPool(
+            CamelContext context, ExecutorService executorService,
+            String id, String sourceId, String routeId, String 
threadPoolProfileId) {
+        return null;
+    }
+
     Object getManagedObjectForEventNotifier(CamelContext context, 
EventNotifier eventNotifier);
 }
diff --git 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/BaseExecutorServiceManager.java
 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/BaseExecutorServiceManager.java
index 92a19b4e36ac..58a14151de1b 100644
--- 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/BaseExecutorServiceManager.java
+++ 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/BaseExecutorServiceManager.java
@@ -372,6 +372,11 @@ public class BaseExecutorServiceManager extends 
ServiceSupport implements Execut
             for (LifecycleStrategy lifecycle : 
camelContext.getLifecycleStrategies()) {
                 lifecycle.onThreadPoolRemove(camelContext, threadPool);
             }
+        } else {
+            // for non-ThreadPoolExecutor instances (e.g., virtual thread 
executors)
+            for (LifecycleStrategy lifecycle : 
camelContext.getLifecycleStrategies()) {
+                lifecycle.onThreadPoolRemove(camelContext, executorService);
+            }
         }
 
         // remove reference as its shutdown (do not remove if fail-safe)
@@ -592,6 +597,11 @@ public class BaseExecutorServiceManager extends 
ServiceSupport implements Execut
             for (LifecycleStrategy lifecycle : 
camelContext.getLifecycleStrategies()) {
                 lifecycle.onThreadPoolAdd(camelContext, threadPool, id, 
sourceId, routeId, threadPoolProfileId);
             }
+        } else {
+            // for non-ThreadPoolExecutor instances (e.g., virtual thread 
executors)
+            for (LifecycleStrategy lifecycle : 
camelContext.getLifecycleStrategies()) {
+                lifecycle.onThreadPoolAdd(camelContext, executorService, id, 
sourceId, routeId, threadPoolProfileId);
+            }
         }
 
         // now call strategy to allow custom logic
diff --git 
a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedVirtualThreadExecutorMBean.java
 
b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedVirtualThreadExecutorMBean.java
new file mode 100644
index 000000000000..90ea5d2fa6b6
--- /dev/null
+++ 
b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedVirtualThreadExecutorMBean.java
@@ -0,0 +1,46 @@
+/*
+ * 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.api.management.mbean;
+
+import org.apache.camel.api.management.ManagedAttribute;
+
+public interface ManagedVirtualThreadExecutorMBean {
+
+    @ManagedAttribute(description = "Camel ID")
+    String getCamelId();
+
+    @ManagedAttribute(description = "Camel ManagementName")
+    String getCamelManagementName();
+
+    @ManagedAttribute(description = "Thread Pool ID")
+    String getId();
+
+    @ManagedAttribute(description = "ID of source for creating Thread Pool")
+    String getSourceId();
+
+    @ManagedAttribute(description = "Route ID for the source, which created 
the Thread Pool")
+    String getRouteId();
+
+    @ManagedAttribute(description = "ID of the thread pool profile which this 
pool is based upon")
+    String getThreadPoolProfileId();
+
+    @ManagedAttribute(description = "Whether this executor uses virtual 
threads")
+    boolean isVirtualThread();
+
+    @ManagedAttribute(description = "Is shutdown")
+    boolean isShutdown();
+}
diff --git 
a/core/camel-management/src/main/java/org/apache/camel/management/DefaultManagementObjectNameStrategy.java
 
b/core/camel-management/src/main/java/org/apache/camel/management/DefaultManagementObjectNameStrategy.java
index 270d0ffd7f4e..9f1ceb1a4083 100644
--- 
a/core/camel-management/src/main/java/org/apache/camel/management/DefaultManagementObjectNameStrategy.java
+++ 
b/core/camel-management/src/main/java/org/apache/camel/management/DefaultManagementObjectNameStrategy.java
@@ -17,6 +17,7 @@
 package org.apache.camel.management;
 
 import java.net.UnknownHostException;
+import java.util.concurrent.ExecutorService;
 import java.util.concurrent.ThreadPoolExecutor;
 
 import javax.management.MalformedObjectNameException;
@@ -54,6 +55,7 @@ import org.apache.camel.management.mbean.ManagedStep;
 import org.apache.camel.management.mbean.ManagedSupervisingRouteController;
 import org.apache.camel.management.mbean.ManagedThreadPool;
 import org.apache.camel.management.mbean.ManagedTracer;
+import org.apache.camel.management.mbean.ManagedVirtualThreadExecutor;
 import org.apache.camel.spi.DataFormat;
 import org.apache.camel.spi.EventNotifier;
 import org.apache.camel.spi.ManagementObjectNameStrategy;
@@ -162,6 +164,9 @@ public class DefaultManagementObjectNameStrategy implements 
ManagementObjectName
             objectName = getObjectNameForTracer(mt.getContext(), 
mt.getTracer());
         } else if (managedObject instanceof ManagedThreadPool mes) {
             objectName = getObjectNameForThreadPool(mes.getContext(), 
mes.getThreadPool(), mes.getId(), mes.getSourceId());
+        } else if (managedObject instanceof ManagedVirtualThreadExecutor mvte) 
{
+            objectName = getObjectNameForThreadPool(
+                    mvte.getContext(), mvte.getExecutorService(), 
mvte.getId(), mvte.getSourceId());
         } else if (managedObject instanceof ManagedClusterService mcs) {
             objectName = getObjectNameForClusterService(mcs.getContext(), 
mcs.getService());
         } else if (managedObject instanceof ManagedService ms) {
@@ -422,6 +427,18 @@ public class DefaultManagementObjectNameStrategy 
implements ManagementObjectName
     public ObjectName getObjectNameForThreadPool(
             CamelContext context, ThreadPoolExecutor threadPool, String id, 
String sourceId)
             throws MalformedObjectNameException {
+        return getObjectNameForThreadPool(context, id, sourceId);
+    }
+
+    @Override
+    public ObjectName getObjectNameForThreadPool(
+            CamelContext context, ExecutorService executorService, String id, 
String sourceId)
+            throws MalformedObjectNameException {
+        return getObjectNameForThreadPool(context, id, sourceId);
+    }
+
+    private ObjectName getObjectNameForThreadPool(CamelContext context, String 
id, String sourceId)
+            throws MalformedObjectNameException {
         StringBuilder buffer = new StringBuilder();
         buffer.append(domainName).append(":");
         
buffer.append(KEY_CONTEXT).append("=").append(getContextId(context)).append(",");
diff --git 
a/core/camel-management/src/main/java/org/apache/camel/management/DefaultManagementObjectStrategy.java
 
b/core/camel-management/src/main/java/org/apache/camel/management/DefaultManagementObjectStrategy.java
index 6db5f7972358..c5beb08537e5 100644
--- 
a/core/camel-management/src/main/java/org/apache/camel/management/DefaultManagementObjectStrategy.java
+++ 
b/core/camel-management/src/main/java/org/apache/camel/management/DefaultManagementObjectStrategy.java
@@ -16,6 +16,7 @@
  */
 package org.apache.camel.management;
 
+import java.util.concurrent.ExecutorService;
 import java.util.concurrent.ThreadPoolExecutor;
 
 import org.apache.camel.CamelContext;
@@ -111,6 +112,7 @@ import 
org.apache.camel.management.mbean.ManagedTopicLoadBalancer;
 import org.apache.camel.management.mbean.ManagedTransformer;
 import org.apache.camel.management.mbean.ManagedUnmarshal;
 import org.apache.camel.management.mbean.ManagedValidate;
+import org.apache.camel.management.mbean.ManagedVirtualThreadExecutor;
 import org.apache.camel.management.mbean.ManagedWeightedLoadBalancer;
 import org.apache.camel.management.mbean.ManagedWireTapProcessor;
 import org.apache.camel.model.ExpressionNode;
@@ -284,6 +286,16 @@ public class DefaultManagementObjectStrategy implements 
ManagementObjectStrategy
         return mtp;
     }
 
+    @Override
+    public Object getManagedObjectForThreadPool(
+            CamelContext context, ExecutorService executorService,
+            String id, String sourceId, String routeId, String 
threadPoolProfileId) {
+        ManagedVirtualThreadExecutor mvte
+                = new ManagedVirtualThreadExecutor(context, executorService, 
id, sourceId, routeId, threadPoolProfileId);
+        mvte.init(context.getManagementStrategy());
+        return mvte;
+    }
+
     @Override
     public Object getManagedObjectForEventNotifier(CamelContext context, 
EventNotifier eventNotifier) {
         ManagedEventNotifier men = new ManagedEventNotifier(context, 
eventNotifier);
diff --git 
a/core/camel-management/src/main/java/org/apache/camel/management/JmxManagementLifecycleStrategy.java
 
b/core/camel-management/src/main/java/org/apache/camel/management/JmxManagementLifecycleStrategy.java
index f654deaa3eaf..b3629136e178 100644
--- 
a/core/camel-management/src/main/java/org/apache/camel/management/JmxManagementLifecycleStrategy.java
+++ 
b/core/camel-management/src/main/java/org/apache/camel/management/JmxManagementLifecycleStrategy.java
@@ -24,6 +24,7 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ExecutorService;
 import java.util.concurrent.ThreadPoolExecutor;
 
 import javax.management.JMException;
@@ -149,7 +150,7 @@ public class JmxManagementLifecycleStrategy extends 
ServiceSupport implements Li
     private final Set<String> knowRouteIds = new HashSet<>();
     private final Map<BacklogTracer, ManagedBacklogTracer> 
managedBacklogTracers = new HashMap<>();
     private final Map<DefaultBacklogDebugger, ManagedBacklogDebugger> 
managedBacklogDebuggers = new HashMap<>();
-    private final Map<ThreadPoolExecutor, Object> managedThreadPools = new 
HashMap<>();
+    private final Map<Object, Object> managedThreadPools = new HashMap<>();
 
     public JmxManagementLifecycleStrategy() {
     }
@@ -815,6 +816,62 @@ public class JmxManagementLifecycleStrategy extends 
ServiceSupport implements Li
         }
     }
 
+    @Override
+    public void onThreadPoolAdd(
+            CamelContext camelContext, ExecutorService executorService, String 
id,
+            String sourceId, String routeId, String threadPoolProfileId) {
+
+        if (!initialized) {
+            preServices
+                    .add(lf -> lf.onThreadPoolAdd(camelContext, 
executorService, id, sourceId, routeId,
+                            threadPoolProfileId));
+            return;
+        }
+
+        if (!shouldRegister(executorService, null)) {
+            return;
+        }
+
+        Object mtp = 
getManagementObjectStrategy().getManagedObjectForThreadPool(camelContext, 
executorService, id, sourceId,
+                routeId, threadPoolProfileId);
+        if (mtp == null) {
+            return;
+        }
+
+        if (getManagementStrategy().isManaged(mtp)) {
+            LOG.trace("The executor service is already managed: {}", 
executorService);
+            return;
+        }
+
+        try {
+            manageObject(mtp);
+            managedThreadPools.put(executorService, mtp);
+        } catch (Exception e) {
+            LOG.warn("Could not register executor service: {} as ThreadPool 
MBean.", executorService, e);
+        }
+    }
+
+    @Override
+    public void onThreadPoolRemove(CamelContext camelContext, ExecutorService 
executorService) {
+        if (!initialized) {
+            return;
+        }
+
+        Object mtp = managedThreadPools.remove(executorService);
+        if (mtp != null) {
+            if (!getManagementStrategy().isManaged(mtp)) {
+                LOG.trace("The executor service is not managed: {}", 
executorService);
+                return;
+            }
+
+            try {
+                unmanageObject(mtp);
+            } catch (Exception e) {
+                LOG.warn("Could not unregister ThreadPool MBean", e);
+            }
+        }
+    }
+
     @Override
     public void onRouteContextCreate(Route route) {
         // Create a map (ProcessorType -> PerformanceCounter)
diff --git 
a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedVirtualThreadExecutor.java
 
b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedVirtualThreadExecutor.java
new file mode 100644
index 000000000000..b403061726dc
--- /dev/null
+++ 
b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedVirtualThreadExecutor.java
@@ -0,0 +1,98 @@
+/*
+ * 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.management.mbean;
+
+import java.util.concurrent.ExecutorService;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.api.management.ManagedResource;
+import org.apache.camel.api.management.mbean.ManagedVirtualThreadExecutorMBean;
+import org.apache.camel.spi.ManagementStrategy;
+
+@ManagedResource(description = "Managed VirtualThread Executor")
+public class ManagedVirtualThreadExecutor implements 
ManagedVirtualThreadExecutorMBean {
+
+    private final CamelContext camelContext;
+    private final ExecutorService executorService;
+    private final String id;
+    private final String sourceId;
+    private final String routeId;
+    private final String threadPoolProfileId;
+
+    public ManagedVirtualThreadExecutor(CamelContext camelContext, 
ExecutorService executorService, String id,
+                                        String sourceId, String routeId, 
String threadPoolProfileId) {
+        this.camelContext = camelContext;
+        this.executorService = executorService;
+        this.id = id;
+        this.sourceId = sourceId;
+        this.routeId = routeId;
+        this.threadPoolProfileId = threadPoolProfileId;
+    }
+
+    public void init(ManagementStrategy strategy) {
+        // do nothing
+    }
+
+    public CamelContext getContext() {
+        return camelContext;
+    }
+
+    public ExecutorService getExecutorService() {
+        return executorService;
+    }
+
+    @Override
+    public String getCamelId() {
+        return camelContext.getName();
+    }
+
+    @Override
+    public String getCamelManagementName() {
+        return camelContext.getManagementName();
+    }
+
+    @Override
+    public String getId() {
+        return id;
+    }
+
+    @Override
+    public String getSourceId() {
+        return sourceId;
+    }
+
+    @Override
+    public String getRouteId() {
+        return routeId;
+    }
+
+    @Override
+    public String getThreadPoolProfileId() {
+        return threadPoolProfileId;
+    }
+
+    @Override
+    public boolean isVirtualThread() {
+        return true;
+    }
+
+    @Override
+    public boolean isShutdown() {
+        return executorService.isShutdown();
+    }
+
+}
diff --git 
a/core/camel-management/src/test/java/org/apache/camel/management/ManagedVirtualThreadExecutorTest.java
 
b/core/camel-management/src/test/java/org/apache/camel/management/ManagedVirtualThreadExecutorTest.java
new file mode 100644
index 000000000000..bc823a9f2adb
--- /dev/null
+++ 
b/core/camel-management/src/test/java/org/apache/camel/management/ManagedVirtualThreadExecutorTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.management;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.spi.LifecycleStrategy;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledForJreRange;
+import org.junit.jupiter.api.condition.JRE;
+
+import static 
org.apache.camel.management.DefaultManagementObjectNameStrategy.TYPE_THREAD_POOL;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@EnabledForJreRange(min = JRE.JAVA_21)
+public class ManagedVirtualThreadExecutorTest extends ManagementTestSupport {
+
+    private ExecutorService vte;
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        CamelContext ctx = super.createCamelContext();
+        // register the virtual thread executor during context creation 
(before it starts)
+        // so that shouldRegister returns true
+        vte = Executors.newVirtualThreadPerTaskExecutor();
+        for (LifecycleStrategy lifecycle : ctx.getLifecycleStrategies()) {
+            lifecycle.onThreadPoolAdd(ctx, vte, "myVirtualPool", "test", null, 
null);
+        }
+        return ctx;
+    }
+
+    @Test
+    public void testManagedVirtualThreadExecutor() throws Exception {
+        MBeanServer mbeanServer = getMBeanServer();
+
+        ObjectName on = getCamelObjectName(TYPE_THREAD_POOL, 
"myVirtualPool(test)");
+
+        Boolean shutdown = (Boolean) mbeanServer.getAttribute(on, "Shutdown");
+        assertFalse(shutdown.booleanValue());
+
+        Boolean virtualThread = (Boolean) mbeanServer.getAttribute(on, 
"VirtualThread");
+        assertTrue(virtualThread.booleanValue());
+
+        String id = (String) mbeanServer.getAttribute(on, "Id");
+        assertEquals("myVirtualPool", id);
+
+        String sourceId = (String) mbeanServer.getAttribute(on, "SourceId");
+        assertEquals("test", sourceId);
+
+        // cleanup
+        for (LifecycleStrategy lifecycle : context.getLifecycleStrategies()) {
+            lifecycle.onThreadPoolRemove(context, vte);
+        }
+        vte.shutdown();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:start").to("mock:result");
+            }
+        };
+    }
+}

Reply via email to