Skip to content

Commit 25bb6d4

Browse files
committed
FELIX-6720 enable virtual thread support in jetty12
- Use org.apache.felix.http.jetty.threadpool.max for virtual threads as well, if configured - Use `VirtualThreadPool` for bounded virtual threads - Document `org.apache.felix.http.jetty.threadpool.max` option - Correct logging for virtual threads - Correct JDK version message to 19 - Add threadpool.max to new IT See https://jetty.org/docs/jetty/12/programming-guide/arch/threads.html#thread-pool-virtual-threads
1 parent b9e7d1f commit 25bb6d4

3 files changed

Lines changed: 81 additions & 6 deletions

File tree

http/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,8 @@ properties can be used (some legacy property names still exist but are not docum
440440
| `org.apache.felix.jetty.alpn.defaultProtocol` | The default protocol when negotiation fails. Default is http/1.1. |
441441
| `org.apache.felix.jakarta.websocket.enable` | Enables Jakarta websocket support. Default is false. |
442442
| `org.apache.felix.jetty.websocket.enable` | Enables Jetty websocket support. Default is false. |
443-
| `org.apache.felix.http.jetty.virtualthreads.enable` | Enables using virtual threads in Jetty 12 (JDK 21 required). Default is false. |
443+
| `org.apache.felix.http.jetty.threadpool.max` | The maximum number of threads in the Jetty thread pool. Default is unlimited. Works for both platform threads and virtual threads (Jetty 12 only). |
444+
| `org.apache.felix.http.jetty.virtualthreads.enable` | Enables using virtual threads in Jetty 12 (JDK 21 required). Default is false. When enabled, `org.apache.felix.http.jetty.threadpool.max` is used for a bounded virtual thread pool. |
444445
### Multiple Servers
445446
446447
It is possible to configure several Http Services, each running on a different port. The first service can be configured as outlined above using the service PID for `"org.apache.felix.http"`. Additional servers can be configured through OSGi factory configurations using `"org.apache.felix.http"` as the factory PID. The properties for the configuration are outlined as above.

http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/JettyService.java

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
import org.eclipse.jetty.util.ssl.SslContextFactory;
6363
import org.eclipse.jetty.util.thread.QueuedThreadPool;
6464
import org.eclipse.jetty.util.thread.ThreadPool;
65+
import org.eclipse.jetty.util.thread.VirtualThreadPool;
6566
import org.osgi.framework.Bundle;
6667
import org.osgi.framework.BundleContext;
6768
import org.osgi.framework.Constants;
@@ -276,18 +277,28 @@ private void initializeJetty() throws Exception
276277
{
277278

278279
final int threadPoolMax = this.config.getThreadPoolMax();
279-
if (threadPoolMax >= 0) {
280+
if (!this.config.isUseVirtualThreads() && threadPoolMax >= 0) {
280281
this.server = new Server(new QueuedThreadPool(threadPoolMax));
281282
} else if (this.config.isUseVirtualThreads()){
282-
QueuedThreadPool threadPool = new QueuedThreadPool();
283+
// See https://jetty.org/docs/jetty/12/programming-guide/arch/threads.html#thread-pool-virtual-threads
283284
Method newVirtualThreadPerTaskExecutorMethod = null;
284285
try {
285286
newVirtualThreadPerTaskExecutorMethod = Executors.class.getMethod("newVirtualThreadPerTaskExecutor");
286287
} catch (NoSuchMethodException e){
287-
throw new IllegalArgumentException("Virtual threads are only available in Java 21 or later, or via preview flags in Java 17-20");
288+
throw new IllegalArgumentException("Virtual threads are only available in Java 21 or later, or via preview flags in Java 19-20");
289+
}
290+
if (threadPoolMax >= 0) {
291+
// Configurable, bounded, virtual thread executor
292+
VirtualThreadPool threadPool = new VirtualThreadPool();
293+
threadPool.setMaxThreads(threadPoolMax);
294+
this.server = new Server(threadPool);
295+
} else {
296+
// Simple, unlimited, virtual thread Executor
297+
QueuedThreadPool threadPool = new QueuedThreadPool();
298+
final Executor virtualExecutor = (Executor) newVirtualThreadPerTaskExecutorMethod.invoke(null);
299+
threadPool.setVirtualThreadsExecutor(virtualExecutor);
300+
this.server = new Server(threadPool);
288301
}
289-
threadPool.setVirtualThreadsExecutor((Executor) newVirtualThreadPerTaskExecutorMethod.invoke(null));
290-
this.server = new Server(threadPool);
291302
} else {
292303
this.server = new Server();
293304
}
@@ -402,6 +413,9 @@ private void initializeJetty() throws Exception
402413
ThreadPool.SizedThreadPool sizedThreadPool = (ThreadPool.SizedThreadPool) threadPool;
403414
message.append("minThreads=").append(sizedThreadPool.getMinThreads()).append(",");
404415
message.append("maxThreads=").append(sizedThreadPool.getMaxThreads()).append(",");
416+
} else if (threadPool instanceof VirtualThreadPool) {
417+
VirtualThreadPool sizedThreadPool = (VirtualThreadPool) threadPool;
418+
message.append("maxVirtualThreads=").append(sizedThreadPool.getMaxThreads()).append(",");
405419
}
406420
Connector connector = this.server.getConnectors()[0];
407421
if (connector instanceof ServerConnector) {
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.felix.http.jetty.it;
18+
19+
import static org.junit.Assert.assertEquals;
20+
import static org.junit.Assert.assertNotNull;
21+
import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
22+
import static org.ops4j.pax.exam.cm.ConfigurationAdminOptions.newConfiguration;
23+
24+
import java.io.IOException;
25+
import java.net.URI;
26+
import java.util.Hashtable;
27+
import java.util.Map;
28+
29+
import javax.inject.Inject;
30+
import jakarta.servlet.Servlet;
31+
import jakarta.servlet.http.HttpServlet;
32+
import jakarta.servlet.http.HttpServletRequest;
33+
import jakarta.servlet.http.HttpServletResponse;
34+
35+
import org.eclipse.jetty.client.ContentResponse;
36+
import org.eclipse.jetty.client.HttpClient;
37+
import org.eclipse.jetty.client.transport.HttpClientTransportOverHTTP;
38+
import org.junit.Before;
39+
import org.junit.Test;
40+
import org.junit.runner.RunWith;
41+
import org.ops4j.pax.exam.Option;
42+
import org.ops4j.pax.exam.junit.PaxExam;
43+
import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
44+
import org.ops4j.pax.exam.spi.reactors.PerClass;
45+
import org.osgi.framework.BundleContext;
46+
import org.osgi.service.http.HttpService;
47+
import org.osgi.service.servlet.whiteboard.HttpWhiteboardConstants;
48+
49+
@RunWith(PaxExam.class)
50+
@ExamReactorStrategy(PerClass.class)
51+
public class JettyVirtualThreadsThreadPoolIT extends JettyVirtualThreadsIT {
52+
@Override
53+
protected Option felixHttpConfig(int httpPort) {
54+
return newConfiguration("org.apache.felix.http")
55+
.put("org.osgi.service.http.port", httpPort)
56+
.put("org.apache.felix.http.jetty.threadpool.max", 100)
57+
.put("org.apache.felix.http.jetty.virtualthreads.enable", Boolean.TRUE.toString())
58+
.asOption();
59+
}
60+
}

0 commit comments

Comments
 (0)