You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2023/05/22 11:35:11 UTC

[tomcat] branch 9.0.x updated (dc3a6bbb21 -> 3c8db89331)

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

markt pushed a change to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


    from dc3a6bbb21 Fix back-port
     new 46aa047410 Back-port virtual thread support
     new 26bbf489b8 Add useVirtualThreads attribute to endpoints.
     new 3c8db89331 Add default prefix for virtual threads and document the default prefixes

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../apache/catalina/core/LocalStrings.properties   |   3 +
 .../core/StandardVirtualThreadExecutor.java        | 103 +++++++++++++++++++++
 .../org/apache/tomcat/util/compat/Jre21Compat.java |  82 ++++++++++++++++
 java/org/apache/tomcat/util/compat/JreCompat.java  |  43 ++++++++-
 .../tomcat/util/compat/LocalStrings.properties     |   4 +
 .../apache/tomcat/util/net/AbstractEndpoint.java   |  23 ++++-
 .../threads/VirtualThreadExecutor.java}            |  34 +++----
 webapps/docs/changelog.xml                         |   7 ++
 webapps/docs/config/ajp.xml                        |   7 ++
 webapps/docs/config/executor.xml                   |  32 ++++++-
 webapps/docs/config/http.xml                       |   7 ++
 11 files changed, 317 insertions(+), 28 deletions(-)
 create mode 100644 java/org/apache/catalina/core/StandardVirtualThreadExecutor.java
 create mode 100644 java/org/apache/tomcat/util/compat/Jre21Compat.java
 copy java/org/apache/tomcat/{websocket/WsExtension.java => util/threads/VirtualThreadExecutor.java} (59%)


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


[tomcat] 03/03: Add default prefix for virtual threads and document the default prefixes

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 3c8db89331f8209f608cdff1005577811ca0d0bf
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Sat May 13 13:29:57 2023 +0100

    Add default prefix for virtual threads and document the default prefixes
---
 java/org/apache/catalina/core/StandardVirtualThreadExecutor.java | 2 +-
 webapps/docs/config/executor.xml                                 | 6 ++++--
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/java/org/apache/catalina/core/StandardVirtualThreadExecutor.java b/java/org/apache/catalina/core/StandardVirtualThreadExecutor.java
index 9a1f254190..6580b079ed 100644
--- a/java/org/apache/catalina/core/StandardVirtualThreadExecutor.java
+++ b/java/org/apache/catalina/core/StandardVirtualThreadExecutor.java
@@ -35,7 +35,7 @@ public class StandardVirtualThreadExecutor extends LifecycleMBeanBase implements
 
     private String name;
     private java.util.concurrent.Executor executor;
-    private String namePrefix;
+    private String namePrefix = "tomcat-virt-";
 
     public void setName(String name) {
         this.name = name;
diff --git a/webapps/docs/config/executor.xml b/webapps/docs/config/executor.xml
index b5e48099f5..f0e7e0fa3e 100644
--- a/webapps/docs/config/executor.xml
+++ b/webapps/docs/config/executor.xml
@@ -95,7 +95,8 @@
     </attribute>
     <attribute name="namePrefix" required="false">
       <p>(String) The name prefix for each thread created by the executor.
-         The thread name for an individual thread will be <code>namePrefix+threadNumber</code></p>
+         The thread name for an individual thread will be <code>namePrefix+threadNumber</code>. The default value is
+         <code>tomcat-exec-</code>.</p>
     </attribute>
     <attribute name="maxThreads" required="false">
       <p>(int) The max number of active threads in this pool, default is <code>200</code></p>
@@ -136,7 +137,8 @@
   <attributes>
     <attribute name="namePrefix" required="false">
       <p>(String) The name prefix for each thread created by the executor.
-         The thread name for an individual thread will be <code>namePrefix+threadNumber</code></p>
+         The thread name for an individual thread will be <code>namePrefix+threadNumber</code>. The default value is
+         <code>tomcat-virt-</code></p>
     </attribute>
   </attributes>
 


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


[tomcat] 02/03: Add useVirtualThreads attribute to endpoints.

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 26bbf489b8a211652cb12b4aef4f0c66bfa461bd
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Fri May 12 17:30:27 2023 +0100

    Add useVirtualThreads attribute to endpoints.
    
    Refactor VirtualThreadExecutor so it can be used by the Endpoint
---
 .../apache/catalina/core/LocalStrings.properties   |  5 +--
 ...tor.java => StandardVirtualThreadExecutor.java} | 21 +++++++-----
 .../apache/tomcat/util/net/AbstractEndpoint.java   | 23 ++++++++++---
 .../tomcat/util/threads/VirtualThreadExecutor.java | 40 ++++++++++++++++++++++
 webapps/docs/changelog.xml                         |  4 +--
 webapps/docs/config/ajp.xml                        |  7 ++++
 webapps/docs/config/executor.xml                   |  4 +--
 webapps/docs/config/http.xml                       |  7 ++++
 8 files changed, 92 insertions(+), 19 deletions(-)

