Skip to content

Commit

Permalink
FELIX-6720 enable virtual thread support in jetty12
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
paulrutter committed Jan 3, 2025
1 parent b9e7d1f commit 25bb6d4
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 6 deletions.
3 changes: 2 additions & 1 deletion http/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,8 @@ properties can be used (some legacy property names still exist but are not docum
| `org.apache.felix.jetty.alpn.defaultProtocol` | The default protocol when negotiation fails. Default is http/1.1. |
| `org.apache.felix.jakarta.websocket.enable` | Enables Jakarta websocket support. Default is false. |
| `org.apache.felix.jetty.websocket.enable` | Enables Jetty websocket support. Default is false. |
| `org.apache.felix.http.jetty.virtualthreads.enable` | Enables using virtual threads in Jetty 12 (JDK 21 required). Default is false. |
| `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). |
| `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. |
### Multiple Servers
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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.util.thread.ThreadPool;
import org.eclipse.jetty.util.thread.VirtualThreadPool;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
Expand Down Expand Up @@ -276,18 +277,28 @@ private void initializeJetty() throws Exception
{

final int threadPoolMax = this.config.getThreadPoolMax();
if (threadPoolMax >= 0) {
if (!this.config.isUseVirtualThreads() && threadPoolMax >= 0) {
this.server = new Server(new QueuedThreadPool(threadPoolMax));
} else if (this.config.isUseVirtualThreads()){
QueuedThreadPool threadPool = new QueuedThreadPool();
// See https://jetty.org/docs/jetty/12/programming-guide/arch/threads.html#thread-pool-virtual-threads
Method newVirtualThreadPerTaskExecutorMethod = null;
try {
newVirtualThreadPerTaskExecutorMethod = Executors.class.getMethod("newVirtualThreadPerTaskExecutor");
} catch (NoSuchMethodException e){
throw new IllegalArgumentException("Virtual threads are only available in Java 21 or later, or via preview flags in Java 17-20");
throw new IllegalArgumentException("Virtual threads are only available in Java 21 or later, or via preview flags in Java 19-20");
}
if (threadPoolMax >= 0) {
// Configurable, bounded, virtual thread executor
VirtualThreadPool threadPool = new VirtualThreadPool();
threadPool.setMaxThreads(threadPoolMax);
this.server = new Server(threadPool);
} else {
// Simple, unlimited, virtual thread Executor
QueuedThreadPool threadPool = new QueuedThreadPool();
final Executor virtualExecutor = (Executor) newVirtualThreadPerTaskExecutorMethod.invoke(null);
threadPool.setVirtualThreadsExecutor(virtualExecutor);
this.server = new Server(threadPool);
}
threadPool.setVirtualThreadsExecutor((Executor) newVirtualThreadPerTaskExecutorMethod.invoke(null));
this.server = new Server(threadPool);
} else {
this.server = new Server();
}
Expand Down Expand Up @@ -402,6 +413,9 @@ private void initializeJetty() throws Exception
ThreadPool.SizedThreadPool sizedThreadPool = (ThreadPool.SizedThreadPool) threadPool;
message.append("minThreads=").append(sizedThreadPool.getMinThreads()).append(",");
message.append("maxThreads=").append(sizedThreadPool.getMaxThreads()).append(",");
} else if (threadPool instanceof VirtualThreadPool) {
VirtualThreadPool sizedThreadPool = (VirtualThreadPool) threadPool;
message.append("maxVirtualThreads=").append(sizedThreadPool.getMaxThreads()).append(",");
}
Connector connector = this.server.getConnectors()[0];
if (connector instanceof ServerConnector) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* 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.felix.http.jetty.it;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
import static org.ops4j.pax.exam.cm.ConfigurationAdminOptions.newConfiguration;

import java.io.IOException;
import java.net.URI;
import java.util.Hashtable;
import java.util.Map;

import javax.inject.Inject;
import jakarta.servlet.Servlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import org.eclipse.jetty.client.ContentResponse;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.transport.HttpClientTransportOverHTTP;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.junit.PaxExam;
import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
import org.ops4j.pax.exam.spi.reactors.PerClass;
import org.osgi.framework.BundleContext;
import org.osgi.service.http.HttpService;
import org.osgi.service.servlet.whiteboard.HttpWhiteboardConstants;

@RunWith(PaxExam.class)
@ExamReactorStrategy(PerClass.class)
public class JettyVirtualThreadsThreadPoolIT extends JettyVirtualThreadsIT {
@Override
protected Option felixHttpConfig(int httpPort) {
return newConfiguration("org.apache.felix.http")
.put("org.osgi.service.http.port", httpPort)
.put("org.apache.felix.http.jetty.threadpool.max", 100)
.put("org.apache.felix.http.jetty.virtualthreads.enable", Boolean.TRUE.toString())
.asOption();
}
}

0 comments on commit 25bb6d4

Please sign in to comment.