diff --git a/bom/pom.xml b/bom/pom.xml
index 9131a686c2..3864b913a3 100644
--- a/bom/pom.xml
+++ b/bom/pom.xml
@@ -83,6 +83,11 @@
jersey-jetty-connector
${project.version}
+
+ org.glassfish.jersey.connectors
+ jersey-jetty-http2-connector
+ ${project.version}
+
org.glassfish.jersey.connectors
jersey-jdk-connector
diff --git a/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainer.java b/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainer.java
index 78d16058c1..8a5366e141 100644
--- a/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainer.java
+++ b/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2023 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
diff --git a/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerProvider.java b/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerProvider.java
index 7c50c759b7..289b022eef 100644
--- a/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerProvider.java
+++ b/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2023 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -39,4 +39,12 @@ public T createContainer(final Class type, final Application application)
return null;
}
+ public T createContainer(final Class type, final Application application,
+ Object parentContext) throws ProcessingException {
+ if (Handler.class == type || JettyHttpContainer.class == type) {
+ return type.cast(new JettyHttpContainer(application, parentContext));
+ }
+ return null;
+ }
+
}
diff --git a/containers/jetty-http2/pom.xml b/containers/jetty-http2/pom.xml
new file mode 100644
index 0000000000..503d6ec780
--- /dev/null
+++ b/containers/jetty-http2/pom.xml
@@ -0,0 +1,112 @@
+
+
+
+
+ 4.0.0
+
+
+ project
+ org.glassfish.jersey.containers
+ 2.40-SNAPSHOT
+
+
+ jersey-container-jetty-http2
+ jar
+ jersey-container-jetty-http2
+
+ Jetty Http2 Container
+
+
+
+ org.glassfish.jersey.containers
+ jersey-container-jetty-http
+ ${project.version}
+
+
+ org.glassfish.hk2.external
+ jakarta.inject
+
+
+ org.eclipse.jetty
+ jetty-server
+
+
+ org.eclipse.jetty
+ jetty-util
+
+
+ org.eclipse.jetty
+ jetty-continuation
+
+
+ org.eclipse.jetty.http2
+ http2-server
+
+
+ org.eclipse.jetty
+ jetty-alpn-conscrypt-server
+
+
+ org.apache.httpcomponents
+ httpclient
+ test
+
+
+ org.hamcrest
+ hamcrest
+ test
+
+
+
+
+
+
+ com.sun.istack
+ istack-commons-maven-plugin
+ true
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ true
+
+
+ org.apache.felix
+ maven-bundle-plugin
+ true
+
+
+
+ ${jetty.osgi.version},
+ *
+
+
+
+
+
+
+
+
+ ${basedir}/src/main/resources
+ true
+
+
+
+
+
diff --git a/containers/jetty-http2/src/main/java/org/glassfish/jersey/jetty/http2/JettyHttp2ContainerFactory.java b/containers/jetty-http2/src/main/java/org/glassfish/jersey/jetty/http2/JettyHttp2ContainerFactory.java
new file mode 100644
index 0000000000..8871e4c929
--- /dev/null
+++ b/containers/jetty-http2/src/main/java/org/glassfish/jersey/jetty/http2/JettyHttp2ContainerFactory.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.jetty.http2;
+
+import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
+import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
+import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
+import org.eclipse.jetty.server.ConnectionFactory;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.SslConnectionFactory;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.glassfish.jersey.jetty.JettyHttpContainer;
+import org.glassfish.jersey.jetty.JettyHttpContainerFactory;
+import org.glassfish.jersey.jetty.JettyHttpContainerProvider;
+import org.glassfish.jersey.jetty.http2.LocalizationMessages;
+import org.glassfish.jersey.server.ContainerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import javax.ws.rs.ProcessingException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+public final class JettyHttp2ContainerFactory {
+
+ private JettyHttp2ContainerFactory() {
+
+ }
+
+ /**
+ * Creates HTTP/2 enabled {@link Server} instance that registers an {@link org.eclipse.jetty.server.Handler}.
+ *
+ * @param uri uri on which the {@link org.glassfish.jersey.server.ApplicationHandler} will be deployed. Only first path
+ * segment will be used as context path, the rest will be ignored.
+ * @return newly created {@link Server}.
+ *
+ * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
+ * @throws IllegalArgumentException if {@code uri} is {@code null}.
+ */
+ public static Server createHttp2Server(final URI uri) throws ProcessingException {
+ return createHttp2Server(uri, null, null, true);
+ }
+
+ /**
+ * Create HTTP/2 enabled {@link Server} that registers an {@link org.eclipse.jetty.server.Handler} that
+ * in turn manages all root resource and provider classes declared by the
+ * resource configuration.
+ *
+ * This implementation defers to the
+ * {@link org.glassfish.jersey.server.ContainerFactory#createContainer(Class, javax.ws.rs.core.Application)} method
+ * for creating an Container that manages the root resources.
+ *
+ * @param uri URI on which the Jersey web application will be deployed. Only first path segment will be
+ * used as context path, the rest will be ignored.
+ * @param configuration web application configuration.
+ * @param start if set to false, server will not get started, which allows to configure the underlying
+ * transport layer, see above for details.
+ * @return newly created {@link Server}.
+ *
+ * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
+ * @throws IllegalArgumentException if {@code uri} is {@code null}.
+ */
+ public static Server createHttp2Server(final URI uri, final ResourceConfig configuration, final boolean start)
+ throws ProcessingException {
+ return createHttp2Server(uri, null,
+ ContainerFactory.createContainer(JettyHttpContainer.class, configuration), start);
+ }
+
+ /**
+ * Creates HTTP/2 enabled {@link Server} instance that registers an {@link org.eclipse.jetty.server.Handler}.
+ *
+ * @param uri uri on which the {@link org.glassfish.jersey.server.ApplicationHandler} will be deployed. Only first path
+ * segment will be used as context path, the rest will be ignored.
+ * @param start if set to false, server will not get started, which allows to configure the underlying transport
+ * layer, see above for details.
+ * @return newly created {@link Server}.
+ *
+ * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
+ * @throws IllegalArgumentException if {@code uri} is {@code null}.
+ *
+ * @since 2.40
+ */
+
+ public static Server createHttp2Server(final URI uri, final boolean start) throws ProcessingException {
+ return createHttp2Server(uri, null, null, start);
+ }
+
+ /**
+ * Create HTTP/2 enabled {@link Server} that registers an {@link org.eclipse.jetty.server.Handler} that
+ * in turn manages all root resource and provider classes declared by the
+ * resource configuration.
+ *
+ * @param uri the URI to create the http server. The URI scheme must be
+ * equal to "https". The URI user information and host
+ * are ignored If the URI port is not present then port 143 will be
+ * used. The URI path, query and fragment components are ignored.
+ * @param config the resource configuration.
+ * @param parentContext DI provider specific context with application's registered bindings.
+ * @param start if set to false, server will not get started, this allows end users to set
+ * additional properties on the underlying listener.
+ * @return newly created {@link Server}.
+ *
+ * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
+ * @throws IllegalArgumentException if {@code uri} is {@code null}.
+ * @see JettyHttpContainer
+ *
+ * @since 2.40
+ */
+ public static Server createHttp2Server(final URI uri, final ResourceConfig config, final boolean start,
+ final Object parentContext) {
+ return createHttp2Server(uri, null,
+ new JettyHttpContainerProvider().createContainer(JettyHttpContainer.class,
+ config, parentContext), start);
+ }
+
+ /**
+ * Create HTTP/2 enabled {@link Server} that registers an {@link org.eclipse.jetty.server.Handler} that
+ * in turn manages all root resource and provider classes found by searching the
+ * classes referenced in the java classpath.
+ *
+ * @param uri the URI to create the http server. The URI scheme must be
+ * equal to {@code https}. The URI user information and host
+ * are ignored. If the URI port is not present then port
+ * {@value org.glassfish.jersey.server.spi.Container#DEFAULT_HTTPS_PORT} will be
+ * used. The URI path, query and fragment components are ignored.
+ * @param sslContextFactory this is the SSL context factory used to configure SSL connector
+ * @param handler the container that handles all HTTP requests
+ * @param start if set to false, server will not get started, this allows end users to set
+ * additional properties on the underlying listener.
+ * @return newly created {@link Server}.
+ *
+ * @throws ProcessingException in case of any failure when creating a new Jetty {@code Server} instance.
+ * @throws IllegalArgumentException if {@code uri} is {@code null}.
+ * @see JettyHttpContainer
+ *
+ * @since 2.40
+ */
+ public static Server createHttp2Server(final URI uri,
+ final SslContextFactory sslContextFactory,
+ final JettyHttpContainer handler,
+ final boolean start) {
+
+ /**
+ * Creating basic Jetty HTTP/1.1 container (but always not started)
+ */
+ final Server server = JettyHttpContainerFactory.createServer(uri, sslContextFactory, handler, false);
+ /**
+ * Obtain configured HTTP connection factory
+ */
+ final ServerConnector httpServerConnector = (ServerConnector) server.getConnectors()[0];
+ final HttpConnectionFactory httpConnectionFactory = httpServerConnector.getConnectionFactory(HttpConnectionFactory.class);
+
+ /**
+ * Obtain prepared config
+ */
+ final HttpConfiguration config = httpConnectionFactory.getHttpConfiguration();
+
+ /**
+ * Add required H2/H2C connection factories using pre-configured config from the HTTP/1.1 server
+ */
+ final List factories = getConnectionFactories(config, sslContextFactory);
+ factories.add(httpConnectionFactory);
+
+ /**
+ * replacing connectors for H2/H2C protocol
+ */
+ httpServerConnector.setConnectionFactories(factories);
+ server.setConnectors(new Connector[]{httpServerConnector});
+
+ /**
+ * Starting the server if required
+ */
+ if (start) {
+ try {
+ // Start the server.
+ server.start();
+ } catch (final Exception e) {
+ throw new ProcessingException(LocalizationMessages.ERROR_WHEN_CREATING_SERVER(), e);
+ }
+ }
+ return server;
+ }
+
+ private static List getConnectionFactories(final HttpConfiguration config,
+ final SslContextFactory sslContextFactory) {
+ final List factories = new ArrayList<>();
+ if (sslContextFactory != null) {
+ factories.add(new HTTP2ServerConnectionFactory(config));
+ final ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
+ alpn.setDefaultProtocol("h2");
+ factories.add(new SslConnectionFactory(sslContextFactory, alpn.getProtocol()));
+ factories.add(alpn);
+ } else {
+ factories.add(new HTTP2CServerConnectionFactory(config));
+ }
+
+ return factories;
+ }
+}
diff --git a/containers/jetty-http2/src/main/java/org/glassfish/jersey/jetty/http2/JettyHttp2ContainerProvider.java b/containers/jetty-http2/src/main/java/org/glassfish/jersey/jetty/http2/JettyHttp2ContainerProvider.java
new file mode 100644
index 0000000000..166f02d048
--- /dev/null
+++ b/containers/jetty-http2/src/main/java/org/glassfish/jersey/jetty/http2/JettyHttp2ContainerProvider.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.jetty.http2;
+
+import org.eclipse.jetty.server.Handler;
+import org.glassfish.jersey.jetty.JettyHttpContainer;
+import org.glassfish.jersey.jetty.JettyHttpContainerProvider;
+import org.glassfish.jersey.server.spi.ContainerProvider;
+
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.core.Application;
+
+public final class JettyHttp2ContainerProvider implements ContainerProvider {
+
+ @Override
+ public T createContainer(final Class type, final Application application) throws ProcessingException {
+ if (Handler.class == type || JettyHttpContainer.class == type) {
+ return type.cast(new JettyHttpContainerProvider().createContainer(JettyHttpContainer.class,
+ application));
+ }
+ return null;
+ }
+}
+
diff --git a/containers/jetty-http2/src/main/java/org/glassfish/jersey/jetty/http2/package-info.java b/containers/jetty-http2/src/main/java/org/glassfish/jersey/jetty/http2/package-info.java
new file mode 100644
index 0000000000..a402b27115
--- /dev/null
+++ b/containers/jetty-http2/src/main/java/org/glassfish/jersey/jetty/http2/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/**
+ * Jersey Jetty HTTP2 container classes.
+ */
+package org.glassfish.jersey.jetty.http2;
diff --git a/containers/jetty-http2/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.ContainerProvider b/containers/jetty-http2/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.ContainerProvider
new file mode 100644
index 0000000000..4d2a88dd12
--- /dev/null
+++ b/containers/jetty-http2/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.ContainerProvider
@@ -0,0 +1 @@
+org.glassfish.jersey.jetty.http2.JettyHttp2ContainerProvider
\ No newline at end of file
diff --git a/containers/jetty-http2/src/main/resources/org/glassfish/jersey/jetty/http2/localization.properties b/containers/jetty-http2/src/main/resources/org/glassfish/jersey/jetty/http2/localization.properties
new file mode 100644
index 0000000000..9728b70270
--- /dev/null
+++ b/containers/jetty-http2/src/main/resources/org/glassfish/jersey/jetty/http2/localization.properties
@@ -0,0 +1,18 @@
+#
+# Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License v. 2.0, which is available at
+# http://www.eclipse.org/legal/epl-2.0.
+#
+# This Source Code may also be made available under the following Secondary
+# Licenses when the conditions for such availability set forth in the
+# Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+# version 2 with the GNU Classpath Exception, which is available at
+# https://www.gnu.org/software/classpath/license.html.
+#
+# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+#
+
+# {0} - status code; {1} - status reason message
+error.when.creating.server=Exception thrown when trying to create jetty server.
\ No newline at end of file
diff --git a/containers/pom.xml b/containers/pom.xml
index 37c90d51a6..1fd172034f 100644
--- a/containers/pom.xml
+++ b/containers/pom.xml
@@ -41,6 +41,7 @@
jersey-servlet-core
jersey-servlet
jetty-http
+ jetty-http2
jetty-servlet
netty-http
simple-http
diff --git a/pom.xml b/pom.xml
index a4bfb4f87f..d1d4c23a78 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1720,6 +1720,16 @@
jetty-continuation
${jetty.version}
+
+ org.eclipse.jetty.http2
+ http2-server
+ ${jetty.version}
+
+
+ org.eclipse.jetty
+ jetty-alpn-conscrypt-server
+ ${jetty.version}
+
org.simpleframework
diff --git a/test-framework/providers/jetty-http2/pom.xml b/test-framework/providers/jetty-http2/pom.xml
new file mode 100644
index 0000000000..afb4eb54c1
--- /dev/null
+++ b/test-framework/providers/jetty-http2/pom.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+ project
+ org.glassfish.jersey.test-framework.providers
+ 2.40-SNAPSHOT
+
+ 4.0.0
+
+ jersey-test-framework-provider-jetty-http2
+ jar
+ jersey-test-framework-provider-jetty-http2
+
+ Jersey Test Framework - Jetty HTTP2 container
+
+
+
+ org.glassfish.jersey.test-framework
+ jersey-test-framework-core
+ ${project.version}
+
+
+ org.glassfish.jersey.containers
+ jersey-container-jetty-http2
+ ${project.version}
+
+
+
+
diff --git a/test-framework/providers/jetty-http2/src/main/java/org/glassfish/jersey/test/jetty/http2/JettyHttp2TestContainerFactory.java b/test-framework/providers/jetty-http2/src/main/java/org/glassfish/jersey/test/jetty/http2/JettyHttp2TestContainerFactory.java
new file mode 100644
index 0000000000..85a673c8bc
--- /dev/null
+++ b/test-framework/providers/jetty-http2/src/main/java/org/glassfish/jersey/test/jetty/http2/JettyHttp2TestContainerFactory.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.test.jetty.http2;
+
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jetty.http2.JettyHttp2ContainerFactory;
+import org.glassfish.jersey.test.DeploymentContext;
+import org.glassfish.jersey.test.spi.TestContainer;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.glassfish.jersey.test.spi.TestHelper;
+
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+
+/**
+ * Factory for testing {@link JettyHttp2ContainerFactory}.
+ *
+ */
+public final class JettyHttp2TestContainerFactory implements TestContainerFactory {
+
+ private static class JettyHttp2TestContainer implements TestContainer {
+
+ private static final Logger LOGGER = Logger.getLogger(JettyHttp2TestContainer.class.getName());
+
+ private URI baseUri;
+ private final Server server;
+
+ private JettyHttp2TestContainer(final URI baseUri, final DeploymentContext context) {
+ final URI base = UriBuilder.fromUri(baseUri).path(context.getContextPath()).build();
+
+ if (!"/".equals(base.getRawPath())) {
+ throw new TestContainerException(String.format(
+ "Cannot deploy on %s. Jetty HTTP2 container only supports deployment on root path.",
+ base.getRawPath()));
+ }
+
+ this.baseUri = base;
+
+ if (LOGGER.isLoggable(Level.INFO)) {
+ LOGGER.info("Creating JettyHttp2TestContainer configured at the base URI "
+ + TestHelper.zeroPortToAvailablePort(baseUri));
+ }
+
+ this.server = JettyHttp2ContainerFactory.createHttp2Server(this.baseUri, context.getResourceConfig(), false);
+ }
+
+ @Override
+ public ClientConfig getClientConfig() {
+ return null;
+ }
+
+ @Override
+ public URI getBaseUri() {
+ return baseUri;
+ }
+
+ @Override
+ public void start() {
+ if (server.isStarted()) {
+ LOGGER.log(Level.WARNING, "Ignoring start request - JettyHttp2TestContainer is already started.");
+ } else {
+ LOGGER.log(Level.FINE, "Starting JettyHttp2TestContainer...");
+ try {
+ server.start();
+
+ if (baseUri.getPort() == 0) {
+ int port = 0;
+ for (final Connector connector : server.getConnectors()) {
+ if (connector instanceof ServerConnector) {
+ port = ((ServerConnector) connector).getLocalPort();
+ break;
+ }
+ }
+
+ baseUri = UriBuilder.fromUri(baseUri).port(port).build();
+
+ LOGGER.log(Level.INFO, "Started JettyHttp2TestContainer at the base URI " + baseUri);
+ }
+ } catch (Exception e) {
+ throw new TestContainerException(e);
+ }
+ }
+ }
+
+ @Override
+ public void stop() {
+ if (server.isStarted()) {
+ LOGGER.log(Level.FINE, "Stopping JettyHttp2TestContainer...");
+ try {
+ this.server.stop();
+ } catch (Exception ex) {
+ LOGGER.log(Level.WARNING, "Error Stopping JettyHttp2TestContainer...", ex);
+ }
+ } else {
+ LOGGER.log(Level.WARNING, "Ignoring stop request - JettyHttp2TestContainer is already stopped.");
+ }
+ }
+ }
+
+ @Override
+ public TestContainer create(final URI baseUri, final DeploymentContext context) throws IllegalArgumentException {
+ return new JettyHttp2TestContainer(baseUri, context);
+ }
+}
diff --git a/test-framework/providers/jetty-http2/src/main/java/org/glassfish/jersey/test/jetty/http2/package-info.java b/test-framework/providers/jetty-http2/src/main/java/org/glassfish/jersey/test/jetty/http2/package-info.java
new file mode 100644
index 0000000000..cd4980f3f4
--- /dev/null
+++ b/test-framework/providers/jetty-http2/src/main/java/org/glassfish/jersey/test/jetty/http2/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/**
+ * Jersey test framework for Jetty HTTP/2 Container.
+ */
+package org.glassfish.jersey.test.jetty.http2;
diff --git a/test-framework/providers/jetty-http2/src/main/resources/META-INF/services/org.glassfish.jersey.test.spi.TestContainerFactory b/test-framework/providers/jetty-http2/src/main/resources/META-INF/services/org.glassfish.jersey.test.spi.TestContainerFactory
new file mode 100644
index 0000000000..42b7847026
--- /dev/null
+++ b/test-framework/providers/jetty-http2/src/main/resources/META-INF/services/org.glassfish.jersey.test.spi.TestContainerFactory
@@ -0,0 +1 @@
+org.glassfish.jersey.test.jetty.http2.JettyHttp2TestContainerFactory
\ No newline at end of file
diff --git a/test-framework/providers/jetty-http2/src/test/java/org/glassfish/jersey/test/jetty/http2/AvailablePortJettyTest.java b/test-framework/providers/jetty-http2/src/test/java/org/glassfish/jersey/test/jetty/http2/AvailablePortJettyTest.java
new file mode 100644
index 0000000000..7e048e7514
--- /dev/null
+++ b/test-framework/providers/jetty-http2/src/test/java/org/glassfish/jersey/test/jetty/http2/AvailablePortJettyTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.test.jetty.http2;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.DeploymentContext;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.test.jetty.http2.JettyHttp2TestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.jupiter.api.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Tests finding an available port for container.
+ *
+ */
+public class AvailablePortJettyTest extends JerseyTest {
+
+ @Override
+ protected TestContainerFactory getTestContainerFactory() {
+ return new JettyHttp2TestContainerFactory();
+ }
+
+ @Path("AvailablePortJettyTest")
+ public static class TestResource {
+ @GET
+ public String get() {
+ return "GET";
+ }
+ }
+
+ @Override
+ protected DeploymentContext configureDeployment() {
+ forceSet(TestProperties.CONTAINER_PORT, "0");
+
+ return DeploymentContext.builder(new ResourceConfig(TestResource.class)).build();
+ }
+
+ @Test
+ public void testGet() {
+ assertThat(target().getUri().getPort(), not(0));
+ assertThat(getBaseUri().getPort(), not(0));
+
+ assertThat(target("AvailablePortJettyTest").request().get(String.class), equalTo("GET"));
+ }
+}
diff --git a/test-framework/providers/jetty-http2/src/test/java/org/glassfish/jersey/test/jetty/http2/JettyContainerTest.java b/test-framework/providers/jetty-http2/src/test/java/org/glassfish/jersey/test/jetty/http2/JettyContainerTest.java
new file mode 100644
index 0000000000..16072f6f55
--- /dev/null
+++ b/test-framework/providers/jetty-http2/src/test/java/org/glassfish/jersey/test/jetty/http2/JettyContainerTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.test.jetty.http2;
+
+import java.net.URI;
+import java.util.List;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.inject.hk2.DelayedHk2InjectionManager;
+import org.glassfish.jersey.inject.hk2.ImmediateHk2InjectionManager;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.jetty.http2.JettyHttp2ContainerFactory;
+import org.glassfish.jersey.jetty.JettyHttpContainer;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.glassfish.hk2.api.ServiceLocator;
+
+import org.jvnet.hk2.internal.ServiceLocatorImpl;
+
+import org.eclipse.jetty.server.Server;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Test class for {@link JettyHttpContainer}.
+ *
+ */
+public class JettyContainerTest extends JerseyTest {
+
+ /**
+ * Creates new instance.
+ */
+ public JettyContainerTest() {
+ super(new JettyHttp2TestContainerFactory());
+ }
+
+ @Override
+ protected ResourceConfig configure() {
+ return new ResourceConfig(Resource.class);
+ }
+
+ /**
+ * Test resource class.
+ */
+ @Path("one")
+ public static class Resource {
+
+ /**
+ * Test resource method.
+ *
+ * @return Test simple string response.
+ */
+ @GET
+ public String getSomething() {
+ return "get";
+ }
+ }
+
+ @Test
+ /**
+ * Test {@link Server Jetty Server} container.
+ */
+ public void testJettyContainerTarget() {
+ final Response response = target().path("one").request().get();
+
+ assertEquals(200, response.getStatus(), "Response status unexpected.");
+ assertEquals("get", response.readEntity(String.class), "Response entity unexpected.");
+ }
+
+ /**
+ * Test that defined ServiceLocator becomes a parent of the newly created service locator.
+ */
+ @Test
+ public void testParentServiceLocator() {
+ final ServiceLocator locator = new ServiceLocatorImpl("MyServiceLocator", null);
+ final Server server = JettyHttp2ContainerFactory.createHttp2Server(URI.create("http://localhost:9876"),
+ new ResourceConfig(Resource.class), false, locator);
+ final JettyHttpContainer container = (JettyHttpContainer) server.getHandler();
+ final InjectionManager injectionManager = container.getApplicationHandler().getInjectionManager();
+
+ ServiceLocator serviceLocator;
+ if (injectionManager instanceof ImmediateHk2InjectionManager) {
+ serviceLocator = ((ImmediateHk2InjectionManager) injectionManager).getServiceLocator();
+ } else if (injectionManager instanceof DelayedHk2InjectionManager) {
+ serviceLocator = ((DelayedHk2InjectionManager) injectionManager).getServiceLocator();
+ } else {
+ throw new RuntimeException("Invalid Hk2 InjectionManager");
+ }
+ assertTrue(serviceLocator.getParent() == locator,
+ "Application injection manager was expected to have defined parent locator");
+ }
+ @Test
+ public void testHttp2Container() {
+ final ServiceLocator locator = new ServiceLocatorImpl("MyServiceLocator", null);
+ final Server server = JettyHttp2ContainerFactory.createHttp2Server(URI.create("http://localhost:9876"),
+ new ResourceConfig(Resource.class), true, locator);
+ final List protocols = server.getConnectors()[0].getProtocols();
+ assertTrue(protocols.contains("h2") || protocols.contains("h2c"));
+ }
+}
diff --git a/test-framework/providers/pom.xml b/test-framework/providers/pom.xml
index 2fd0fa1746..3d22043a18 100644
--- a/test-framework/providers/pom.xml
+++ b/test-framework/providers/pom.xml
@@ -38,6 +38,7 @@
grizzly2
inmemory
jetty
+ jetty-http2
jdk-http
netty
simple