diff --git a/java/org/apache/catalina/core/LocalStrings.properties b/java/org/apache/catalina/core/LocalStrings.properties
index 878f3c047c..3067823783 100644
--- a/java/org/apache/catalina/core/LocalStrings.properties
+++ b/java/org/apache/catalina/core/LocalStrings.properties
@@ -288,6 +288,9 @@ standardService.stop.name=Stopping service [{0}]
 
 standardThreadExecutor.notStarted=The executor has not been started
 
+standardVirtualThreadExecutor.notStarted=The executor has not been started
+standardVirtualThreadExecutor.noVirtualThreads=Virtual threads require a minimum Java version of Java 21
+
 standardWrapper.allocate=Error allocating a servlet instance
 standardWrapper.allocateException=Allocate exception for servlet [{0}]
 standardWrapper.deallocateException=Deallocate exception for servlet [{0}]
@@ -311,5 +314,3 @@ standardWrapper.waiting=Waiting for [{0}] instance(s) to be deallocated for Serv
 
 threadLocalLeakPreventionListener.containerEvent.error=Exception processing container event [{0}]
 threadLocalLeakPreventionListener.lifecycleEvent.error=Exception processing lifecycle event [{0}]
-
-virtualThreadExecutor.noVirtualThreads=Virtual threads require a minimum Java version of Java 21
\ No newline at end of file
diff --git a/java/org/apache/catalina/core/VirtualThreadExecutor.java b/java/org/apache/catalina/core/StandardVirtualThreadExecutor.java
similarity index 80%
rename from java/org/apache/catalina/core/VirtualThreadExecutor.java
rename to java/org/apache/catalina/core/StandardVirtualThreadExecutor.java
index 496aeb3303..9a1f254190 100644
--- a/java/org/apache/catalina/core/VirtualThreadExecutor.java
+++ b/java/org/apache/catalina/core/StandardVirtualThreadExecutor.java
@@ -24,18 +24,17 @@ import org.apache.catalina.LifecycleState;
 import org.apache.catalina.util.LifecycleMBeanBase;
 import org.apache.tomcat.util.compat.JreCompat;
 import org.apache.tomcat.util.res.StringManager;
+import org.apache.tomcat.util.threads.VirtualThreadExecutor;
 
 /**
  * An executor that uses a new virtual thread for each task.
  */
-public class VirtualThreadExecutor extends LifecycleMBeanBase implements Executor {
+public class StandardVirtualThreadExecutor extends LifecycleMBeanBase implements Executor {
 
-    private static final StringManager sm = StringManager.getManager(VirtualThreadExecutor.class);
-
-    private final JreCompat jreCompat = JreCompat.getInstance();
+    private static final StringManager sm = StringManager.getManager(StandardVirtualThreadExecutor.class);
 
     private String name;
-    private Object threadBuilder;
+    private java.util.concurrent.Executor executor;
     private String namePrefix;
 
     public void setName(String name) {
@@ -57,7 +56,11 @@ public class VirtualThreadExecutor extends LifecycleMBeanBase implements Executo
 
     @Override
     public void execute(Runnable command) {
-        JreCompat.getInstance().threadBuilderStart(threadBuilder, command);
+        if (executor == null) {
+            throw new IllegalStateException(sm.getString("standardVirtualThreadExecutor.notStarted"));
+        } else {
+            executor.execute(command);
+        }
     }
 
 
@@ -71,19 +74,19 @@ public class VirtualThreadExecutor extends LifecycleMBeanBase implements Executo
     protected void initInternal() throws LifecycleException {
         super.initInternal();
         if (!JreCompat.isJre21Available()) {
-            throw new LifecycleException(sm.getString("virtualThreadExecutor.noVirtualThreads"));
+            throw new LifecycleException(sm.getString("standardVirtualThreadExecutor.noVirtualThreads"));
         }
     }
 
     @Override
     protected void startInternal() throws LifecycleException {
-        threadBuilder = jreCompat.createVirtualThreadBuilder(getNamePrefix());
+        executor = new VirtualThreadExecutor(getNamePrefix());
         setState(LifecycleState.STARTING);
     }
 
     @Override
     protected void stopInternal() throws LifecycleException {
-        threadBuilder = null;
+        executor = null;
         setState(LifecycleState.STOPPING);
     }
 
diff --git a/java/org/apache/tomcat/util/net/AbstractEndpoint.java b/java/org/apache/tomcat/util/net/AbstractEndpoint.java
index 2a14da3d45..603b1269f3 100644
--- a/java/org/apache/tomcat/util/net/AbstractEndpoint.java
+++ b/java/org/apache/tomcat/util/net/AbstractEndpoint.java
@@ -58,6 +58,7 @@ import org.apache.tomcat.util.threads.ResizableExecutor;
 import org.apache.tomcat.util.threads.TaskQueue;
 import org.apache.tomcat.util.threads.TaskThreadFactory;
 import org.apache.tomcat.util.threads.ThreadPoolExecutor;
+import org.apache.tomcat.util.threads.VirtualThreadExecutor;
 
 /**
  * @param <S> The type used by the socket wrapper associated with this endpoint.
@@ -631,6 +632,15 @@ public abstract class AbstractEndpoint<S,U> {
     public Executor getExecutor() { return executor; }
 
 
+    private boolean useVirtualThreads = false;
+    public void setUseVirtualThreads(boolean useVirtualThreads) {
+        this.useVirtualThreads = useVirtualThreads;
+    }
+    public boolean getVirtualThreads() {
+        return useVirtualThreads;
+    }
+
+
     /**
      * External Executor based thread pool for utility tasks.
      */
@@ -1070,12 +1080,17 @@ public abstract class AbstractEndpoint<S,U> {
 
     public void createExecutor() {
         internalExecutor = true;
-        TaskQueue taskqueue = new TaskQueue();
-        TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());
-        executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
-        taskqueue.setParent( (ThreadPoolExecutor) executor);
+        if (useVirtualThreads) {
+            executor = new VirtualThreadExecutor(getName() + "-exec-");
+        } else {
+            TaskQueue taskqueue = new TaskQueue();
+            TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());
+            executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
+            taskqueue.setParent( (ThreadPoolExecutor) executor);
+        }
     }
 
+
     public void shutdownExecutor() {
         Executor executor = this.executor;
         if (executor != null && internalExecutor) {
diff --git a/java/org/apache/tomcat/util/threads/VirtualThreadExecutor.java b/java/org/apache/tomcat/util/threads/VirtualThreadExecutor.java
new file mode 100644
index 0000000000..93a00f8d42
--- /dev/null
+++ b/java/org/apache/tomcat/util/threads/VirtualThreadExecutor.java
@@ -0,0 +1,40 @@
+/*
+ *  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.tomcat.util.threads;
+
+import java.util.concurrent.Executor;
+
+import org.apache.tomcat.util.compat.JreCompat;
+
+/**
+ * An executor that uses a new virtual thread for each task.
+ */
+public class VirtualThreadExecutor implements Executor {
+
+    private final JreCompat jreCompat = JreCompat.getInstance();
+
+    private Object threadBuilder;
+
+    public VirtualThreadExecutor(String namePrefix) {
+        threadBuilder = jreCompat.createVirtualThreadBuilder(namePrefix);
+    }
+
+    @Override
+    public void execute(Runnable command) {
+        jreCompat.threadBuilderStart(threadBuilder, command);
+    }
+}
\ No newline at end of file
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 34534cd5b4..fc0075a63a 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -117,8 +117,8 @@
         <code>start()</code>/<code>stop()</code> methods. (markt)
       </scode>
       <add>
-        Add <code>org.apache.catalina.core.VirtualThreadExecutor</code>, a
-        virtual thread based executor that may be used with one or more
+        Add <code>org.apache.catalina.core.StandardVirtualThreadExecutor</code>,
+        a virtual thread based executor that may be used with one or more
         Connectors to process requests received by those Connectors using
         virtual threads. This Executor requires a minimum Java version of Java
         21. (markt)
diff --git a/webapps/docs/config/ajp.xml b/webapps/docs/config/ajp.xml
index 976282f6f4..019108340c 100644
--- a/webapps/docs/config/ajp.xml
+++ b/webapps/docs/config/ajp.xml
@@ -573,6 +573,13 @@
       <code>false</code>.</p>
     </attribute>
 
+    <attribute name="useVirtualThreads" required="false">
+      <p>(bool) Use this attribute to enable or disable usage of virtual threads
+      with the internal executor. If an executor is associated with this
+      connector, this attribute is ignored. The default value is
+      <code>false</code>.</p>
+    </attribute>
+
   </attributes>
 
   </subsection>
diff --git a/webapps/docs/config/executor.xml b/webapps/docs/config/executor.xml
index 59cf023605..b5e48099f5 100644
--- a/webapps/docs/config/executor.xml
+++ b/webapps/docs/config/executor.xml
@@ -128,8 +128,8 @@
   <p>This implemtenation uses a new virtual thread to execute each task assigned to the Executor. This Executor requires
      a minimum Java version of Java 21.</p>
 
-  <p>The <code>className</code> attribute must be <code>org.apache.catalina.core.VirtualThreadExecutor</code> to use
-     this implementation.</p>
+  <p>The <code>className</code> attribute must be <code>org.apache.catalina.core.StandardVirtualThreadExecutor</code> to
+     use this implementation.</p>
 
   <p>The virtual thread implementation supports the follow attributes:</p>
 
diff --git a/webapps/docs/config/http.xml b/webapps/docs/config/http.xml
index 4ec2722b58..3a1f2b6aff 100644
--- a/webapps/docs/config/http.xml
+++ b/webapps/docs/config/http.xml
@@ -747,6 +747,13 @@
       Internet-Draft</a>. The default value is <code>true</code>.</p>
     </attribute>
 
+    <attribute name="useVirtualThreads" required="false">
+      <p>(bool) Use this attribute to enable or disable usage of virtual threads
+      with the internal executor. If an executor is associated with this
+      connector, this attribute is ignored. The default value is
+      <code>false</code>.</p>
+    </attribute>
+
   </attributes>
 
   </subsection>


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


[tomcat] 01/03: Back-port virtual thread support

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 46aa04741034e8d958544eaa464440d2f03f1236
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Fri May 12 09:54:55 2023 +0100

    Back-port virtual thread support
---
 .../apache/catalina/core/LocalStrings.properties   |   2 +
 .../catalina/core/VirtualThreadExecutor.java       | 100 +++++++++++++++++++++
 .../org/apache/tomcat/util/compat/Jre21Compat.java |  82 +++++++++++++++++
 java/org/apache/tomcat/util/compat/JreCompat.java  |  43 ++++++++-
 .../tomcat/util/compat/LocalStrings.properties     |   4 +
 webapps/docs/changelog.xml                         |   7 ++
 webapps/docs/config/executor.xml                   |  28 +++++-
 7 files changed, 263 insertions(+), 3 deletions(-)

diff --git a/java/org/apache/catalina/core/LocalStrings.properties b/java/org/apache/catalina/core/LocalStrings.properties
index c81a1a91a5..878f3c047c 100644
--- a/java/org/apache/catalina/core/LocalStrings.properties
+++ b/java/org/apache/catalina/core/LocalStrings.properties
@@ -311,3 +311,5 @@ standardWrapper.waiting=Waiting for [{0}] instance(s) to be deallocated for Serv
 
 threadLocalLeakPreventionListener.containerEvent.error=Exception processing container event [{0}]
 threadLocalLeakPreventionListener.lifecycleEvent.error=Exception processing lifecycle event [{0}]
+
+virtualThreadExecutor.noVirtualThreads=Virtual threads require a minimum Java version of Java 21
\ No newline at end of file
diff --git a/java/org/apache/catalina/core/VirtualThreadExecutor.java b/java/org/apache/catalina/core/VirtualThreadExecutor.java
new file mode 100644
index 0000000000..496aeb3303
--- /dev/null
+++ b/java/org/apache/catalina/core/VirtualThreadExecutor.java
@@ -0,0 +1,100 @@
+/*
+ *  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.catalina.core;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.catalina.Executor;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleState;
+import org.apache.catalina.util.LifecycleMBeanBase;
+import org.apache.tomcat.util.compat.JreCompat;
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * An executor that uses a new virtual thread for each task.
+ */
+public class VirtualThreadExecutor extends LifecycleMBeanBase implements Executor {
+
+    private static final StringManager sm = StringManager.getManager(VirtualThreadExecutor.class);
+
+    private final JreCompat jreCompat = JreCompat.getInstance();
+
+    private String name;
+    private Object threadBuilder;
+    private String namePrefix;
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    public String getNamePrefix() {
+        return namePrefix;
+    }
+
+    public void setNamePrefix(String namePrefix) {
+        this.namePrefix = namePrefix;
+    }
+
+    @Override
+    public void execute(Runnable command) {
+        JreCompat.getInstance().threadBuilderStart(threadBuilder, command);
+    }
+
+
+    @Override
+    public void execute(Runnable command, long timeout, TimeUnit unit) {
+        JreCompat.getInstance().threadBuilderStart(threadBuilder, command);
+    }
+
+
+    @Override
+    protected void initInternal() throws LifecycleException {
+        super.initInternal();
+        if (!JreCompat.isJre21Available()) {
+            throw new LifecycleException(sm.getString("virtualThreadExecutor.noVirtualThreads"));
+        }
+    }
+
+    @Override
+    protected void startInternal() throws LifecycleException {
+        threadBuilder = jreCompat.createVirtualThreadBuilder(getNamePrefix());
+        setState(LifecycleState.STARTING);
+    }
+
+    @Override
+    protected void stopInternal() throws LifecycleException {
+        threadBuilder = null;
+        setState(LifecycleState.STOPPING);
+    }
+
+    @Override
+    protected String getDomainInternal() {
+        // No way to navigate to Engine. Needs to have domain set.
+        return null;
+    }
+
+    @Override
+    protected String getObjectNameKeyProperties() {
+        return "type=Executor,name=" + getName();
+    }
+}
\ No newline at end of file
diff --git a/java/org/apache/tomcat/util/compat/Jre21Compat.java b/java/org/apache/tomcat/util/compat/Jre21Compat.java
new file mode 100644
index 0000000000..d06c8519a7
--- /dev/null
+++ b/java/org/apache/tomcat/util/compat/Jre21Compat.java
@@ -0,0 +1,82 @@
+/*
+ *  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.tomcat.util.compat;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.res.StringManager;
+
+public class Jre21Compat extends Jre19Compat {
+
+    private static final Log log = LogFactory.getLog(Jre21Compat.class);
+    private static final StringManager sm = StringManager.getManager(Jre21Compat.class);
+
+    private static final Method nameMethod;
+    private static final Method startMethod;
+    private static final Method ofVirtualMethod;
+
+
+    static {
+        Class<?> c1 = null;
+        Method m1 = null;
+        Method m2 = null;
+        Method m3 = null;
+
+        try {
+            c1 = Class.forName("java.lang.Thread$Builder");
+            m1 = c1.getMethod("name", String.class, long.class);
+            m2 = c1.getMethod("start", Runnable.class);
+            m3 = Thread.class.getMethod("ofVirtual", (Class<?>[]) null);
+        } catch (ClassNotFoundException e) {
+            // Must be pre-Java 21
+            log.debug(sm.getString("jre21Compat.javaPre21"), e);
+        } catch (ReflectiveOperationException e) {
+            // Should never happen
+            log.error(sm.getString("jre21Compat.unexpected"), e);
+        }
+        nameMethod = m1;
+        startMethod = m2;
+        ofVirtualMethod = m3;
+    }
+
+    static boolean isSupported() {
+        return ofVirtualMethod != null;
+    }
+
+    @Override
+    public Object createVirtualThreadBuilder(String name) {
+        try {
+            Object threadBuilder = ofVirtualMethod.invoke(null, (Object[]) null);
+            nameMethod.invoke(threadBuilder, name, Long.valueOf(0));
+            return threadBuilder;
+        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            throw new UnsupportedOperationException(e);
+        }
+    }
+
+    @Override
+    public void threadBuilderStart(Object threadBuilder, Runnable command) {
+        try {
+            startMethod.invoke(threadBuilder, command);
+        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            throw new UnsupportedOperationException(e);
+        }
+    }
+}
diff --git a/java/org/apache/tomcat/util/compat/JreCompat.java b/java/org/apache/tomcat/util/compat/JreCompat.java
index d468589002..c0ac778ba2 100644
--- a/java/org/apache/tomcat/util/compat/JreCompat.java
+++ b/java/org/apache/tomcat/util/compat/JreCompat.java
@@ -50,6 +50,7 @@ public class JreCompat {
     private static final boolean jre11Available;
     private static final boolean jre16Available;
     private static final boolean jre19Available;
+    private static final boolean jre21Available;
     private static final StringManager sm = StringManager.getManager(JreCompat.class);
 
     protected static final Method setApplicationProtocolsMethod;
@@ -69,23 +70,33 @@ public class JreCompat {
 
         // This is Tomcat 9 with a minimum Java version of Java 8.
         // Look for the highest supported JVM first
-        if (Jre19Compat.isSupported()) {
+        if (Jre21Compat.isSupported()) {
+            instance = new Jre21Compat();
+            jre21Available = true;
+            jre19Available = true;
+            jre16Available = true;
+            jre9Available = true;
+        } else if (Jre19Compat.isSupported()) {
             instance = new Jre19Compat();
+            jre21Available = false;
             jre19Available = true;
             jre16Available = true;
             jre9Available = true;
         } else if (Jre16Compat.isSupported()) {
             instance = new Jre16Compat();
+            jre21Available = false;
             jre19Available = false;
             jre16Available = true;
             jre9Available = true;
         } else if (Jre9Compat.isSupported()) {
             instance = new Jre9Compat();
+            jre21Available = false;
             jre19Available = false;
             jre16Available = false;
             jre9Available = true;
         } else {
             instance = new JreCompat();
+            jre21Available = false;
             jre19Available = false;
             jre16Available = false;
             jre9Available = false;
@@ -140,6 +151,11 @@ public class JreCompat {
     }
 
 
+    public static boolean isJre21Available() {
+        return jre21Available;
+    }
+
+
     // Java 8 implementation of Java 9 methods
 
     /**
@@ -405,4 +421,29 @@ public class JreCompat {
 
         return result;
     }
+
+
+    // Java 8 implementations of Java 21 methods
+
+    /**
+     * Create a thread builder for virtual threads using the given name to name the threads.
+     *
+     * @param name The base name for the threads
+     *
+     * @return The thread buidler for virtual threads
+     */
+    public Object createVirtualThreadBuilder(String name) {
+        throw new UnsupportedOperationException(sm.getString("jreCompat.noVirtualThreads"));
+    }
+
+
+    /**
+     * Create a thread with the given thread builder and use it to execute the given runnable.
+     *
+     * @param threadBuilder The thread builder to use to create a thread
+     * @param command       The command to run
+     */
+    public void threadBuilderStart(Object threadBuilder, Runnable command) {
+        throw new UnsupportedOperationException(sm.getString("jreCompat.noVirtualThreads"));
+    }
 }
diff --git a/java/org/apache/tomcat/util/compat/LocalStrings.properties b/java/org/apache/tomcat/util/compat/LocalStrings.properties
index c4c2f7d159..1bbb424956 100644
--- a/java/org/apache/tomcat/util/compat/LocalStrings.properties
+++ b/java/org/apache/tomcat/util/compat/LocalStrings.properties
@@ -18,6 +18,9 @@ jre16Compat.unexpected=Failed to create references to Java 16 classes and method
 
 jre19Compat.javaPre19=Class not found so assuming code is running on a pre-Java 19 JVM
 
+jre21Compat.javaPre21=Class not found so assuming code is running on a pre-Java 21 JVM
+jre21Compat.unexpected=Failed to create references to Java 21 classes and methods
+
 jre9Compat.invalidModuleUri=The module URI provided [{0}] could not be converted to a URL for the JarScanner to process
 jre9Compat.javaPre9=Class not found so assuming code is running on a pre-Java 9 JVM
 jre9Compat.unexpected=Failed to create references to Java 9 classes and methods
@@ -25,3 +28,4 @@ jre9Compat.unexpected=Failed to create references to Java 9 classes and methods
 jreCompat.noApplicationProtocol=Java Runtime does not support SSLEngine.getApplicationProtocol(). You must use Java 9 to use this feature.
 jreCompat.noApplicationProtocols=Java Runtime does not support SSLParameters.setApplicationProtocols(). You must use Java 9 to use this feature.
 jreCompat.noUnixDomainSocket=Java Runtime does not support Unix domain sockets. You must use Java 16 to use this feature.
+jreCompat.noVirtualThreads=Java Runtime does not support virtual threads. You must use Java 21 or later to use this feature.
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 978c599ab9..34534cd5b4 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -116,6 +116,13 @@
         <code>init()</code>/<code>destroy()</code> methods of components to the
         <code>start()</code>/<code>stop()</code> methods. (markt)
       </scode>
+      <add>
+        Add <code>org.apache.catalina.core.VirtualThreadExecutor</code>, a
+        virtual thread based executor that may be used with one or more
+        Connectors to process requests received by those Connectors using
+        virtual threads. This Executor requires a minimum Java version of Java
+        21. (markt)
+      </add>
     </changelog>
   </subsection>
   <subsection name="Coyote">
diff --git a/webapps/docs/config/executor.xml b/webapps/docs/config/executor.xml
index 05b1420b8e..59cf023605 100644
--- a/webapps/docs/config/executor.xml
+++ b/webapps/docs/config/executor.xml
@@ -77,8 +77,12 @@
 
   <subsection name="Standard Implementation">
 
-  <p>
-  The default implementation supports the following attributes:</p>
+  <p>This implemtenation uses a pool of platform threads to execute the tasks assigned to the Executor.</p>
+
+  <p>The <code>className</code> attribute must be <code>org.apache.catalina.core.StandardThreadExecutor</code> to use
+     this implementation.</p>
+
+  <p>The standard implementation supports the following attributes:</p>
 
   <attributes>
 
@@ -118,6 +122,26 @@
 
 
   </subsection>
+
+  <subsection name="Virtual Thread Implementation">
+
+  <p>This implemtenation uses a new virtual thread to execute each task assigned to the Executor. This Executor requires
+     a minimum Java version of Java 21.</p>
+
+  <p>The <code>className</code> attribute must be <code>org.apache.catalina.core.VirtualThreadExecutor</code> to use
+     this implementation.</p>
+
+  <p>The virtual thread implementation supports the follow attributes:</p>
+
+  <attributes>
+    <attribute name="namePrefix" required="false">
+      <p>(String) The name prefix for each thread created by the executor.
+         The thread name for an individual thread will be <code>namePrefix+threadNumber</code></p>
+    </attribute>
+  </attributes>
+
+  </subsection>
+
 </section>
 
 


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org