Merge remote-tracking branch 'MSTR/3.x' into 3.1_merge_3.0_220222
diff --git a/bom/pom.xml b/bom/pom.xml
index f7af29c..fe7cac3 100644
--- a/bom/pom.xml
+++ b/bom/pom.xml
@@ -75,6 +75,11 @@
             </dependency>
             <dependency>
                 <groupId>org.glassfish.jersey.connectors</groupId>
+                <artifactId>jersey-java-connector</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.glassfish.jersey.connectors</groupId>
                 <artifactId>jersey-jetty-connector</artifactId>
                 <version>${project.version}</version>
             </dependency>
diff --git a/bundles/jaxrs-ri/pom.xml b/bundles/jaxrs-ri/pom.xml
index e5a5b3c..6c8ac8b 100644
--- a/bundles/jaxrs-ri/pom.xml
+++ b/bundles/jaxrs-ri/pom.xml
@@ -182,7 +182,6 @@
         <dependency>
             <groupId>jakarta.servlet</groupId>
             <artifactId>jakarta.servlet-api</artifactId>
-            <version>${servlet5.version}</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
@@ -254,7 +253,7 @@
                             <includeGroupIds>jakarta.ws.rs,org.glassfish.jersey.core,org.glassfish.jersey.containers,org.glassfish.jersey.jaxb,org.glassfish.jersey.inject</includeGroupIds>
                             <includeClassifiers>sources</includeClassifiers>
                             <outputDirectory>${generated.src.dir}</outputDirectory>
-                            <excludes>**/NOTICE.md,**/NOTICE.markdown</excludes>
+                            <excludes>module-info.*,META-INF/MANIFEST.MF,**/NOTICE.md,**/NOTICE.markdown</excludes>
                         </configuration>
                     </execution>
                 </executions>
@@ -278,9 +277,9 @@
                             jersey.repackaged.org.objectweb.asm.*;version=${project.version}
                         </Export-Package>
                         <Import-Package><![CDATA[
-                            jakarta.servlet.annotation.*;resolution:=optional;version="[5.0,6.0)",
-                            jakarta.servlet.descriptor.*;resolution:=optional;version="[5.0,6.0)",
-                            jakarta.servlet.*;version="[5.0,6.0)",
+                            jakarta.servlet.annotation.*;resolution:=optional;version="[5.0,7.0)",
+                            jakarta.servlet.descriptor.*;resolution:=optional;version="[5.0,7.0)",
+                            jakarta.servlet.*;version="[5.0,7.0)",
                             ${jakarta.annotation.osgi.version},
                             jakarta.persistence.*;resolution:=optional,
                             jakarta.validation.*;resolution:=optional;version="[3,4)",
diff --git a/connectors/java-connector/pom.xml b/connectors/java-connector/pom.xml
new file mode 100644
index 0000000..c2f1ef0
--- /dev/null
+++ b/connectors/java-connector/pom.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2021 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>project</artifactId>
+        <groupId>org.glassfish.jersey.connectors</groupId>
+        <version>3.1.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>jersey-java-connector</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-connectors-java</name>
+
+    <description>Jersey Client Transport via Java's HttpClient</description>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <version>${project.version}</version>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.awaitility</groupId>
+            <artifactId>awaitility</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>com.sun.istack</groupId>
+                <artifactId>istack-commons-maven-plugin</artifactId>
+                <inherited>true</inherited>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <inherited>true</inherited>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file
diff --git a/connectors/java-connector/src/main/java/org/glassfish/jersey/java/connector/JavaClientProperties.java b/connectors/java-connector/src/main/java/org/glassfish/jersey/java/connector/JavaClientProperties.java
new file mode 100644
index 0000000..cb8f60e
--- /dev/null
+++ b/connectors/java-connector/src/main/java/org/glassfish/jersey/java/connector/JavaClientProperties.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2021 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.java.connector;
+
+import org.glassfish.jersey.internal.util.PropertiesClass;
+
+import java.net.http.HttpClient;
+
+/**
+ * Provides configuration properties for a {@link JavaConnector}.
+ *
+ * @author Steffen Nießing
+ */
+@PropertiesClass
+public class JavaClientProperties {
+    /**
+     * Configuration of the {@link java.net.CookieHandler} that should be used by the {@link HttpClient}.
+     * If this option is not set, {@link HttpClient#cookieHandler()} will return an empty {@link java.util.Optional}
+     * and therefore no cookie handler will be used.
+     *
+     * A provided value to this option has to be of type {@link java.net.CookieHandler}.
+     */
+    public static final String COOKIE_HANDLER = "jersey.config.java.client.cookieHandler";
+
+    /**
+     * Configuration of SSL parameters used by the {@link HttpClient}.
+     * If this option is not set, then the {@link HttpClient} will use <it>implementation specific</it> default values.
+     *
+     * A provided value to this option has to be of type {@link javax.net.ssl.SSLParameters}.
+     */
+    public static final String SSL_PARAMETERS = "jersey.config.java.client.sslParameters";
+
+    /**
+     * Prevent this class from instantiation.
+     */
+    private JavaClientProperties() {}
+}
diff --git a/connectors/java-connector/src/main/java/org/glassfish/jersey/java/connector/JavaConnector.java b/connectors/java-connector/src/main/java/org/glassfish/jersey/java/connector/JavaConnector.java
new file mode 100644
index 0000000..9c104ab
--- /dev/null
+++ b/connectors/java-connector/src/main/java/org/glassfish/jersey/java/connector/JavaConnector.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2021 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.java.connector;
+
+import jakarta.ws.rs.ProcessingException;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.core.Configuration;
+import jakarta.ws.rs.core.MultivaluedMap;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.client.ClientRequest;
+import org.glassfish.jersey.client.ClientResponse;
+import org.glassfish.jersey.client.spi.AsyncConnectorCallback;
+import org.glassfish.jersey.client.spi.Connector;
+import org.glassfish.jersey.internal.Version;
+import org.glassfish.jersey.message.internal.OutboundMessageContext;
+import org.glassfish.jersey.message.internal.Statuses;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLParameters;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.CookieHandler;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.time.Duration;
+import java.time.temporal.ChronoUnit;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Future;
+import java.util.logging.Logger;
+
+/**
+ * Provides a Jersey client {@link Connector}, which internally uses Java's {@link HttpClient}.
+ * The following properties are provided to Java's {@link HttpClient.Builder} during creation of the {@link HttpClient}:
+ * <ul>
+ *     <li>{@link ClientProperties#CONNECT_TIMEOUT}</li>
+ *     <li>{@link ClientProperties#FOLLOW_REDIRECTS}</li>
+ *     <li>{@link JavaClientProperties#COOKIE_HANDLER}</li>
+ *     <li>{@link JavaClientProperties#SSL_PARAMETERS}</li>
+ * </ul>
+ *
+ * @author Steffen Nießing
+ */
+public class JavaConnector implements Connector {
+    private static final Logger LOGGER = Logger.getLogger(JavaConnector.class.getName());
+
+    private final HttpClient httpClient;
+
+    /**
+     * Constructs a new {@link Connector} for a Jersey client instance using Java's {@link HttpClient}.
+     *
+     * @param client a Jersey client instance to get additional configuration properties from (e.g. {@link SSLContext})
+     * @param configuration the configuration properties for this connector
+     */
+    public JavaConnector(final Client client, final Configuration configuration) {
+        HttpClient.Builder httpClientBuilder = HttpClient.newBuilder();
+        httpClientBuilder.version(HttpClient.Version.HTTP_1_1);
+        SSLContext sslContext = client.getSslContext();
+        if (sslContext != null) {
+            httpClientBuilder.sslContext(sslContext);
+        }
+        Integer connectTimeout = getPropertyOrNull(configuration, ClientProperties.CONNECT_TIMEOUT, Integer.class);
+        if (connectTimeout != null) {
+            httpClientBuilder.connectTimeout(Duration.of(connectTimeout, ChronoUnit.MILLIS));
+        }
+        CookieHandler cookieHandler = getPropertyOrNull(configuration, JavaClientProperties.COOKIE_HANDLER, CookieHandler.class);
+        if (cookieHandler != null) {
+            httpClientBuilder.cookieHandler(cookieHandler);
+        }
+        Boolean redirect = getPropertyOrNull(configuration, ClientProperties.FOLLOW_REDIRECTS, Boolean.class);
+        if (redirect != null) {
+            httpClientBuilder.followRedirects(redirect ? HttpClient.Redirect.ALWAYS : HttpClient.Redirect.NEVER);
+        } else {
+            httpClientBuilder.followRedirects(HttpClient.Redirect.NORMAL);
+        }
+        SSLParameters sslParameters = getPropertyOrNull(configuration, JavaClientProperties.SSL_PARAMETERS, SSLParameters.class);
+        if (sslParameters != null) {
+            httpClientBuilder.sslParameters(sslParameters);
+        }
+        this.httpClient = httpClientBuilder.build();
+    }
+
+    /**
+     * Implements a {@link org.glassfish.jersey.message.internal.OutboundMessageContext.StreamProvider}
+     * for a {@link ByteArrayOutputStream}.
+     */
+    private static class ByteArrayOutputStreamProvider implements OutboundMessageContext.StreamProvider {
+        private ByteArrayOutputStream byteArrayOutputStream;
+
+        public ByteArrayOutputStream getByteArrayOutputStream() {
+            return byteArrayOutputStream;
+        }
+
+        @Override
+        public OutputStream getOutputStream(int contentLength) throws IOException {
+            return this.byteArrayOutputStream = new ByteArrayOutputStream(contentLength);
+        }
+    }
+
+    /**
+     * Builds a request for the {@link HttpClient} from Jersey's {@link ClientRequest}.
+     *
+     * @param request the Jersey request to get request data from
+     * @return the {@link HttpRequest} instance for the {@link HttpClient} request
+     */
+    private HttpRequest getHttpRequest(ClientRequest request) {
+        HttpRequest.Builder builder = HttpRequest.newBuilder();
+        builder.uri(request.getUri());
+        HttpRequest.BodyPublisher bodyPublisher = HttpRequest.BodyPublishers.noBody();
+        if (request.hasEntity()) {
+            try {
+                request.enableBuffering();
+                ByteArrayOutputStreamProvider byteBufferStreamProvider = new ByteArrayOutputStreamProvider();
+                request.setStreamProvider(byteBufferStreamProvider);
+                request.writeEntity();
+                bodyPublisher = HttpRequest.BodyPublishers.ofByteArray(
+                        byteBufferStreamProvider.getByteArrayOutputStream().toByteArray()
+                );
+            } catch (IOException e) {
+                throw new ProcessingException(LocalizationMessages.ERROR_INVALID_ENTITY(), e);
+            }
+        }
+        builder.method(request.getMethod(), bodyPublisher);
+        for (Map.Entry<String, List<String>> entry : request.getRequestHeaders().entrySet()) {
+            String headerName = entry.getKey();
+            for (String headerValue : entry.getValue()) {
+                builder.header(headerName, headerValue);
+            }
+        }
+        return builder.build();
+    }
+
+    /**
+     * Retrieves a property from the configuration, if it was provided.
+     *
+     * @param configuration the {@link Configuration} to get the property information from
+     * @param propertyKey the name of the property to retrieve
+     * @param resultClass the type to which the property value should be case
+     * @param <T> the generic type parameter of the result type
+     * @return the requested property or {@code null}, if it was not provided or has the wrong type
+     */
+    @SuppressWarnings("unchecked")
+    private <T> T getPropertyOrNull(final Configuration configuration, final String propertyKey, final Class<T> resultClass) {
+        Object propertyObject = configuration.getProperty(propertyKey);
+        if (propertyObject == null) {
+            return null;
+        }
+        if (!resultClass.isInstance(propertyObject)) {
+            LOGGER.warning(LocalizationMessages.ERROR_INVALID_CLASS(propertyKey, resultClass.getName()));
+            return null;
+        }
+        return (T) propertyObject;
+    }
+
+    /**
+     * Translates a {@link HttpResponse} from the {@link HttpClient} to a Jersey {@link ClientResponse}.
+     *
+     * @param request the {@link ClientRequest} to get additional information (e.g. header values) from
+     * @param response the {@link HttpClient} response object
+     * @return the translated Jersey {@link ClientResponse} object
+     */
+    private ClientResponse buildClientResponse(ClientRequest request, HttpResponse<InputStream> response) {
+        ClientResponse clientResponse = new ClientResponse(Statuses.from(response.statusCode()), request);
+        MultivaluedMap<String, String> headers = clientResponse.getHeaders();
+        for (Map.Entry<String, List<String>> entry : response.headers().map().entrySet()) {
+            String headerName = entry.getKey();
+            if (headers.get(headerName) != null) {
+                headers.get(headerName).addAll(entry.getValue());
+            } else {
+                headers.put(headerName, entry.getValue());
+            }
+        }
+        clientResponse.setEntityStream(response.body());
+        return clientResponse;
+    }
+
+    /**
+     * Returns the underlying {@link HttpClient} instance used by this connector.
+     *
+     * @return the Java {@link HttpClient} instance
+     */
+    public HttpClient getHttpClient() {
+        return httpClient;
+    }
+
+    @Override
+    public ClientResponse apply(ClientRequest request) {
+        HttpRequest httpRequest = getHttpRequest(request);
+        try {
+            HttpResponse<InputStream> response = this.httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofInputStream());
+            return buildClientResponse(request, response);
+        } catch (IOException | InterruptedException e) {
+            throw new ProcessingException(e);
+        }
+    }
+
+    @Override
+    public Future<?> apply(ClientRequest request, AsyncConnectorCallback callback) {
+        HttpRequest httpRequest = getHttpRequest(request);
+        CompletableFuture<ClientResponse> response = this.httpClient
+                .sendAsync(httpRequest, HttpResponse.BodyHandlers.ofInputStream())
+                .thenApply(httpResponse -> buildClientResponse(request, httpResponse));
+        response.thenAccept(callback::response);
+        return response;
+    }
+
+    @Override
+    public String getName() {
+        return "Java HttpClient Connector " + Version.getVersion();
+    }
+
+    @Override
+    public void close() {
+
+    }
+}
diff --git a/connectors/java-connector/src/main/java/org/glassfish/jersey/java/connector/JavaConnectorProvider.java b/connectors/java-connector/src/main/java/org/glassfish/jersey/java/connector/JavaConnectorProvider.java
new file mode 100644
index 0000000..1fa38ab
--- /dev/null
+++ b/connectors/java-connector/src/main/java/org/glassfish/jersey/java/connector/JavaConnectorProvider.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2021 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.java.connector;
+
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.core.Configurable;
+import jakarta.ws.rs.core.Configuration;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.client.Initializable;
+import org.glassfish.jersey.client.spi.Connector;
+import org.glassfish.jersey.client.spi.ConnectorProvider;
+
+import java.net.http.HttpClient;
+
+/**
+ * A provider class for a Jersey client {@link Connector} using Java's {@link HttpClient}.
+ * <p>
+ *     The following configuration properties are available:
+ *     <ul>
+ *         <li>{@link ClientProperties#CONNECT_TIMEOUT}</li>
+ *         <li>{@link ClientProperties#FOLLOW_REDIRECTS} (defaults to {@link java.net.http.HttpClient.Redirect#NORMAL} when unset)</li>
+ *         <li>{@link JavaClientProperties#COOKIE_HANDLER}</li>
+ *         <li>{@link JavaClientProperties#SSL_PARAMETERS}</li>
+ *     </ul>
+ * </p>
+ *
+ * @author Steffen Nießing
+ */
+public class JavaConnectorProvider implements ConnectorProvider {
+    @Override
+    public Connector getConnector(Client client, Configuration runtimeConfig) {
+        return new JavaConnector(client, runtimeConfig);
+    }
+
+    /**
+     * Retrieve the Java {@link HttpClient} used by the provided {@link JavaConnector}.
+     *
+     * @param component the component from which the {@link JavaConnector} should be retrieved
+     * @return a Java {@link HttpClient} instance
+     * @throws java.lang.IllegalArgumentException if a {@link JavaConnector} cannot be provided from the given {@code component}
+     */
+    public static HttpClient getHttpClient(Configurable<?> component) {
+        try {
+            final Initializable<?> initializable = (Initializable<?>) component;
+
+            Connector connector = initializable.getConfiguration().getConnector() != null
+                    ? initializable.getConfiguration().getConnector()
+                    : initializable.preInitialize().getConfiguration().getConnector();
+
+            if (connector instanceof JavaConnector) {
+                return ((JavaConnector) connector).getHttpClient();
+            } else {
+                throw new IllegalArgumentException(LocalizationMessages.EXPECTED_CONNECTOR_PROVIDER_NOT_USED());
+            }
+        } catch (ClassCastException classCastException) {
+            throw new IllegalArgumentException(
+                    LocalizationMessages.INVALID_CONFIGURABLE_COMPONENT_TYPE(component.getClass().getName()),
+                    classCastException
+            );
+        }
+    }
+}
diff --git a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java b/connectors/java-connector/src/main/java/org/glassfish/jersey/java/connector/package-info.java
similarity index 71%
copy from core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java
copy to connectors/java-connector/src/main/java/org/glassfish/jersey/java/connector/package-info.java
index dd25372..e50e0bb 100644
--- a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java
+++ b/connectors/java-connector/src/main/java/org/glassfish/jersey/java/connector/package-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021 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
@@ -14,7 +14,8 @@
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
  */
 
-package org.glassfish.jersey.internal.jsr166;
-
-public interface JerseyFlowSubscriber<T> extends Flow.Subscriber<T> {
-}
+/**
+ * Jersey client {@link org.glassfish.jersey.client.spi.Connector connector} based on
+ * Java's {@link java.net.http.HttpClient}.
+ */
+package org.glassfish.jersey.java.connector;
\ No newline at end of file
diff --git a/connectors/java-connector/src/main/resources/org/glassfish/jersey/java/connector/localization.properties b/connectors/java-connector/src/main/resources/org/glassfish/jersey/java/connector/localization.properties
new file mode 100644
index 0000000..ca98e99
--- /dev/null
+++ b/connectors/java-connector/src/main/resources/org/glassfish/jersey/java/connector/localization.properties
@@ -0,0 +1,21 @@
+#
+# Copyright (c) 2021 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
+#
+
+error.body.publisher=Could not determine BodyPublisher for entity.
+error.invalid.class={0} is not an instance of {1}. Ignoring property.
+error.invalid.entity=Could not serialize entity.
+invalid.configurable.component.type=The supplied component "{0}" is not assignable from JerseyClient or JerseyWebTarget.
+expected.connector.provider.not.used=The supplied component is not configured to use a JavaConnectorProvider.
diff --git a/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/AbstractJavaConnectorTest.java b/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/AbstractJavaConnectorTest.java
new file mode 100644
index 0000000..7d3980d
--- /dev/null
+++ b/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/AbstractJavaConnectorTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2021 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.java.connector;
+
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.DefaultValue;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.QueryParam;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.container.AsyncResponse;
+import jakarta.ws.rs.container.Suspended;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Logger;
+
+/**
+ * An abstract base class for tests of the {@link JavaConnector} providing common resources and utility methods.
+ */
+abstract class AbstractJavaConnectorTest extends JerseyTest {
+    private static final Logger LOGGER = Logger.getLogger(AbstractJavaConnectorTest.class.getName());
+    public static final String RESOURCE_PATH = "java-connector";
+
+    @Path(RESOURCE_PATH)
+    public static class JavaConnectorTestResource {
+        @GET
+        public String helloWorld() {
+            return "Hello World!";
+        }
+
+        @GET
+        @Path("redirect")
+        public Response redirectToHelloWorld() throws URISyntaxException {
+            return Response.seeOther(new URI(RESOURCE_PATH)).build();
+        }
+
+        @POST
+        @Path("echo")
+        @Produces(MediaType.TEXT_PLAIN)
+        @Consumes(MediaType.TEXT_PLAIN)
+        public String echo(String entity) {
+            return entity;
+        }
+
+        @POST
+        @Path("echo-byte-array")
+        @Produces(MediaType.APPLICATION_OCTET_STREAM)
+        @Consumes(MediaType.APPLICATION_OCTET_STREAM)
+        public byte[] echoByteArray(byte[] byteArray) {
+            return byteArray;
+        }
+
+        @POST
+        @Path("async")
+        public void asyncPostWithTimeout(@QueryParam("timeout") @DefaultValue("10") Long timeoutSeconds,
+                                         @Suspended final AsyncResponse asyncResponse,
+                                         String message) {
+            asyncResponse.setTimeoutHandler(asyncResponse1 ->
+                    asyncResponse1.resume(Response.status(Response.Status.SERVICE_UNAVAILABLE).entity("Timeout").build()));
+            asyncResponse.setTimeout(timeoutSeconds, TimeUnit.SECONDS);
+            CompletableFuture.runAsync(() -> {
+                        try {
+                            Thread.sleep(3000);
+                        } catch (InterruptedException e) {
+                            Thread.currentThread().interrupt();
+                            throw new RuntimeException(e);
+                        }
+                    })
+                    .handleAsync((unused, throwable) -> throwable != null ? "INTERRUPTED" : message)
+                    .thenApplyAsync(asyncResponse::resume);
+        }
+    }
+
+    protected Response request(String path) {
+        return target().path(path).request().get();
+    }
+
+    protected Response requestWithEntity(String path, String method, Entity<?> entity) {
+        return target().path(path).request().method(method, entity);
+    }
+
+    protected Future<Response> requestAsync(String path) {
+        return target().path(path).request().async().get();
+    }
+
+    protected Future<Response> requestAsyncWithEntity(String path, String method, Entity<?> entity) {
+        return target().path(path).request().async().method(method, entity);
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(JavaConnectorTestResource.class)
+                .register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+        config.connectorProvider(new JavaConnectorProvider());
+    }
+}
diff --git a/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/AsyncTest.java b/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/AsyncTest.java
new file mode 100644
index 0000000..dc08521
--- /dev/null
+++ b/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/AsyncTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2021 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.java.connector;
+
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Response;
+import org.junit.Test;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.awaitility.Awaitility.await;
+
+/**
+ * Tests asynchronous and interleaved requests.
+ */
+public class AsyncTest extends AbstractJavaConnectorTest {
+    /**
+     * Checks, that 3 interleaved requests all complete and return their associated responses.
+     * Additionally checks, that all requests complete in 3 times the running time on the server.
+     */
+    @Test
+    public void testAsyncRequestsWithoutTimeout() {
+        Future<Response> request1 = this.requestAsyncWithEntity("java-connector/async", "POST", Entity.text("request1"));
+        Future<Response> request2 = this.requestAsyncWithEntity("java-connector/async", "POST", Entity.text("request2"));
+        Future<Response> request3 = this.requestAsyncWithEntity("java-connector/async", "POST", Entity.text("request3"));
+
+        assertThatCode(() -> {
+            // wait 3 times the processing time and throw if not completed until then
+            await().atMost(3 * 3000, TimeUnit.MILLISECONDS)
+                    .until(() -> request1.isDone() && request2.isDone() && request3.isDone());
+            String response1 = request1.get().readEntity(String.class);
+            String response2 = request2.get().readEntity(String.class);
+            String response3 = request3.get().readEntity(String.class);
+            assertThat(response1).isEqualTo("request1");
+            assertThat(response2).isEqualTo("request2");
+            assertThat(response3).isEqualTo("request3");
+        }).doesNotThrowAnyException();
+    }
+
+    /**
+     * Checks, that a status {@link Response.Status#SERVICE_UNAVAILABLE} is thrown, if a request computes too long.
+     */
+    @Test
+    public void testAsyncRequestsWithTimeout() throws ExecutionException, InterruptedException {
+        try {
+            Response response = target().path("java-connector").path("async").queryParam("timeout", 1)
+                    .request().async().post(Entity.text("")).get();
+            assertThat(response.getStatus()).isEqualTo(Response.Status.SERVICE_UNAVAILABLE.getStatusCode());
+            assertThat(response.readEntity(String.class)).isEqualTo("Timeout");
+        } catch (InterruptedException | ExecutionException ex) {
+            throw new RuntimeException("Could not correctly get response", ex);
+        }
+    }
+}
diff --git a/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/BodyPublisherTest.java b/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/BodyPublisherTest.java
new file mode 100644
index 0000000..dbbbe82
--- /dev/null
+++ b/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/BodyPublisherTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2021 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.java.connector;
+
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import org.junit.Test;
+
+import java.nio.charset.StandardCharsets;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+
+/**
+ * Checks, that request entities are correctly serialized and deserialized.
+ */
+public class BodyPublisherTest extends AbstractJavaConnectorTest {
+    /**
+     * Checks with a simple plain text entity.
+     */
+    @Test
+    public void testStringEntity() {
+        Response response = this.requestWithEntity("java-connector/echo", "POST", Entity.text("Echo"));
+        assertThat(response.getStatus()).isEqualTo(200);
+        assertThatCode(() -> {
+            assertThat(response.readEntity(String.class)).isEqualTo("Echo");
+        }).doesNotThrowAnyException();
+    }
+
+    /**
+     * Checks with an octet stream entity.
+     */
+    @Test
+    public void testByteArrayEntity() {
+        String test = "test-string";
+        Response response = this.requestWithEntity("java-connector/echo-byte-array", "POST",
+                Entity.entity(test.getBytes(StandardCharsets.UTF_8), MediaType.APPLICATION_OCTET_STREAM_TYPE));
+        assertThat(response.getStatus()).isEqualTo(200);
+        assertThatCode(() -> {
+            assertThat(response.readEntity(byte[].class))
+                    .satisfies(bytes -> assertThat(new String(bytes, StandardCharsets.UTF_8)).isEqualTo("test-string"));
+        }).doesNotThrowAnyException();
+    }
+}
diff --git a/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/OptionsMethodTest.java b/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/OptionsMethodTest.java
new file mode 100644
index 0000000..27ec125
--- /dev/null
+++ b/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/OptionsMethodTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2021 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.java.connector;
+
+import jakarta.ws.rs.core.Response;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Checks, that an {@code OPTIONS} request may be sent to a {@code GET} endpoint.
+ */
+public class OptionsMethodTest extends AbstractJavaConnectorTest {
+    /**
+     * Sends an {@code OPTIONS} request to the root {@code GET} endpoint and assumes a code 200.
+     */
+    @Test
+    public void testOptionsMethod() {
+        assertThat(this.requestWithEntity("java-connector", "OPTIONS", null).getStatus())
+                .isEqualTo(Response.Status.OK.getStatusCode());
+    }
+}
diff --git a/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/RedirectTest.java b/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/RedirectTest.java
new file mode 100644
index 0000000..1cd0c3d
--- /dev/null
+++ b/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/RedirectTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2021 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.java.connector;
+
+import jakarta.ws.rs.core.Response;
+import org.glassfish.jersey.client.ClientProperties;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Checks that the connector provider correctly handles redirects.
+ */
+public class RedirectTest extends AbstractJavaConnectorTest {
+    /**
+     * Checks, that without further configuration redirects are taken.
+     */
+    @Test
+    public void testRedirect() {
+        assertThat(this.request("java-connector/redirect").readEntity(String.class)).isEqualTo("Hello World!");
+    }
+
+    /**
+     * Checks, that no redirect happens, if the redirects are switched off.
+     */
+    @Test
+    public void testNotFollowRedirects() {
+        Response response = target().path("java-connector").path("redirect")
+                .property(ClientProperties.FOLLOW_REDIRECTS, false)
+                .request()
+                .get();
+        assertThat(response.getStatus()).isEqualTo(Response.Status.SEE_OTHER.getStatusCode());
+    }
+}
diff --git a/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/RetrieveHttpClientFromConnectorProviderTest.java b/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/RetrieveHttpClientFromConnectorProviderTest.java
new file mode 100644
index 0000000..aa831de
--- /dev/null
+++ b/connectors/java-connector/src/test/java/org/glassfish/jersey/java/connector/RetrieveHttpClientFromConnectorProviderTest.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2021 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.java.connector;
+
+import org.junit.Test;
+
+import java.net.http.HttpClient;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests access to the {@link HttpClient} instance provided from the {@link JavaConnectorProvider}.
+ */
+public class RetrieveHttpClientFromConnectorProviderTest extends AbstractJavaConnectorTest {
+    /**
+     * Checks, that the {@link jakarta.ws.rs.client.Client} and {@link jakarta.ws.rs.client.WebTarget} instances
+     * correctly return the internally used {@link HttpClient}.
+     */
+    @Test
+    public void testClientUsesJavaConnector() {
+        assertThat(JavaConnectorProvider.getHttpClient(client())).isInstanceOf(HttpClient.class);
+        assertThat(JavaConnectorProvider.getHttpClient(target())).isInstanceOf(HttpClient.class);
+        assertThat(JavaConnectorProvider.getHttpClient(client()))
+                .isEqualTo(JavaConnectorProvider.getHttpClient(target()));
+    }
+}
diff --git a/connectors/jdk-connector/pom.xml b/connectors/jdk-connector/pom.xml
index da0ccd9..37a68e9 100644
--- a/connectors/jdk-connector/pom.xml
+++ b/connectors/jdk-connector/pom.xml
@@ -34,6 +34,8 @@
 
     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <!-- https://bugs.openjdk.java.net/browse/JDK-8211426 -->
+        <surefire.security.argline>-Djdk.tls.server.protocols=TLSv1.2</surefire.security.argline>
     </properties>
 
     <dependencies>
@@ -78,16 +80,6 @@
 
     <profiles>
         <profile>
-            <id>jdk11+</id>
-            <activation>
-                <jdk>[11,)</jdk>
-            </activation>
-            <properties>
-                <!-- https://bugs.openjdk.java.net/browse/JDK-8211426 -->
-                <surefire.security.argline>-Djdk.tls.server.protocols=TLSv1.2</surefire.security.argline>
-            </properties>
-        </profile>
-        <profile>
             <id>disable_tls1and11</id>
             <!-- TLS 1 and TLS 1.1 are disabled for JDK 16 -->
             <activation>
@@ -110,5 +102,4 @@
             </build>
         </profile>
     </profiles>
-
 </project>
diff --git a/connectors/jetty-connector/pom.xml b/connectors/jetty-connector/pom.xml
index 6aca80a..a98019c 100644
--- a/connectors/jetty-connector/pom.xml
+++ b/connectors/jetty-connector/pom.xml
@@ -34,10 +34,6 @@
 
     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-        <java8.build.outputDirectory>${project.basedir}/target</java8.build.outputDirectory>
-        <java8.sourceDirectory>${project.basedir}/src/main/java8</java8.sourceDirectory>
-        <java11.build.outputDirectory>${project.basedir}/target11</java11.build.outputDirectory>
-        <java11.sourceDirectory>${project.basedir}/src/main/java11</java11.sourceDirectory>
     </properties>
 
     <dependencies>
@@ -62,12 +58,12 @@
             <version>${project.version}</version>
             <scope>test</scope>
         </dependency>
-<!--        <dependency>-->
-<!--            <groupId>org.glassfish.jersey.media</groupId>-->
-<!--            <artifactId>jersey-media-json-jackson</artifactId>-->
-<!--            <version>${project.version}</version>-->
-<!--            <scope>test</scope>-->
-<!--        </dependency>-->
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jackson</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.test-framework.providers</groupId>
             <artifactId>jersey-test-framework-provider-jetty</artifactId>
@@ -84,6 +80,11 @@
             <artifactId>jakarta.xml.bind-api</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>com.sun.xml.bind</groupId>
+            <artifactId>jaxb-osgi</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
@@ -102,174 +103,6 @@
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
             </plugin>
-            <plugin>
-                <!-- TODO remove after jakartification -->
-            <groupId>org.apache.maven.plugins</groupId>
-            <artifactId>maven-surefire-plugin</artifactId>
-            <executions>
-            <execution>
-                <id>default-test</id> <!-- jakartification-excluded-tests -->
-                <configuration>
-                    <excludes>
-                        <exclude>org/glassfish/jersey/jetty/connector/EntityTest.java</exclude>
-                    </excludes>
-                </configuration>
-            </execution>
-            </executions>
-            </plugin>
         </plugins>
     </build>
-
-    <profiles>
-        <profile>
-            <id>JettyExclude</id>
-            <activation>
-                <jdk>1.8</jdk>
-            </activation>
-            <properties>
-                <jetty.version>9.4.28.v20200408</jetty.version>
-            </properties>
-            <dependencies>
-                <dependency>
-                    <groupId>org.eclipse.jetty</groupId>
-                    <artifactId>jetty-client</artifactId>
-                    <version>${jetty.version}</version>
-                </dependency>
-                <dependency>
-                    <groupId>org.eclipse.jetty</groupId>
-                    <artifactId>jetty-util</artifactId>
-                    <version>${jetty.version}</version>
-                </dependency>
-            </dependencies>
-            <build>
-                <directory>${java8.build.outputDirectory}</directory>
-                <plugins>
-                    <plugin>
-                        <groupId>org.codehaus.mojo</groupId>
-                        <artifactId>build-helper-maven-plugin</artifactId>
-                        <executions>
-                            <execution>
-                                <phase>generate-sources</phase>
-                                <goals>
-                                    <goal>add-source</goal>
-                                </goals>
-                                <configuration>
-                                    <sources>
-                                        <source>${java8.sourceDirectory}</source>
-                                    </sources>
-                                </configuration>
-                            </execution>
-                        </executions>
-                    </plugin>
-                    <plugin>
-                        <groupId>org.apache.maven.plugins</groupId>
-                        <artifactId>maven-compiler-plugin</artifactId>
-                        <configuration>
-                            <testExcludes>
-                                <testExclude>org/glassfish/jersey/jetty/connector/*.java</testExclude>
-                            </testExcludes>
-                        </configuration>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-        <profile>
-            <id>Jetty11</id>
-            <activation>
-                <jdk>[11,)</jdk>
-            </activation>
-            <build>
-                <directory>${java11.build.outputDirectory}</directory>
-                <plugins>
-                    <plugin>
-                        <groupId>org.codehaus.mojo</groupId>
-                        <artifactId>build-helper-maven-plugin</artifactId>
-                        <executions>
-                            <execution>
-                                <phase>generate-sources</phase>
-                                <goals>
-                                    <goal>add-source</goal>
-                                </goals>
-                                <configuration>
-                                    <sources>
-                                        <source>${java11.sourceDirectory}</source>
-                                    </sources>
-                                </configuration>
-                            </execution>
-                        </executions>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-        <profile>
-            <id>copyJDK11FilesToMultiReleaseJar</id>
-            <activation>
-                <file>
-                    <!-- ${java11.build.outputDirectory} does not work here -->
-                    <exists>target11/classes/org/glassfish/jersey/jetty/connector/JettyConnectorProvider.class</exists>
-                </file>
-                <jdk>1.8</jdk>
-            </activation>
-            <build>
-                <plugins>
-                    <plugin>
-                        <groupId>org.apache.felix</groupId>
-                        <artifactId>maven-bundle-plugin</artifactId>
-                        <inherited>true</inherited>
-                        <extensions>true</extensions>
-                        <configuration>
-                            <instructions>
-                                <Multi-Release>true</Multi-Release>
-                            </instructions>
-                        </configuration>
-                    </plugin>
-                    <plugin>
-                        <groupId>org.apache.maven.plugins</groupId>
-                        <artifactId>maven-resources-plugin</artifactId>
-                        <inherited>true</inherited>
-                        <executions>
-                            <execution>
-                                <id>copy-jdk11-classes</id>
-                                <phase>prepare-package</phase>
-                                <goals>
-                                    <goal>copy-resources</goal>
-                                </goals>
-                                <configuration>
-                                    <outputDirectory>${java8.build.outputDirectory}/classes/META-INF/versions/11</outputDirectory>
-                                    <resources>
-                                        <resource>
-                                            <directory>${java11.build.outputDirectory}/classes</directory>
-                                        </resource>
-                                    </resources>
-                                </configuration>
-                            </execution>
-                        </executions>
-                    </plugin>
-                    <plugin>
-                        <groupId>org.apache.maven.plugins</groupId>
-                        <artifactId>maven-antrun-plugin</artifactId>
-                        <executions>
-                            <execution>
-                                <id>copy-jdk11-sources</id>
-                                <phase>package</phase>
-                                <configuration>
-                                    <target>
-                                        <property name="sources-jar" value="${java8.build.outputDirectory}/${project.artifactId}-${project.version}-sources.jar"/>
-                                        <echo>sources-jar: ${sources-jar}</echo>
-                                        <zip destfile="${sources-jar}" update="true">
-                                            <zipfileset dir="${java11.sourceDirectory}" prefix="META-INF/versions/11"/>
-                                        </zip>
-                                    </target>
-                                </configuration>
-                                <goals>
-                                    <goal>run</goal>
-                                </goals>
-                            </execution>
-                        </executions>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-    </profiles>
-
 </project>
diff --git a/connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyConnector.java b/connectors/jetty-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyConnector.java
similarity index 100%
rename from connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyConnector.java
rename to connectors/jetty-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyConnector.java
diff --git a/connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyConnectorProvider.java b/connectors/jetty-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyConnectorProvider.java
similarity index 98%
rename from connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyConnectorProvider.java
rename to connectors/jetty-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyConnectorProvider.java
index 44146c8..933ad39 100644
--- a/connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyConnectorProvider.java
+++ b/connectors/jetty-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyConnectorProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2021 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/connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyHttpClientContract.java b/connectors/jetty-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyHttpClientContract.java
similarity index 100%
rename from connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyHttpClientContract.java
rename to connectors/jetty-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyHttpClientContract.java
diff --git a/connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyHttpClientSupplier.java b/connectors/jetty-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyHttpClientSupplier.java
similarity index 100%
rename from connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyHttpClientSupplier.java
rename to connectors/jetty-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyHttpClientSupplier.java
diff --git a/connectors/jetty-connector/src/main/java8/org/glassfish/jersey/jetty/connector/JettyConnectorProvider.java b/connectors/jetty-connector/src/main/java8/org/glassfish/jersey/jetty/connector/JettyConnectorProvider.java
deleted file mode 100644
index 8723025..0000000
--- a/connectors/jetty-connector/src/main/java8/org/glassfish/jersey/jetty/connector/JettyConnectorProvider.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (c) 2020 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.connector;
-
-import jakarta.ws.rs.ProcessingException;
-import jakarta.ws.rs.client.Client;
-import jakarta.ws.rs.core.Configuration;
-
-import org.glassfish.jersey.client.spi.Connector;
-import org.glassfish.jersey.client.spi.ConnectorProvider;
-
-import org.glassfish.jersey.internal.util.JdkVersion;
-
-/**
- * JDK 1.8 Jetty Connector stub which only throws exception when running on JDK 1.8
- * New Jetty (11+) does not support JDKs prior to 11
- *
- * @since 3.0.0
- */
-public class JettyConnectorProvider implements ConnectorProvider {
-
-    @Override
-    public Connector getConnector(Client client, Configuration runtimeConfig) {
-        if (JdkVersion.getJdkVersion().getMajor() < 11) {
-            throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
-        }
-        return null; // does not work at JDK 1.8
-    }
-
-}
diff --git a/connectors/jetty-connector/src/main/java8/org/glassfish/jersey/jetty/connector/JettyHttpClientContract.java b/connectors/jetty-connector/src/main/java8/org/glassfish/jersey/jetty/connector/JettyHttpClientContract.java
deleted file mode 100644
index b061ef5..0000000
--- a/connectors/jetty-connector/src/main/java8/org/glassfish/jersey/jetty/connector/JettyHttpClientContract.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (c) 2021 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.connector;
-
-import org.eclipse.jetty.client.HttpClient;
-import org.glassfish.jersey.spi.Contract;
-
-/**
- * A contract that allows for an optional registration of user predefined Jetty {@code HttpClient}
- * that is consequently used by {@link JettyConnector}
- */
-@Contract
-public interface JettyHttpClientContract {
-    /**
-     * Supply a user predefined HttpClient
-     * @return a user predefined HttpClient
-     */
-    HttpClient getHttpClient();
-}
diff --git a/connectors/jetty-connector/src/main/java8/org/glassfish/jersey/jetty/connector/JettyHttpClientSupplier.java b/connectors/jetty-connector/src/main/java8/org/glassfish/jersey/jetty/connector/JettyHttpClientSupplier.java
deleted file mode 100644
index 59c8fd3..0000000
--- a/connectors/jetty-connector/src/main/java8/org/glassfish/jersey/jetty/connector/JettyHttpClientSupplier.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (c) 2021 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.connector;
-
-import jakarta.ws.rs.ProcessingException;
-import org.eclipse.jetty.client.HttpClient;
-import org.glassfish.jersey.internal.util.JdkVersion;
-
-/**
- * Jetty HttpClient supplier to be registered into Jersey configuration to be used by {@link JettyConnector}.
- * Not every possible configuration option is covered by the Jetty Connector and this supplier offers a way to provide
- * an HttpClient that has configured the options not covered by the Jetty Connector.
- * <p>
- *     Typical usage:
- * </p>
- * <pre>
- * {@code
- * HttpClient httpClient = ...
- *
- * ClientConfig config = new ClientConfig();
- * config.connectorProvider(new JettyConnectorProvider());
- * config.register(new JettyHttpClientSupplier(httpClient));
- * Client client = ClientBuilder.newClient(config);
- * }
- * </pre>
- * <p>
- *     The {@code HttpClient} is configured as if it was created by {@link JettyConnector} the usual way.
- * </p>
- */
-public class JettyHttpClientSupplier implements JettyHttpClientContract {
-    private final HttpClient httpClient;
-
-    /**
-     * {@code HttpClient} supplier to be optionally registered to a {@link org.glassfish.jersey.client.ClientConfig}
-     * @param httpClient a HttpClient to be supplied when {@link JettyConnector#getHttpClient()} is called.
-     */
-    public JettyHttpClientSupplier(HttpClient httpClient) {
-        this.httpClient = httpClient;
-    }
-
-    @Override
-    public HttpClient getHttpClient() {
-        if (JdkVersion.getJdkVersion().getMajor() < 11) {
-            throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
-        }
-        return null; // does not work at JDK 1.8
-    }
-}
diff --git a/connectors/pom.xml b/connectors/pom.xml
index ba3844a..0853950 100644
--- a/connectors/pom.xml
+++ b/connectors/pom.xml
@@ -39,6 +39,7 @@
         <module>jdk-connector</module>
         <module>jetty-connector</module>
         <module>netty-connector</module>
+        <module>java-connector</module>
     </modules>
 
     <dependencies>
diff --git a/containers/glassfish/jersey-gf-ejb/pom.xml b/containers/glassfish/jersey-gf-ejb/pom.xml
index cb47571..b261c00 100644
--- a/containers/glassfish/jersey-gf-ejb/pom.xml
+++ b/containers/glassfish/jersey-gf-ejb/pom.xml
@@ -112,12 +112,12 @@
                 <extensions>true</extensions>
                 <configuration>
                     <instructions>
-                        <!-- Explicitly set versions for packages from GlassFish to allow future uptake of GlassFish 5.x-->
+                        <!-- Explicitly set versions for packages from GlassFish to allow future uptake of GlassFish 7.x-->
                         <Import-Package>
-                            com.sun.*;version="[4.0,7)",
+                            com.sun.*;version="[6.0,8)",
                             ${jakarta.annotation.osgi.version},
-                            org.glassfish.ejb.*;version="[4.0,7)",
-                            org.glassfish.internal.*;version="[4.0,7)",
+                            org.glassfish.ejb.*;version="[6.0,8)",
+                            org.glassfish.internal.*;version="[6.0,8)",
                             ${hk2.osgi.version},
                             *
                         </Import-Package>
diff --git a/containers/grizzly2-http/pom.xml b/containers/grizzly2-http/pom.xml
index 5c628c8..9083238 100644
--- a/containers/grizzly2-http/pom.xml
+++ b/containers/grizzly2-http/pom.xml
@@ -50,6 +50,11 @@
             <groupId>org.glassfish.grizzly</groupId>
             <artifactId>grizzly-http-server</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-library</artifactId>
+            <scope>test</scope>
+        </dependency>
 
         <dependency>
             <groupId>org.glassfish.grizzly</groupId>
diff --git a/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpContainer.java b/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpContainer.java
index 686f50f..616ea14 100644
--- a/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpContainer.java
+++ b/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpContainer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2021 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
@@ -307,9 +307,16 @@
      * @param application JAX-RS / Jersey application to be deployed on Grizzly HTTP container.
      */
     /* package */ GrizzlyHttpContainer(final Application application) {
-        this.appHandler = new ApplicationHandler(application, new GrizzlyBinder());
-        cacheConfigSetStatusOverSendError();
-        cacheConfigEnableLeadingContextPathSlashes();
+        this(new ApplicationHandler(application, new GrizzlyBinder()));
+    }
+
+    /**
+     * Create a new Grizzly HTTP container.
+     *
+     * @param applicationClass JAX-RS / Jersey application to be deployed on Grizzly HTTP container.
+     */
+    /* package */ GrizzlyHttpContainer(final Class<? extends Application> applicationClass) {
+        this(new ApplicationHandler(applicationClass, new GrizzlyBinder()));
     }
 
     /**
@@ -319,7 +326,11 @@
      * @param parentContext DI provider specific context with application's registered bindings.
      */
     /* package */ GrizzlyHttpContainer(final Application application, final Object parentContext) {
-        this.appHandler = new ApplicationHandler(application, new GrizzlyBinder(), parentContext);
+        this(new ApplicationHandler(application, new GrizzlyBinder(), parentContext));
+    }
+
+    private GrizzlyHttpContainer(ApplicationHandler applicationHandler) {
+        this.appHandler = applicationHandler;
         cacheConfigSetStatusOverSendError();
         cacheConfigEnableLeadingContextPathSlashes();
     }
diff --git a/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServer.java b/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServer.java
new file mode 100644
index 0000000..c3463dc
--- /dev/null
+++ b/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServer.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018 Markus KARG. 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.grizzly2.httpserver;
+
+import static jakarta.ws.rs.SeBootstrap.Configuration.SSLClientAuthentication.MANDATORY;
+import static jakarta.ws.rs.SeBootstrap.Configuration.SSLClientAuthentication.OPTIONAL;
+
+import java.io.IOException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+
+import javax.net.ssl.SSLContext;
+import jakarta.ws.rs.SeBootstrap;
+import jakarta.ws.rs.core.Application;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
+import org.glassfish.jersey.server.JerseySeBootstrapConfiguration;
+import org.glassfish.jersey.server.spi.WebServer;
+
+/**
+ * Jersey {@code Server} implementation based on Grizzly {@link HttpServer}.
+ *
+ * @author Markus KARG (markus@headcrashing.eu)
+ * @since 3.1.0
+ */
+final class GrizzlyHttpServer implements WebServer {
+
+    private final GrizzlyHttpContainer container;
+
+    private final HttpServer httpServer;
+
+    GrizzlyHttpServer(final Application application, final JerseySeBootstrapConfiguration configuration) {
+        this(new GrizzlyHttpContainer(application), configuration);
+    }
+
+    GrizzlyHttpServer(final Class<? extends Application> applicationClass,
+                      final JerseySeBootstrapConfiguration configuration) {
+        this(new GrizzlyHttpContainer(applicationClass), configuration);
+    }
+
+    private GrizzlyHttpServer(final GrizzlyHttpContainer container, final JerseySeBootstrapConfiguration configuration) {
+        final SSLContext sslContext = configuration.sslContext();
+        final SeBootstrap.Configuration.SSLClientAuthentication sslClientAuthentication = configuration
+                .sslClientAuthentication();
+
+        this.container = container;
+        this.httpServer = GrizzlyHttpServerFactory.createHttpServer(
+                configuration.uri(false),
+                this.container,
+                configuration.isHttps(),
+                configuration.isHttps() ? new SSLEngineConfigurator(sslContext, false,
+                        sslClientAuthentication == MANDATORY,
+                        sslClientAuthentication == OPTIONAL) : null,
+                configuration.autoStart());
+    }
+
+    @Override
+    public final GrizzlyHttpContainer container() {
+        return this.container;
+    }
+
+    @Override
+    public final int port() {
+        return this.httpServer.getListener("grizzly").getPort();
+    }
+
+    @Override
+    public final CompletableFuture<Void> start() {
+        return CompletableFuture.runAsync(() -> {
+            try {
+                this.httpServer.start();
+            } catch (final IOException e) {
+                throw new CompletionException(e);
+            }
+        });
+    }
+
+    @Override
+    public final CompletableFuture<Void> stop() {
+        return CompletableFuture.runAsync(this.httpServer::shutdownNow);
+    }
+
+    @Override
+    public final <T> T unwrap(final Class<T> nativeClass) {
+        return nativeClass.cast(this.httpServer);
+    }
+
+}
diff --git a/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerProvider.java b/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerProvider.java
new file mode 100644
index 0000000..a3f911c
--- /dev/null
+++ b/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerProvider.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018 Markus KARG. 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.grizzly2.httpserver;
+
+import jakarta.ws.rs.core.Application;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.jersey.server.JerseySeBootstrapConfiguration;
+import org.glassfish.jersey.server.spi.WebServer;
+import org.glassfish.jersey.server.spi.WebServerProvider;
+
+/**
+ * Server provider for servers based on Grizzly {@link HttpServer}.
+ *
+ * @author Markus KARG (markus@headcrashing.eu)
+ * @since 3.1.0
+ */
+public final class GrizzlyHttpServerProvider implements WebServerProvider {
+
+    @Override
+    public final <T extends WebServer> T createServer(final Class<T> type, final Application application,
+                                                      final JerseySeBootstrapConfiguration configuration) {
+        return GrizzlyHttpServer.class == type || WebServer.class == type
+                ? type.cast(new GrizzlyHttpServer(application, configuration))
+                : null;
+    }
+
+    @Override
+    public <T extends WebServer> T createServer(Class<T> type, Class<? extends Application> applicationClass,
+                                                JerseySeBootstrapConfiguration configuration) {
+        return GrizzlyHttpServer.class == type || WebServer.class == type
+                ? type.cast(new GrizzlyHttpServer(applicationClass, configuration))
+                : null;
+    }
+}
diff --git a/containers/grizzly2-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider b/containers/grizzly2-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider
new file mode 100644
index 0000000..dea5f90
--- /dev/null
+++ b/containers/grizzly2-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider
@@ -0,0 +1 @@
+org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerProvider
\ No newline at end of file
diff --git a/containers/grizzly2-http/src/test/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerProviderTest.java b/containers/grizzly2-http/src/test/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerProviderTest.java
new file mode 100644
index 0000000..a7310d0
--- /dev/null
+++ b/containers/grizzly2-http/src/test/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerProviderTest.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018 Markus KARG. 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.grizzly2.httpserver;
+
+import static java.lang.Boolean.FALSE;
+import static java.lang.Boolean.TRUE;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.junit.Assert.assertThat;
+
+import java.security.AccessController;
+import java.security.NoSuchAlgorithmException;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.ExecutionException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.net.ssl.SSLContext;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.SeBootstrap;
+import jakarta.ws.rs.SeBootstrap.Configuration.SSLClientAuthentication;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.UriBuilder;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.jersey.internal.util.PropertiesHelper;
+import org.glassfish.jersey.server.JerseySeBootstrapConfiguration;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.server.spi.Container;
+import org.glassfish.jersey.server.spi.WebServer;
+import org.glassfish.jersey.server.spi.WebServerProvider;
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link GrizzlyHttpServerProvider}.
+ *
+ * @author Markus KARG (markus@headcrashing.eu)
+ * @since 3.1.0
+ */
+public final class GrizzlyHttpServerProviderTest {
+
+    @Test(timeout = 15000)
+    public void shouldProvideServer() throws InterruptedException, ExecutionException {
+        // given
+        final Resource resource = new Resource();
+        shouldProvideServer(ShouldProvideServerApplication.class, resource);
+    }
+
+    @Test(timeout = 15000)
+    public void shouldProvideServerWithClass() throws InterruptedException, ExecutionException {
+        // given
+        final Resource resource = new Resource();
+        final Application application = new ShouldProvideServerApplication();
+        shouldProvideServer(application.getClass(), resource);
+    }
+
+    private void shouldProvideServer(final Object application, final Resource resource)
+            throws InterruptedException, ExecutionException {
+        // given
+        final WebServerProvider webServerProvider = new GrizzlyHttpServerProvider();
+        final SeBootstrap.Configuration configuration = configuration(getPort());
+
+        // when
+        final JerseySeBootstrapConfiguration jerseySeConfig = JerseySeBootstrapConfiguration.from(configuration);
+        final WebServer webServer = Application.class.isInstance(application)
+                ? webServerProvider.createServer(WebServer.class, (Application) application, jerseySeConfig)
+                : webServerProvider.createServer(WebServer.class, (Class<Application>) application, jerseySeConfig);
+        final Object nativeHandle = webServer.unwrap(Object.class);
+        final CompletionStage<?> start = webServer.start();
+        final Object startResult = start.toCompletableFuture().get();
+        final Container container = webServer.container();
+        final int port = webServer.port();
+        final String entity = ClientBuilder.newClient()
+                .target(UriBuilder.newInstance().scheme("http").host("localhost").port(port).build()).request()
+                .get(String.class);
+        final CompletionStage<?> stop = webServer.stop();
+        final Object stopResult = stop.toCompletableFuture().get();
+
+        // then
+        assertThat(webServer, is(instanceOf(GrizzlyHttpServer.class)));
+        assertThat(nativeHandle, is(instanceOf(HttpServer.class)));
+        assertThat(startResult, is(nullValue()));
+        assertThat(container, is(instanceOf(GrizzlyHttpContainer.class)));
+        assertThat(port, is(greaterThan(0)));
+        assertThat(entity, is(resource.toString()));
+        assertThat(stopResult, is(nullValue()));
+    }
+
+    @Path("/")
+    protected static final class Resource {
+        @GET
+        @Override
+        public String toString() {
+            return Resource.class.getName();
+        }
+    }
+
+    protected static class ShouldProvideServerApplication extends Application {
+        @Override
+        public final Set<Object> getSingletons() {
+            return Collections.singleton(new Resource());
+        }
+    }
+
+    private static final Logger LOGGER = Logger.getLogger(GrizzlyHttpServerProviderTest.class.getName());
+
+    private static final int DEFAULT_PORT = 0;
+
+    private static final int getPort() {
+        final String value = AccessController
+                .doPrivileged(PropertiesHelper.getSystemProperty("jersey.config.test.container.port"));
+        if (value != null) {
+            try {
+                final int i = Integer.parseInt(value);
+                if (i < 0) {
+                    throw new NumberFormatException("Value is negative.");
+                }
+                return i;
+            } catch (final NumberFormatException e) {
+                LOGGER.log(Level.CONFIG,
+                        "Value of 'jersey.config.test.container.port'"
+                                + " property is not a valid non-negative integer [" + value + "]."
+                                + " Reverting to default [" + DEFAULT_PORT + "].",
+                        e);
+            }
+        }
+
+        return DEFAULT_PORT;
+    }
+
+    @Test(timeout = 15000)
+    public void shouldScanFreePort() {
+        // given
+        final WebServerProvider webServerProvider = new GrizzlyHttpServerProvider();
+        final Application application = new Application();
+        final SeBootstrap.Configuration configuration = configuration(SeBootstrap.Configuration.FREE_PORT);
+
+        // when
+        final JerseySeBootstrapConfiguration jerseySeConfig = JerseySeBootstrapConfiguration.from(configuration);
+        final WebServer webServer = webServerProvider.createServer(WebServer.class, application, jerseySeConfig);
+
+        // then
+        assertThat(webServer.port(), is(greaterThan(0)));
+    }
+
+    private SeBootstrap.Configuration configuration(int port) {
+        return (SeBootstrap.Configuration) name -> {
+            switch (name) {
+                case SeBootstrap.Configuration.PROTOCOL:
+                    return "HTTP";
+                case SeBootstrap.Configuration.HOST:
+                    return "localhost";
+                case SeBootstrap.Configuration.PORT:
+                    return port;
+                case SeBootstrap.Configuration.ROOT_PATH:
+                    return "/";
+                case SeBootstrap.Configuration.SSL_CLIENT_AUTHENTICATION:
+                    return SSLClientAuthentication.NONE;
+                case SeBootstrap.Configuration.SSL_CONTEXT:
+                    try {
+                        return SSLContext.getDefault();
+                    } catch (final NoSuchAlgorithmException e) {
+                        throw new RuntimeException(e);
+                    }
+                case ServerProperties.WEBSERVER_AUTO_START:
+                    return TRUE;
+                default:
+                    return null;
+            }
+        };
+    }
+
+}
diff --git a/containers/grizzly2-http/src/test/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerTest.java b/containers/grizzly2-http/src/test/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerTest.java
index 5fb5e08..695c906 100644
--- a/containers/grizzly2-http/src/test/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerTest.java
+++ b/containers/grizzly2-http/src/test/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Payara Foundation and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2022 Payara Foundation 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/grizzly2-http/src/test/java/org/glassfish/jersey/grizzly2/httpserver/test/application/TestedEndpoint.java b/containers/grizzly2-http/src/test/java/org/glassfish/jersey/grizzly2/httpserver/test/application/TestedEndpoint.java
index 3ad7fb8..0a195f1 100644
--- a/containers/grizzly2-http/src/test/java/org/glassfish/jersey/grizzly2/httpserver/test/application/TestedEndpoint.java
+++ b/containers/grizzly2-http/src/test/java/org/glassfish/jersey/grizzly2/httpserver/test/application/TestedEndpoint.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Payara Foundation and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2022 Payara Foundation 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/grizzly2-servlet/pom.xml b/containers/grizzly2-servlet/pom.xml
index feaf63a..02fe0f2 100644
--- a/containers/grizzly2-servlet/pom.xml
+++ b/containers/grizzly2-servlet/pom.xml
@@ -36,7 +36,6 @@
         <dependency>
             <groupId>jakarta.servlet</groupId>
             <artifactId>jakarta.servlet-api</artifactId>
-            <version>${servlet5.version}</version>
         </dependency>
 
         <dependency>
@@ -71,7 +70,7 @@
                 <configuration>
                     <instructions>
                         <Import-Package>
-                            jakarta.servlet.*;version="[5.0,6.0)",
+                            jakarta.servlet.*;version="[5.0,7.0)",
                             *
                         </Import-Package>
                     </instructions>
diff --git a/containers/jdk-http/pom.xml b/containers/jdk-http/pom.xml
index 8420b2b..7b1d28d 100644
--- a/containers/jdk-http/pom.xml
+++ b/containers/jdk-http/pom.xml
@@ -38,6 +38,11 @@
             <artifactId>guava</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-library</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
@@ -74,45 +79,8 @@
         </resources>
     </build>
 
-    <profiles>
-        <profile>
-            <id>jdk11+</id>
-            <activation>
-                <jdk>[11,)</jdk>
-            </activation>
-            <properties>
-                <!-- https://bugs.openjdk.java.net/browse/JDK-8211426 -->
-                <surefire.security.argline>-Djdk.tls.client.protocols=TLSv1.2</surefire.security.argline>
-            </properties>
-        </profile>
-        <profile>
-            <id>windows</id>
-            <activation>
-                <jdk>1.8</jdk>
-                <os>
-                    <family>windows</family>
-                </os>
-            </activation>
-            <build>
-                <plugins>
-                    <plugin>
-                        <groupId>org.apache.maven.plugins</groupId>
-                        <artifactId>maven-surefire-plugin</artifactId>
-                        <configuration>
-                            <!-- Exclude unit tests regarding JDK HTTP Server because of failing a server shutdown in a class
-                            with several tests. "java.net.BindException: Address already in use: bind"
-                            bug reported on https://bugs.openjdk.java.net/browse/JDK-8015692 -->
-                            <excludes>
-                                <exclude>org/glassfish/jersey/jdkhttp/BasicJdkHttpServerTest.java</exclude>
-                                <exclude>org/glassfish/jersey/jdkhttp/JdkHttpPackageTest.java</exclude>
-                                <exclude>org/glassfish/jersey/jdkhttp/JdkHttpsServerTest.java</exclude>
-                                <exclude>org/glassfish/jersey/jdkhttp/LifecycleListenerTest.java</exclude>
-                            </excludes>
-                        </configuration>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-    </profiles>
-
+    <properties>
+        <!-- https://bugs.openjdk.java.net/browse/JDK-8211426 -->
+        <surefire.security.argline>-Djdk.tls.client.protocols=TLSv1.2</surefire.security.argline>
+    </properties>
 </project>
diff --git a/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpHandlerContainer.java b/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpHandlerContainer.java
index 89f47d2..5acf6a1 100644
--- a/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpHandlerContainer.java
+++ b/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpHandlerContainer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2021 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
@@ -64,7 +64,7 @@
     private volatile ApplicationHandler appHandler;
 
     /**
-     * Create new lightweight Java SE HTTP server container.
+     * Create new lightweight Java SE HTTP server container.
      *
      * @param application JAX-RS / Jersey application to be deployed on the container.
      */
@@ -73,7 +73,16 @@
     }
 
     /**
-     * Create new lightweight Java SE HTTP server container.
+     * Create new lightweight Java SE HTTP server container.
+     *
+     * @param applicationClass class of JAX-RS / Jersey application to be deployed on the container.
+     */
+    JdkHttpHandlerContainer(final Class<? extends Application> applicationClass) {
+        this.appHandler = new ApplicationHandler(applicationClass);
+    }
+
+    /**
+     * Create new lightweight Java SE HTTP server container.
      *
      * @param application   JAX-RS / Jersey application to be deployed on the container.
      * @param parentContext DI provider specific context with application's registered bindings.
diff --git a/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpServer.java b/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpServer.java
new file mode 100644
index 0000000..d53a758
--- /dev/null
+++ b/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpServer.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018 Markus KARG. 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.jdkhttp;
+
+import static jakarta.ws.rs.SeBootstrap.Configuration.SSLClientAuthentication.MANDATORY;
+import static jakarta.ws.rs.SeBootstrap.Configuration.SSLClientAuthentication.OPTIONAL;
+
+import java.util.concurrent.CompletableFuture;
+
+import jakarta.ws.rs.SeBootstrap;
+import jakarta.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.JerseySeBootstrapConfiguration;
+import org.glassfish.jersey.server.spi.WebServer;
+
+import com.sun.net.httpserver.HttpServer;
+
+/**
+ * Jersey {@code Server} implementation based on JDK {@link HttpServer}.
+ *
+ * @author Markus KARG (markus@headcrashing.eu)
+ * @since 3.1.0
+ */
+final class JdkHttpServer implements WebServer {
+
+    private final JdkHttpHandlerContainer container;
+
+    private final HttpServer httpServer;
+
+    JdkHttpServer(final Application application, final JerseySeBootstrapConfiguration configuration) {
+        this(new JdkHttpHandlerContainer(application), configuration);
+    }
+
+    JdkHttpServer(final Class<? extends Application> applicationClass,
+                  final JerseySeBootstrapConfiguration configuration) {
+        this(new JdkHttpHandlerContainer(applicationClass), configuration);
+    }
+
+    JdkHttpServer(final JdkHttpHandlerContainer container, final JerseySeBootstrapConfiguration configuration) {
+        final SeBootstrap.Configuration.SSLClientAuthentication sslClientAuthentication = configuration
+                .sslClientAuthentication();
+
+        this.container = container;
+        this.httpServer = JdkHttpServerFactory.createHttpServer(
+                configuration.uri(false),
+                this.container,
+                configuration.sslContext(),
+                sslClientAuthentication == OPTIONAL,
+                sslClientAuthentication == MANDATORY,
+                configuration.autoStart());
+    }
+
+    @Override
+    public final JdkHttpHandlerContainer container() {
+        return this.container;
+    }
+
+    @Override
+    public final int port() {
+        return this.httpServer.getAddress().getPort();
+    }
+
+    @Override
+    public final CompletableFuture<Void> start() {
+        return CompletableFuture.runAsync(this.httpServer::start);
+    }
+
+    @Override
+    public final CompletableFuture<Void> stop() {
+        return CompletableFuture.runAsync(() -> this.httpServer.stop(0));
+    }
+
+    @Override
+    public final <T> T unwrap(final Class<T> nativeClass) {
+        return nativeClass.cast(this.httpServer);
+    }
+
+}
diff --git a/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpServerFactory.java b/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpServerFactory.java
index 1c60336..a136edf 100644
--- a/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpServerFactory.java
+++ b/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpServerFactory.java
@@ -26,6 +26,7 @@
 import jakarta.ws.rs.ProcessingException;
 
 import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLParameters;
 
 import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;
 import org.glassfish.jersey.jdkhttp.internal.LocalizationMessages;
@@ -37,6 +38,7 @@
 import com.sun.net.httpserver.HttpHandler;
 import com.sun.net.httpserver.HttpServer;
 import com.sun.net.httpserver.HttpsConfigurator;
+import com.sun.net.httpserver.HttpsParameters;
 import com.sun.net.httpserver.HttpsServer;
 
 /**
@@ -177,8 +179,17 @@
     }
 
     private static HttpServer createHttpServer(final URI uri,
+            final JdkHttpHandlerContainer handler,
+            final SSLContext sslContext,
+            final boolean start) {
+        return createHttpServer(uri, handler, sslContext, false, false, start);
+    }
+
+    static HttpServer createHttpServer(final URI uri,
                                                final JdkHttpHandlerContainer handler,
                                                final SSLContext sslContext,
+                                               final boolean sslClientAuthWanted,
+                                               final boolean sslClientAuthNeeded,
                                                final boolean start) {
         if (uri == null) {
             throw new IllegalArgumentException(LocalizationMessages.ERROR_CONTAINER_URI_NULL());
@@ -187,7 +198,14 @@
         final String scheme = uri.getScheme();
         final boolean isHttp = "http".equalsIgnoreCase(scheme);
         final boolean isHttps = "https".equalsIgnoreCase(scheme);
-        final HttpsConfigurator httpsConfigurator = sslContext != null ? new HttpsConfigurator(sslContext) : null;
+        final HttpsConfigurator httpsConfigurator = sslContext != null ? new HttpsConfigurator(sslContext) {
+            public final void configure(final HttpsParameters httpsParameters) {
+                final SSLParameters sslParameters = sslContext.getDefaultSSLParameters();
+                sslParameters.setWantClientAuth(sslClientAuthWanted);
+                sslParameters.setNeedClientAuth(sslClientAuthNeeded);
+                httpsParameters.setSSLParameters(sslParameters);
+            }
+        } : null;
 
         if (isHttp) {
             if (httpsConfigurator != null) {
diff --git a/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpServerProvider.java b/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpServerProvider.java
new file mode 100644
index 0000000..4510c53
--- /dev/null
+++ b/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpServerProvider.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018 Markus KARG. 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.jdkhttp;
+
+import jakarta.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.JerseySeBootstrapConfiguration;
+import org.glassfish.jersey.server.spi.WebServer;
+import org.glassfish.jersey.server.spi.WebServerProvider;
+
+import com.sun.net.httpserver.HttpServer;
+
+/**
+ * Server provider for servers based on JDK {@link HttpServer}.
+ *
+ * @author Markus KARG (markus@headcrashing.eu)
+ * @since 3.1.0
+ */
+public final class JdkHttpServerProvider implements WebServerProvider {
+
+    @Override
+    public <T extends WebServer> T createServer(final Class<T> type, final Application application,
+                                                      final JerseySeBootstrapConfiguration configuration) {
+        return JdkHttpServer.class == type || WebServer.class == type
+                ? type.cast(new JdkHttpServer(application, configuration))
+                : null;
+    }
+
+    @Override
+    public <T extends WebServer> T createServer(final Class<T> type, final Class<? extends Application> applicationClass,
+                                                final JerseySeBootstrapConfiguration configuration) {
+        return JdkHttpServer.class == type || WebServer.class == type
+                ? type.cast(new JdkHttpServer(applicationClass, configuration))
+                : null;
+    }
+}
diff --git a/containers/jdk-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider b/containers/jdk-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider
new file mode 100644
index 0000000..2d8abc3
--- /dev/null
+++ b/containers/jdk-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider
@@ -0,0 +1 @@
+org.glassfish.jersey.jdkhttp.JdkHttpServerProvider
\ No newline at end of file
diff --git a/containers/jdk-http/src/test/java/org/glassfish/jersey/jdkhttp/AbstractJdkHttpServerTester.java b/containers/jdk-http/src/test/java/org/glassfish/jersey/jdkhttp/AbstractJdkHttpServerTester.java
index c7cd54b..03070a9 100644
--- a/containers/jdk-http/src/test/java/org/glassfish/jersey/jdkhttp/AbstractJdkHttpServerTester.java
+++ b/containers/jdk-http/src/test/java/org/glassfish/jersey/jdkhttp/AbstractJdkHttpServerTester.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2021 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
@@ -21,6 +21,7 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import javax.net.ssl.SSLContext;
 import jakarta.ws.rs.core.UriBuilder;
 
 import com.sun.net.httpserver.HttpServer;
@@ -37,7 +38,7 @@
 public abstract class AbstractJdkHttpServerTester {
 
     public static final String CONTEXT = "";
-    private final int DEFAULT_PORT = 9998;
+    private final int DEFAULT_PORT = 0;
 
     private static final Logger LOGGER = Logger.getLogger(AbstractJdkHttpServerTester.class.getName());
 
@@ -47,20 +48,24 @@
      * @return The HTTP port of the URI
      */
     protected final int getPort() {
-        final String value =
-                AccessController.doPrivileged(PropertiesHelper.getSystemProperty("jersey.config.test.container.port"));
+        if (server != null) {
+            return server.getAddress().getPort();
+        }
+
+        final String value = AccessController.doPrivileged(
+                PropertiesHelper.getSystemProperty("jersey.config.test.container.port"));
         if (value != null) {
 
             try {
                 final int i = Integer.parseInt(value);
-                if (i <= 0) {
-                    throw new NumberFormatException("Value not positive.");
+                if (i < 0) {
+                    throw new NumberFormatException("Value is negative.");
                 }
                 return i;
             } catch (NumberFormatException e) {
                 LOGGER.log(Level.CONFIG,
                         "Value of 'jersey.config.test.container.port'"
-                                + " property is not a valid positive integer [" + value + "]."
+                                + " property is not a valid non-negative integer [" + value + "]."
                                 + " Reverting to default [" + DEFAULT_PORT + "].",
                         e);
             }
@@ -76,17 +81,23 @@
 
     public void startServer(Class... resources) {
         ResourceConfig config = new ResourceConfig(resources);
-        config.register(LoggingFeature.class);
-        final URI baseUri = getBaseUri();
-        server = JdkHttpServerFactory.createHttpServer(baseUri, config);
-        LOGGER.log(Level.INFO, "jdk-http server started on base uri: " + baseUri);
+        startServer(config);
     }
 
     public void startServer(ResourceConfig config) {
         final URI baseUri = getBaseUri();
         config.register(LoggingFeature.class);
         server = JdkHttpServerFactory.createHttpServer(baseUri, config);
-        LOGGER.log(Level.INFO, "jdk-http server started on base uri: " + baseUri);
+        LOGGER.log(Level.INFO, "jdk-http server started on base uri: " + getBaseUri());
+    }
+
+    public HttpServer startServer(final URI uri, final ResourceConfig config,
+            final SSLContext sslContext, final boolean start) {
+        config.register(LoggingFeature.class);
+        server = JdkHttpServerFactory.createHttpServer(uri, config, sslContext, start);
+        LOGGER.log(Level.INFO,
+                "jdk-http server started on base uri: " + UriBuilder.fromUri(uri).port(getPort()).build());
+        return server;
     }
 
     public URI getBaseUri() {
diff --git a/containers/jdk-http/src/test/java/org/glassfish/jersey/jdkhttp/JdkHttpServerProviderTest.java b/containers/jdk-http/src/test/java/org/glassfish/jersey/jdkhttp/JdkHttpServerProviderTest.java
new file mode 100644
index 0000000..ade5284
--- /dev/null
+++ b/containers/jdk-http/src/test/java/org/glassfish/jersey/jdkhttp/JdkHttpServerProviderTest.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018 Markus KARG. 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.jdkhttp;
+
+import static java.lang.Boolean.FALSE;
+import static java.lang.Boolean.TRUE;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.junit.Assert.assertThat;
+
+import java.security.AccessController;
+import java.security.NoSuchAlgorithmException;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.ExecutionException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.net.ssl.SSLContext;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.SeBootstrap;
+import jakarta.ws.rs.SeBootstrap.Configuration.SSLClientAuthentication;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.internal.util.PropertiesHelper;
+import org.glassfish.jersey.server.JerseySeBootstrapConfiguration;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.server.spi.Container;
+import org.glassfish.jersey.server.spi.WebServer;
+import org.glassfish.jersey.server.spi.WebServerProvider;
+import org.junit.Test;
+
+import com.sun.net.httpserver.HttpServer;
+
+/**
+ * Unit tests for {@link JdkHttpServerProvider}.
+ *
+ * @author Markus KARG (markus@headcrashing.eu)
+ * @since 3.1.0
+ */
+public final class JdkHttpServerProviderTest {
+
+    @Test(timeout = 15000)
+    public void shouldProvideServer() throws InterruptedException, ExecutionException {
+        // given
+        final Resource resource = new Resource();
+        shouldProvideServer(ShouldProvideServerApplication.class, resource);
+    }
+
+    @Test(timeout = 15000)
+    public void shouldProvideServerWithClass() throws InterruptedException, ExecutionException {
+        // given
+        final Resource resource = new Resource();
+        final Application application = new ShouldProvideServerApplication();
+        shouldProvideServer(application.getClass(), resource);
+    }
+
+    private void shouldProvideServer(final Object application, final Resource resource)
+            throws InterruptedException, ExecutionException {
+        // given
+        final WebServerProvider webServerProvider = new JdkHttpServerProvider();
+        final SeBootstrap.Configuration configuration = configuration(getPort());
+
+        // when
+        final JerseySeBootstrapConfiguration jerseySeConfig = JerseySeBootstrapConfiguration.from(configuration);
+        final WebServer webServer = Application.class.isInstance(application)
+                ? webServerProvider.createServer(WebServer.class, (Application) application, jerseySeConfig)
+                : webServerProvider.createServer(WebServer.class, (Class<Application>) application, jerseySeConfig);
+        final Object nativeHandle = webServer.unwrap(Object.class);
+        final CompletionStage<?> start = webServer.start();
+        final Object startResult = start.toCompletableFuture().get();
+        final Container container = webServer.container();
+        final int port = webServer.port();
+        final String entity = ClientBuilder.newClient()
+                .target(UriBuilder.newInstance().scheme("http").host("localhost").port(port).build()).request()
+                .get(String.class);
+        final CompletionStage<?> stop = webServer.stop();
+        final Object stopResult = stop.toCompletableFuture().get();
+
+        // then
+        assertThat(webServer, is(instanceOf(JdkHttpServer.class)));
+        assertThat(nativeHandle, is(instanceOf(HttpServer.class)));
+        assertThat(startResult, is(nullValue()));
+        assertThat(container, is(instanceOf(JdkHttpHandlerContainer.class)));
+        assertThat(port, is(greaterThan(0)));
+        assertThat(entity, is(resource.toString()));
+        assertThat(stopResult, is(nullValue()));
+    }
+
+    @Path("/")
+    protected static final class Resource {
+        @GET
+        @Override
+        public String toString() {
+            return Resource.class.getName();
+        }
+    }
+
+    protected static class ShouldProvideServerApplication extends Application {
+        @Override
+        public Set<Object> getSingletons() {
+            return Collections.singleton(new Resource());
+        }
+    }
+
+    private static final Logger LOGGER = Logger.getLogger(JdkHttpServerProviderTest.class.getName());
+
+    private static final int DEFAULT_PORT = 0;
+
+    private static final int getPort() {
+        final String value = AccessController
+                .doPrivileged(PropertiesHelper.getSystemProperty("jersey.config.test.container.port"));
+        if (value != null) {
+            try {
+                final int i = Integer.parseInt(value);
+                if (i < 0) {
+                    throw new NumberFormatException("Value is negative.");
+                }
+                return i;
+            } catch (final NumberFormatException e) {
+                LOGGER.log(Level.CONFIG,
+                        "Value of 'jersey.config.test.container.port'"
+                                + " property is not a valid non-negative integer [" + value + "]."
+                                + " Reverting to default [" + DEFAULT_PORT + "].",
+                        e);
+            }
+        }
+
+        return DEFAULT_PORT;
+    }
+
+    @Test(timeout = 15000)
+    public final void shouldScanFreePort() throws InterruptedException, ExecutionException {
+        // given
+        final WebServerProvider webServerProvider = new JdkHttpServerProvider();
+        final Application application = new Application();
+        final SeBootstrap.Configuration configuration = configuration(SeBootstrap.Configuration.FREE_PORT);
+
+        // when
+        final JerseySeBootstrapConfiguration jerseySeConfig = JerseySeBootstrapConfiguration.from(configuration);
+        final WebServer webServer = webServerProvider.createServer(WebServer.class, application, jerseySeConfig);
+
+        // then
+        assertThat(webServer.port(), is(greaterThan(0)));
+    }
+
+    private SeBootstrap.Configuration configuration(int port) {
+        return (SeBootstrap.Configuration) name -> {
+            switch (name) {
+                case SeBootstrap.Configuration.PROTOCOL:
+                    return "HTTP";
+                case SeBootstrap.Configuration.HOST:
+                    return "localhost";
+                case SeBootstrap.Configuration.PORT:
+                    return port;
+                case SeBootstrap.Configuration.ROOT_PATH:
+                    return "/";
+                case SeBootstrap.Configuration.SSL_CLIENT_AUTHENTICATION:
+                    return SSLClientAuthentication.NONE;
+                case SeBootstrap.Configuration.SSL_CONTEXT:
+                    try {
+                        return SSLContext.getDefault();
+                    } catch (final NoSuchAlgorithmException e) {
+                        throw new RuntimeException(e);
+                    }
+                case ServerProperties.WEBSERVER_AUTO_START:
+                    return FALSE;
+                default:
+                    return null;
+            }
+        };
+    }
+}
diff --git a/containers/jdk-http/src/test/java/org/glassfish/jersey/jdkhttp/JdkHttpsServerTest.java b/containers/jdk-http/src/test/java/org/glassfish/jersey/jdkhttp/JdkHttpsServerTest.java
index 64ae39a..a1fc7b3 100644
--- a/containers/jdk-http/src/test/java/org/glassfish/jersey/jdkhttp/JdkHttpsServerTest.java
+++ b/containers/jdk-http/src/test/java/org/glassfish/jersey/jdkhttp/JdkHttpsServerTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2021 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
@@ -31,10 +31,8 @@
 import javax.net.ssl.SSLHandshakeException;
 
 import org.glassfish.jersey.SslConfigurator;
-import org.glassfish.jersey.internal.util.JdkVersion;
 import org.glassfish.jersey.server.ResourceConfig;
 
-import org.junit.After;
 import org.junit.Test;
 
 import com.google.common.io.ByteStreams;
@@ -65,9 +63,6 @@
     private static final String TRUSTSTORE_SERVER_FILE = "./truststore_server";
     private static final String TRUSTSTORE_SERVER_PWD = "asdfgh";
 
-    private HttpServer server;
-    private final URI httpsUri = UriBuilder.fromUri("https://localhost/").port(getPort()).build();
-    private final URI httpUri = UriBuilder.fromUri("http://localhost/").port(getPort()).build();
     private final ResourceConfig rc = new ResourceConfig(TestResource.class);
 
     @Path("/testHttps")
@@ -84,12 +79,8 @@
      */
     @Test
     public void testCreateHttpsServerNoSslContext() throws Exception {
-        server = JdkHttpServerFactory.createHttpServer(httpsUri, rc, null, false);
+        HttpServer server = startServer(getHttpsUri(), rc, null, false);
         assertThat(server, instanceOf(HttpsServer.class));
-
-        if (JdkVersion.getJdkVersion().getMajor() > 8) {
-            server.start(); // Address already in bind otherwise
-        }
     }
 
     /**
@@ -98,7 +89,7 @@
      */
     @Test(expected = IllegalArgumentException.class)
     public void testStartHttpServerNoSslContext() throws Exception {
-        server = JdkHttpServerFactory.createHttpServer(httpsUri, rc, null, true);
+        startServer(getHttpsUri(), rc, null, true);
     }
 
     /**
@@ -108,13 +99,13 @@
      */
     @Test(expected = SSLHandshakeException.class)
     public void testCreateHttpsServerDefaultSslContext() throws Throwable {
-        server = JdkHttpServerFactory.createHttpServer(httpsUri, rc, SSLContext.getDefault(), true);
+        HttpServer server = startServer(getHttpsUri(), rc, SSLContext.getDefault(), true);
         assertThat(server, instanceOf(HttpsServer.class));
 
         // access the https server with not configured client
         final Client client = ClientBuilder.newBuilder().newClient();
         try {
-            client.target(httpsUri).path("testHttps").request().get(String.class);
+            client.target(UriBuilder.fromUri("https://localhost/").port(getPort())).path("testHttps").request().get(String.class);
         } catch (final ProcessingException e) {
             throw e.getCause();
         }
@@ -127,13 +118,13 @@
      */
     @Test(expected = IOException.class)
     public void testHttpsServerNoSslContextDelayedStart() throws Throwable {
-        server = JdkHttpServerFactory.createHttpServer(httpsUri, rc, null, false);
+        HttpServer server = startServer(getHttpsUri(), rc, null, false);
         assertThat(server, instanceOf(HttpsServer.class));
         server.start();
 
         final Client client = ClientBuilder.newBuilder().newClient();
         try {
-            client.target(httpsUri).path("testHttps").request().get(String.class);
+            client.target(getHttpsUri()).path("testHttps").request().get(String.class);
         } catch (final ProcessingException e) {
             throw e.getCause();
         }
@@ -145,7 +136,7 @@
      */
     @Test(expected = IllegalStateException.class)
     public void testConfigureSslContextAfterStart() throws Throwable {
-        server = JdkHttpServerFactory.createHttpServer(httpsUri, rc, null, false);
+        HttpServer server = startServer(getHttpsUri(), rc, null, false);
         assertThat(server, instanceOf(HttpsServer.class));
         server.start();
         ((HttpsServer) server).setHttpsConfigurator(new HttpsConfigurator(getServerSslContext()));
@@ -159,14 +150,14 @@
     public void testCreateHttpsServerRoundTrip() throws IOException {
         final SSLContext serverSslContext = getServerSslContext();
 
-        server = JdkHttpServerFactory.createHttpServer(httpsUri, rc, serverSslContext, true);
+        HttpServer server = startServer(getHttpsUri(), rc, serverSslContext, true);
 
         final SSLContext foundContext = ((HttpsServer) server).getHttpsConfigurator().getSSLContext();
         assertEquals(serverSslContext, foundContext);
 
         final SSLContext clientSslContext = getClientSslContext();
         final Client client = ClientBuilder.newBuilder().sslContext(clientSslContext).build();
-        final String response = client.target(httpsUri).path("testHttps").request().get(String.class);
+        final String response = client.target(UriBuilder.fromUri("https://localhost/").port(getPort())).path("testHttps").request().get(String.class);
 
         assertEquals("test", response);
     }
@@ -177,7 +168,7 @@
      */
     @Test
     public void testHttpWithSsl() throws IOException {
-        server = JdkHttpServerFactory.createHttpServer(httpUri, rc, getServerSslContext(), true);
+        HttpServer server = startServer(getBaseUri(), rc, getServerSslContext(), true);
         assertThat(server, instanceOf(HttpServer.class));
         assertThat(server, not(instanceOf(HttpsServer.class)));
     }
@@ -209,11 +200,7 @@
         return sslConfigServer.createSSLContext();
     }
 
-    @After
-    public void tearDown() {
-        if (server != null) {
-            server.stop(0);
-            server = null;
-        }
+    private URI getHttpsUri() {
+        return UriBuilder.fromUri("https://localhost/").port(getPort()).build();
     }
 }
diff --git a/containers/jersey-servlet-core/pom.xml b/containers/jersey-servlet-core/pom.xml
index 439888c..2bcfc85 100644
--- a/containers/jersey-servlet-core/pom.xml
+++ b/containers/jersey-servlet-core/pom.xml
@@ -36,7 +36,7 @@
         <dependency>
             <groupId>jakarta.servlet</groupId>
             <artifactId>jakarta.servlet-api</artifactId>
-            <version>${servlet5.version}</version>
+            <version>${servlet6.version}</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
@@ -71,7 +71,7 @@
                         <!-- Note: When you're changing these properties change them also in bundles/jax-rs-ri/bundle/pom.xml. -->
                         <Import-Package>
                             jakarta.persistence.*;resolution:=optional,
-                            jakarta.servlet.*;version="[5.0,6.0)",
+                            jakarta.servlet.*;version="[5.0,7.0)",
                             ${jakarta.annotation.osgi.version},
                             *
                         </Import-Package>
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/ServletContainer.java b/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/ServletContainer.java
index d91c43a..1f3d80a 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/ServletContainer.java
+++ b/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/ServletContainer.java
@@ -317,8 +317,7 @@
         final Response.Status badRequest = Response.Status.BAD_REQUEST;
         if (webComponent.configSetStatusOverSendError) {
             response.reset();
-            //noinspection deprecation
-            response.setStatus(badRequest.getStatusCode(), badRequest.getReasonPhrase());
+            response.setStatus(badRequest.getStatusCode());
         } else {
             response.sendError(badRequest.getStatusCode(), badRequest.getReasonPhrase());
         }
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebComponent.java b/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebComponent.java
index 3aaf758..0a12abf 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebComponent.java
+++ b/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebComponent.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2022 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
@@ -401,8 +401,7 @@
 
             if (configSetStatusOverSendError) {
                 servletResponse.reset();
-                //noinspection deprecation
-                servletResponse.setStatus(status.getStatusCode(), status.getReasonPhrase());
+                servletResponse.setStatus(status.getStatusCode());
             } else {
                 servletResponse.sendError(status.getStatusCode(), status.getReasonPhrase());
             }
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/ResponseWriter.java b/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/ResponseWriter.java
index 0db4e76..81205c0 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/ResponseWriter.java
+++ b/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/ResponseWriter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2022 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
@@ -144,7 +144,7 @@
 
         final String reasonPhrase = responseContext.getStatusInfo().getReasonPhrase();
         if (reasonPhrase != null) {
-            response.setStatus(responseContext.getStatus(), reasonPhrase);
+            response.setStatus(responseContext.getStatus());
         } else {
             response.setStatus(responseContext.getStatus());
         }
@@ -217,7 +217,7 @@
                     if (configSetStatusOverSendError) {
                         response.reset();
                         //noinspection deprecation
-                        response.setStatus(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), "Request failed.");
+                        response.setStatus(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode());
                     } else {
                         response.sendError(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), "Request failed.");
                     }
diff --git a/containers/jersey-servlet/pom.xml b/containers/jersey-servlet/pom.xml
index db15ede..b5244a6 100644
--- a/containers/jersey-servlet/pom.xml
+++ b/containers/jersey-servlet/pom.xml
@@ -36,7 +36,7 @@
         <dependency>
             <groupId>jakarta.servlet</groupId>
             <artifactId>jakarta.servlet-api</artifactId>
-            <version>${servlet5.version}</version>
+            <version>${servlet6.version}</version>
             <scope>provided</scope>
         </dependency>
 
@@ -74,7 +74,7 @@
                     <instructions>
                         <!-- Note: When you're changing these properties change them also in bundles/jax-rs-ri/bundle/pom.xml. -->
                         <Import-Package>
-                            jakarta.servlet.*;version="[5.0,6.0)",
+                            jakarta.servlet.*;version="[5.0,7.0)",
                             ${jakarta.annotation.osgi.version},
                             *
                         </Import-Package>
diff --git a/containers/jetty-http/pom.xml b/containers/jetty-http/pom.xml
index e601b0b..52930aa 100644
--- a/containers/jetty-http/pom.xml
+++ b/containers/jetty-http/pom.xml
@@ -62,6 +62,11 @@
             <artifactId>httpclient</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-library</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
@@ -91,162 +96,4 @@
         </resources>
     </build>
 
-    <properties>
-        <java8.build.outputDirectory>${project.basedir}/target</java8.build.outputDirectory>
-        <java8.sourceDirectory>${project.basedir}/src/main/java8</java8.sourceDirectory>
-        <java11.build.outputDirectory>${project.basedir}/target11</java11.build.outputDirectory>
-        <java11.sourceDirectory>${project.basedir}/src/main/java11</java11.sourceDirectory>
-    </properties>
-
-    <profiles>
-        <profile>
-            <id>JettyExclude</id>
-            <activation>
-                <jdk>1.8</jdk>
-            </activation>
-            <properties>
-                <jetty.version>9.4.28.v20200408</jetty.version>
-            </properties>
-            <dependencies>
-                <dependency>
-                    <groupId>org.eclipse.jetty</groupId>
-                    <artifactId>jetty-client</artifactId>
-                    <version>${jetty.version}</version>
-                </dependency>
-                <dependency>
-                    <groupId>org.eclipse.jetty</groupId>
-                    <artifactId>jetty-util</artifactId>
-                    <version>${jetty.version}</version>
-                </dependency>
-            </dependencies>
-            <build>
-                <directory>${java8.build.outputDirectory}</directory>
-                <plugins>
-                    <plugin>
-                        <groupId>org.codehaus.mojo</groupId>
-                        <artifactId>build-helper-maven-plugin</artifactId>
-                        <executions>
-                            <execution>
-                                <phase>generate-sources</phase>
-                                <goals>
-                                    <goal>add-source</goal>
-                                </goals>
-                                <configuration>
-                                    <sources>
-                                        <source>${java8.sourceDirectory}</source>
-                                    </sources>
-                                </configuration>
-                            </execution>
-                        </executions>
-                    </plugin>
-                    <plugin>
-                        <groupId>org.apache.maven.plugins</groupId>
-                        <artifactId>maven-compiler-plugin</artifactId>
-                        <configuration>
-                            <testExcludes>
-                                <testExclude>org/glassfish/jersey/jetty/*.java</testExclude>
-                            </testExcludes>
-                        </configuration>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-        <profile>
-            <id>Jetty11</id>
-            <activation>
-                <jdk>[11,)</jdk>
-            </activation>
-            <build>
-                <directory>${java11.build.outputDirectory}</directory>
-                <plugins>
-                    <plugin>
-                        <groupId>org.codehaus.mojo</groupId>
-                        <artifactId>build-helper-maven-plugin</artifactId>
-                        <executions>
-                            <execution>
-                                <phase>generate-sources</phase>
-                                <goals>
-                                    <goal>add-source</goal>
-                                </goals>
-                                <configuration>
-                                    <sources>
-                                        <source>${java11.sourceDirectory}</source>
-                                    </sources>
-                                </configuration>
-                            </execution>
-                        </executions>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-        <profile>
-            <id>copyJDK11FilesToMultiReleaseJar</id>
-            <activation>
-                <file>
-                    <!-- ${java11.build.outputDirectory} does not work here -->
-                    <exists>target11/classes/org/glassfish/jersey/jetty/JettyHttpContainer.class</exists>
-                </file>
-                <jdk>1.8</jdk>
-            </activation>
-            <build>
-                <plugins>
-                    <plugin>
-                        <groupId>org.apache.felix</groupId>
-                        <artifactId>maven-bundle-plugin</artifactId>
-                        <inherited>true</inherited>
-                        <extensions>true</extensions>
-                        <configuration>
-                            <instructions>
-                                <Multi-Release>true</Multi-Release>
-                            </instructions>
-                        </configuration>
-                    </plugin>
-                    <plugin>
-                        <groupId>org.apache.maven.plugins</groupId>
-                        <artifactId>maven-resources-plugin</artifactId>
-                        <inherited>true</inherited>
-                        <executions>
-                            <execution>
-                                <id>copy-jdk11-classes</id>
-                                <phase>prepare-package</phase>
-                                <goals>
-                                    <goal>copy-resources</goal>
-                                </goals>
-                                <configuration>
-                                    <outputDirectory>${java8.build.outputDirectory}/classes/META-INF/versions/11</outputDirectory>
-                                    <resources>
-                                        <resource>
-                                            <directory>${java11.build.outputDirectory}/classes</directory>
-                                        </resource>
-                                    </resources>
-                                </configuration>
-                            </execution>
-                        </executions>
-                    </plugin>
-                    <plugin>
-                        <groupId>org.apache.maven.plugins</groupId>
-                        <artifactId>maven-antrun-plugin</artifactId>
-                        <executions>
-                            <execution>
-                                <id>copy-jdk11-sources</id>
-                                <phase>package</phase>
-                                <configuration>
-                                    <target>
-                                        <property name="sources-jar" value="${java8.build.outputDirectory}/${project.artifactId}-${project.version}-sources.jar"/>
-                                        <echo>sources-jar: ${sources-jar}</echo>
-                                        <zip destfile="${sources-jar}" update="true">
-                                            <zipfileset dir="${java11.sourceDirectory}" prefix="META-INF/versions/11"/>
-                                        </zip>
-                                    </target>
-                                </configuration>
-                                <goals>
-                                    <goal>run</goal>
-                                </goals>
-                            </execution>
-                        </executions>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-    </profiles>
 </project>
diff --git a/containers/jetty-http/src/main/java11/org/glassfish/jersey/jetty/JettyHttpContainer.java b/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainer.java
similarity index 97%
rename from containers/jetty-http/src/main/java11/org/glassfish/jersey/jetty/JettyHttpContainer.java
rename to containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainer.java
index 26ccd92..ec14d86 100644
--- a/containers/jetty-http/src/main/java11/org/glassfish/jersey/jetty/JettyHttpContainer.java
+++ b/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainer.java
@@ -492,6 +492,17 @@
     }
 
     /**
+     * Create a new Jetty HTTP container.
+     *
+     * @param applicationClass JAX-RS / Jersey class of application to be deployed on Jetty HTTP container.
+     */
+    JettyHttpContainer(final Class<? extends Application> applicationClass) {
+        this.appHandler = new ApplicationHandler(applicationClass, new JettyBinder());
+
+        cacheConfigSetStatusOverSendError();
+    }
+
+    /**
      * The method reads and caches value of configuration property
      * {@link ServerProperties#RESPONSE_SET_STATUS_OVER_SEND_ERROR} for future purposes.
      */
diff --git a/containers/jetty-http/src/main/java11/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java b/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java
similarity index 99%
rename from containers/jetty-http/src/main/java11/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java
rename to containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java
index 39f23e8..3ccb7b8 100644
--- a/containers/jetty-http/src/main/java11/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java
+++ b/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2021 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/JettyHttpServer.java b/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpServer.java
new file mode 100644
index 0000000..9f014f4
--- /dev/null
+++ b/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpServer.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018 Markus KARG. 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;
+
+import static jakarta.ws.rs.SeBootstrap.Configuration.SSLClientAuthentication.MANDATORY;
+import static jakarta.ws.rs.SeBootstrap.Configuration.SSLClientAuthentication.OPTIONAL;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+
+import jakarta.ws.rs.SeBootstrap;
+import jakarta.ws.rs.core.Application;
+
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.glassfish.jersey.server.ContainerFactory;
+import org.glassfish.jersey.server.JerseySeBootstrapConfiguration;
+import org.glassfish.jersey.server.spi.WebServer;
+
+/**
+ * Jersey {@code Server} implementation based on Jetty
+ * {@link org.eclipse.jetty.server.Server Server}.
+ *
+ * @author Markus KARG (markus@headcrashing.eu)
+ * @since 3.1.0
+ */
+final class JettyHttpServer implements WebServer {
+
+    private final JettyHttpContainer container;
+
+    private final org.eclipse.jetty.server.Server httpServer;
+
+    JettyHttpServer(final Application application, final JerseySeBootstrapConfiguration configuration) {
+        this(ContainerFactory.createContainer(JettyHttpContainer.class, application), configuration);
+    }
+
+    JettyHttpServer(final Class<? extends Application> applicationClass,
+                    final JerseySeBootstrapConfiguration configuration) {
+        this(new JettyHttpContainer(applicationClass), configuration);
+    }
+
+    JettyHttpServer(final JettyHttpContainer container, final JerseySeBootstrapConfiguration configuration) {
+        final SeBootstrap.Configuration.SSLClientAuthentication sslClientAuthentication = configuration
+                .sslClientAuthentication();
+        final SslContextFactory.Server sslContextFactory;
+        if (configuration.isHttps()) {
+            sslContextFactory = new SslContextFactory.Server();
+            sslContextFactory.setSslContext(configuration.sslContext());
+            sslContextFactory.setWantClientAuth(sslClientAuthentication == OPTIONAL);
+            sslContextFactory.setNeedClientAuth(sslClientAuthentication == MANDATORY);
+        } else {
+            sslContextFactory = null;
+        }
+        this.container = container;
+        this.httpServer = JettyHttpContainerFactory.createServer(
+                configuration.uri(false),
+                sslContextFactory,
+                this.container,
+                configuration.autoStart());
+    }
+
+    @Override
+    public final JettyHttpContainer container() {
+        return this.container;
+    }
+
+    @Override
+    public final int port() {
+        final ServerConnector serverConnector = (ServerConnector) this.httpServer.getConnectors()[0];
+        final int configuredPort = serverConnector.getPort();
+        final int localPort = serverConnector.getLocalPort();
+        return localPort < 0 ? configuredPort : localPort;
+    }
+
+    @Override
+    public final CompletableFuture<Void> start() {
+        return CompletableFuture.runAsync(() -> {
+            try {
+                this.httpServer.start();
+            } catch (final Exception e) {
+                throw new CompletionException(e);
+            }
+        });
+    }
+
+    @Override
+    public final CompletableFuture<Void> stop() {
+        return CompletableFuture.runAsync(() -> {
+            try {
+                this.httpServer.stop();
+            } catch (final Exception e) {
+                throw new CompletionException(e);
+            }
+        });
+    }
+
+    @Override
+    public final <T> T unwrap(final Class<T> nativeClass) {
+        return nativeClass.cast(this.httpServer);
+    }
+
+}
diff --git a/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpServerProvider.java b/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpServerProvider.java
new file mode 100644
index 0000000..fe776b9
--- /dev/null
+++ b/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpServerProvider.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018 Markus KARG. 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;
+
+import jakarta.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.JerseySeBootstrapConfiguration;
+import org.glassfish.jersey.server.spi.WebServer;
+import org.glassfish.jersey.server.spi.WebServerProvider;
+
+/**
+ * Server provider for servers based on Jetty
+ * {@link org.eclipse.jetty.server.Server Server}.
+ *
+ * @author Markus KARG (markus@headcrashing.eu)
+ * @since 3.1.0
+ */
+public final class JettyHttpServerProvider implements WebServerProvider {
+
+    @Override
+    public final <T extends WebServer> T createServer(final Class<T> type, final Application application,
+                                                      final JerseySeBootstrapConfiguration configuration) {
+        return JettyHttpServer.class == type || WebServer.class == type
+                ? type.cast(new JettyHttpServer(application, configuration))
+                : null;
+    }
+
+    @Override
+    public <T extends WebServer> T createServer(final Class<T> type, final Class<? extends Application> applicationClass,
+                                                final JerseySeBootstrapConfiguration configuration) {
+        return JettyHttpServer.class == type || WebServer.class == type
+                ? type.cast(new JettyHttpServer(applicationClass, configuration))
+                : null;
+    }
+}
diff --git a/containers/jetty-http/src/main/java8/org/glassfish/jersey/jetty/JettyHttpContainer.java b/containers/jetty-http/src/main/java8/org/glassfish/jersey/jetty/JettyHttpContainer.java
deleted file mode 100644
index b852e54..0000000
--- a/containers/jetty-http/src/main/java8/org/glassfish/jersey/jetty/JettyHttpContainer.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (c) 2020 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;
-
-import jakarta.ws.rs.ProcessingException;
-import jakarta.ws.rs.core.Application;
-import org.glassfish.jersey.jetty.internal.LocalizationMessages;
-import org.glassfish.jersey.server.ApplicationHandler;
-import org.glassfish.jersey.server.ResourceConfig;
-import org.glassfish.jersey.server.spi.Container;
-
-/**
- * Jersey {@code Container} stub based on Jetty {@link org.eclipse.jetty.server.Handler}.
- *
- * For JDK 1.8 only since Jetty 11 does not support JDKs below 11
- *
- */
-public final class JettyHttpContainer implements Container {
-
-    public JettyHttpContainer(Application application) {
-        throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
-    }
-
-    @Override
-    public ResourceConfig getConfiguration() {
-        throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
-    }
-
-    @Override
-    public ApplicationHandler getApplicationHandler() {
-        throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
-    }
-
-    @Override
-    public void reload() {
-        throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
-    }
-
-    @Override
-    public void reload(ResourceConfig configuration) {
-        throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
-    }
-
-}
diff --git a/containers/jetty-http/src/main/java8/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java b/containers/jetty-http/src/main/java8/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java
deleted file mode 100644
index 1218c2e..0000000
--- a/containers/jetty-http/src/main/java8/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (c) 2021 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;
-
-import jakarta.ws.rs.ProcessingException;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.glassfish.jersey.internal.util.JdkVersion;
-import org.glassfish.jersey.jetty.internal.LocalizationMessages;
-import org.glassfish.jersey.server.ResourceConfig;
-
-import java.net.URI;
-
-/**
- * Jersey {@code Container} stub.
- *
- * For JDK 1.8 only since Jetty 11 does not support JDKs below 11
- *
- */
-public final class JettyHttpContainerFactory {
-
-    private JettyHttpContainerFactory() {
-    }
-
-    public static Server createServer(final URI uri) throws ProcessingException {
-        validateJdk();
-        return null; // does not work at JDK 1.8
-    }
-
-    public static Server createServer(final URI uri, final boolean start) throws ProcessingException {
-        validateJdk();
-        return null; // does not work at JDK 1.8
-    }
-
-    public static Server createServer(final URI uri, final ResourceConfig config)
-            throws ProcessingException {
-
-        validateJdk();
-        return null; // does not work at JDK 1.8
-    }
-
-    public static Server createServer(final URI uri, final ResourceConfig configuration, final boolean start)
-            throws ProcessingException {
-        validateJdk();
-        return null; // does not work at JDK 1.8
-    }
-
-    public static Server createServer(final URI uri, final ResourceConfig config, final boolean start,
-                                      final Object parentContext) {
-        validateJdk();
-        return null; // does not work at JDK 1.8
-    }
-
-    public static Server createServer(final URI uri, final ResourceConfig config, final Object parentContext) {
-        validateJdk();
-        return null; // does not work at JDK 1.8
-    }
-
-    public static Server createServer(final URI uri, final SslContextFactory.Server sslContextFactory,
-                                      final ResourceConfig config)
-            throws ProcessingException {
-        validateJdk();
-        return null; // does not work at JDK 1.8    }
-    }
-
-    public static Server createServer(final URI uri,
-                                      final SslContextFactory.Server sslContextFactory,
-                                      final JettyHttpContainer handler,
-                                      final boolean start) {
-        validateJdk();
-        return null; // does not work at JDK 1.8
-    }
-
-    private static void validateJdk() {
-        if (JdkVersion.getJdkVersion().getMajor() < 11) {
-            throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
-        }
-    }
-}
diff --git a/containers/jetty-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider b/containers/jetty-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider
new file mode 100644
index 0000000..403cc50
--- /dev/null
+++ b/containers/jetty-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider
@@ -0,0 +1 @@
+org.glassfish.jersey.jetty.JettyHttpServerProvider
\ No newline at end of file
diff --git a/containers/jetty-http/src/test/java/org/glassfish/jersey/jetty/AbstractJettyServerTester.java b/containers/jetty-http/src/test/java/org/glassfish/jersey/jetty/AbstractJettyServerTester.java
index 32ffbcd..e4314ae 100644
--- a/containers/jetty-http/src/test/java/org/glassfish/jersey/jetty/AbstractJettyServerTester.java
+++ b/containers/jetty-http/src/test/java/org/glassfish/jersey/jetty/AbstractJettyServerTester.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2021 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
@@ -28,6 +28,7 @@
 import org.glassfish.jersey.server.ResourceConfig;
 
 import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
 import org.junit.After;
 
 /**
@@ -42,7 +43,7 @@
     private static final Logger LOGGER = Logger.getLogger(AbstractJettyServerTester.class.getName());
 
     public static final String CONTEXT = "";
-    private static final int DEFAULT_PORT = 9998;
+    private static final int DEFAULT_PORT = 0;
 
     /**
      * Get the port to be used for test application deployments.
@@ -50,20 +51,24 @@
      * @return The HTTP port of the URI
      */
     protected final int getPort() {
+        if (server != null) {
+            return ((ServerConnector) server.getConnectors()[0]).getLocalPort();
+        }
+
         final String value = AccessController
                 .doPrivileged(PropertiesHelper.getSystemProperty("jersey.config.test.container.port"));
         if (value != null) {
 
             try {
                 final int i = Integer.parseInt(value);
-                if (i <= 0) {
-                    throw new NumberFormatException("Value not positive.");
+                if (i < 0) {
+                    throw new NumberFormatException("Value is negative.");
                 }
                 return i;
             } catch (NumberFormatException e) {
                 LOGGER.log(Level.CONFIG,
                         "Value of 'jersey.config.test.container.port'"
-                                + " property is not a valid positive integer [" + value + "]."
+                                + " property is not a valid non-negative integer [" + value + "]."
                                 + " Reverting to default [" + DEFAULT_PORT + "].",
                         e);
             }
@@ -80,15 +85,13 @@
     public void startServer(Class... resources) {
         ResourceConfig config = new ResourceConfig(resources);
         config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
-        final URI baseUri = getBaseUri();
-        server = JettyHttpContainerFactory.createServer(baseUri, config);
-        LOGGER.log(Level.INFO, "Jetty-http server started on base uri: " + baseUri);
+        startServer(config);
     }
 
     public void startServer(ResourceConfig config) {
         final URI baseUri = getBaseUri();
         server = JettyHttpContainerFactory.createServer(baseUri, config);
-        LOGGER.log(Level.INFO, "Jetty-http server started on base uri: " + baseUri);
+        LOGGER.log(Level.INFO, "Jetty-http server started on base uri: " + getBaseUri());
     }
 
     public URI getBaseUri() {
diff --git a/containers/jetty-http/src/test/java/org/glassfish/jersey/jetty/JettyHttpServerProviderTest.java b/containers/jetty-http/src/test/java/org/glassfish/jersey/jetty/JettyHttpServerProviderTest.java
new file mode 100644
index 0000000..1aa8b45
--- /dev/null
+++ b/containers/jetty-http/src/test/java/org/glassfish/jersey/jetty/JettyHttpServerProviderTest.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018 Markus KARG. 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;
+
+import static java.lang.Boolean.FALSE;
+import static java.lang.Boolean.TRUE;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.junit.Assert.assertThat;
+
+import java.security.AccessController;
+import java.security.NoSuchAlgorithmException;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.ExecutionException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.net.ssl.SSLContext;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.SeBootstrap;
+import jakarta.ws.rs.SeBootstrap.Configuration.SSLClientAuthentication;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.internal.util.PropertiesHelper;
+import org.glassfish.jersey.server.JerseySeBootstrapConfiguration;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.server.spi.Container;
+import org.glassfish.jersey.server.spi.WebServer;
+import org.glassfish.jersey.server.spi.WebServerProvider;
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link JettyHttpServerProvider}.
+ *
+ * @author Markus KARG (markus@headcrashing.eu)
+ * @since 3.1.0
+ */
+public final class JettyHttpServerProviderTest {
+
+    @Test(timeout = 15000)
+    public void shouldProvideServer() throws InterruptedException, ExecutionException {
+        // given
+        final Resource resource = new Resource();
+        shouldProvideServer(ShouldProvideServerApplication.class, resource);
+    }
+
+    @Test(timeout = 15000)
+    public void shouldProvideServerWithClass() throws InterruptedException, ExecutionException {
+        // given
+        final Resource resource = new Resource();
+        final Application application = new ShouldProvideServerApplication();
+        shouldProvideServer(application.getClass(), resource);
+    }
+
+    private void shouldProvideServer(final Object application, final Resource resource)
+            throws InterruptedException, ExecutionException {
+        // given
+        final WebServerProvider webServerProvider = new JettyHttpServerProvider();
+        final SeBootstrap.Configuration configuration = configuration(getPort(), FALSE);
+
+        // when
+        final JerseySeBootstrapConfiguration jerseySeConfig = JerseySeBootstrapConfiguration.from(configuration);
+        final WebServer webServer = Application.class.isInstance(application)
+                ? webServerProvider.createServer(WebServer.class, (Application) application, jerseySeConfig)
+                : webServerProvider.createServer(WebServer.class, (Class<Application>) application, jerseySeConfig);
+        final Object nativeHandle = webServer.unwrap(Object.class);
+        final CompletionStage<?> start = webServer.start();
+        final Object startResult = start.toCompletableFuture().get();
+        final Container container = webServer.container();
+        final int port = webServer.port();
+        final String entity = ClientBuilder.newClient()
+                .target(UriBuilder.newInstance().scheme("http").host("localhost").port(port).build()).request()
+                .get(String.class);
+        final CompletionStage<?> stop = webServer.stop();
+        final Object stopResult = stop.toCompletableFuture().get();
+
+        // then
+        assertThat(webServer, is(instanceOf(JettyHttpServer.class)));
+        assertThat(nativeHandle, is(instanceOf(org.eclipse.jetty.server.Server.class)));
+        assertThat(startResult, is(nullValue()));
+        assertThat(container, is(instanceOf(JettyHttpContainer.class)));
+        assertThat(port, is(greaterThan(0)));
+        assertThat(entity, is(resource.toString()));
+        assertThat(stopResult, is(nullValue()));
+    }
+
+    @Path("/")
+    protected static final class Resource {
+        @GET
+        @Override
+        public String toString() {
+            return Resource.class.getName();
+        }
+    }
+
+    protected static class ShouldProvideServerApplication extends Application {
+        @Override
+        public Set<Object> getSingletons() {
+            return Collections.singleton(new Resource());
+        }
+    }
+
+    private static final Logger LOGGER = Logger.getLogger(JettyHttpServerProviderTest.class.getName());
+
+    private static final int DEFAULT_PORT = 0;
+
+    private static final int getPort() {
+        final String value = AccessController
+                .doPrivileged(PropertiesHelper.getSystemProperty("jersey.config.test.container.port"));
+        if (value != null) {
+            try {
+                final int i = Integer.parseInt(value);
+                if (i < 0) {
+                    throw new NumberFormatException("Value is negative.");
+                }
+                return i;
+            } catch (final NumberFormatException e) {
+                LOGGER.log(Level.CONFIG,
+                        "Value of 'jersey.config.test.container.port'"
+                                + " property is not a valid non-negative integer [" + value + "]."
+                                + " Reverting to default [" + DEFAULT_PORT + "].",
+                        e);
+            }
+        }
+
+        return DEFAULT_PORT;
+    }
+
+    @Test(timeout = 15000)
+    public final void shouldScanFreePort() throws InterruptedException, ExecutionException {
+        // given
+        final WebServerProvider webServerProvider = new JettyHttpServerProvider();
+        final Application application = new Application();
+        final SeBootstrap.Configuration configuration = configuration(SeBootstrap.Configuration.FREE_PORT, TRUE);
+
+        // when
+        final JerseySeBootstrapConfiguration jerseySeConfig = JerseySeBootstrapConfiguration.from(configuration);
+        final WebServer webServer = webServerProvider.createServer(WebServer.class, application, jerseySeConfig);
+
+        // then
+        assertThat(webServer.port(), is(greaterThan(0)));
+    }
+
+    private SeBootstrap.Configuration configuration(int port, boolean autoStart) {
+        return (SeBootstrap.Configuration) name -> {
+            switch (name) {
+                case SeBootstrap.Configuration.PROTOCOL:
+                    return "HTTP";
+                case SeBootstrap.Configuration.HOST:
+                    return "localhost";
+                case SeBootstrap.Configuration.PORT:
+                    return port;
+                case SeBootstrap.Configuration.ROOT_PATH:
+                    return "/";
+                case SeBootstrap.Configuration.SSL_CLIENT_AUTHENTICATION:
+                    return SSLClientAuthentication.NONE;
+                case SeBootstrap.Configuration.SSL_CONTEXT:
+                    try {
+                        return SSLContext.getDefault();
+                    } catch (final NoSuchAlgorithmException e) {
+                        throw new RuntimeException(e);
+                    }
+                case ServerProperties.WEBSERVER_AUTO_START:
+                    return autoStart;
+                default:
+                    return null;
+            }
+        };
+    }
+
+}
diff --git a/containers/jetty-servlet/pom.xml b/containers/jetty-servlet/pom.xml
index 15537d3..62d7adc 100644
--- a/containers/jetty-servlet/pom.xml
+++ b/containers/jetty-servlet/pom.xml
@@ -48,6 +48,15 @@
             <artifactId>jakarta.servlet-api</artifactId>
             <version>${servlet5.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-webapp</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-jetty-http</artifactId>
+            <version>${project.version}</version>
+        </dependency>
     </dependencies>
 
     <build>
@@ -65,7 +74,7 @@
                 <configuration>
                     <instructions>
                         <Import-Package>
-                            jakarta.servlet.*;version="[5.0,6.0)",
+                            jakarta.servlet.*;version="[5.0,7.0)",
                             *
                         </Import-Package>
                     </instructions>
@@ -75,190 +84,4 @@
         </plugins>
     </build>
 
-    <properties>
-        <java8.build.outputDirectory>${project.basedir}/target</java8.build.outputDirectory>
-        <java8.sourceDirectory>${project.basedir}/src/main/java8</java8.sourceDirectory>
-        <java11.build.outputDirectory>${project.basedir}/target11</java11.build.outputDirectory>
-        <java11.sourceDirectory>${project.basedir}/src/main/java11</java11.sourceDirectory>
-        <jetty.javax.version>9.4.28.v20200408</jetty.javax.version>
-    </properties>
-
-    <profiles>
-        <profile>
-            <id>JettyExclude</id>
-            <activation>
-                <jdk>1.8</jdk>
-            </activation>
-            <dependencies>
-                <dependency>
-                    <groupId>org.eclipse.jetty</groupId>
-                    <artifactId>jetty-server</artifactId>
-                    <version>${jetty.javax.version}</version>
-                    <scope>provided</scope>
-                </dependency>
-                <dependency>
-                    <groupId>org.eclipse.jetty</groupId>
-                    <artifactId>jetty-webapp</artifactId>
-                    <version>${jetty.javax.version}</version>
-                    <scope>provided</scope>
-                    <exclusions>
-                        <exclusion>
-                            <groupId>org.eclipse.jetty</groupId>
-                            <artifactId>jetty-server</artifactId>
-                        </exclusion>
-                    </exclusions>
-                </dependency>
-                <dependency>
-                    <groupId>org.glassfish.jersey.containers</groupId>
-                    <artifactId>jersey-container-jetty-http</artifactId>
-                    <version>${project.version}</version>
-                    <exclusions>
-                        <exclusion>
-                            <groupId>org.eclipse.jetty</groupId>
-                            <artifactId>jetty-server</artifactId>
-                        </exclusion>
-                    </exclusions>
-                </dependency>
-            </dependencies>
-            <build>
-                <directory>${java8.build.outputDirectory}</directory>
-                <plugins>
-                    <plugin>
-                        <groupId>org.codehaus.mojo</groupId>
-                        <artifactId>build-helper-maven-plugin</artifactId>
-                        <executions>
-                            <execution>
-                                <phase>generate-sources</phase>
-                                <goals>
-                                    <goal>add-source</goal>
-                                </goals>
-                                <configuration>
-                                    <sources>
-                                        <source>${java8.sourceDirectory}</source>
-                                    </sources>
-                                </configuration>
-                            </execution>
-                        </executions>
-                    </plugin>
-                    <plugin>
-                        <groupId>org.apache.maven.plugins</groupId>
-                        <artifactId>maven-compiler-plugin</artifactId>
-                        <configuration>
-                            <testExcludes>
-                                <testExclude>org/glassfish/jersey/jetty/*.java</testExclude>
-                            </testExcludes>
-                        </configuration>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-        <profile>
-            <id>Jetty11</id>
-            <activation>
-                <jdk>[11,)</jdk>
-            </activation>
-            <dependencies>
-                <dependency>
-                    <groupId>org.eclipse.jetty</groupId>
-                    <artifactId>jetty-webapp</artifactId>
-                </dependency>
-                <dependency>
-                    <groupId>org.glassfish.jersey.containers</groupId>
-                    <artifactId>jersey-container-jetty-http</artifactId>
-                    <version>${project.version}</version>
-                </dependency>
-            </dependencies>
-            <build>
-                <directory>${java11.build.outputDirectory}</directory>
-                <plugins>
-                    <plugin>
-                        <groupId>org.codehaus.mojo</groupId>
-                        <artifactId>build-helper-maven-plugin</artifactId>
-                        <executions>
-                            <execution>
-                                <phase>generate-sources</phase>
-                                <goals>
-                                    <goal>add-source</goal>
-                                </goals>
-                                <configuration>
-                                    <sources>
-                                        <source>${java11.sourceDirectory}</source>
-                                    </sources>
-                                </configuration>
-                            </execution>
-                        </executions>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-        <profile>
-            <id>copyJDK11FilesToMultiReleaseJar</id>
-            <activation>
-                <file>
-                    <!-- ${java11.build.outputDirectory} does not work here -->
-                    <exists>target11/classes/org/glassfish/jersey/jetty/servlet/JettyWebContainerFactory.class</exists>
-                </file>
-                <jdk>1.8</jdk>
-            </activation>
-            <build>
-                <plugins>
-                    <plugin>
-                        <groupId>org.apache.felix</groupId>
-                        <artifactId>maven-bundle-plugin</artifactId>
-                        <inherited>true</inherited>
-                        <extensions>true</extensions>
-                        <configuration>
-                            <instructions>
-                                <Multi-Release>true</Multi-Release>
-                            </instructions>
-                        </configuration>
-                    </plugin>
-                    <plugin>
-                        <groupId>org.apache.maven.plugins</groupId>
-                        <artifactId>maven-resources-plugin</artifactId>
-                        <inherited>true</inherited>
-                        <executions>
-                            <execution>
-                                <id>copy-jdk11-classes</id>
-                                <phase>prepare-package</phase>
-                                <goals>
-                                    <goal>copy-resources</goal>
-                                </goals>
-                                <configuration>
-                                    <outputDirectory>${java8.build.outputDirectory}/classes/META-INF/versions/11</outputDirectory>
-                                    <resources>
-                                        <resource>
-                                            <directory>${java11.build.outputDirectory}/classes</directory>
-                                        </resource>
-                                    </resources>
-                                </configuration>
-                            </execution>
-                        </executions>
-                    </plugin>
-                    <plugin>
-                        <groupId>org.apache.maven.plugins</groupId>
-                        <artifactId>maven-antrun-plugin</artifactId>
-                        <executions>
-                            <execution>
-                                <id>copy-jdk11-sources</id>
-                                <phase>package</phase>
-                                <configuration>
-                                    <target>
-                                        <property name="sources-jar" value="${java8.build.outputDirectory}/${project.artifactId}-${project.version}-sources.jar"/>
-                                        <echo>sources-jar: ${sources-jar}</echo>
-                                        <zip destfile="${sources-jar}" update="true">
-                                            <zipfileset dir="${java11.sourceDirectory}" prefix="META-INF/versions/11"/>
-                                        </zip>
-                                    </target>
-                                </configuration>
-                                <goals>
-                                    <goal>run</goal>
-                                </goals>
-                            </execution>
-                        </executions>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-    </profiles>
 </project>
diff --git a/containers/jetty-servlet/src/main/java11/org/glassfish/jersey/jetty/servlet/JettyWebContainerFactory.java b/containers/jetty-servlet/src/main/java/org/glassfish/jersey/jetty/servlet/JettyWebContainerFactory.java
similarity index 99%
rename from containers/jetty-servlet/src/main/java11/org/glassfish/jersey/jetty/servlet/JettyWebContainerFactory.java
rename to containers/jetty-servlet/src/main/java/org/glassfish/jersey/jetty/servlet/JettyWebContainerFactory.java
index 7f46e7b..a663a50 100644
--- a/containers/jetty-servlet/src/main/java11/org/glassfish/jersey/jetty/servlet/JettyWebContainerFactory.java
+++ b/containers/jetty-servlet/src/main/java/org/glassfish/jersey/jetty/servlet/JettyWebContainerFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2021 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-servlet/src/main/java8/org/glassfish/jersey/jetty/servlet/JettyWebContainerFactory.java b/containers/jetty-servlet/src/main/java8/org/glassfish/jersey/jetty/servlet/JettyWebContainerFactory.java
deleted file mode 100644
index 3d87ae8..0000000
--- a/containers/jetty-servlet/src/main/java8/org/glassfish/jersey/jetty/servlet/JettyWebContainerFactory.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (c) 2020 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.servlet;
-
-import java.net.URI;
-import java.util.Map;
-
-import jakarta.servlet.Servlet;
-
-import jakarta.ws.rs.ProcessingException;
-import org.eclipse.jetty.server.Server;
-import org.glassfish.jersey.jetty.internal.LocalizationMessages;
-
-/**
- * Jersey {@code Server} stub based on Jetty {@link org.eclipse.jetty.server.Server}.
- * <p>
- * For JDK 1.8 only since Jetty 11 does not support JDKs below 11
- */
-public final class JettyWebContainerFactory {
-
-    private JettyWebContainerFactory() {
-    }
-
-
-    public static Server create(String u)
-            throws Exception {
-        throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
-    }
-
-    public static Server create(String u, Map<String, String> initParams)
-            throws Exception {
-        throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
-    }
-
-    public static Server create(URI u)
-            throws Exception {
-        throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
-    }
-
-    public static Server create(URI u, Map<String, String> initParams)
-            throws Exception {
-        throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
-    }
-
-    public static Server create(String u, Class<? extends Servlet> c)
-            throws Exception {
-        throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
-    }
-
-    public static Server create(String u, Class<? extends Servlet> c,
-                                Map<String, String> initParams)
-            throws Exception {
-        throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
-    }
-
-    public static Server create(URI u, Class<? extends Servlet> c)
-            throws Exception {
-        throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
-    }
-
-    public static Server create(URI u, Class<? extends Servlet> c, Map<String, String> initParams)
-            throws Exception {
-        throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
-    }
-
-    private static Server create(URI u, Class<? extends Servlet> c, Servlet servlet,
-                                 Map<String, String> initParams, Map<String, String> contextInitParams)
-            throws Exception {
-        throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
-    }
-
-    public static Server create(URI u, Servlet servlet, Map<String, String> initParams, Map<String, String> contextInitParams)
-            throws Exception {
-        throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
-    }
-}
\ No newline at end of file
diff --git a/containers/netty-http/pom.xml b/containers/netty-http/pom.xml
index 77adb41..ff8d536 100644
--- a/containers/netty-http/pom.xml
+++ b/containers/netty-http/pom.xml
@@ -42,6 +42,11 @@
             <artifactId>jersey-netty-connector</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-library</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/JerseyHttp2ServerHandler.java b/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/JerseyHttp2ServerHandler.java
index 924fb9a..3212cd7 100644
--- a/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/JerseyHttp2ServerHandler.java
+++ b/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/JerseyHttp2ServerHandler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2022 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
@@ -135,6 +135,11 @@
                     private final Map<String, Object> properties = new HashMap<>();
 
                     @Override
+                    public boolean hasProperty(final String name) {
+                        return properties.containsKey(name);
+                    }
+
+                    @Override
                     public Object getProperty(String name) {
                         return properties.get(name);
                     }
diff --git a/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/JerseyServerHandler.java b/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/JerseyServerHandler.java
index dbe8dc7..00758b4 100644
--- a/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/JerseyServerHandler.java
+++ b/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/JerseyServerHandler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2022 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
@@ -170,6 +170,11 @@
                     private final Map<String, Object> properties = new HashMap<>();
 
                     @Override
+                    public boolean hasProperty(final String name) {
+                        return properties.containsKey(name);
+                    }
+
+                    @Override
                     public Object getProperty(String name) {
                         return properties.get(name);
                     }
diff --git a/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyHttpContainer.java b/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyHttpContainer.java
index bbbb5dc..fc5fced 100644
--- a/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyHttpContainer.java
+++ b/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyHttpContainer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2021 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
@@ -41,6 +41,11 @@
         this.appHandler.onStartup(this);
     }
 
+    NettyHttpContainer(Class<? extends Application> applicationClass) {
+        this.appHandler = new ApplicationHandler(applicationClass);
+        this.appHandler.onStartup(this);
+    }
+
     @Override
     public ResourceConfig getConfiguration() {
         return appHandler.getConfiguration();
diff --git a/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyHttpContainerProvider.java b/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyHttpContainerProvider.java
index 6f926c0..3d78837 100644
--- a/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyHttpContainerProvider.java
+++ b/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyHttpContainerProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2021 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
@@ -58,7 +58,7 @@
      * Create and start Netty server.
      *
      * @param baseUri       base uri.
-     * @param configuration Jersey configuration.
+     * @param container     Jersey container.
      * @param sslContext    Netty SSL context (can be null).
      * @param block         when {@code true}, this method will block until the server is stopped. When {@code false}, the
      *                      execution will
@@ -66,25 +66,64 @@
      * @return Netty channel instance.
      * @throws ProcessingException when there is an issue with creating new container.
      */
-    public static Channel createServer(final URI baseUri, final ResourceConfig configuration, SslContext sslContext,
+    public static Channel createServer(final URI baseUri, final NettyHttpContainer container, SslContext sslContext,
                                        final boolean block)
             throws ProcessingException {
 
+        final ServerBootstrap serverBootstrap = createServerBootstrap(baseUri, container, sslContext);
+        return startServer(getPort(baseUri), container, serverBootstrap, block);
+    }
+
+    /**
+     * Create but not start Netty server.
+     *
+     * @param baseUri       base uri.
+     * @param container     Jersey container.
+     * @param sslContext    Netty SSL context (can be null).
+     * @return Netty bootstrap instance.
+     * @throws ProcessingException when there is an issue with creating new container.
+     */
+    static ServerBootstrap createServerBootstrap(final URI baseUri, final NettyHttpContainer container, SslContext sslContext) {
+        final JerseyServerInitializer jerseyServerInitializer =
+                new JerseyServerInitializer(baseUri, sslContext, container, container.getConfiguration());
+        return createServerBootstrap(jerseyServerInitializer);
+    }
+
+    private static ServerBootstrap createServerBootstrap(final JerseyServerInitializer jerseyServerInitializer) {
         // Configure the server.
         final EventLoopGroup bossGroup = new NioEventLoopGroup(1);
         final EventLoopGroup workerGroup = new NioEventLoopGroup();
-        final NettyHttpContainer container = new NettyHttpContainer(configuration);
+
+        ServerBootstrap b = new ServerBootstrap();
+        b.option(ChannelOption.SO_BACKLOG, 1024);
+        b.group(bossGroup, workerGroup)
+                .channel(NioServerSocketChannel.class)
+                .childHandler(jerseyServerInitializer);
+
+        return b;
+    }
+
+    /**
+     * Start Netty server.
+     *
+     * @param port          IP port to listen.
+     * @param container     Jersey container.
+     * @param serverBootstrap Netty server bootstrap (i. e. prepared but unstarted server instance)
+     * @param block         when {@code true}, this method will block until the server is stopped. When {@code false}, the
+     *                      execution will
+     *                      end immediately after the server is started.
+     * @return Netty channel instance.
+     * @throws ProcessingException when there is an issue with creating new container.
+     */
+    static Channel startServer(final int port, final NettyHttpContainer container, final ServerBootstrap serverBootstrap,
+                                       final boolean block)
+            throws ProcessingException {
 
         try {
-            ServerBootstrap b = new ServerBootstrap();
-            b.option(ChannelOption.SO_BACKLOG, 1024);
-            b.group(bossGroup, workerGroup)
-             .channel(NioServerSocketChannel.class)
-             .childHandler(new JerseyServerInitializer(baseUri, sslContext, container, configuration));
+            final EventLoopGroup bossGroup = serverBootstrap.config().group();
+            final EventLoopGroup workerGroup = serverBootstrap.config().childGroup();
 
-            int port = getPort(baseUri);
-
-            Channel ch = b.bind(port).sync().channel();
+            Channel ch = serverBootstrap.bind(port).sync().channel();
 
             ch.closeFuture().addListener(new GenericFutureListener<Future<? super Void>>() {
                 @Override
@@ -112,6 +151,24 @@
      *
      * @param baseUri       base uri.
      * @param configuration Jersey configuration.
+     * @param sslContext    Netty SSL context (can be null).
+     * @param block         when {@code true}, this method will block until the server is stopped. When {@code false}, the
+     *                      execution will
+     *                      end immediately after the server is started.
+     * @return Netty channel instance.
+     * @throws ProcessingException when there is an issue with creating new container.
+     */
+    public static Channel createServer(final URI baseUri, final ResourceConfig configuration, SslContext sslContext,
+                                       final boolean block)
+            throws ProcessingException {
+        return createServer(baseUri, new NettyHttpContainer(configuration), sslContext, block);
+    }
+
+    /**
+     * Create and start Netty server.
+     *
+     * @param baseUri       base uri.
+     * @param configuration Jersey configuration.
      * @param block         when {@code true}, this method will block until the server is stopped. When {@code false}, the
      *                      execution will
      *                      end immediately after the server is started.
@@ -140,39 +197,18 @@
     public static Channel createHttp2Server(final URI baseUri, final ResourceConfig configuration, SslContext sslContext) throws
             ProcessingException {
 
-        final EventLoopGroup bossGroup = new NioEventLoopGroup(1);
-        final EventLoopGroup workerGroup = new NioEventLoopGroup();
         final NettyHttpContainer container = new NettyHttpContainer(configuration);
 
-        try {
-            ServerBootstrap b = new ServerBootstrap();
-            b.option(ChannelOption.SO_BACKLOG, 1024);
-            b.group(bossGroup, workerGroup)
-             .channel(NioServerSocketChannel.class)
-             .childHandler(new JerseyServerInitializer(baseUri, sslContext, container, configuration, true));
+        final JerseyServerInitializer jerseyServerInitializer =
+                new JerseyServerInitializer(baseUri, sslContext, container, configuration, true);
+        ServerBootstrap serverBootstrap = createServerBootstrap(jerseyServerInitializer);
 
-            int port = getPort(baseUri);
+        int port = getPort(baseUri);
 
-            Channel ch = b.bind(port).sync().channel();
-
-            ch.closeFuture().addListener(new GenericFutureListener<Future<? super Void>>() {
-                @Override
-                public void operationComplete(Future<? super Void> future) throws Exception {
-                    container.getApplicationHandler().onShutdown(container);
-
-                    bossGroup.shutdownGracefully();
-                    workerGroup.shutdownGracefully();
-                }
-            });
-
-            return ch;
-
-        } catch (InterruptedException e) {
-            throw new ProcessingException(e);
-        }
+        return startServer(port, container, serverBootstrap, false);
     }
 
-    private static int getPort(URI uri) {
+    static int getPort(URI uri) {
         if (uri.getPort() == -1) {
             if ("http".equalsIgnoreCase(uri.getScheme())) {
                 return 80;
diff --git a/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyHttpServer.java b/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyHttpServer.java
new file mode 100644
index 0000000..fece127
--- /dev/null
+++ b/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyHttpServer.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018 Markus KARG. 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.netty.httpserver;
+
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+
+import javax.net.ssl.SSLContext;
+import jakarta.ws.rs.SeBootstrap;
+import jakarta.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.JerseySeBootstrapConfiguration;
+import org.glassfish.jersey.server.spi.WebServer;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.Channel;
+import io.netty.handler.ssl.ClientAuth;
+import io.netty.handler.ssl.JdkSslContext;
+
+/**
+ * Jersey {@code Server} implementation based on Netty {@link Channel}.
+ *
+ * @author Markus KARG (markus@headcrashing.eu)
+ * @since 3.1.0
+ */
+final class NettyHttpServer implements WebServer {
+
+    private final NettyHttpContainer container;
+
+    private final ServerBootstrap serverBootstrap;
+
+    private volatile Channel channel;
+
+    private final int port;
+
+    NettyHttpServer(final Application application, final JerseySeBootstrapConfiguration configuration) {
+        this(new NettyHttpContainer(application), configuration);
+    }
+
+    NettyHttpServer(final Class<? extends Application> applicationClass,
+                    final JerseySeBootstrapConfiguration configuration) {
+        this(new NettyHttpContainer(applicationClass), configuration);
+    }
+
+    NettyHttpServer(final NettyHttpContainer container, final JerseySeBootstrapConfiguration configuration) {
+        final SSLContext sslContext = configuration.sslContext();
+        final SeBootstrap.Configuration.SSLClientAuthentication sslClientAuthentication = configuration
+                .sslClientAuthentication();
+
+        final URI uri = configuration.uri(false);
+        this.port = NettyHttpContainerProvider.getPort(uri);
+
+        this.container = container;
+        this.serverBootstrap = NettyHttpContainerProvider.createServerBootstrap(
+                uri,
+                this.container,
+                configuration.isHttps()
+                        ? new JdkSslContext(sslContext, false, nettyClientAuth(sslClientAuthentication))
+                        : null
+        );
+
+        if (configuration.autoStart()) {
+            this.channel = NettyHttpContainerProvider.startServer(this.port, this.container, this.serverBootstrap, false);
+        }
+    }
+
+    private static final ClientAuth nettyClientAuth(
+            final SeBootstrap.Configuration.SSLClientAuthentication sslClientAuthentication) {
+        switch (sslClientAuthentication) {
+        case MANDATORY:
+            return ClientAuth.REQUIRE;
+        case OPTIONAL:
+            return ClientAuth.OPTIONAL;
+        default:
+            return ClientAuth.NONE;
+        }
+    }
+
+    @Override
+    public final NettyHttpContainer container() {
+        return this.container;
+    }
+
+    @Override
+    public final int port() {
+        return this.channel == null ? this.port : ((InetSocketAddress) this.channel.localAddress()).getPort();
+    }
+
+    @Override
+    public final CompletableFuture<Object> start() {
+        return this.channel != null ? CompletableFuture.completedFuture(this.channel)
+                : CompletableFuture.supplyAsync(() -> {
+                    try {
+                        this.channel = NettyHttpContainerProvider.startServer(this.port, this.container,
+                                this.serverBootstrap, false);
+                        return this.channel;
+                    } catch (final Exception e) {
+                        throw new CompletionException(e);
+                    }
+                });
+    }
+
+    @Override
+    public final CompletableFuture<Void> stop() {
+        return this.channel == null ? CompletableFuture.completedFuture(null) : CompletableFuture.supplyAsync(() -> {
+            try {
+                return this.channel.close().get();
+            } catch (final Exception e) {
+                throw new CompletionException(e);
+            }
+        });
+    }
+
+    @Override
+    public final <T> T unwrap(final Class<T> nativeClass) {
+        return nativeClass.cast(this.channel == null ? this.serverBootstrap : this.channel);
+    }
+
+}
diff --git a/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyHttpServerProvider.java b/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyHttpServerProvider.java
new file mode 100644
index 0000000..34c700e
--- /dev/null
+++ b/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/NettyHttpServerProvider.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018 Markus KARG. 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.netty.httpserver;
+
+import jakarta.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.JerseySeBootstrapConfiguration;
+import org.glassfish.jersey.server.spi.WebServer;
+import org.glassfish.jersey.server.spi.WebServerProvider;
+
+import io.netty.channel.Channel;
+
+/**
+ * Server provider for servers based on Netty {@link Channel}.
+ *
+ * @author Markus KARG (markus@headcrashing.eu)
+ * @since 3.1.0
+ */
+public final class NettyHttpServerProvider implements WebServerProvider {
+
+    @Override
+    public <T extends WebServer> T createServer(final Class<T> type, final Application application,
+                                                final JerseySeBootstrapConfiguration configuration) {
+        return NettyHttpServer.class == type || WebServer.class == type
+                ? type.cast(new NettyHttpServer(application, configuration))
+                : null;
+    }
+
+    @Override
+    public <T extends WebServer> T createServer(final Class<T> type, final Class<? extends Application> applicationClass,
+                                                final JerseySeBootstrapConfiguration configuration) {
+        return NettyHttpServer.class == type || WebServer.class == type
+                ? type.cast(new NettyHttpServer(applicationClass, configuration))
+                : null;
+    }
+}
diff --git a/containers/netty-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider b/containers/netty-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider
new file mode 100644
index 0000000..61e6e1b
--- /dev/null
+++ b/containers/netty-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider
@@ -0,0 +1 @@
+org.glassfish.jersey.netty.httpserver.NettyHttpServerProvider
\ No newline at end of file
diff --git a/containers/netty-http/src/test/java/org/glassfish/jersey/netty/httpserver/NettyHttpServerProviderTest.java b/containers/netty-http/src/test/java/org/glassfish/jersey/netty/httpserver/NettyHttpServerProviderTest.java
new file mode 100644
index 0000000..3837377
--- /dev/null
+++ b/containers/netty-http/src/test/java/org/glassfish/jersey/netty/httpserver/NettyHttpServerProviderTest.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018 Markus KARG. 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.netty.httpserver;
+
+import static java.lang.Boolean.FALSE;
+import static java.lang.Boolean.TRUE;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.junit.Assert.assertThat;
+
+import java.security.AccessController;
+import java.security.NoSuchAlgorithmException;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.ExecutionException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.net.ssl.SSLContext;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.SeBootstrap;
+import jakarta.ws.rs.SeBootstrap.Configuration.SSLClientAuthentication;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.internal.util.PropertiesHelper;
+import org.glassfish.jersey.server.JerseySeBootstrapConfiguration;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.server.spi.Container;
+import org.glassfish.jersey.server.spi.WebServer;
+import org.glassfish.jersey.server.spi.WebServerProvider;
+import org.junit.Test;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.Channel;
+
+/**
+ * Unit tests for {@link NettyHttpServerProvider}.
+ *
+ * @author Markus KARG (markus@headcrashing.eu)
+ * @since 3.1.0
+ */
+public final class NettyHttpServerProviderTest {
+
+    @Test(timeout = 15000)
+    public void shouldProvideServer2() throws InterruptedException, ExecutionException {
+        // given
+        final Resource resource = new Resource();
+        shouldProvideServer(ShouldProvideServerApplication.class, resource);
+    }
+
+    @Test(timeout = 15000)
+    public void shouldProvideServerWithClass() throws InterruptedException, ExecutionException {
+        // given
+        final Resource resource = new Resource();
+        final Application application = new ShouldProvideServerApplication();
+        shouldProvideServer(application.getClass(), resource);
+    }
+
+    private void shouldProvideServer(final Object application, final Resource resource)
+            throws InterruptedException, ExecutionException {
+        // given
+        final WebServerProvider webServerProvider = new NettyHttpServerProvider();
+        final SeBootstrap.Configuration configuration = configuration(getPort(), FALSE);
+
+        // when
+        final JerseySeBootstrapConfiguration jerseySeConfig = JerseySeBootstrapConfiguration.from(configuration);
+        final WebServer webServer = Application.class.isInstance(application)
+                ? webServerProvider.createServer(WebServer.class, (Application) application, jerseySeConfig)
+                : webServerProvider.createServer(WebServer.class, (Class<Application>) application, jerseySeConfig);
+        final Object nativeHandle = webServer.unwrap(Object.class);
+        final CompletionStage<?> start = webServer.start();
+        final Object startResult = start.toCompletableFuture().get();
+        final Container container = webServer.container();
+        final int port = webServer.port();
+        final String entity = ClientBuilder.newClient()
+                .target(UriBuilder.newInstance().scheme("http").host("localhost").port(port).build()).request()
+                .get(String.class);
+        final CompletionStage<?> stop = webServer.stop();
+        final Object stopResult = stop.toCompletableFuture().get();
+
+        // then
+        assertThat(webServer, is(instanceOf(NettyHttpServer.class)));
+        assertThat(nativeHandle, is(instanceOf(ServerBootstrap.class)));
+        assertThat(startResult, is(instanceOf(Channel.class)));
+        assertThat(container, is(instanceOf(NettyHttpContainer.class)));
+        assertThat(port, is(greaterThan(0)));
+        assertThat(entity, is(resource.toString()));
+        assertThat(stopResult, is(nullValue()));
+    }
+
+
+    @Path("/")
+    protected static final class Resource {
+        @GET
+        @Override
+        public String toString() {
+            return Resource.class.getName();
+        }
+    }
+
+    protected static class ShouldProvideServerApplication extends Application {
+        @Override
+        public Set<Object> getSingletons() {
+            return Collections.singleton(new Resource());
+        }
+    }
+
+    private static final Logger LOGGER = Logger.getLogger(NettyHttpServerProviderTest.class.getName());
+
+    private static final int DEFAULT_PORT = 0;
+
+    private static final int getPort() {
+        final String value = AccessController
+                .doPrivileged(PropertiesHelper.getSystemProperty("jersey.config.test.container.port"));
+        if (value != null) {
+            try {
+                final int i = Integer.parseInt(value);
+                if (i < 0) {
+                    throw new NumberFormatException("Value is negative.");
+                }
+                return i;
+            } catch (final NumberFormatException e) {
+                LOGGER.log(Level.CONFIG,
+                        "Value of 'jersey.config.test.container.port'"
+                                + " property is not a valid non-negative integer [" + value + "]."
+                                + " Reverting to default [" + DEFAULT_PORT + "].",
+                        e);
+            }
+        }
+
+        return DEFAULT_PORT;
+    }
+
+    @Test(timeout = 15000)
+    public final void shouldScanFreePort() throws InterruptedException, ExecutionException {
+        // given
+        final WebServerProvider webServerProvider = new NettyHttpServerProvider();
+        final Application application = new Application();
+        final SeBootstrap.Configuration configuration = configuration(SeBootstrap.Configuration.FREE_PORT, TRUE);
+
+        // when
+        final JerseySeBootstrapConfiguration jerseySeConfig = JerseySeBootstrapConfiguration.from(configuration);
+        final WebServer webServer = webServerProvider.createServer(WebServer.class, application, jerseySeConfig);
+
+        // then
+        assertThat(webServer.port(), is(greaterThan(0)));
+    }
+
+    private SeBootstrap.Configuration configuration(int port, boolean autoStart) {
+        return (SeBootstrap.Configuration) name -> {
+            switch (name) {
+                case SeBootstrap.Configuration.PROTOCOL:
+                    return "HTTP";
+                case SeBootstrap.Configuration.HOST:
+                    return "localhost";
+                case SeBootstrap.Configuration.PORT:
+                    return port;
+                case SeBootstrap.Configuration.ROOT_PATH:
+                    return "/";
+                case SeBootstrap.Configuration.SSL_CLIENT_AUTHENTICATION:
+                    return SSLClientAuthentication.NONE;
+                case SeBootstrap.Configuration.SSL_CONTEXT:
+                    try {
+                        return SSLContext.getDefault();
+                    } catch (final NoSuchAlgorithmException e) {
+                        throw new RuntimeException(e);
+                    }
+                case ServerProperties.WEBSERVER_AUTO_START:
+                    return autoStart;
+                default:
+                    return null;
+            }
+        };
+    }
+
+}
diff --git a/containers/simple-http/pom.xml b/containers/simple-http/pom.xml
index 3aa97bb..af27e4a 100644
--- a/containers/simple-http/pom.xml
+++ b/containers/simple-http/pom.xml
@@ -49,6 +49,11 @@
             <groupId>org.simpleframework</groupId>
             <artifactId>simple-common</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-library</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainer.java b/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainer.java
index 327b2cf..6e4e885 100644
--- a/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainer.java
+++ b/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2021 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
@@ -465,4 +465,15 @@
         this.appHandler = new ApplicationHandler(application, new SimpleBinder());
         this.scheduler = new ScheduledThreadPoolExecutor(2, new DaemonFactory(TimeoutDispatcher.class));
     }
+
+    /**
+     * Create a new Simple framework HTTP container.
+     *
+     * @param applicationClass JAX-RS / Jersey application class to be deployed on Simple framework HTTP
+     *                    container.
+     */
+    SimpleContainer(final Class<? extends Application> applicationClass) {
+        this.appHandler = new ApplicationHandler(applicationClass, new SimpleBinder());
+        this.scheduler = new ScheduledThreadPoolExecutor(2, new DaemonFactory(TimeoutDispatcher.class));
+    }
 }
diff --git a/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainerFactory.java b/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainerFactory.java
index 6740c6b..519fe7d 100644
--- a/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainerFactory.java
+++ b/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainerFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2021 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
@@ -19,19 +19,19 @@
 import java.io.Closeable;
 import java.io.IOException;
 import java.net.InetSocketAddress;
-import java.net.SocketAddress;
 import java.net.URI;
 
-import jakarta.ws.rs.ProcessingException;
-
 import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import jakarta.ws.rs.SeBootstrap.Configuration.SSLClientAuthentication;
+import jakarta.ws.rs.ProcessingException;
 
 import org.glassfish.jersey.internal.util.collection.UnsafeValue;
 import org.glassfish.jersey.server.ResourceConfig;
 import org.glassfish.jersey.simple.internal.LocalizationMessages;
-
 import org.simpleframework.http.core.Container;
 import org.simpleframework.http.core.ContainerSocketProcessor;
+import org.simpleframework.transport.Socket;
 import org.simpleframework.transport.SocketProcessor;
 import org.simpleframework.transport.connect.Connection;
 import org.simpleframework.transport.connect.SocketConnection;
@@ -149,7 +149,55 @@
             public SocketProcessor get() throws IOException {
                 return new ContainerSocketProcessor(container);
             }
-        });
+        }, true);
+    }
+
+    /**
+     * Create a {@link Closeable} that registers an {@link Container} that in turn manages all root
+     * resource and provider classes found by searching the classes referenced in the java classpath.
+     *
+     * @param address   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 context   this is the SSL context used for SSL connections.
+     * @param container the Simple container with ResourceConfig.
+     * @param sslClientAuthentication Secure socket client authentication policy.
+     * @param start whether the server shall listen to connections immediately
+     * @return the closeable connection, with the endpoint started.
+     * @throws ProcessingException      thrown when problems during server creation.
+     * @throws IllegalArgumentException if {@code address} is {@code null}.
+     */
+    public static SimpleServer create(final URI address, final SSLContext context,
+            final SSLClientAuthentication sslClientAuthentication, final SimpleContainer container, final boolean start) {
+        return _create(address, context, container, new UnsafeValue<SocketProcessor, IOException>() {
+            @Override
+            public SocketProcessor get() throws IOException {
+                return new ContainerSocketProcessor(container) {
+                    @Override
+                    public final void process(final Socket socket) throws IOException {
+                        final SSLEngine sslEngine = socket.getEngine();
+                        if (sslEngine != null) {
+                            switch (sslClientAuthentication) {
+                            case MANDATORY: {
+                                sslEngine.setNeedClientAuth(true);
+                                break;
+                            }
+                            case OPTIONAL: {
+                                sslEngine.setWantClientAuth(true);
+                                break;
+                            }
+                            default: {
+                                sslEngine.setNeedClientAuth(false);
+                                break;
+                            }
+                            }
+                        }
+                        super.process(socket);
+                    }
+                };
+            }
+        }, start);
     }
 
     /**
@@ -201,12 +249,13 @@
             public SocketProcessor get() throws IOException {
                 return new ContainerSocketProcessor(container, count, select);
             }
-        });
+        }, true);
     }
 
     private static SimpleServer _create(final URI address, final SSLContext context,
                                         final SimpleContainer container,
-                                        final UnsafeValue<SocketProcessor, IOException> serverProvider)
+                                        final UnsafeValue<SocketProcessor, IOException> serverProvider,
+                                        final boolean start)
             throws ProcessingException {
         if (address == null) {
             throw new IllegalArgumentException(LocalizationMessages.URI_CANNOT_BE_NULL());
@@ -236,22 +285,26 @@
             final SocketProcessor server = serverProvider.get();
             connection = new SocketConnection(server, analyzer);
 
+            final SimpleServer simpleServer = new SimpleServer() {
+                private InetSocketAddress socketAddr = listen;
 
-            final SocketAddress socketAddr = connection.connect(listen, context);
-            container.onServerStart();
-
-            return new SimpleServer() {
+                @Override
+                public final void start() throws IOException {
+                    this.socketAddr = (InetSocketAddress) connection.connect(listen, context);
+                    container.onServerStart();
+                }
 
                 @Override
                 public void close() throws IOException {
                     container.onServerStop();
                     analyzer.stop();
                     connection.close();
+                    this.socketAddr = listen;
                 }
 
                 @Override
                 public int getPort() {
-                    return ((InetSocketAddress) socketAddr).getPort();
+                    return this.socketAddr.getPort();
                 }
 
                 @Override
@@ -268,6 +321,12 @@
                     }
                 }
             };
+
+            if (start) {
+                simpleServer.start();
+            }
+
+            return simpleServer;
         } catch (final IOException ex) {
             throw new ProcessingException(LocalizationMessages.ERROR_WHEN_CREATING_SERVER(), ex);
         }
diff --git a/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleHttpServer.java b/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleHttpServer.java
new file mode 100644
index 0000000..e63d3f4
--- /dev/null
+++ b/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleHttpServer.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018 Markus KARG. 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.simple;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+
+import jakarta.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.JerseySeBootstrapConfiguration;
+import org.glassfish.jersey.server.spi.WebServer;
+
+/**
+ * Jersey {@code Server} implementation based on Simple framework
+ * {@link SimpleServer}.
+ *
+ * @author Markus KARG (markus@headcrashing.eu)
+ * @since 3.1.0
+ */
+final class SimpleHttpServer implements WebServer {
+
+    private final SimpleContainer container;
+
+    private final SimpleServer simpleServer;
+
+    SimpleHttpServer(final Application application, final JerseySeBootstrapConfiguration configuration) {
+        this(new SimpleContainer(application), configuration);
+    }
+
+    SimpleHttpServer(final Class<?extends Application> applicationClass,
+                     final JerseySeBootstrapConfiguration configuration) {
+        this(new SimpleContainer(applicationClass), configuration);
+    }
+
+    SimpleHttpServer(final SimpleContainer container, final JerseySeBootstrapConfiguration configuration) {
+        this.container = container;
+        this.simpleServer = SimpleContainerFactory.create(
+                configuration.uri(false),
+                configuration.sslContext(),
+                configuration.sslClientAuthentication(),
+                this.container,
+                configuration.autoStart());
+    }
+
+    @Override
+    public final SimpleContainer container() {
+        return this.container;
+    }
+
+    @Override
+    public final int port() {
+        return this.simpleServer.getPort();
+    }
+
+    @Override
+    public final CompletableFuture<Void> start() {
+        return CompletableFuture.runAsync(() -> {
+            try {
+                this.simpleServer.start();
+            } catch (final Exception e) {
+                throw new CompletionException(e);
+            }
+        });
+    }
+
+    @Override
+    public final CompletableFuture<Void> stop() {
+        return CompletableFuture.runAsync(() -> {
+            try {
+                this.simpleServer.close();
+            } catch (final Exception e) {
+                throw new CompletionException(e);
+            }
+        });
+    }
+
+    @Override
+    public final <T> T unwrap(final Class<T> nativeClass) {
+        return nativeClass.cast(this.simpleServer);
+    }
+
+}
diff --git a/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleHttpServerProvider.java b/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleHttpServerProvider.java
new file mode 100644
index 0000000..c4a26a7
--- /dev/null
+++ b/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleHttpServerProvider.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018 Markus KARG. 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.simple;
+
+import jakarta.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.JerseySeBootstrapConfiguration;
+import org.glassfish.jersey.server.spi.WebServer;
+import org.glassfish.jersey.server.spi.WebServerProvider;
+
+/**
+ * Server provider for servers based on Simple framework {@link SimpleServer}.
+ *
+ * @author Markus KARG (markus@headcrashing.eu)
+ * @since 3.1.0
+ */
+public final class SimpleHttpServerProvider implements WebServerProvider {
+
+    @Override
+    public <T extends WebServer> T createServer(final Class<T> type, final Application application,
+                                                      final JerseySeBootstrapConfiguration configuration) {
+        return SimpleHttpServer.class == type || WebServer.class == type
+                ? type.cast(new SimpleHttpServer(application, configuration))
+                : null;
+    }
+
+    @Override
+    public <T extends WebServer> T createServer(final Class<T> type, final Class<? extends Application> applicationClass,
+                                                final JerseySeBootstrapConfiguration configuration) {
+        return SimpleHttpServer.class == type || WebServer.class == type
+                ? type.cast(new SimpleHttpServer(applicationClass, configuration))
+                : null;
+    }
+}
diff --git a/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleServer.java b/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleServer.java
index 02aec75..7560c52 100644
--- a/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleServer.java
+++ b/containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleServer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2021 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
@@ -17,6 +17,7 @@
 package org.glassfish.jersey.simple;
 
 import java.io.Closeable;
+import java.io.IOException;
 
 /**
  * Simple server facade providing convenient methods to obtain info about the server (i.e. port).
@@ -26,6 +27,8 @@
  */
 public interface SimpleServer extends Closeable {
 
+    public void start() throws IOException;
+
     /**
      * The port the server is listening to for incomming HTTP connections. If the port is not
      * specified the {@link org.glassfish.jersey.server.spi.Container.DEFAULT_PORT} is used.
diff --git a/containers/simple-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider b/containers/simple-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider
new file mode 100644
index 0000000..54d8c53
--- /dev/null
+++ b/containers/simple-http/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.WebServerProvider
@@ -0,0 +1 @@
+org.glassfish.jersey.simple.SimpleHttpServerProvider
\ No newline at end of file
diff --git a/containers/simple-http/src/test/java/org/glassfish/jersey/simple/AbstractSimpleServerTester.java b/containers/simple-http/src/test/java/org/glassfish/jersey/simple/AbstractSimpleServerTester.java
index 3df302e..3878a4e 100644
--- a/containers/simple-http/src/test/java/org/glassfish/jersey/simple/AbstractSimpleServerTester.java
+++ b/containers/simple-http/src/test/java/org/glassfish/jersey/simple/AbstractSimpleServerTester.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2021 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,7 +39,7 @@
 public abstract class AbstractSimpleServerTester {
 
     public static final String CONTEXT = "";
-    private final int DEFAULT_PORT = 9998;
+    private final int DEFAULT_PORT = 0;
 
     private static final Logger LOGGER = Logger.getLogger(AbstractSimpleServerTester.class.getName());
 
@@ -49,21 +49,25 @@
      * @return The HTTP port of the URI
      */
     protected final int getPort() {
+        if (server != null) {
+            return server.getPort();
+        }
+
         final String value = AccessController
                 .doPrivileged(PropertiesHelper.getSystemProperty("jersey.config.test.container.port"));
         if (value != null) {
 
             try {
                 final int i = Integer.parseInt(value);
-                if (i <= 0) {
-                    throw new NumberFormatException("Value not positive.");
+                if (i < 0) {
+                    throw new NumberFormatException("Value is negative.");
                 }
                 return i;
             } catch (NumberFormatException e) {
                 LOGGER.log(
                         Level.CONFIG,
                         "Value of 'jersey.config.test.container.port'"
-                        + " property is not a valid positive integer [" + value + "]."
+                        + " property is not a valid non-negative integer [" + value + "]."
                         + " Reverting to default [" + DEFAULT_PORT + "].",
                         e);
             }
@@ -83,28 +87,28 @@
         config.register(LoggingFeature.class);
         final URI baseUri = getBaseUri();
         server = SimpleContainerFactory.create(baseUri, config);
-        LOGGER.log(Level.INFO, "Simple-http server started on base uri: " + baseUri);
+        LOGGER.log(Level.INFO, "Simple-http server started on base uri: " + getBaseUri());
     }
 
     public void startServerNoLoggingFilter(Class... resources) {
         ResourceConfig config = new ResourceConfig(resources);
         final URI baseUri = getBaseUri();
         server = SimpleContainerFactory.create(baseUri, config);
-        LOGGER.log(Level.INFO, "Simple-http server started on base uri: " + baseUri);
+        LOGGER.log(Level.INFO, "Simple-http server started on base uri: " + getBaseUri());
     }
 
     public void startServer(ResourceConfig config) {
         final URI baseUri = getBaseUri();
         config.register(LoggingFeature.class);
         server = SimpleContainerFactory.create(baseUri, config);
-        LOGGER.log(Level.INFO, "Simple-http server started on base uri: " + baseUri);
+        LOGGER.log(Level.INFO, "Simple-http server started on base uri: " + getBaseUri());
     }
 
     public void startServer(ResourceConfig config, int count, int select) {
         final URI baseUri = getBaseUri();
         config.register(LoggingFeature.class);
         server = SimpleContainerFactory.create(baseUri, config, count, select);
-        LOGGER.log(Level.INFO, "Simple-http server started on base uri: " + baseUri);
+        LOGGER.log(Level.INFO, "Simple-http server started on base uri: " + getBaseUri());
     }
 
     public URI getBaseUri() {
diff --git a/containers/simple-http/src/test/java/org/glassfish/jersey/simple/SimpleHttpServerProviderTest.java b/containers/simple-http/src/test/java/org/glassfish/jersey/simple/SimpleHttpServerProviderTest.java
new file mode 100644
index 0000000..02314ea
--- /dev/null
+++ b/containers/simple-http/src/test/java/org/glassfish/jersey/simple/SimpleHttpServerProviderTest.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018 Markus KARG. 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.simple;
+
+import static java.lang.Boolean.FALSE;
+import static java.lang.Boolean.TRUE;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.junit.Assert.assertThat;
+
+import java.security.AccessController;
+import java.security.NoSuchAlgorithmException;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.ExecutionException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.net.ssl.SSLContext;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.SeBootstrap;
+import jakarta.ws.rs.SeBootstrap.Configuration.SSLClientAuthentication;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.internal.util.PropertiesHelper;
+import org.glassfish.jersey.server.JerseySeBootstrapConfiguration;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.server.spi.Container;
+import org.glassfish.jersey.server.spi.WebServer;
+import org.glassfish.jersey.server.spi.WebServerProvider;
+import org.hamcrest.CoreMatchers;
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link SimpleHttpServerProvider}.
+ *
+ * @author Markus KARG (markus@headcrashing.eu)
+ * @since 3.1.0
+ */
+public final class SimpleHttpServerProviderTest {
+
+    @Test(timeout = 15000)
+    public void shouldProvideServer2() throws InterruptedException, ExecutionException {
+        // given
+        final Resource resource = new Resource();
+        shouldProvideServer(ShouldProvideServerApplication.class, resource);
+    }
+
+    @Test(timeout = 15000)
+    public void shouldProvideServerWithClass() throws InterruptedException, ExecutionException {
+        // given
+        final Resource resource = new Resource();
+        final Application application = new ShouldProvideServerApplication();
+        shouldProvideServer(application.getClass(), resource);
+    }
+
+    private void shouldProvideServer(final Object application, final Resource resource)
+            throws InterruptedException, ExecutionException {
+        // given
+        final WebServerProvider webServerProvider = new SimpleHttpServerProvider();
+        final SeBootstrap.Configuration configuration = configuration(getPort(), FALSE);
+
+        // when
+        final JerseySeBootstrapConfiguration jerseySeConfig = JerseySeBootstrapConfiguration.from(configuration);
+        final WebServer webServer = Application.class.isInstance(application)
+                ? webServerProvider.createServer(WebServer.class, (Application) application, jerseySeConfig)
+                : webServerProvider.createServer(WebServer.class, (Class<Application>) application, jerseySeConfig);
+        final Object nativeHandle = webServer.unwrap(Object.class);
+        final CompletionStage<?> start = webServer.start();
+        final Object startResult = start.toCompletableFuture().get();
+        final Container container = webServer.container();
+        final int port = webServer.port();
+        final String entity = ClientBuilder.newClient()
+                .target(UriBuilder.newInstance().scheme("http").host("localhost").port(port).build()).request()
+                .get(String.class);
+        final CompletionStage<?> stop = webServer.stop();
+        final Object stopResult = stop.toCompletableFuture().get();
+
+        // then
+        assertThat(webServer, is(instanceOf(SimpleHttpServer.class)));
+        assertThat(nativeHandle, is(instanceOf(SimpleServer.class)));
+        assertThat(startResult, is(nullValue()));
+        assertThat(container, is(instanceOf(SimpleContainer.class)));
+        assertThat(port, is(greaterThan(0)));
+        assertThat(entity, is(resource.toString()));
+        assertThat(stopResult, is(nullValue()));
+    }
+
+    @Path("/")
+    protected static final class Resource {
+        @GET
+        @Override
+        public String toString() {
+            return Resource.class.getName();
+        }
+    }
+
+    protected static class ShouldProvideServerApplication extends Application {
+        @Override
+        public Set<Object> getSingletons() {
+            return Collections.singleton(new Resource());
+        }
+    }
+
+    private static final Logger LOGGER = Logger.getLogger(SimpleHttpServerProviderTest.class.getName());
+
+    private static final int DEFAULT_PORT = 0;
+
+    private static final int getPort() {
+        final String value = AccessController
+                .doPrivileged(PropertiesHelper.getSystemProperty("jersey.config.test.container.port"));
+        if (value != null) {
+            try {
+                final int i = Integer.parseInt(value);
+                if (i < 0) {
+                    throw new NumberFormatException("Value is negative.");
+                }
+                return i;
+            } catch (final NumberFormatException e) {
+                LOGGER.log(Level.CONFIG,
+                        "Value of 'jersey.config.test.container.port'"
+                                + " property is not a valid non-negative integer [" + value + "]."
+                                + " Reverting to default [" + DEFAULT_PORT + "].",
+                        e);
+            }
+        }
+
+        return DEFAULT_PORT;
+    }
+
+    @Test(timeout = 15000)
+    public final void shouldScanFreePort() throws InterruptedException, ExecutionException {
+        // given
+        final WebServerProvider webServerProvider = new SimpleHttpServerProvider();
+        final Application application = new Application();
+        final SeBootstrap.Configuration configuration = configuration(SeBootstrap.Configuration.FREE_PORT, TRUE);
+
+        // when
+        final JerseySeBootstrapConfiguration jerseySeConfig = JerseySeBootstrapConfiguration.from(configuration);
+        final WebServer webServer = webServerProvider.createServer(WebServer.class, application, jerseySeConfig);
+
+        // then
+        assertThat(webServer.port(), is(greaterThan(0)));
+    }
+
+    private SeBootstrap.Configuration configuration(int port, boolean autoStart) {
+        return (SeBootstrap.Configuration) name -> {
+            switch (name) {
+                case SeBootstrap.Configuration.PROTOCOL:
+                    return "HTTP";
+                case SeBootstrap.Configuration.HOST:
+                    return "localhost";
+                case SeBootstrap.Configuration.PORT:
+                    return port;
+                case SeBootstrap.Configuration.ROOT_PATH:
+                    return "/";
+                case SeBootstrap.Configuration.SSL_CLIENT_AUTHENTICATION:
+                    return SSLClientAuthentication.NONE;
+                case SeBootstrap.Configuration.SSL_CONTEXT:
+                    try {
+                        return SSLContext.getDefault();
+                    } catch (final NoSuchAlgorithmException e) {
+                        throw new RuntimeException(e);
+                    }
+                case ServerProperties.WEBSERVER_AUTO_START:
+                    return autoStart;
+                default:
+                    return null;
+            }
+        };
+    }
+
+}
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/ClientConfig.java b/core-client/src/main/java/org/glassfish/jersey/client/ClientConfig.java
index 43737f1..fb98632 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/ClientConfig.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/ClientConfig.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2018 Payara Foundation and/or its affiliates.
  *
  * This program and the accompanying materials are made available under the
@@ -330,6 +330,11 @@
         }
 
         @Override
+        public boolean hasProperty(final String name) {
+            return commonConfig.getConfiguration().hasProperty(name);
+        }
+
+        @Override
         public Object getProperty(final String name) {
             return commonConfig.getConfiguration().getProperty(name);
         }
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/ClientRequest.java b/core-client/src/main/java/org/glassfish/jersey/client/ClientRequest.java
index f9539b0..45cb994 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/ClientRequest.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/ClientRequest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2022 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
@@ -139,6 +139,11 @@
     }
 
     @Override
+    public boolean hasProperty(final String name) {
+        return propertiesDelegate.hasProperty(name);
+    }
+
+    @Override
     public Object getProperty(final String name) {
         return propertiesDelegate.getProperty(name);
     }
@@ -229,9 +234,19 @@
         return clientConfig;
     }
 
+    /**
+     * Get the values of an HTTP request header if the header exists on the current request. The returned value will be
+     * a read-only List if the specified header exists or {@code null} if it does not. This is a shortcut for
+     * {@code getRequestHeaders().get(name)}.
+     *
+     * @param name the header name, case insensitive.
+     * @return a read-only list of header values if the specified header exists, otherwise {@code null}.
+     * @throws java.lang.IllegalStateException if called outside the scope of a request.
+     */
     @Override
     public List<String> getRequestHeader(String name) {
-        return HeaderUtils.asStringList(getHeaders().get(name), clientConfig.getConfiguration());
+        final List<Object> values = getHeaders().get(name);
+        return values == null ? null : HeaderUtils.asStringList(values, clientConfig.getConfiguration());
     }
 
     @Override
diff --git a/core-client/src/test/java/org/glassfish/jersey/client/ClientConfigTest.java b/core-client/src/test/java/org/glassfish/jersey/client/ClientConfigTest.java
index 8f301f2..d2179f6 100644
--- a/core-client/src/test/java/org/glassfish/jersey/client/ClientConfigTest.java
+++ b/core-client/src/test/java/org/glassfish/jersey/client/ClientConfigTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2022 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
@@ -104,6 +104,13 @@
     }
 
     @Test
+    public void testHasProperty() {
+        ClientConfig instance = new ClientConfig().property("name", "value");
+        assertTrue(instance.hasProperty("name"));
+        assertFalse(instance.hasProperty("other"));
+    }
+
+    @Test
     public void testGetProperty() {
         ClientConfig instance = new ClientConfig().property("name", "value");
         assertEquals("value", instance.getProperty("name"));
diff --git a/core-client/src/test/java/org/glassfish/jersey/client/ClientRequestTest.java b/core-client/src/test/java/org/glassfish/jersey/client/ClientRequestTest.java
index b432d91..9604438 100644
--- a/core-client/src/test/java/org/glassfish/jersey/client/ClientRequestTest.java
+++ b/core-client/src/test/java/org/glassfish/jersey/client/ClientRequestTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2022 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
@@ -92,6 +92,9 @@
         assertFalse(request.getConfiguration().getPropertyNames().contains("name"));
         assertFalse(request.getPropertyNames().contains("name"));
 
+        assertFalse(request.getConfiguration().hasProperty("name"));
+        assertFalse(request.hasProperty("name"));
+
         assertNull(request.getConfiguration().getProperty("name"));
         assertNull(request.getProperty("name"));
 
@@ -109,6 +112,9 @@
         assertTrue(request.getConfiguration().getPropertyNames().contains("name"));
         assertFalse(request.getPropertyNames().contains("name"));
 
+        assertTrue(request.getConfiguration().hasProperty("name"));
+        assertFalse(request.hasProperty("name"));
+
         assertEquals("value-global", request.getConfiguration().getProperty("name"));
         assertNull(request.getProperty("name"));
 
@@ -127,6 +133,9 @@
         assertFalse(request.getConfiguration().getPropertyNames().contains("name"));
         assertTrue(request.getPropertyNames().contains("name"));
 
+        assertFalse(request.getConfiguration().hasProperty("name"));
+        assertTrue(request.hasProperty("name"));
+
         assertNull(request.getConfiguration().getProperty("name"));
         assertEquals("value-request", request.getProperty("name"));
 
@@ -145,6 +154,9 @@
         assertTrue(request.getConfiguration().getPropertyNames().contains("name"));
         assertTrue(request.getPropertyNames().contains("name"));
 
+        assertTrue(request.getConfiguration().hasProperty("name"));
+        assertTrue(request.hasProperty("name"));
+
         assertEquals("value-global", request.getConfiguration().getProperty("name"));
         assertEquals("value-request", request.getProperty("name"));
 
diff --git a/core-common/pom.xml b/core-common/pom.xml
index 3e1bbfd..1a8fa0f 100644
--- a/core-common/pom.xml
+++ b/core-common/pom.xml
@@ -230,446 +230,6 @@
 
     <profiles>
         <profile>
-            <id>jdk8</id>
-            <activation>
-                <jdk>1.8</jdk>
-            </activation>
-            <build>
-                <plugins>
-                    <plugin>
-                        <groupId>org.codehaus.mojo</groupId>
-                        <artifactId>build-helper-maven-plugin</artifactId>
-                        <executions>
-                            <execution>
-                                <phase>generate-sources</phase>
-                                <goals>
-                                    <goal>add-source</goal>
-                                </goals>
-                                <configuration>
-                                    <sources>
-                                        <source>src/main/jsr166</source>
-                                        <source>src/main/java8</source>
-                                    </sources>
-                                </configuration>
-                            </execution>
-                        </executions>
-                    </plugin>
-                    <plugin>
-                        <artifactId>maven-antrun-plugin</artifactId>
-                        <dependencies>
-                            <dependency>
-                                <groupId>com.sun</groupId>
-                                <artifactId>tools</artifactId>
-                                <version>1.8.0</version>
-                                <scope>system</scope>
-                                <systemPath>${java.home}/../lib/tools.jar</systemPath>
-                            </dependency>
-                        </dependencies>
-                        <executions>
-                            <execution>
-                                <phase>validate</phase>
-                                <goals>
-                                    <goal>run</goal>
-                                </goals>
-                                <configuration>
-                                    <target>
-                                        <echo>Building for JDK8</echo>
-                                    </target>
-                                </configuration>
-                            </execution>
-                            <execution>
-                                <!-- need to compile this to be able to compile-2-java8 -->
-                                <id>compile-1-jsr166</id>
-                                <phase>process-resources</phase>
-                                <configuration>
-                                    <target>
-                                        <javac srcdir="${jsr166.sourceDirectory}" destdir="${project.build.outputDirectory}"
-                                               classpath="${project.build.outputDirectory}" includeantruntime="false" />
-                                    </target>
-                                </configuration>
-                                <goals>
-                                    <goal>run</goal>
-                                </goals>
-                            </execution>
-                            <execution>
-                                <!-- Compile these files with jdk 8 and put them aside to be included in multirelase jar -->
-                                <!-- Multi-release jar is built by jdk 11+, but these classes are buildable by jdk 8 only -->
-                                <id>compile-2-java8</id>
-                                <phase>process-resources</phase>
-                                <configuration>
-                                    <target>
-                                        <mkdir dir="${java8.build.outputDirectory}" />
-                                        <javac srcdir="${java8.sourceDirectory}" destdir="${java8.build.outputDirectory}"
-                                               classpath="${project.build.outputDirectory}" includeantruntime="false" />
-                                    </target>
-                                </configuration>
-                                <goals>
-                                    <goal>run</goal>
-                                </goals>
-                            </execution>
-                        </executions>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-
-        <profile>
-            <id>jdk11+</id>
-            <activation>
-                <jdk>[11,)</jdk>
-            </activation>
-            <build>
-                <plugins>
-                    <plugin>
-                        <groupId>org.apache.maven.plugins</groupId>
-                        <artifactId>maven-antrun-plugin</artifactId>
-                        <executions>
-                            <execution>
-                                <phase>validate</phase>
-                                <goals>
-                                    <goal>run</goal>
-                                </goals>
-                                <configuration>
-                                    <target>
-                                        <echo>Building for JDK 11+</echo>
-                                    </target>
-                                </configuration>
-                            </execution>
-                            <execution>
-                                <id>compile-1-jsr166</id>
-                                <phase>process-resources</phase>
-                                <configuration>
-                                    <target>
-                                        <javac srcdir="${jsr166.sourceDirectory}" destdir="${project.build.outputDirectory}"
-                                               classpath="${project.build.outputDirectory}" includeantruntime="false" />
-                                    </target>
-                                </configuration>
-                                <goals>
-                                    <goal>run</goal>
-                                </goals>
-                            </execution>
-                            <execution>
-                                <!-- build these java 11 specific classes to be put to META-INF/versions/11 later -->
-                                <id>compile-2-java11</id>
-                                <phase>process-resources</phase>
-                                <configuration>
-                                    <target>
-                                        <mkdir dir="${java11.build.outputDirectory}" />
-                                        <javac srcdir="${java11.sourceDirectory}" destdir="${java11.build.outputDirectory}"
-                                               classpath="${project.build.outputDirectory}" includeantruntime="false" release="11" />
-                                    </target>
-                                </configuration>
-                                <goals>
-                                    <goal>run</goal>
-                                </goals>
-                            </execution>
-                        </executions>
-                    </plugin>
-                    <plugin>
-                        <groupId>org.codehaus.mojo</groupId>
-                        <artifactId>build-helper-maven-plugin</artifactId>
-                        <executions>
-                            <execution>
-                                <id>compile-0-addsources</id>
-                                <phase>process-sources</phase>
-                                <goals>
-                                    <goal>add-source</goal>
-                                </goals>
-                                <configuration>
-                                    <sources>
-                                        <source>src/main/jsr166</source>
-                                        <source>src/main/java11</source>
-                                    </sources>
-                                </configuration>
-                            </execution>
-                        </executions>
-                    </plugin>
-                    <plugin>
-                        <groupId>org.apache.maven.plugins</groupId>
-                        <artifactId>maven-compiler-plugin</artifactId>
-                        <executions>
-                            <execution>
-                                <id>default-compile</id>
-                                <configuration>
-                                    <!-- compile everything to ensure module-info contains right entries -->
-                                    <release>11</release>
-                                </configuration>
-                            </execution>
-                       </executions>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-        <profile>
-            <id>copyJDK11FilesToMultiReleaseJar</id>
-            <activation>
-                <file>
-                    <!-- ${java11.build.outputDirectory} does not work here -->
-                    <exists>target/classes-java11/org/glassfish/jersey/internal/jsr166/SubmissionPublisher.class</exists>
-                </file>
-                <jdk>1.8</jdk>
-            </activation>
-            <build>
-                <plugins>
-                    <plugin>
-                        <groupId>org.apache.felix</groupId>
-                        <artifactId>maven-bundle-plugin</artifactId>
-                        <inherited>true</inherited>
-                        <extensions>true</extensions>
-                        <configuration>
-                            <instructions>
-                                <Multi-Release>true</Multi-Release>
-                            </instructions>
-                        </configuration>
-                    </plugin>
-                    <plugin>
-                        <artifactId>maven-clean-plugin</artifactId>
-                        <!-- only one file set per execution works -->
-                        <executions>
-                            <execution>
-                                <id>remove-jdk11-generated-sources</id>
-                                <phase>initialize</phase>
-                                <goals>
-                                    <goal>clean</goal>
-                                </goals>
-                                <configuration>
-                                    <excludeDefaultDirectories>true</excludeDefaultDirectories>
-                                    <filesets>
-                                        <fileset>
-                                            <directory>${project.build.directory}/generated-sources</directory>
-                                        </fileset>
-                                    </filesets>
-                                </configuration>
-                            </execution>
-                            <execution>
-                                <id>remove-jdk11-classes</id>
-                                <phase>initialize</phase>
-                                <goals>
-                                    <goal>clean</goal>
-                                </goals>
-                                <configuration>
-                                    <excludeDefaultDirectories>true</excludeDefaultDirectories>
-                                    <filesets>
-                                        <fileset>
-                                            <directory>${project.build.directory}/classes</directory>
-                                        </fileset>
-                                    </filesets>
-                                </configuration>
-                            </execution>
-                        </executions>
-                    </plugin>
-                    <plugin>
-                        <groupId>org.apache.maven.plugins</groupId>
-                        <artifactId>maven-resources-plugin</artifactId>
-                        <inherited>true</inherited>
-                        <executions>
-                            <execution>
-                                <id>copy-jdk11-sources</id>
-                                <phase>prepare-package</phase>
-                                <goals>
-                                    <goal>copy-resources</goal>
-                                </goals>
-                                <configuration>
-                                    <outputDirectory>${project.build.directory}/generated-sources/rsrc-gen/META-INF/versions/11/org/glassfish/jersey/internal/jsr166</outputDirectory>
-                                    <resources>
-                                        <resource>
-                                            <directory>${java11.sourceDirectory}/org/glassfish/jersey/internal/jsr166</directory>
-                                        </resource>
-                                    </resources>
-                                </configuration>
-                            </execution>
-                            <execution>
-                                <id>copy-jdk11-classes-to-meta-inf</id>
-                                <phase>prepare-package</phase>
-                                <goals>
-                                    <goal>copy-resources</goal>
-                                </goals>
-                                <configuration>
-                                    <outputDirectory>${project.build.outputDirectory}/META-INF/versions/11</outputDirectory>
-                                    <resources>
-                                        <resource>
-                                            <directory>${java11.build.outputDirectory}</directory>
-                                        </resource>
-                                    </resources>
-                                </configuration>
-                            </execution>
-                        </executions>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-        <profile>
-            <id>copyJDK8FilesToMultiReleaseJar</id>
-            <activation>
-                <file>
-                    <!-- ${java8.build.outputDirectory} does not work here -->
-                    <exists>target/classes-java8/org/glassfish/jersey/internal/jsr166/UnsafeAccessor.class</exists>
-                </file>
-                <jdk>[11,)</jdk>
-            </activation>
-            <build>
-                <plugins>
-                    <plugin>
-                        <groupId>org.apache.felix</groupId>
-                        <artifactId>maven-bundle-plugin</artifactId>
-                        <inherited>true</inherited>
-                        <extensions>true</extensions>
-                        <configuration>
-                            <instructions>
-                                <Multi-Release>true</Multi-Release>
-                            </instructions>
-                        </configuration>
-                    </plugin>
-                    <plugin>
-                        <artifactId>maven-clean-plugin</artifactId>
-                        <!-- only one file set per execution works -->
-                        <executions>
-                            <execution>
-                                <id>remove-jdk11-jsr166-sources</id>
-                                <phase>initialize</phase>
-                                <goals>
-                                    <goal>clean</goal>
-                                </goals>
-                                <configuration>
-                                    <excludeDefaultDirectories>true</excludeDefaultDirectories>
-                                    <filesets>
-                                        <fileset>
-                                            <directory>${project.build.directory}/generated-sources/rsrc-gen/org/glassfish/jersey/internal/jsr166</directory>
-                                        </fileset>
-                                    </filesets>
-                                </configuration>
-                            </execution>
-                            <execution>
-                                <id>remove-jdk11-jsr166-META-INF-sources</id>
-                                <phase>initialize</phase>
-                                <goals>
-                                    <goal>clean</goal>
-                                </goals>
-                                <configuration>
-                                    <excludeDefaultDirectories>true</excludeDefaultDirectories>
-                                    <filesets>
-                                        <fileset>
-                                            <directory>${project.build.directory}/generated-sources/rsrc-gen/META-INF</directory>
-                                        </fileset>
-                                    </filesets>
-                                </configuration>
-                            </execution>
-                            <execution>
-                                <id>remove-jdk11-jsr166-classes</id>
-                                <phase>prepare-package</phase>
-                                <goals>
-                                    <goal>clean</goal>
-                                </goals>
-                                <configuration>
-                                    <excludeDefaultDirectories>true</excludeDefaultDirectories>
-                                    <filesets>
-                                        <fileset>
-                                            <directory>${project.build.outputDirectory}/org/glassfish/jersey/internal/jsr166</directory>
-                                            <includes>
-                                                <include>*.class</include>
-                                            </includes>
-                                            <excludes>
-                                                <exclude>Flow*.class</exclude>
-                                                <exclude>SubmittableFlowPublisher.class</exclude>
-                                                <exclude>package-info.class</exclude>
-                                            </excludes>
-                                        </fileset>
-                                    </filesets>
-                                </configuration>
-                            </execution>
-                        </executions>
-                    </plugin>
-                    <plugin>
-                        <groupId>org.apache.maven.plugins</groupId>
-                        <artifactId>maven-resources-plugin</artifactId>
-                        <inherited>true</inherited>
-                        <executions>
-                            <execution>
-                                <id>copy-jdk8-classes-ouputDirectory</id>
-                                <phase>prepare-package</phase>
-                                <goals>
-                                    <goal>copy-resources</goal>
-                                </goals>
-                                <configuration>
-                                    <outputDirectory>${project.build.outputDirectory}</outputDirectory>
-                                    <resources>
-                                        <resource>
-                                            <directory>${java8.build.outputDirectory}</directory>
-                                        </resource>
-                                    </resources>
-                                </configuration>
-                            </execution>
-                            <execution>
-                                <id>copy-jdk8-sources</id>
-                                <phase>prepare-package</phase>
-                                <goals>
-                                    <goal>copy-resources</goal>
-                                </goals>
-                                <configuration>
-                                    <outputDirectory>${project.build.directory}/generated-sources/rsrc-gen/org/glassfish/jersey/internal/jsr166</outputDirectory>
-                                    <resources>
-                                        <resource>
-                                            <directory>${java8.sourceDirectory}/org/glassfish/jersey/internal/jsr166</directory>
-                                        </resource>
-                                    </resources>
-                                </configuration>
-                            </execution>
-                            <execution>
-                                <id>copy-jdk11-sources</id>
-                                <phase>prepare-package</phase>
-                                <goals>
-                                    <goal>copy-resources</goal>
-                                </goals>
-                                <configuration>
-                                    <outputDirectory>${project.build.directory}/generated-sources/rsrc-gen/META-INF/versions/11/org/glassfish/jersey/internal/jsr166</outputDirectory>
-                                    <resources>
-                                        <resource>
-                                            <directory>${java11.sourceDirectory}/org/glassfish/jersey/internal/jsr166</directory>
-                                        </resource>
-                                    </resources>
-                                </configuration>
-                            </execution>
-                            <execution>
-                                <id>copy-jdk11-classes-to-meta-inf</id>
-                                <phase>prepare-package</phase>
-                                <goals>
-                                    <goal>copy-resources</goal>
-                                </goals>
-                                <configuration>
-                                    <outputDirectory>${project.build.outputDirectory}/META-INF/versions/11</outputDirectory>
-                                    <resources>
-                                        <resource>
-                                            <directory>${java11.build.outputDirectory}</directory>
-                                        </resource>
-                                    </resources>
-                                </configuration>
-                            </execution>
-                        </executions>
-                    </plugin>
-                    <plugin>
-                        <groupId>org.apache.maven.plugins</groupId>
-                        <artifactId>maven-source-plugin</artifactId>
-                        <version>3.0.1</version>
-                        <executions>
-                            <execution>
-                                <id>attach-sources</id>
-                                <phase>package</phase>
-                                <goals>
-                                    <goal>jar-no-fork</goal>
-                                </goals>
-                                <configuration>
-                                    <excludes>
-                                        <exclude>org/glassfish/jersey/internal/jsr166/Jdk9SubmissionPublisher.java</exclude>
-                                    </excludes>
-                                </configuration>
-                            </execution>
-                        </executions>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-
-        <profile>
             <id>securityOff</id>
             <properties>
                <surefire.security.argline />
@@ -709,11 +269,6 @@
 
     <properties>
         <surefire.security.argline>-Djava.security.manager -Djava.security.policy=${project.build.directory}/test-classes/surefire.policy</surefire.security.argline>
-        <jsr166.sourceDirectory>${project.basedir}/src/main/jsr166</jsr166.sourceDirectory>
-        <java8.build.outputDirectory>${project.build.directory}/classes-java8</java8.build.outputDirectory>
-        <java8.sourceDirectory>${project.basedir}/src/main/java8</java8.sourceDirectory>
-        <java11.build.outputDirectory>${project.build.directory}/classes-java11</java11.build.outputDirectory>
-        <java11.sourceDirectory>${project.basedir}/src/main/java11</java11.sourceDirectory>
     </properties>
 
 </project>
diff --git a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java b/core-common/src/main/java/org/glassfish/jersey/innate/package-info.java
similarity index 75%
rename from core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java
rename to core-common/src/main/java/org/glassfish/jersey/innate/package-info.java
index dd25372..b0648e7 100644
--- a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java
+++ b/core-common/src/main/java/org/glassfish/jersey/innate/package-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021 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
@@ -14,7 +14,7 @@
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
  */
 
-package org.glassfish.jersey.internal.jsr166;
-
-public interface JerseyFlowSubscriber<T> extends Flow.Subscriber<T> {
-}
+/**
+ * Jersey innate packages. The innate packages will not be opened by JPMS outside of Jersey.
+ */
+package org.glassfish.jersey.innate;
diff --git a/core-common/src/main/java/org/glassfish/jersey/innate/spi/EntityPartBuilderProvider.java b/core-common/src/main/java/org/glassfish/jersey/innate/spi/EntityPartBuilderProvider.java
new file mode 100644
index 0000000..606e212
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/innate/spi/EntityPartBuilderProvider.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2021 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.innate.spi;
+
+import jakarta.ws.rs.core.EntityPart;
+
+/**
+ * Jersey extension of provider of EntityPart.Builder.
+ * A service meant to be implemented solely by Jersey.
+ *
+ * @since 3.1.0
+ */
+public interface EntityPartBuilderProvider {
+
+    /**
+     * @param partName name of the part to create within the multipart entity.
+     * @return {@link EntityPart.Builder} for building new {@link EntityPart} instances.
+     */
+    public EntityPart.Builder withName(String partName);
+}
diff --git a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java b/core-common/src/main/java/org/glassfish/jersey/innate/spi/package-info.java
similarity index 75%
copy from core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java
copy to core-common/src/main/java/org/glassfish/jersey/innate/spi/package-info.java
index dd25372..3d70312 100644
--- a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java
+++ b/core-common/src/main/java/org/glassfish/jersey/innate/spi/package-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021 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
@@ -14,7 +14,7 @@
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
  */
 
-package org.glassfish.jersey.internal.jsr166;
-
-public interface JerseyFlowSubscriber<T> extends Flow.Subscriber<T> {
-}
+/**
+ * Common Jersey innate SPI classes. The innate package will not be opened by JPMS.
+ */
+package org.glassfish.jersey.innate.spi;
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/AbstractRuntimeDelegate.java b/core-common/src/main/java/org/glassfish/jersey/internal/AbstractRuntimeDelegate.java
index aa439db..39940fb 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/AbstractRuntimeDelegate.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/AbstractRuntimeDelegate.java
@@ -25,14 +25,20 @@
 import jakarta.ws.rs.core.CacheControl;
 import jakarta.ws.rs.core.Configuration;
 import jakarta.ws.rs.core.Cookie;
+import jakarta.ws.rs.core.EntityPart;
 import jakarta.ws.rs.core.EntityTag;
 import jakarta.ws.rs.core.Link;
 import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.NewCookie;
 import jakarta.ws.rs.core.Response.ResponseBuilder;
 import jakarta.ws.rs.core.UriBuilder;
+import jakarta.ws.rs.ext.ParamConverter;
 import jakarta.ws.rs.ext.RuntimeDelegate;
 
+import org.glassfish.jersey.innate.spi.EntityPartBuilderProvider;
+import org.glassfish.jersey.internal.util.collection.LazyValue;
+import org.glassfish.jersey.internal.util.collection.Value;
+import org.glassfish.jersey.internal.util.collection.Values;
 import org.glassfish.jersey.message.internal.JerseyLink;
 import org.glassfish.jersey.message.internal.OutboundJaxrsResponse;
 import org.glassfish.jersey.message.internal.OutboundMessageContext;
@@ -50,6 +56,8 @@
 
     private final Set<HeaderDelegateProvider> hps;
     private final Map<Class<?>, HeaderDelegate<?>> map;
+    private LazyValue<EntityPartBuilderProvider> entityPartBuilderProvider = Values.lazy(
+            (Value<EntityPartBuilderProvider>) () -> findEntityPartBuilderProvider());
 
     /**
      * Initialization constructor. The injection manager will be shut down.
@@ -117,4 +125,24 @@
 
         return null;
     }
+
+    @Override
+    public EntityPart.Builder createEntityPartBuilder(String partName) throws IllegalArgumentException {
+        return entityPartBuilderProvider.get().withName(partName);
+    }
+
+    /**
+     * Obtain a {@code RuntimeDelegate} instance using the method described in {@link #getInstance}.
+     *
+     * @return an instance of {@code RuntimeDelegate}.
+     */
+    private static EntityPartBuilderProvider findEntityPartBuilderProvider() {
+        for (final EntityPartBuilderProvider entityPartBuilder : ServiceFinder.find(EntityPartBuilderProvider.class)) {
+            if (entityPartBuilder != null) {
+                return entityPartBuilder;
+            }
+        }
+
+        throw new IllegalArgumentException(LocalizationMessages.NO_ENTITYPART_BUILDER_FOUND());
+    }
 }
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/DynamicFeatureConfigurator.java b/core-common/src/main/java/org/glassfish/jersey/internal/DynamicFeatureConfigurator.java
index 65ac48b..12eb78b 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/DynamicFeatureConfigurator.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/DynamicFeatureConfigurator.java
@@ -23,7 +23,6 @@
 import jakarta.ws.rs.RuntimeType;
 import jakarta.ws.rs.container.DynamicFeature;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/ExceptionMapperFactory.java b/core-common/src/main/java/org/glassfish/jersey/internal/ExceptionMapperFactory.java
index 4af235b..b64fef0 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/ExceptionMapperFactory.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/ExceptionMapperFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2021 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/core-common/src/main/java/org/glassfish/jersey/internal/MapPropertiesDelegate.java b/core-common/src/main/java/org/glassfish/jersey/internal/MapPropertiesDelegate.java
index aed1cfe..3cce5bd 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/MapPropertiesDelegate.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/MapPropertiesDelegate.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2022 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
@@ -64,6 +64,11 @@
     }
 
     @Override
+    public boolean hasProperty(final String name) {
+        return store.containsKey(name);
+    }
+
+    @Override
     public Object getProperty(String name) {
         return store.get(name);
     }
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/PropertiesDelegate.java b/core-common/src/main/java/org/glassfish/jersey/internal/PropertiesDelegate.java
index 70833b9..3413de0 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/PropertiesDelegate.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/PropertiesDelegate.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2022 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
@@ -43,6 +43,22 @@
      */
     public Object getProperty(String name);
 
+    /**
+     * Returns {@code true} if the property with the given name registered in the current request/response
+     * exchange context, or {@code false} if there is no property by that name.
+     * <p>
+     * Use the {@link #getProperty} method with a property name to get the value of
+     * a property.
+     * </p>
+     *
+     * @return {@code true} if a property matching the given name exists, or
+     *         {@code false} otherwise.
+     * @see #getProperty
+     * @since 3.1.0
+     */
+    public default boolean hasProperty(String name) {
+        return getProperty(name) != null;
+    }
 
     /**
      * Returns an immutable {@link java.util.Collection collection} containing the property
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/RuntimeDelegateDecorator.java b/core-common/src/main/java/org/glassfish/jersey/internal/RuntimeDelegateDecorator.java
index 5f954ba..b21616d 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/RuntimeDelegateDecorator.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/RuntimeDelegateDecorator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2021 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
@@ -19,16 +19,20 @@
 import org.glassfish.jersey.internal.util.PropertiesHelper;
 import org.glassfish.jersey.spi.HeaderDelegateProvider;
 
+import jakarta.ws.rs.SeBootstrap;
 import jakarta.ws.rs.core.Application;
 import jakarta.ws.rs.core.Configuration;
+import jakarta.ws.rs.core.EntityPart;
 import jakarta.ws.rs.core.Link;
 import jakarta.ws.rs.core.Response;
 import jakarta.ws.rs.core.UriBuilder;
 import jakarta.ws.rs.core.Variant;
 import jakarta.ws.rs.ext.RuntimeDelegate;
+
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
+import java.util.concurrent.CompletionStage;
 
 /**
  * RuntimeDelegate Decorator that changes behaviour due to provided runtime information.
@@ -102,6 +106,27 @@
             return runtimeDelegate.createLinkBuilder();
         }
 
+        @Override
+        public SeBootstrap.Configuration.Builder createConfigurationBuilder() {
+            return runtimeDelegate.createConfigurationBuilder();
+        }
+
+        @Override
+        public CompletionStage<SeBootstrap.Instance> bootstrap(Application application, SeBootstrap.Configuration configuration) {
+            return runtimeDelegate.bootstrap(application, configuration);
+        }
+
+        @Override
+        public CompletionStage<SeBootstrap.Instance> bootstrap(Class<? extends Application> applicationClass,
+                                                               SeBootstrap.Configuration configuration) {
+            return runtimeDelegate.bootstrap(applicationClass, configuration);
+        }
+
+        @Override
+        public EntityPart.Builder createEntityPartBuilder(String partName) throws IllegalArgumentException {
+            return runtimeDelegate.createEntityPartBuilder(partName);
+        }
+
         private <T> HeaderDelegate<T> _createHeaderDelegate(final Class<T> type) {
             for (final HeaderDelegateProvider hp : headerDelegateProviders) {
                 if (hp.supports(type)) {
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/RuntimeDelegateImpl.java b/core-common/src/main/java/org/glassfish/jersey/internal/RuntimeDelegateImpl.java
index 80dac78..89e7089 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/RuntimeDelegateImpl.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/RuntimeDelegateImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2021 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
@@ -16,11 +16,16 @@
 
 package org.glassfish.jersey.internal;
 
+import jakarta.ws.rs.SeBootstrap;
 import jakarta.ws.rs.core.Application;
 
+import jakarta.ws.rs.core.EntityPart;
 import jakarta.ws.rs.ext.RuntimeDelegate;
 import org.glassfish.jersey.message.internal.MessagingBinders;
 
+import java.util.Optional;
+import java.util.concurrent.CompletionStage;
+
 /**
  * Default implementation of JAX-RS {@link jakarta.ws.rs.ext.RuntimeDelegate}.
  * The {@link jakarta.ws.rs.ext.RuntimeDelegate} class looks for the implementations registered
@@ -42,13 +47,49 @@
     public <T> T createEndpoint(Application application, Class<T> endpointType)
             throws IllegalArgumentException, UnsupportedOperationException {
 
-        // TODO : Do we need multiple RuntimeDelegates?
+        final RuntimeDelegate runtimeDelegate = findServerDelegate();
+        if (runtimeDelegate != null) {
+            return runtimeDelegate.createEndpoint(application, endpointType);
+        }
+        throw new UnsupportedOperationException(LocalizationMessages.NO_CONTAINER_AVAILABLE());
+    }
+
+    @Override
+    public SeBootstrap.Configuration.Builder createConfigurationBuilder() {
+        final RuntimeDelegate runtimeDelegate = findServerDelegate();
+        if (runtimeDelegate != null) {
+            return runtimeDelegate.createConfigurationBuilder();
+        }
+        throw new UnsupportedOperationException(LocalizationMessages.NO_CONTAINER_AVAILABLE());
+    }
+
+    @Override
+    public CompletionStage<SeBootstrap.Instance> bootstrap(Application application, SeBootstrap.Configuration configuration) {
+        final RuntimeDelegate runtimeDelegate = findServerDelegate();
+        if (runtimeDelegate != null) {
+            return runtimeDelegate.bootstrap(application, configuration);
+        }
+        throw new UnsupportedOperationException(LocalizationMessages.NO_CONTAINER_AVAILABLE());
+    }
+
+    @Override
+    public CompletionStage<SeBootstrap.Instance> bootstrap(Class<? extends Application> applicationClass,
+                                                           SeBootstrap.Configuration configuration) {
+        final RuntimeDelegate runtimeDelegate = findServerDelegate();
+        if (runtimeDelegate != null) {
+            return runtimeDelegate.bootstrap(applicationClass, configuration);
+        }
+        throw new UnsupportedOperationException(LocalizationMessages.NO_CONTAINER_AVAILABLE());
+    }
+
+    // TODO : Do we need multiple RuntimeDelegates?
+    private RuntimeDelegate findServerDelegate() {
         for (RuntimeDelegate delegate : ServiceFinder.find(RuntimeDelegate.class)) {
             // try to find runtime delegate from core-server
             if (delegate.getClass() != RuntimeDelegateImpl.class) {
-                return delegate.createEndpoint(application, endpointType);
+                return delegate;
             }
         }
-        throw new UnsupportedOperationException(LocalizationMessages.NO_CONTAINER_AVAILABLE());
+        return null;
     }
 }
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/inject/ParamConverters.java b/core-common/src/main/java/org/glassfish/jersey/internal/inject/ParamConverters.java
index 6a234e5..d1b3e9a 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/inject/ParamConverters.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/inject/ParamConverters.java
@@ -17,12 +17,16 @@
 
 package org.glassfish.jersey.internal.inject;
 
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
+import java.nio.charset.StandardCharsets;
 import java.security.AccessController;
 import java.text.ParseException;
 import java.util.Date;
@@ -256,6 +260,39 @@
     }
 
     /**
+     * Provider of {@link ParamConverter param converter} that convert the supplied string into a Java
+     * {@link InputStream} instance.
+     */
+    public static class InputStreamProvider implements ParamConverterProvider {
+
+        @Override
+        public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation[] annotations) {
+            return rawType != InputStream.class ? null : new ParamConverter<T>() {
+
+                @Override
+                public T fromString(String value) {
+                    if (value == null) {
+                        throw new IllegalArgumentException(LocalizationMessages.METHOD_PARAMETER_CANNOT_BE_NULL("value"));
+                    }
+                    return rawType.cast(new ByteArrayInputStream(value.getBytes(StandardCharsets.UTF_8)));
+                }
+
+                @Override
+                public String toString(T value) {
+                    if (value == null) {
+                        throw new IllegalArgumentException(LocalizationMessages.METHOD_PARAMETER_CANNOT_BE_NULL("value"));
+                    }
+                    try {
+                        return new String(((InputStream) value).readAllBytes());
+                    } catch (IOException ioe) {
+                        throw new ExtractorException(ioe);
+                    }
+                }
+            };
+        }
+    }
+
+    /**
      * Provider of {@link ParamConverter param converter} that produce the Optional instance
      * by invoking {@link ParamConverterProvider}.
      */
@@ -414,6 +451,7 @@
                     new TypeFromStringEnum(),
                     new TypeValueOf(),
                     new CharacterProvider(),
+                    new InputStreamProvider(),
                     new TypeFromString(),
                     new StringConstructor(),
                     new OptionalCustomProvider(manager),
diff --git a/core-common/src/main/jsr166/org/glassfish/jersey/internal/jsr166/Flow.java b/core-common/src/main/java/org/glassfish/jersey/internal/jsr166/Flow.java
similarity index 100%
rename from core-common/src/main/jsr166/org/glassfish/jersey/internal/jsr166/Flow.java
rename to core-common/src/main/java/org/glassfish/jersey/internal/jsr166/Flow.java
diff --git a/core-common/src/main/java11/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java b/core-common/src/main/java/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java
similarity index 100%
rename from core-common/src/main/java11/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java
rename to core-common/src/main/java/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java
diff --git a/core-common/src/main/java11/org/glassfish/jersey/internal/jsr166/SubmissionPublisher.java b/core-common/src/main/java/org/glassfish/jersey/internal/jsr166/SubmissionPublisher.java
similarity index 100%
rename from core-common/src/main/java11/org/glassfish/jersey/internal/jsr166/SubmissionPublisher.java
rename to core-common/src/main/java/org/glassfish/jersey/internal/jsr166/SubmissionPublisher.java
diff --git a/core-common/src/main/java11/org/glassfish/jersey/internal/jsr166/SubmissionPublisherFactory.java b/core-common/src/main/java/org/glassfish/jersey/internal/jsr166/SubmissionPublisherFactory.java
similarity index 100%
rename from core-common/src/main/java11/org/glassfish/jersey/internal/jsr166/SubmissionPublisherFactory.java
rename to core-common/src/main/java/org/glassfish/jersey/internal/jsr166/SubmissionPublisherFactory.java
diff --git a/core-common/src/main/jsr166/org/glassfish/jersey/internal/jsr166/SubmittableFlowPublisher.java b/core-common/src/main/java/org/glassfish/jersey/internal/jsr166/SubmittableFlowPublisher.java
similarity index 100%
rename from core-common/src/main/jsr166/org/glassfish/jersey/internal/jsr166/SubmittableFlowPublisher.java
rename to core-common/src/main/java/org/glassfish/jersey/internal/jsr166/SubmittableFlowPublisher.java
diff --git a/core-common/src/main/jsr166/org/glassfish/jersey/internal/jsr166/package-info.java b/core-common/src/main/java/org/glassfish/jersey/internal/jsr166/package-info.java
similarity index 100%
rename from core-common/src/main/jsr166/org/glassfish/jersey/internal/jsr166/package-info.java
rename to core-common/src/main/java/org/glassfish/jersey/internal/jsr166/package-info.java
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/CookiesParser.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/CookiesParser.java
index f8bec0a..bcd7e5f 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/CookiesParser.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/CookiesParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2021 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
@@ -120,6 +120,7 @@
         boolean secure = false;
         boolean httpOnly = false;
         Date expiry = null;
+        NewCookie.SameSite sameSite = null;
 
         public MutableNewCookie(String name, String value) {
             this.name = name;
@@ -127,7 +128,7 @@
         }
 
         public NewCookie getImmutableNewCookie() {
-            return new NewCookie(name, value, path, domain, version, comment, maxAge, expiry, secure, httpOnly);
+            return new NewCookie(name, value, path, domain, version, comment, maxAge, expiry, secure, httpOnly, sameSite);
         }
     }
 
@@ -163,6 +164,8 @@
                     cookie.version = Integer.parseInt(value);
                 } else if (param.startsWith("httponly")) {
                     cookie.httpOnly = true;
+                } else if (param.startsWith("samesite")) {
+                    cookie.sameSite = NewCookie.SameSite.valueOf(value.toUpperCase());
                 }  else if (param.startsWith("expires")) {
                     try {
                         cookie.expiry = HttpDateFormat.readDate(value + ", " + bites[++i]);
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/InterceptorExecutor.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/InterceptorExecutor.java
index 3040bf6..9acc012 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/InterceptorExecutor.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/InterceptorExecutor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2022 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
@@ -89,6 +89,11 @@
     }
 
     @Override
+    public boolean hasProperty(final String name) {
+        return propertiesDelegate.hasProperty(name);
+    }
+
+    @Override
     public Object getProperty(final String name) {
         return propertiesDelegate.getProperty(name);
     }
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/NewCookieProvider.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/NewCookieProvider.java
index 9c53c74..15ab8d0 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/NewCookieProvider.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/NewCookieProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2021 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
@@ -73,6 +73,10 @@
         if (cookie.isHttpOnly()) {
             b.append(";HttpOnly");
         }
+        if (cookie.getSameSite() != null) {
+            b.append(";SameSite=");
+            b.append(cookie.getSameSite());
+        }
         if (cookie.getExpiry() != null) {
             b.append(";Expires=");
             b.append(HttpDateFormat.getPreferredDateFormat().format(cookie.getExpiry()));
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/TracingAwarePropertiesDelegate.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/TracingAwarePropertiesDelegate.java
index 97afd68..a75e554 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/TracingAwarePropertiesDelegate.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/TracingAwarePropertiesDelegate.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2022 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
@@ -59,6 +59,14 @@
     }
 
     @Override
+    public boolean hasProperty(final String name) {
+        if (tracingLogger != null && TracingLogger.PROPERTY_NAME.equals(name)) {
+            return true;
+        }
+        return propertiesDelegate.hasProperty(name);
+    }
+
+    @Override
     public Object getProperty(String name) {
         if (tracingLogger != null && TracingLogger.PROPERTY_NAME.equals(name)) {
             return tracingLogger;
diff --git a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/SubmissionPublisher.java b/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/SubmissionPublisher.java
deleted file mode 100644
index 6eb4cb5..0000000
--- a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/SubmissionPublisher.java
+++ /dev/null
@@ -1,1626 +0,0 @@
-/*
- * Written by Doug Lea with assistance from members of JCP JSR-166
- * Expert Group and released to the public domain, as explained at
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-package org.glassfish.jersey.internal.jsr166;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ForkJoinPool;
-import java.util.concurrent.ForkJoinTask;
-import java.util.concurrent.RejectedExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.locks.LockSupport;
-import java.util.function.BiConsumer;
-import java.util.function.BiPredicate;
-import java.util.function.Consumer;
-
-/**
- * A {@link Flow.Publisher} that asynchronously issues submitted
- * (non-null) items to current subscribers until it is closed.  Each
- * current subscriber receives newly submitted items in the same order
- * unless drops or exceptions are encountered.  Using a
- * SubmissionPublisher allows item generators to act as compliant <a
- * href="http://www.reactive-streams.org/"> reactive-streams</a>
- * Publishers relying on drop handling and/or blocking for flow
- * control.
- * <p>
- * <p>A SubmissionPublisher uses the {@link Executor} supplied in its
- * constructor for delivery to subscribers. The best choice of
- * Executor depends on expected usage. If the generator(s) of
- * submitted items run in separate threads, and the number of
- * subscribers can be estimated, consider using a {@link
- * Executors#newFixedThreadPool}. Otherwise consider using the
- * default, normally the {@link ForkJoinPool#commonPool}.
- * <p>
- * <p>Buffering allows producers and consumers to transiently operate
- * at different rates.  Each subscriber uses an independent buffer.
- * Buffers are created upon first use and expanded as needed up to the
- * given maximum. (The enforced capacity may be rounded up to the
- * nearest power of two and/or bounded by the largest value supported
- * by this implementation.)  Invocations of {@link
- * Flow.Subscription#request(long) request} do not directly result in
- * buffer expansion, but risk saturation if unfilled requests exceed
- * the maximum capacity.  The default value of {@link
- * Flow#defaultBufferSize()} may provide a useful starting point for
- * choosing a capacity based on expected rates, resources, and usages.
- * <p>
- * <p>Publication methods support different policies about what to do
- * when buffers are saturated. Method {@link #submit(Object) submit}
- * blocks until resources are available. This is simplest, but least
- * responsive.  The {@code offer} methods may drop items (either
- * immediately or with bounded timeout), but provide an opportunity to
- * interpose a handler and then retry.
- * <p>
- * <p>If any Subscriber method throws an exception, its subscription
- * is cancelled.  If a handler is supplied as a constructor argument,
- * it is invoked before cancellation upon an exception in method
- * {@link Flow.Subscriber#onNext onNext}, but exceptions in methods
- * {@link Flow.Subscriber#onSubscribe onSubscribe},
- * {@link Flow.Subscriber#onError(Throwable) onError} and
- * {@link Flow.Subscriber#onComplete() onComplete} are not recorded or
- * handled before cancellation.  If the supplied Executor throws
- * {@link RejectedExecutionException} (or any other RuntimeException
- * or Error) when attempting to execute a task, or a drop handler
- * throws an exception when processing a dropped item, then the
- * exception is rethrown. In these cases, not all subscribers will
- * have been issued the published item. It is usually good practice to
- * {@link #closeExceptionally closeExceptionally} in these cases.
- * <p>
- * <p>Method {@link #consume(Consumer)} simplifies support for a
- * common case in which the only action of a subscriber is to request
- * and process all items using a supplied function.
- * <p>
- * <p>This class may also serve as a convenient base for subclasses
- * that generate items, and use the methods in this class to publish
- * them.  For example here is a class that periodically publishes the
- * items generated from a supplier. (In practice you might add methods
- * to independently start and stop generation, to share Executors
- * among publishers, and so on, or use a SubmissionPublisher as a
- * component rather than a superclass.)
- * <p>
- * <pre> {@code
- * class PeriodicPublisher<T> extends SubmissionPublisher<T> {
- *   final ScheduledFuture<?> periodicTask;
- *   final ScheduledExecutorService scheduler;
- *   PeriodicPublisher(Executor executor, int maxBufferCapacity,
- *                     Supplier<? extends T> supplier,
- *                     long period, TimeUnit unit) {
- *     super(executor, maxBufferCapacity);
- *     scheduler = new ScheduledThreadPoolExecutor(1);
- *     periodicTask = scheduler.scheduleAtFixedRate(
- *       () -> submit(supplier.get()), 0, period, unit);
- *   }
- *   public void close() {
- *     periodicTask.cancel(false);
- *     scheduler.shutdown();
- *     super.close();
- *   }
- * }}</pre>
- * <p>
- * <p>Here is an example of a {@link Flow.Processor} implementation.
- * It uses single-step requests to its publisher for simplicity of
- * illustration. A more adaptive version could monitor flow using the
- * lag estimate returned from {@code submit}, along with other utility
- * methods.
- * <p>
- * <pre> {@code
- * class TransformProcessor<S,T> extends SubmissionPublisher<T>
- *   implements Flow.Processor<S,T> {
- *   final Function<? super S, ? extends T> function;
- *   Flow.Subscription subscription;
- *   TransformProcessor(Executor executor, int maxBufferCapacity,
- *                      Function<? super S, ? extends T> function) {
- *     super(executor, maxBufferCapacity);
- *     this.function = function;
- *   }
- *   public void onSubscribe(Flow.Subscription subscription) {
- *     (this.subscription = subscription).request(1);
- *   }
- *   public void onNext(S item) {
- *     subscription.request(1);
- *     submit(function.apply(item));
- *   }
- *   public void onError(Throwable ex) { closeExceptionally(ex); }
- *   public void onComplete() { close(); }
- * }}</pre>
- *
- * @param <T> the published item type
- * @author Doug Lea
- * @since 9
- */
-public class SubmissionPublisher<T> implements Flow.Publisher<T>, SubmittableFlowPublisher<T>,
-        AutoCloseable {
-    /*
-     * Most mechanics are handled by BufferedSubscription. This class
-     * mainly tracks subscribers and ensures sequentiality, by using
-     * built-in synchronization locks across public methods. (Using
-     * built-in locks works well in the most typical case in which
-     * only one thread submits items).
-     */
-
-    /**
-     * The largest possible power of two array size.
-     */
-    static final int BUFFER_CAPACITY_LIMIT = 1 << 30;
-
-    /**
-     * Round capacity to power of 2, at most limit.
-     */
-    static final int roundCapacity(int cap) {
-        int n = cap - 1;
-        n |= n >>> 1;
-        n |= n >>> 2;
-        n |= n >>> 4;
-        n |= n >>> 8;
-        n |= n >>> 16;
-        return (n <= 0) ? 1 : // at least 1
-                (n >= BUFFER_CAPACITY_LIMIT) ? BUFFER_CAPACITY_LIMIT : n + 1;
-    }
-
-    // default Executor setup; nearly the same as CompletableFuture
-
-    /**
-     * Default executor -- ForkJoinPool.commonPool() unless it cannot
-     * support parallelism.
-     */
-    private static final Executor ASYNC_POOL =
-            (ForkJoinPool.getCommonPoolParallelism() > 1) ? ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();
-
-    /**
-     * Fallback if ForkJoinPool.commonPool() cannot support parallelism
-     */
-    private static final class ThreadPerTaskExecutor implements Executor {
-        public void execute(Runnable r) {
-            new Thread(r).start();
-        }
-    }
-
-    /**
-     * Clients (BufferedSubscriptions) are maintained in a linked list
-     * (via their "next" fields). This works well for publish loops.
-     * It requires O(n) traversal to check for duplicate subscribers,
-     * but we expect that subscribing is much less common than
-     * publishing. Unsubscribing occurs only during traversal loops,
-     * when BufferedSubscription methods return negative values
-     * signifying that they have been disabled.  To reduce
-     * head-of-line blocking, submit and offer methods first call
-     * BufferedSubscription.offer on each subscriber, and place
-     * saturated ones in retries list (using nextRetry field), and
-     * retry, possibly blocking or dropping.
-     */
-    BufferedSubscription<T> clients;
-
-    /**
-     * Run status, updated only within locks
-     */
-    volatile boolean closed;
-    /**
-     * If non-null, the exception in closeExceptionally
-     */
-    volatile Throwable closedException;
-
-    // Parameters for constructing BufferedSubscriptions
-    final Executor executor;
-    final BiConsumer<? super Flow.Subscriber<? super T>, ? super Throwable> onNextHandler;
-    final int maxBufferCapacity;
-
-    /**
-     * Creates a new SubmissionPublisher using the given Executor for
-     * async delivery to subscribers, with the given maximum buffer size
-     * for each subscriber, and, if non-null, the given handler invoked
-     * when any Subscriber throws an exception in method {@link
-     * Flow.Subscriber#onNext(Object) onNext}.
-     *
-     * @param executor          the executor to use for async delivery,
-     *                          supporting creation of at least one independent thread
-     * @param maxBufferCapacity the maximum capacity for each
-     *                          subscriber's buffer (the enforced capacity may be rounded up to
-     *                          the nearest power of two and/or bounded by the largest value
-     *                          supported by this implementation; method {@link #getMaxBufferCapacity}
-     *                          returns the actual value)
-     * @param handler           if non-null, procedure to invoke upon exception
-     *                          thrown in method {@code onNext}
-     * @throws NullPointerException     if executor is null
-     * @throws IllegalArgumentException if maxBufferCapacity not
-     *                                  positive
-     */
-    public SubmissionPublisher(Executor executor, int maxBufferCapacity,
-                               BiConsumer<? super Flow.Subscriber<? super T>, ? super Throwable> handler) {
-        if (executor == null) {
-            throw new NullPointerException();
-        }
-        if (maxBufferCapacity <= 0) {
-            throw new IllegalArgumentException("capacity must be positive");
-        }
-        this.executor = executor;
-        this.onNextHandler = handler;
-        this.maxBufferCapacity = roundCapacity(maxBufferCapacity);
-    }
-
-    /**
-     * Creates a new SubmissionPublisher using the given Executor for
-     * async delivery to subscribers, with the given maximum buffer size
-     * for each subscriber, and no handler for Subscriber exceptions in
-     * method {@link Flow.Subscriber#onNext(Object) onNext}.
-     *
-     * @param executor          the executor to use for async delivery,
-     *                          supporting creation of at least one independent thread
-     * @param maxBufferCapacity the maximum capacity for each
-     *                          subscriber's buffer (the enforced capacity may be rounded up to
-     *                          the nearest power of two and/or bounded by the largest value
-     *                          supported by this implementation; method {@link #getMaxBufferCapacity}
-     *                          returns the actual value)
-     * @throws NullPointerException     if executor is null
-     * @throws IllegalArgumentException if maxBufferCapacity not
-     *                                  positive
-     */
-    public SubmissionPublisher(Executor executor, int maxBufferCapacity) {
-        this(executor, maxBufferCapacity, null);
-    }
-
-    /**
-     * Creates a new SubmissionPublisher using the {@link
-     * ForkJoinPool#commonPool()} for async delivery to subscribers
-     * (unless it does not support a parallelism level of at least two,
-     * in which case, a new Thread is created to run each task), with
-     * maximum buffer capacity of {@link Flow#defaultBufferSize}, and no
-     * handler for Subscriber exceptions in method {@link
-     * Flow.Subscriber#onNext(Object) onNext}.
-     */
-    public SubmissionPublisher() {
-        this(ASYNC_POOL, Flow.defaultBufferSize(), null);
-    }
-
-    /**
-     * Adds the given Subscriber unless already subscribed.  If already
-     * subscribed, the Subscriber's {@link
-     * Flow.Subscriber#onError(Throwable) onError} method is invoked on
-     * the existing subscription with an {@link IllegalStateException}.
-     * Otherwise, upon success, the Subscriber's {@link
-     * Flow.Subscriber#onSubscribe onSubscribe} method is invoked
-     * asynchronously with a new {@link Flow.Subscription}.  If {@link
-     * Flow.Subscriber#onSubscribe onSubscribe} throws an exception, the
-     * subscription is cancelled. Otherwise, if this SubmissionPublisher
-     * was closed exceptionally, then the subscriber's {@link
-     * Flow.Subscriber#onError onError} method is invoked with the
-     * corresponding exception, or if closed without exception, the
-     * subscriber's {@link Flow.Subscriber#onComplete() onComplete}
-     * method is invoked.  Subscribers may enable receiving items by
-     * invoking the {@link Flow.Subscription#request(long) request}
-     * method of the new Subscription, and may unsubscribe by invoking
-     * its {@link Flow.Subscription#cancel() cancel} method.
-     *
-     * @param subscriber the subscriber
-     * @throws NullPointerException if subscriber is null
-     */
-    public void subscribe(Flow.Subscriber<? super T> subscriber) {
-        if (subscriber == null) {
-            throw new NullPointerException();
-        }
-        BufferedSubscription<T> subscription =
-                new BufferedSubscription<T>(subscriber, executor,
-                        onNextHandler, maxBufferCapacity);
-        synchronized (this) {
-            for (BufferedSubscription<T> b = clients, pred = null;;) {
-                if (b == null) {
-                    Throwable ex;
-                    subscription.onSubscribe();
-                    if ((ex = closedException) != null) {
-                        subscription.onError(ex);
-                    } else if (closed) {
-                        subscription.onComplete();
-                    } else if (pred == null) {
-                        clients = subscription;
-                    } else {
-                        pred.next = subscription;
-                    }
-                    break;
-                }
-                BufferedSubscription<T> next = b.next;
-                if (b.isDisabled()) { // remove
-                    b.next = null;    // detach
-                    if (pred == null) {
-                        clients = next;
-                    } else {
-                        pred.next = next;
-                    }
-                } else if (subscriber.equals(b.subscriber)) {
-                    b.onError(new IllegalStateException("Duplicate subscribe"));
-                    break;
-                } else {
-                    pred = b;
-                }
-                b = next;
-            }
-        }
-    }
-
-    /**
-     * Publishes the given item to each current subscriber by
-     * asynchronously invoking its {@link Flow.Subscriber#onNext(Object)
-     * onNext} method, blocking uninterruptibly while resources for any
-     * subscriber are unavailable. This method returns an estimate of
-     * the maximum lag (number of items submitted but not yet consumed)
-     * among all current subscribers. This value is at least one
-     * (accounting for this submitted item) if there are any
-     * subscribers, else zero.
-     * <p>
-     * <p>If the Executor for this publisher throws a
-     * RejectedExecutionException (or any other RuntimeException or
-     * Error) when attempting to asynchronously notify subscribers,
-     * then this exception is rethrown, in which case not all
-     * subscribers will have been issued this item.
-     *
-     * @param item the (non-null) item to publish
-     * @return the estimated maximum lag among subscribers
-     * @throws IllegalStateException      if closed
-     * @throws NullPointerException       if item is null
-     * @throws RejectedExecutionException if thrown by Executor
-     */
-    public int submit(T item) {
-        if (item == null) {
-            throw new NullPointerException();
-        }
-        int lag = 0;
-        boolean complete;
-        synchronized (this) {
-            complete = closed;
-            BufferedSubscription<T> b = clients;
-            if (!complete) {
-                BufferedSubscription<T> pred = null, r = null, rtail = null;
-                while (b != null) {
-                    BufferedSubscription<T> next = b.next;
-                    int stat = b.offer(item);
-                    if (stat < 0) {           // disabled
-                        b.next = null;
-                        if (pred == null) {
-                            clients = next;
-                        } else {
-                            pred.next = next;
-                        }
-                    } else {
-                        if (stat > lag) {
-                            lag = stat;
-                        } else if (stat == 0) { // place on retry list
-                            b.nextRetry = null;
-                            if (rtail == null) {
-                                r = b;
-                            } else {
-                                rtail.nextRetry = b;
-                            }
-                            rtail = b;
-                        }
-                        pred = b;
-                    }
-                    b = next;
-                }
-                while (r != null) {
-                    BufferedSubscription<T> nextRetry = r.nextRetry;
-                    r.nextRetry = null;
-                    int stat = r.submit(item);
-                    if (stat > lag) {
-                        lag = stat;
-                    } else if (stat < 0 && clients == r) {
-                        clients = r.next; // postpone internal unsubscribes
-                    }
-                    r = nextRetry;
-                }
-            }
-        }
-        if (complete) {
-            throw new IllegalStateException("Closed");
-        } else {
-            return lag;
-        }
-    }
-
-    /**
-     * Publishes the given item, if possible, to each current subscriber
-     * by asynchronously invoking its {@link
-     * Flow.Subscriber#onNext(Object) onNext} method. The item may be
-     * dropped by one or more subscribers if resource limits are
-     * exceeded, in which case the given handler (if non-null) is
-     * invoked, and if it returns true, retried once.  Other calls to
-     * methods in this class by other threads are blocked while the
-     * handler is invoked.  Unless recovery is assured, options are
-     * usually limited to logging the error and/or issuing an {@link
-     * Flow.Subscriber#onError(Throwable) onError} signal to the
-     * subscriber.
-     * <p>
-     * <p>This method returns a status indicator: If negative, it
-     * represents the (negative) number of drops (failed attempts to
-     * issue the item to a subscriber). Otherwise it is an estimate of
-     * the maximum lag (number of items submitted but not yet
-     * consumed) among all current subscribers. This value is at least
-     * one (accounting for this submitted item) if there are any
-     * subscribers, else zero.
-     * <p>
-     * <p>If the Executor for this publisher throws a
-     * RejectedExecutionException (or any other RuntimeException or
-     * Error) when attempting to asynchronously notify subscribers, or
-     * the drop handler throws an exception when processing a dropped
-     * item, then this exception is rethrown.
-     *
-     * @param item   the (non-null) item to publish
-     * @param onDrop if non-null, the handler invoked upon a drop to a
-     *               subscriber, with arguments of the subscriber and item; if it
-     *               returns true, an offer is re-attempted (once)
-     * @return if negative, the (negative) number of drops; otherwise
-     * an estimate of maximum lag
-     * @throws IllegalStateException      if closed
-     * @throws NullPointerException       if item is null
-     * @throws RejectedExecutionException if thrown by Executor
-     */
-    public int offer(T item,
-                     BiPredicate<Flow.Subscriber<? super T>, ? super T> onDrop) {
-        return doOffer(0L, item, onDrop);
-    }
-
-    /**
-     * Publishes the given item, if possible, to each current subscriber
-     * by asynchronously invoking its {@link
-     * Flow.Subscriber#onNext(Object) onNext} method, blocking while
-     * resources for any subscription are unavailable, up to the
-     * specified timeout or until the caller thread is interrupted, at
-     * which point the given handler (if non-null) is invoked, and if it
-     * returns true, retried once. (The drop handler may distinguish
-     * timeouts from interrupts by checking whether the current thread
-     * is interrupted.)  Other calls to methods in this class by other
-     * threads are blocked while the handler is invoked.  Unless
-     * recovery is assured, options are usually limited to logging the
-     * error and/or issuing an {@link Flow.Subscriber#onError(Throwable)
-     * onError} signal to the subscriber.
-     * <p>
-     * <p>This method returns a status indicator: If negative, it
-     * represents the (negative) number of drops (failed attempts to
-     * issue the item to a subscriber). Otherwise it is an estimate of
-     * the maximum lag (number of items submitted but not yet
-     * consumed) among all current subscribers. This value is at least
-     * one (accounting for this submitted item) if there are any
-     * subscribers, else zero.
-     * <p>
-     * <p>If the Executor for this publisher throws a
-     * RejectedExecutionException (or any other RuntimeException or
-     * Error) when attempting to asynchronously notify subscribers, or
-     * the drop handler throws an exception when processing a dropped
-     * item, then this exception is rethrown.
-     *
-     * @param item    the (non-null) item to publish
-     * @param timeout how long to wait for resources for any subscriber
-     *                before giving up, in units of {@code unit}
-     * @param unit    a {@code TimeUnit} determining how to interpret the
-     *                {@code timeout} parameter
-     * @param onDrop  if non-null, the handler invoked upon a drop to a
-     *                subscriber, with arguments of the subscriber and item; if it
-     *                returns true, an offer is re-attempted (once)
-     * @return if negative, the (negative) number of drops; otherwise
-     * an estimate of maximum lag
-     * @throws IllegalStateException      if closed
-     * @throws NullPointerException       if item is null
-     * @throws RejectedExecutionException if thrown by Executor
-     */
-    public int offer(T item, long timeout, TimeUnit unit,
-                     BiPredicate<Flow.Subscriber<? super T>, ? super T> onDrop) {
-        return doOffer(unit.toNanos(timeout), item, onDrop);
-    }
-
-    /**
-     * Common implementation for both forms of offer
-     */
-    final int doOffer(long nanos, T item,
-                      BiPredicate<Flow.Subscriber<? super T>, ? super T> onDrop) {
-        if (item == null) {
-            throw new NullPointerException();
-        }
-        int lag = 0, drops = 0;
-        boolean complete;
-        synchronized (this) {
-            complete = closed;
-            BufferedSubscription<T> b = clients;
-            if (!complete) {
-                BufferedSubscription<T> pred = null, r = null, rtail = null;
-                while (b != null) {
-                    BufferedSubscription<T> next = b.next;
-                    int stat = b.offer(item);
-                    if (stat < 0) {
-                        b.next = null;
-                        if (pred == null) {
-                            clients = next;
-                        } else {
-                            pred.next = next;
-                        }
-                    } else {
-                        if (stat > lag) {
-                            lag = stat;
-                        } else if (stat == 0) {
-                            b.nextRetry = null;
-                            if (rtail == null) {
-                                r = b;
-                            } else {
-                                rtail.nextRetry = b;
-                            }
-                            rtail = b;
-                        } else if (stat > lag) {
-                            lag = stat;
-                        }
-                        pred = b;
-                    }
-                    b = next;
-                }
-                while (r != null) {
-                    BufferedSubscription<T> nextRetry = r.nextRetry;
-                    r.nextRetry = null;
-                    int stat = (nanos > 0L)
-                            ? r.timedOffer(item, nanos)
-                            : r.offer(item);
-                    if (stat == 0 && onDrop != null && onDrop.test(r.subscriber, item)) {
-                        stat = r.offer(item);
-                    }
-                    if (stat == 0) {
-                        ++drops;
-                    } else if (stat > lag) {
-                        lag = stat;
-                    } else if (stat < 0 && clients == r) {
-                        clients = r.next;
-                    }
-                    r = nextRetry;
-                }
-            }
-        }
-        if (complete) {
-            throw new IllegalStateException("Closed");
-        } else {
-            return (drops > 0) ? -drops : lag;
-        }
-    }
-
-    /**
-     * Unless already closed, issues {@link
-     * Flow.Subscriber#onComplete() onComplete} signals to current
-     * subscribers, and disallows subsequent attempts to publish.
-     * Upon return, this method does <em>NOT</em> guarantee that all
-     * subscribers have yet completed.
-     */
-    public void close() {
-        if (!closed) {
-            BufferedSubscription<T> b;
-            synchronized (this) {
-                b = clients;
-                clients = null;
-                closed = true;
-            }
-            while (b != null) {
-                BufferedSubscription<T> next = b.next;
-                b.next = null;
-                b.onComplete();
-                b = next;
-            }
-        }
-    }
-
-    /**
-     * Unless already closed, issues {@link
-     * Flow.Subscriber#onError(Throwable) onError} signals to current
-     * subscribers with the given error, and disallows subsequent
-     * attempts to publish.  Future subscribers also receive the given
-     * error. Upon return, this method does <em>NOT</em> guarantee
-     * that all subscribers have yet completed.
-     *
-     * @param error the {@code onError} argument sent to subscribers
-     * @throws NullPointerException if error is null
-     */
-    public void closeExceptionally(Throwable error) {
-        if (error == null) {
-            throw new NullPointerException();
-        }
-        if (!closed) {
-            BufferedSubscription<T> b;
-            synchronized (this) {
-                b = clients;
-                clients = null;
-                closed = true;
-                closedException = error;
-            }
-            while (b != null) {
-                BufferedSubscription<T> next = b.next;
-                b.next = null;
-                b.onError(error);
-                b = next;
-            }
-        }
-    }
-
-    /**
-     * Returns true if this publisher is not accepting submissions.
-     *
-     * @return true if closed
-     */
-    public boolean isClosed() {
-        return closed;
-    }
-
-    /**
-     * Returns the exception associated with {@link
-     * #closeExceptionally(Throwable) closeExceptionally}, or null if
-     * not closed or if closed normally.
-     *
-     * @return the exception, or null if none
-     */
-    public Throwable getClosedException() {
-        return closedException;
-    }
-
-    /**
-     * Returns true if this publisher has any subscribers.
-     *
-     * @return true if this publisher has any subscribers
-     */
-    public boolean hasSubscribers() {
-        boolean nonEmpty = false;
-        if (!closed) {
-            synchronized (this) {
-                for (BufferedSubscription<T> b = clients; b != null; ) {
-                    BufferedSubscription<T> next = b.next;
-                    if (b.isDisabled()) {
-                        b.next = null;
-                        b = clients = next;
-                    } else {
-                        nonEmpty = true;
-                        break;
-                    }
-                }
-            }
-        }
-        return nonEmpty;
-    }
-
-    /**
-     * Returns the number of current subscribers.
-     *
-     * @return the number of current subscribers
-     */
-    public int getNumberOfSubscribers() {
-        int count = 0;
-        if (!closed) {
-            synchronized (this) {
-                BufferedSubscription<T> pred = null, next;
-                for (BufferedSubscription<T> b = clients; b != null; b = next) {
-                    next = b.next;
-                    if (b.isDisabled()) {
-                        b.next = null;
-                        if (pred == null) {
-                            clients = next;
-                        } else {
-                            pred.next = next;
-                        }
-                    } else {
-                        pred = b;
-                        ++count;
-                    }
-                }
-            }
-        }
-        return count;
-    }
-
-    /**
-     * Returns the Executor used for asynchronous delivery.
-     *
-     * @return the Executor used for asynchronous delivery
-     */
-    public Executor getExecutor() {
-        return executor;
-    }
-
-    /**
-     * Returns the maximum per-subscriber buffer capacity.
-     *
-     * @return the maximum per-subscriber buffer capacity
-     */
-    public int getMaxBufferCapacity() {
-        return maxBufferCapacity;
-    }
-
-    /**
-     * Returns a list of current subscribers for monitoring and
-     * tracking purposes, not for invoking {@link Flow.Subscriber}
-     * methods on the subscribers.
-     *
-     * @return list of current subscribers
-     */
-    public List<Flow.Subscriber<? super T>> getSubscribers() {
-        ArrayList<Flow.Subscriber<? super T>> subs = new ArrayList<>();
-        synchronized (this) {
-            BufferedSubscription<T> pred = null, next;
-            for (BufferedSubscription<T> b = clients; b != null; b = next) {
-                next = b.next;
-                if (b.isDisabled()) {
-                    b.next = null;
-                    if (pred == null) {
-                        clients = next;
-                    } else {
-                        pred.next = next;
-                    }
-                } else {
-                    subs.add(b.subscriber);
-                }
-            }
-        }
-        return subs;
-    }
-
-    /**
-     * Returns true if the given Subscriber is currently subscribed.
-     *
-     * @param subscriber the subscriber
-     * @return true if currently subscribed
-     * @throws NullPointerException if subscriber is null
-     */
-    public boolean isSubscribed(Flow.Subscriber<? super T> subscriber) {
-        if (subscriber == null) {
-            throw new NullPointerException();
-        }
-        if (!closed) {
-            synchronized (this) {
-                BufferedSubscription<T> pred = null, next;
-                for (BufferedSubscription<T> b = clients; b != null; b = next) {
-                    next = b.next;
-                    if (b.isDisabled()) {
-                        b.next = null;
-                        if (pred == null) {
-                            clients = next;
-                        } else {
-                            pred.next = next;
-                        }
-                    } else if (subscriber.equals(b.subscriber)) {
-                        return true;
-                    } else {
-                        pred = b;
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Returns an estimate of the minimum number of items requested
-     * (via {@link Flow.Subscription#request(long) request}) but not
-     * yet produced, among all current subscribers.
-     *
-     * @return the estimate, or zero if no subscribers
-     */
-    public long estimateMinimumDemand() {
-        long min = Long.MAX_VALUE;
-        boolean nonEmpty = false;
-        synchronized (this) {
-            BufferedSubscription<T> pred = null, next;
-            for (BufferedSubscription<T> b = clients; b != null; b = next) {
-                int n;
-                long d;
-                next = b.next;
-                if ((n = b.estimateLag()) < 0) {
-                    b.next = null;
-                    if (pred == null) {
-                        clients = next;
-                    } else {
-                        pred.next = next;
-                    }
-                } else {
-                    if ((d = b.demand - n) < min) {
-                        min = d;
-                    }
-                    nonEmpty = true;
-                    pred = b;
-                }
-            }
-        }
-        return nonEmpty ? min : 0;
-    }
-
-    /**
-     * Returns an estimate of the maximum number of items produced but
-     * not yet consumed among all current subscribers.
-     *
-     * @return the estimate
-     */
-    public int estimateMaximumLag() {
-        int max = 0;
-        synchronized (this) {
-            BufferedSubscription<T> pred = null, next;
-            for (BufferedSubscription<T> b = clients; b != null; b = next) {
-                int n;
-                next = b.next;
-                if ((n = b.estimateLag()) < 0) {
-                    b.next = null;
-                    if (pred == null) {
-                        clients = next;
-                    } else {
-                        pred.next = next;
-                    }
-                } else {
-                    if (n > max) {
-                        max = n;
-                    }
-                    pred = b;
-                }
-            }
-        }
-        return max;
-    }
-
-    /**
-     * Processes all published items using the given Consumer function.
-     * Returns a CompletableFuture that is completed normally when this
-     * publisher signals {@link Flow.Subscriber#onComplete()
-     * onComplete}, or completed exceptionally upon any error, or an
-     * exception is thrown by the Consumer, or the returned
-     * CompletableFuture is cancelled, in which case no further items
-     * are processed.
-     *
-     * @param consumer the function applied to each onNext item
-     * @return a CompletableFuture that is completed normally
-     * when the publisher signals onComplete, and exceptionally
-     * upon any error or cancellation
-     * @throws NullPointerException if consumer is null
-     */
-    public CompletableFuture<Void> consume(Consumer<? super T> consumer) {
-        if (consumer == null) {
-            throw new NullPointerException();
-        }
-        CompletableFuture<Void> status = new CompletableFuture<>();
-        subscribe(new ConsumerSubscriber<T>(status, consumer));
-        return status;
-    }
-
-    /**
-     * Subscriber for method consume
-     */
-    private static final class ConsumerSubscriber<T>
-            implements Flow.Subscriber<T> {
-        final CompletableFuture<Void> status;
-        final Consumer<? super T> consumer;
-        Flow.Subscription subscription;
-
-        ConsumerSubscriber(CompletableFuture<Void> status,
-                           Consumer<? super T> consumer) {
-            this.status = status;
-            this.consumer = consumer;
-        }
-
-        public final void onSubscribe(Flow.Subscription subscription) {
-            this.subscription = subscription;
-            status.whenComplete((v, e) -> subscription.cancel());
-            if (!status.isDone()) {
-                subscription.request(Long.MAX_VALUE);
-            }
-        }
-
-        public final void onError(Throwable ex) {
-            status.completeExceptionally(ex);
-        }
-
-        public final void onComplete() {
-            status.complete(null);
-        }
-
-        public final void onNext(T item) {
-            try {
-                consumer.accept(item);
-            } catch (Throwable ex) {
-                subscription.cancel();
-                status.completeExceptionally(ex);
-            }
-        }
-    }
-
-    /**
-     * A task for consuming buffer items and signals, created and
-     * executed whenever they become available. A task consumes as
-     * many items/signals as possible before terminating, at which
-     * point another task is created when needed. The dual Runnable
-     * and ForkJoinTask declaration saves overhead when executed by
-     * ForkJoinPools, without impacting other kinds of Executors.
-     */
-    @SuppressWarnings("serial")
-    static final class ConsumerTask<T> extends ForkJoinTask<Void>
-            implements Runnable, CompletableFuture.AsynchronousCompletionTask {
-        final BufferedSubscription<T> consumer;
-
-        ConsumerTask(BufferedSubscription<T> consumer) {
-            this.consumer = consumer;
-        }
-
-        public final Void getRawResult() {
-            return null;
-        }
-
-        public final void setRawResult(Void v) {
-        }
-
-        public final boolean exec() {
-            consumer.consume();
-            return false;
-        }
-
-        public final void run() {
-            consumer.consume();
-        }
-    }
-
-    /**
-     * A bounded (ring) buffer with integrated control to start a
-     * consumer task whenever items are available.  The buffer
-     * algorithm is similar to one used inside ForkJoinPool (see its
-     * internal documentation for details) specialized for the case of
-     * at most one concurrent producer and consumer, and power of two
-     * buffer sizes. This allows methods to operate without locks even
-     * while supporting resizing, blocking, task-triggering, and
-     * garbage-free buffers (nulling out elements when consumed),
-     * although supporting these does impose a bit of overhead
-     * compared to plain fixed-size ring buffers.
-     * <p>
-     * The publisher guarantees a single producer via its lock.  We
-     * ensure in this class that there is at most one consumer.  The
-     * request and cancel methods must be fully thread-safe but are
-     * coded to exploit the most common case in which they are only
-     * called by consumers (usually within onNext).
-     * <p>
-     * Execution control is managed using the ACTIVE ctl bit. We
-     * ensure that a task is active when consumable items (and
-     * usually, SUBSCRIBE, ERROR or COMPLETE signals) are present and
-     * there is demand (unfilled requests).  This is complicated on
-     * the creation side by the possibility of exceptions when trying
-     * to execute tasks. These eventually force DISABLED state, but
-     * sometimes not directly. On the task side, termination (clearing
-     * ACTIVE) that would otherwise race with producers or request()
-     * calls uses the CONSUME keep-alive bit to force a recheck.
-     * <p>
-     * The ctl field also manages run state. When DISABLED, no further
-     * updates are possible. Disabling may be preceded by setting
-     * ERROR or COMPLETE (or both -- ERROR has precedence), in which
-     * case the associated Subscriber methods are invoked, possibly
-     * synchronously if there is no active consumer task (including
-     * cases where execute() failed). The cancel() method is supported
-     * by treating as ERROR but suppressing onError signal.
-     * <p>
-     * Support for blocking also exploits the fact that there is only
-     * one possible waiter. ManagedBlocker-compatible control fields
-     * are placed in this class itself rather than in wait-nodes.
-     * Blocking control relies on the "waiter" field. Producers set
-     * the field before trying to block, but must then recheck (via
-     * offer) before parking. Signalling then just unparks and clears
-     * waiter field. If the producer and/or consumer are using a
-     * ForkJoinPool, the producer attempts to help run consumer tasks
-     * via ForkJoinPool.helpAsyncBlocker before blocking.
-     * <p>
-     * This class uses @Contended and heuristic field declaration
-     * ordering to reduce false-sharing-based memory contention among
-     * instances of BufferedSubscription, but it does not currently
-     * attempt to avoid memory contention among buffers. This field
-     * and element packing can hurt performance especially when each
-     * publisher has only one client operating at a high rate.
-     * Addressing this may require allocating substantially more space
-     * than users expect.
-     */
-    @SuppressWarnings("serial")
-    @sun.misc.Contended
-    private static final class BufferedSubscription<T>
-            implements Flow.Subscription, ForkJoinPool.ManagedBlocker {
-        // Order-sensitive field declarations
-        long timeout;                      // > 0 if timed wait
-        volatile long demand;              // # unfilled requests
-        int maxCapacity;                   // reduced on OOME
-        int putStat;                       // offer result for ManagedBlocker
-        volatile int ctl;                  // atomic run state flags
-        volatile int head;                 // next position to take
-        int tail;                          // next position to put
-        Object[] array;                    // buffer: null if disabled
-        Flow.Subscriber<? super T> subscriber; // null if disabled
-        Executor executor;                 // null if disabled
-        BiConsumer<? super Flow.Subscriber<? super T>, ? super Throwable> onNextHandler;
-        volatile Throwable pendingError;   // holds until onError issued
-        volatile Thread waiter;            // blocked producer thread
-        T putItem;                         // for offer within ManagedBlocker
-        BufferedSubscription<T> next;      // used only by publisher
-        BufferedSubscription<T> nextRetry; // used only by publisher
-
-        // ctl values
-        static final int ACTIVE = 0x01; // consumer task active
-        static final int CONSUME = 0x02; // keep-alive for consumer task
-        static final int DISABLED = 0x04; // final state
-        static final int ERROR = 0x08; // signal onError then disable
-        static final int SUBSCRIBE = 0x10; // signal onSubscribe
-        static final int COMPLETE = 0x20; // signal onComplete when done
-
-        static final long INTERRUPTED = -1L; // timeout vs interrupt sentinel
-
-        /**
-         * Initial buffer capacity used when maxBufferCapacity is
-         * greater. Must be a power of two.
-         */
-        static final int DEFAULT_INITIAL_CAP = 32;
-
-        BufferedSubscription(Flow.Subscriber<? super T> subscriber,
-                             Executor executor,
-                             BiConsumer<? super Flow.Subscriber<? super T>,
-                                     ? super Throwable> onNextHandler,
-                             int maxBufferCapacity) {
-            this.subscriber = subscriber;
-            this.executor = executor;
-            this.onNextHandler = onNextHandler;
-            this.maxCapacity = maxBufferCapacity;
-            this.array = new Object[maxBufferCapacity < DEFAULT_INITIAL_CAP
-                    ? (maxBufferCapacity < 2 // at least 2 slots
-                        ? 2 : maxBufferCapacity)
-                        : DEFAULT_INITIAL_CAP];
-        }
-
-        @Override
-        public String toString() {
-            if (subscriber != null) {
-                return subscriber.toString();
-            } else {
-                return super.toString();
-            }
-        }
-
-        final boolean isDisabled() {
-            return ctl == DISABLED;
-        }
-
-        /**
-         * Returns estimated number of buffered items, or -1 if
-         * disabled.
-         */
-        final int estimateLag() {
-            int n;
-            return (ctl == DISABLED) ? -1 : ((n = tail - head) > 0) ? n : 0;
-        }
-
-        /**
-         * Tries to add item and start consumer task if necessary.
-         *
-         * @return -1 if disabled, 0 if dropped, else estimated lag
-         */
-        final int offer(T item) {
-            int h = head, t = tail, cap, size, stat;
-            Object[] a = array;
-            if (a != null && (cap = a.length) > 0 && cap >= (size = t + 1 - h)) {
-                a[(cap - 1) & t] = item;    // relaxed writes OK
-                tail = t + 1;
-                stat = size;
-            } else {
-                stat = growAndAdd(a, item);
-            }
-            return (stat > 0 && (ctl & (ACTIVE | CONSUME)) != (ACTIVE | CONSUME)) ? startOnOffer(stat) : stat;
-        }
-
-        /**
-         * Tries to create or expand buffer, then adds item if possible.
-         */
-        private int growAndAdd(Object[] a, T item) {
-            boolean alloc;
-            int cap, stat;
-            if ((ctl & (ERROR | DISABLED)) != 0) {
-                cap = 0;
-                stat = -1;
-                alloc = false;
-            } else if (a == null || (cap = a.length) <= 0) {
-                cap = 0;
-                stat = 1;
-                alloc = true;
-            } else {
-                U.fullFence();                   // recheck
-                int h = head, t = tail, size = t + 1 - h;
-                if (cap >= size) {
-                    a[(cap - 1) & t] = item;
-                    tail = t + 1;
-                    stat = size;
-                    alloc = false;
-                } else if (cap >= maxCapacity) {
-                    stat = 0;                    // cannot grow
-                    alloc = false;
-                } else {
-                    stat = cap + 1;
-                    alloc = true;
-                }
-            }
-            if (alloc) {
-                int newCap = (cap > 0) ? cap << 1 : 1;
-                if (newCap <= cap) {
-                    stat = 0;
-                } else {
-                    Object[] newArray = null;
-                    try {
-                        newArray = new Object[newCap];
-                    } catch (Throwable ex) {     // try to cope with OOME
-                    }
-                    if (newArray == null) {
-                        if (cap > 0) {
-                            maxCapacity = cap;   // avoid continuous failure
-                        }
-                        stat = 0;
-                    } else {
-                        array = newArray;
-                        int t = tail;
-                        int newMask = newCap - 1;
-                        if (a != null && cap > 0) {
-                            int mask = cap - 1;
-                            for (int j = head; j != t; ++j) {
-                                long k = ((long) (j & mask) << ASHIFT) + ABASE;
-                                Object x = U.getObjectVolatile(a, k);
-                                if (x != null && // races with consumer
-                                        U.compareAndSwapObject(a, k, x, null)) {
-                                    newArray[j & newMask] = x;
-                                }
-                            }
-                        }
-                        newArray[t & newMask] = item;
-                        tail = t + 1;
-                    }
-                }
-            }
-            return stat;
-        }
-
-        /**
-         * Spins/helps/blocks while offer returns 0.  Called only if
-         * initial offer return 0.
-         */
-        final int submit(T item) {
-            int stat;
-            if ((stat = offer(item)) == 0) {
-                putItem = item;
-                timeout = 0L;
-                putStat = 0;
-                // safe to comment out when executor != ForkJoinPool (TODO)
-                // ForkJoinPool.helpAsyncBlocker(executor, this);
-                if ((stat = putStat) == 0) {
-                    try {
-                        ForkJoinPool.managedBlock(this);
-                    } catch (InterruptedException ie) {
-                        timeout = INTERRUPTED;
-                    }
-                    stat = putStat;
-                }
-                if (timeout < 0L) {
-                    Thread.currentThread().interrupt();
-                }
-            }
-            return stat;
-        }
-
-        /**
-         * Timeout version; similar to submit.
-         */
-        final int timedOffer(T item, long nanos) {
-            int stat;
-            if ((stat = offer(item)) == 0 && (timeout = nanos) > 0L) {
-                putItem = item;
-                putStat = 0;
-                // safe to comment out when executor != ForkJoinPool (TODO)
-                // ForkJoinPool.helpAsyncBlocker(executor, this);
-                if ((stat = putStat) == 0) {
-                    try {
-                        ForkJoinPool.managedBlock(this);
-                    } catch (InterruptedException ie) {
-                        timeout = INTERRUPTED;
-                    }
-                    stat = putStat;
-                }
-                if (timeout < 0L) {
-                    Thread.currentThread().interrupt();
-                }
-            }
-            return stat;
-        }
-
-        /**
-         * Tries to start consumer task after offer.
-         *
-         * @return -1 if now disabled, else argument
-         */
-        private int startOnOffer(int stat) {
-            for (;;) {
-                Executor e;
-                int c;
-                if ((c = ctl) == DISABLED || (e = executor) == null) {
-                    stat = -1;
-                    break;
-                } else if ((c & ACTIVE) != 0) { // ensure keep-alive
-                    if ((c & CONSUME) != 0 || U.compareAndSwapInt(this, CTL, c, c | CONSUME)) {
-                        break;
-                    }
-                } else if (demand == 0L || tail == head) {
-                    break;
-                } else if (U.compareAndSwapInt(this, CTL, c,
-                        c | (ACTIVE | CONSUME))) {
-                    try {
-                        e.execute(new ConsumerTask<T>(this));
-                        break;
-                    } catch (RuntimeException | Error ex) { // back out
-                        do {
-                        } while (((c = ctl) & DISABLED) == 0
-                                && (c & ACTIVE) != 0
-                                && !U.compareAndSwapInt(this, CTL, c, c & ~ACTIVE));
-                        throw ex;
-                    }
-                }
-            }
-            return stat;
-        }
-
-        private void signalWaiter(Thread w) {
-            waiter = null;
-            LockSupport.unpark(w);    // release producer
-        }
-
-        /**
-         * Nulls out most fields, mainly to avoid garbage retention
-         * until publisher unsubscribes, but also to help cleanly stop
-         * upon error by nulling required components.
-         */
-        private void detach() {
-            Thread w = waiter;
-            executor = null;
-            subscriber = null;
-            pendingError = null;
-            signalWaiter(w);
-        }
-
-        /**
-         * Issues error signal, asynchronously if a task is running,
-         * else synchronously.
-         */
-        final void onError(Throwable ex) {
-            for (int c;;) {
-                if (((c = ctl) & (ERROR | DISABLED)) != 0) {
-                    break;
-                } else if ((c & ACTIVE) != 0) {
-                    pendingError = ex;
-                    if (U.compareAndSwapInt(this, CTL, c, c | ERROR)) {
-                        break; // cause consumer task to exit
-                    }
-                } else if (U.compareAndSwapInt(this, CTL, c, DISABLED)) {
-                    Flow.Subscriber<? super T> s = subscriber;
-                    if (s != null && ex != null) {
-                        try {
-                            s.onError(ex);
-                        } catch (Throwable ignore) {
-                        }
-                    }
-                    detach();
-                    break;
-                }
-            }
-        }
-
-        /**
-         * Tries to start consumer task upon a signal or request;
-         * disables on failure.
-         */
-        private void startOrDisable() {
-            Executor e;
-            if ((e = executor) != null) { // skip if already disabled
-                try {
-                    e.execute(new ConsumerTask<T>(this));
-                } catch (Throwable ex) {  // back out and force signal
-                    for (int c;;) {
-                        if ((c = ctl) == DISABLED || (c & ACTIVE) == 0) {
-                            break;
-                        }
-                        if (U.compareAndSwapInt(this, CTL, c, c & ~ACTIVE)) {
-                            onError(ex);
-                            break;
-                        }
-                    }
-                }
-            }
-        }
-
-        final void onComplete() {
-            for (int c;;) {
-                if ((c = ctl) == DISABLED) {
-                    break;
-                }
-                if (U.compareAndSwapInt(this, CTL, c,
-                        c | (ACTIVE | CONSUME | COMPLETE))) {
-                    if ((c & ACTIVE) == 0) {
-                        startOrDisable();
-                    }
-                    break;
-                }
-            }
-        }
-
-        final void onSubscribe() {
-            for (int c;;) {
-                if ((c = ctl) == DISABLED) {
-                    break;
-                }
-                if (U.compareAndSwapInt(this, CTL, c,
-                        c | (ACTIVE | CONSUME | SUBSCRIBE))) {
-                    if ((c & ACTIVE) == 0) {
-                        startOrDisable();
-                    }
-                    break;
-                }
-            }
-        }
-
-        /**
-         * Causes consumer task to exit if active (without reporting
-         * onError unless there is already a pending error), and
-         * disables.
-         */
-        public void cancel() {
-            for (int c;;) {
-                if ((c = ctl) == DISABLED) {
-                    break;
-                } else if ((c & ACTIVE) != 0) {
-                    if (U.compareAndSwapInt(this, CTL, c,
-                            c | (CONSUME | ERROR))) {
-                        break;
-                    }
-                } else if (U.compareAndSwapInt(this, CTL, c, DISABLED)) {
-                    detach();
-                    break;
-                }
-            }
-        }
-
-        /**
-         * Adds to demand and possibly starts task.
-         */
-        public void request(long n) {
-            if (n > 0L) {
-                for (;;) {
-                    long prev = demand, d;
-                    if ((d = prev + n) < prev) { // saturate
-                        d = Long.MAX_VALUE;
-                    }
-                    if (U.compareAndSwapLong(this, DEMAND, prev, d)) {
-                        for (int c, h;; ) {
-                            if ((c = ctl) == DISABLED) {
-                                break;
-                            } else if ((c & ACTIVE) != 0) {
-                                if ((c & CONSUME) != 0 || U.compareAndSwapInt(this, CTL, c, c | CONSUME)) {
-                                    break;
-                                }
-                            } else if ((h = head) != tail) {
-                                if (U.compareAndSwapInt(this, CTL, c,
-                                        c | (ACTIVE | CONSUME))) {
-                                    startOrDisable();
-                                    break;
-                                }
-                            } else if (head == h && tail == h) {
-                                break;          // else stale
-                            }
-                            if (demand == 0L) {
-                                break;
-                            }
-                        }
-                        break;
-                    }
-                }
-            } else if (n < 0L) {
-                onError(new IllegalArgumentException(
-                        "negative subscription request"));
-            }
-        }
-
-        public final boolean isReleasable() { // for ManagedBlocker
-            T item = putItem;
-            if (item != null) {
-                if ((putStat = offer(item)) == 0) {
-                    return false;
-                }
-                putItem = null;
-            }
-            return true;
-        }
-
-        public final boolean block() { // for ManagedBlocker
-            T item = putItem;
-            if (item != null) {
-                putItem = null;
-                long nanos = timeout;
-                long deadline = (nanos > 0L) ? System.nanoTime() + nanos : 0L;
-                while ((putStat = offer(item)) == 0) {
-                    if (Thread.interrupted()) {
-                        timeout = INTERRUPTED;
-                        if (nanos > 0L) {
-                            break;
-                        }
-                    } else if (nanos > 0L && (nanos = deadline - System.nanoTime()) <= 0L) {
-                        break;
-                    } else if (waiter == null) {
-                        waiter = Thread.currentThread();
-                    } else {
-                        if (nanos > 0L) {
-                            LockSupport.parkNanos(this, nanos);
-                        } else {
-                            LockSupport.park(this);
-                        }
-                        waiter = null;
-                    }
-                }
-            }
-            waiter = null;
-            return true;
-        }
-
-        /**
-         * Consumer loop, called from ConsumerTask, or indirectly
-         * when helping during submit.
-         */
-        final void consume() {
-            Flow.Subscriber<? super T> s;
-            int h = head;
-            if ((s = subscriber) != null) {           // else disabled
-                for (;;) {
-                    long d = demand;
-                    int c;
-                    Object[] a;
-                    int n;
-                    long i;
-                    Object x;
-                    Thread w;
-                    if (((c = ctl) & (ERROR | SUBSCRIBE | DISABLED)) != 0) {
-                        if (!checkControl(s, c)) {
-                            break;
-                        }
-                    } else if ((a = array) == null || h == tail
-                            || (n = a.length) == 0
-                            || (x = U.getObjectVolatile(a, (i = ((long) ((n - 1) & h) << ASHIFT) + ABASE))) == null) {
-                        if (!checkEmpty(s, c)) {
-                            break;
-                        }
-                    } else if (d == 0L) {
-                        if (!checkDemand(c)) {
-                            break;
-                        }
-                    } else if (((c & CONSUME) != 0
-                            || U.compareAndSwapInt(this, CTL, c, c | CONSUME))
-                            && U.compareAndSwapObject(a, i, x, null)) {
-                        U.putOrderedInt(this, HEAD, ++h);
-                        U.getAndAddLong(this, DEMAND, -1L);
-                        if ((w = waiter) != null) {
-                            signalWaiter(w);
-                        }
-                        try {
-                            @SuppressWarnings("unchecked") T y = (T) x;
-                            s.onNext(y);
-                        } catch (Throwable ex) {
-                            handleOnNext(s, ex);
-                        }
-                    }
-                }
-            }
-        }
-
-        /**
-         * Responds to control events in consume().
-         */
-        private boolean checkControl(Flow.Subscriber<? super T> s, int c) {
-            boolean stat = true;
-            if ((c & SUBSCRIBE) != 0) {
-                if (U.compareAndSwapInt(this, CTL, c, c & ~SUBSCRIBE)) {
-                    try {
-                        if (s != null) {
-                            s.onSubscribe(this);
-                        }
-                    } catch (Throwable ex) {
-                        onError(ex);
-                    }
-                }
-            } else if ((c & ERROR) != 0) {
-                Throwable ex = pendingError;
-                ctl = DISABLED;           // no need for CAS
-                if (ex != null) {         // null if errorless cancel
-                    try {
-                        if (s != null) {
-                            s.onError(ex);
-                        }
-                    } catch (Throwable ignore) {
-                    }
-                }
-            } else {
-                detach();
-                stat = false;
-            }
-            return stat;
-        }
-
-        /**
-         * Responds to apparent emptiness in consume().
-         */
-        private boolean checkEmpty(Flow.Subscriber<? super T> s, int c) {
-            boolean stat = true;
-            if (head == tail) {
-                if ((c & CONSUME) != 0) {
-                    U.compareAndSwapInt(this, CTL, c, c & ~CONSUME);
-                } else if ((c & COMPLETE) != 0) {
-                    if (U.compareAndSwapInt(this, CTL, c, DISABLED)) {
-                        try {
-                            if (s != null) {
-                                s.onComplete();
-                            }
-                        } catch (Throwable ignore) {
-                        }
-                    }
-                } else if (U.compareAndSwapInt(this, CTL, c, c & ~ACTIVE)) {
-                    stat = false;
-                }
-            }
-            return stat;
-        }
-
-        /**
-         * Responds to apparent zero demand in consume().
-         */
-        private boolean checkDemand(int c) {
-            boolean stat = true;
-            if (demand == 0L) {
-                if ((c & CONSUME) != 0) {
-                    U.compareAndSwapInt(this, CTL, c, c & ~CONSUME);
-                } else if (U.compareAndSwapInt(this, CTL, c, c & ~ACTIVE)) {
-                    stat = false;
-                }
-            }
-            return stat;
-        }
-
-        /**
-         * Processes exception in Subscriber.onNext.
-         */
-        private void handleOnNext(Flow.Subscriber<? super T> s, Throwable ex) {
-            BiConsumer<? super Flow.Subscriber<? super T>, ? super Throwable> h;
-            if ((h = onNextHandler) != null) {
-                try {
-                    h.accept(s, ex);
-                } catch (Throwable ignore) {
-                }
-            }
-            onError(ex);
-        }
-
-        // Unsafe mechanics
-        private static final sun.misc.Unsafe U = UnsafeAccessor.getUnsafe();
-        private static final long CTL;
-        private static final long TAIL;
-        private static final long HEAD;
-        private static final long DEMAND;
-        private static final int ABASE;
-        private static final int ASHIFT;
-
-        static {
-            try {
-                CTL = U.objectFieldOffset(BufferedSubscription.class.getDeclaredField("ctl"));
-                TAIL = U.objectFieldOffset(BufferedSubscription.class.getDeclaredField("tail"));
-                HEAD = U.objectFieldOffset(BufferedSubscription.class.getDeclaredField("head"));
-                DEMAND = U.objectFieldOffset(BufferedSubscription.class.getDeclaredField("demand"));
-
-                ABASE = U.arrayBaseOffset(Object[].class);
-                int scale = U.arrayIndexScale(Object[].class);
-                if ((scale & (scale - 1)) != 0) {
-                    throw new Error("data type scale not a power of two");
-                }
-                ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
-            } catch (ReflectiveOperationException e) {
-                throw new Error(e);
-            }
-
-            // Reduce the risk of rare disastrous classloading in first call to
-            // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773
-            Class<?> ensureLoaded = LockSupport.class;
-        }
-    }
-}
diff --git a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/SubmissionPublisherFactory.java b/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/SubmissionPublisherFactory.java
deleted file mode 100644
index 3729469..0000000
--- a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/SubmissionPublisherFactory.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (c) 2019 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.internal.jsr166;
-
-import java.util.concurrent.Executor;
-import java.util.concurrent.ForkJoinPool;
-import java.util.function.BiConsumer;
-
-/**
- * Factory creating JDK8 compatible SubmissionPublisher (Jdk8SubmissionPublisher) or JDK 9+ SubmissionPublisher
- */
-public class SubmissionPublisherFactory {
-
-    /**
-     * Creates a new SubmissionPublisher using the {@link
-     * ForkJoinPool#commonPool()} for async delivery to subscribers
-     * (unless it does not support a parallelism level of at least two,
-     * in which case, a new Thread is created to run each task), with
-     * maximum buffer capacity of {@link Flow#defaultBufferSize}, and no
-     * handler for Subscriber exceptions in method {@link
-     * Flow.Subscriber#onNext(Object) onNext}.
-     */
-    public static <T> SubmittableFlowPublisher<T> createSubmissionPublisher() {
-        return new SubmissionPublisher<T>();
-    }
-
-    public static <T> SubmittableFlowPublisher<T> createSubmissionPublisher(Executor executor,
-                                                                            int maxBufferCapacity) {
-        return new SubmissionPublisher<T>(executor, maxBufferCapacity);
-    }
-
-    public static <T> SubmittableFlowPublisher<T> createSubmissionPublisher(Executor executor,
-                                                                            int maxBufferCapacity,
-                                                                            BiConsumer<? super Flow.Subscriber<? super T>,
-                                                                                    ? super Throwable> handler) {
-        return new SubmissionPublisher<T>(executor, maxBufferCapacity, handler);
-    }
-
-}
diff --git a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/UnsafeAccessor.java b/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/UnsafeAccessor.java
deleted file mode 100644
index 19f9887..0000000
--- a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/UnsafeAccessor.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (c) 2017, 2019 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.internal.jsr166;
-
-import sun.misc.Unsafe;
-
-import java.lang.reflect.Field;
-import java.security.PrivilegedExceptionAction;
-
-/**
- * Helper for classes in the jsr166 package to access the {@code sun.misc.Unsafe} instance.
- *
- * @author Adam Lindenthal
- */
-class UnsafeAccessor {
-    static sun.misc.Unsafe getUnsafe() {
-        try {
-            return sun.misc.Unsafe.getUnsafe();
-        } catch (SecurityException tryReflectionInstead) {
-        }
-        try {
-            return java.security.AccessController.doPrivileged((PrivilegedExceptionAction<Unsafe>) () -> {
-                Class<Unsafe> k = Unsafe.class;
-                for (Field f : k.getDeclaredFields()) {
-                    f.setAccessible(true);
-                    Object x = f.get(null);
-                    if (k.isInstance(x)) {
-                        return k.cast(x);
-                    }
-                }
-                throw new NoSuchFieldError("the Unsafe");
-            });
-        } catch (java.security.PrivilegedActionException e) {
-            throw new RuntimeException("Could not initialize intrinsics", e.getCause());
-        }
-
-
-    }
-}
diff --git a/core-common/src/main/resources/org/glassfish/jersey/internal/localization.properties b/core-common/src/main/resources/org/glassfish/jersey/internal/localization.properties
index 285b9de..b60bf25 100644
--- a/core-common/src/main/resources/org/glassfish/jersey/internal/localization.properties
+++ b/core-common/src/main/resources/org/glassfish/jersey/internal/localization.properties
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
 # Copyright (c) 2018 Payara Foundation and/or its affiliates.
 #
 # This program and the accompanying materials are made available under the
@@ -116,6 +116,7 @@
 new.cookie.is.null=New cookie is null.
 no.container.available=No container available.
 no.error.processing.in.scope=There is no error processing in scope.
+no.entitypart.builder.found="No EntityPart.Builder implementation found. Is jersey-media-multipart on a classpath?";
 not.supported.on.outbound.message=Method not supported on an outbound message.
 osgi.registry.error.opening.resource.stream=Unable to open an input stream for resource {0}.
 osgi.registry.error.processing.resource.stream=Unexpected error occurred while processing resource stream {0}.
diff --git a/core-common/src/test/java/org/glassfish/jersey/internal/TestRuntimeDelegate.java b/core-common/src/test/java/org/glassfish/jersey/internal/TestRuntimeDelegate.java
index 821b62c..2bb7d12 100644
--- a/core-common/src/test/java/org/glassfish/jersey/internal/TestRuntimeDelegate.java
+++ b/core-common/src/test/java/org/glassfish/jersey/internal/TestRuntimeDelegate.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2021 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
@@ -16,8 +16,10 @@
 
 package org.glassfish.jersey.internal;
 
+import jakarta.ws.rs.SeBootstrap;
 import jakarta.ws.rs.WebApplicationException;
 import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.EntityPart;
 import jakarta.ws.rs.core.Link;
 import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.Response;
@@ -29,6 +31,8 @@
 
 import org.junit.Assert;
 
+import java.util.concurrent.CompletionStage;
+
 /**
  * Test runtime delegate.
  *
@@ -46,6 +50,22 @@
         throw new UnsupportedOperationException("Not supported yet.");
     }
 
+    @Override
+    public SeBootstrap.Configuration.Builder createConfigurationBuilder() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public CompletionStage<SeBootstrap.Instance> bootstrap(Application application, SeBootstrap.Configuration configuration) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public CompletionStage<SeBootstrap.Instance> bootstrap(Class<? extends Application> aClass,
+                                                           SeBootstrap.Configuration configuration) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
     public void testMediaType() {
         MediaType m = new MediaType("text", "plain");
         Assert.assertNotNull(m);
diff --git a/core-common/src/test/java/org/glassfish/jersey/message/internal/NewCookieProviderTest.java b/core-common/src/test/java/org/glassfish/jersey/message/internal/NewCookieProviderTest.java
new file mode 100644
index 0000000..5a4ba2f
--- /dev/null
+++ b/core-common/src/test/java/org/glassfish/jersey/message/internal/NewCookieProviderTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2021 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.message.internal;
+
+import jakarta.ws.rs.core.NewCookie;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Date;
+
+public class NewCookieProviderTest {
+
+    private final NewCookie newCookie = new NewCookie(
+            "test",
+            "value",
+            "/",
+            "localhost",
+            1,
+            "comment",
+            60,
+            new Date(),
+            true,
+            true,
+            NewCookie.SameSite.STRICT
+    );
+
+    @Test
+    public void SameSiteTest() {
+        final NewCookieProvider provider = new NewCookieProvider();
+        final String newCookieString = provider.toString(newCookie);
+        Assert.assertTrue(newCookieString.contains("SameSite=STRICT"));
+        Assert.assertEquals(NewCookie.SameSite.STRICT, provider.fromString(newCookieString).getSameSite());
+    }
+
+}
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ApplicationHandler.java b/core-server/src/main/java/org/glassfish/jersey/server/ApplicationHandler.java
index 42d11dd..7d75df1 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/ApplicationHandler.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/ApplicationHandler.java
@@ -222,7 +222,20 @@
      *                              application handler.
      */
     public ApplicationHandler(final Class<? extends Application> jaxrsApplicationClass) {
-        initialize(new ApplicationConfigurator(jaxrsApplicationClass), Injections.createInjectionManager(), null);
+        this(jaxrsApplicationClass, null);
+    }
+
+    /**
+     * Create a new Jersey server-side application handler configured by a
+     * {@link Application JAX-RS Application (sub-)class}.
+     *
+     * @param applicationClass JAX-RS {@code Application} (sub-)class that will be
+     *                              instantiated and used to configure the new Jersey
+     *                              application handler.
+     * @param customBinder additional custom bindings used to configure the application's.
+     */
+    public ApplicationHandler(final Class<? extends Application> applicationClass, final Binder customBinder) {
+        initialize(new ApplicationConfigurator(applicationClass), Injections.createInjectionManager(), customBinder);
     }
 
     /**
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ContainerRequest.java b/core-server/src/main/java/org/glassfish/jersey/server/ContainerRequest.java
index a91521d..c7a8615 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/ContainerRequest.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/ContainerRequest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2022 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
@@ -301,6 +301,11 @@
     }
 
     @Override
+    public boolean hasProperty(final String name) {
+        return propertiesDelegate.hasProperty(name);
+    }
+
+    @Override
     public Object getProperty(final String name) {
         return propertiesDelegate.getProperty(name);
     }
@@ -821,13 +826,13 @@
     }
 
     /**
-     * Get the values of a HTTP request header. The returned List is read-only.
-     * This is a shortcut for {@code getRequestHeaders().get(name)}.
+     * Get the values of an HTTP request header if the header exists on the current request. The returned value will be
+     * a read-only List if the specified header exists or {@code null} if it does not. This is a shortcut for
+     * {@code getRequestHeaders().get(name)}.
      *
      * @param name the header name, case insensitive.
-     * @return a read-only list of header values.
-     *
-     * @throws IllegalStateException if called outside the scope of a request.
+     * @return a read-only list of header values if the specified header exists, otherwise {@code null}.
+     * @throws java.lang.IllegalStateException if called outside the scope of a request.
      */
     @Override
     public List<String> getRequestHeader(final String name) {
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/DefaultExceptionMapper.java b/core-server/src/main/java/org/glassfish/jersey/server/DefaultExceptionMapper.java
new file mode 100644
index 0000000..b120e69
--- /dev/null
+++ b/core-server/src/main/java/org/glassfish/jersey/server/DefaultExceptionMapper.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2021 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.server;
+
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.ext.ExceptionMapper;
+
+class DefaultExceptionMapper implements ExceptionMapper<Throwable> {
+    @Override
+    public Response toResponse(Throwable exception) {
+        return (exception instanceof WebApplicationException)
+                ? processWebApplicationException((WebApplicationException) exception)
+                : processDefaultException(exception);
+    }
+
+    private static Response processWebApplicationException(WebApplicationException exception) {
+        return (exception.getResponse() == null)
+                ? processDefaultException(exception)
+                : exception.getResponse();
+    }
+
+    private static Response processDefaultException(Throwable exception) {
+        return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(exception.getMessage()).build();
+    }
+}
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/JerseySeBootstrapConfiguration.java b/core-server/src/main/java/org/glassfish/jersey/server/JerseySeBootstrapConfiguration.java
new file mode 100644
index 0000000..e433adb
--- /dev/null
+++ b/core-server/src/main/java/org/glassfish/jersey/server/JerseySeBootstrapConfiguration.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2021 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.server;
+
+import jakarta.ws.rs.SeBootstrap;
+import jakarta.ws.rs.core.UriBuilder;
+import org.glassfish.jersey.server.internal.LocalizationMessages;
+import org.glassfish.jersey.server.spi.Container;
+import org.glassfish.jersey.server.spi.WebServer;
+
+import javax.net.ssl.SSLContext;
+import java.net.URI;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.BiFunction;
+import java.util.logging.Logger;
+
+import static java.lang.Boolean.TRUE;
+
+/**
+ * Jersey implementation of {@link SeBootstrap.Configuration} implementing arbitrary methods for acquiring
+ * the configuration settings.
+ * @since 3.1.0
+ */
+public final class JerseySeBootstrapConfiguration implements SeBootstrap.Configuration {
+    private static final Logger LOGGER = Logger.getLogger(JerseySeBootstrapConfiguration.class.getName());
+    private final SeBootstrap.Configuration configuration;
+
+    private JerseySeBootstrapConfiguration(SeBootstrap.Configuration configuration) {
+        this.configuration = configuration;
+    }
+
+    @Override
+    public Object property(String name) {
+        return configuration.property(name);
+    }
+
+    /**
+     * Compose {@link URI} based on properties defined in this configuration.
+     * @param resolveDefaultPort if {@code true} the port is not set, it is resolved as
+     *                           {@link Container#DEFAULT_HTTP_PORT} or {@link Container#DEFAULT_HTTPS_PORT}
+     *                           based on the protocol scheme.
+     * @return Composed {@link URI} based on properties defined in this configuration.
+     */
+    public URI uri(boolean resolveDefaultPort) {
+        final String protocol = configuration.protocol();
+        final String host = configuration.host();
+        final int configPort = configuration.port();
+        final int port = (configPort < 0 && resolveDefaultPort)
+                ? isHttps() ? Container.DEFAULT_HTTPS_PORT : Container.DEFAULT_HTTP_PORT
+                : configPort;
+        final String rootPath = configuration.rootPath();
+        final URI uri = UriBuilder.newInstance().scheme(protocol.toLowerCase()).host(host).port(port).path(rootPath)
+                .build();
+        return uri;
+    }
+
+    /**
+     * Return {@link SSLContext} in the configuration if the protocol scheme is {@code HTTPS}.
+     * @return the SSLContext in the configuration.
+     */
+    @Override
+    public SSLContext sslContext() {
+        final SSLContext sslContext = configuration.sslContext();
+        return isHttps() ? sslContext : null;
+    }
+
+    /**
+     * If the protocol schema is {@code HTTPS}, return {@code true}.
+     * @return {@code true} when the protocol schema is {@code HTTPS}.
+     */
+    public boolean isHttps() {
+        return "HTTPS".equalsIgnoreCase(configuration.protocol());
+    }
+
+    /**
+     * Defines if the {@link WebServer} should automatically start.
+     * @return false if {@link ServerProperties#WEBSERVER_AUTO_START} is {@code false}, {@code true} otherwise.
+     */
+    public boolean autoStart() {
+        final boolean autoStart = Optional.ofNullable(
+                (Boolean) configuration.property(ServerProperties.WEBSERVER_AUTO_START))
+                .orElse(TRUE);
+        return autoStart;
+    }
+
+    /**
+     * Factory method creating {@code JerseySeBootstrapConfiguration} wrapper around {@link SeBootstrap.Configuration}.
+     * @param configuration wrapped configuration
+     * @return {@code JerseySeBootstrapConfiguration} wrapper around {@link SeBootstrap.Configuration}.
+     */
+    public static JerseySeBootstrapConfiguration from(SeBootstrap.Configuration configuration) {
+        return JerseySeBootstrapConfiguration.class.isInstance(configuration)
+                ? (JerseySeBootstrapConfiguration) configuration
+                : new JerseySeBootstrapConfiguration(configuration);
+    }
+
+    /**
+     * Return a Jersey instance of {@link SeBootstrap.Configuration.Builder} with prefilled values.
+     * @return a Jersey instance of {@link SeBootstrap.Configuration.Builder}.
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    public static final class Builder implements SeBootstrap.Configuration.Builder {
+        private static final Map<String, Class<?>> PROPERTY_TYPES = new HashMap<>();
+
+        static {
+            PROPERTY_TYPES.put(SeBootstrap.Configuration.PROTOCOL, String.class);
+            PROPERTY_TYPES.put(SeBootstrap.Configuration.HOST, String.class);
+            PROPERTY_TYPES.put(SeBootstrap.Configuration.PORT, Integer.class);
+            PROPERTY_TYPES.put(SeBootstrap.Configuration.ROOT_PATH, String.class);
+            PROPERTY_TYPES.put(SeBootstrap.Configuration.SSL_CONTEXT, SSLContext.class);
+            PROPERTY_TYPES.put(SeBootstrap.Configuration.SSL_CLIENT_AUTHENTICATION, SSLClientAuthentication.class);
+            PROPERTY_TYPES.put(ServerProperties.WEBSERVER_CLASS, Class.class);
+            PROPERTY_TYPES.put(ServerProperties.WEBSERVER_AUTO_START, Boolean.class);
+        }
+
+        private final Map<String, Object> properties = new HashMap<>();
+
+        private Builder() {
+            this.properties.put(SeBootstrap.Configuration.PROTOCOL, "HTTP"); // upper case mandated by javadoc
+            this.properties.put(SeBootstrap.Configuration.HOST, "localhost");
+            this.properties.put(SeBootstrap.Configuration.PORT, -1); // Auto-select port 80 for HTTP or 443 for HTTPS
+            this.properties.put(SeBootstrap.Configuration.ROOT_PATH, "/");
+            this.properties.put(ServerProperties.WEBSERVER_CLASS, WebServer.class); // Auto-select first provider
+            try {
+                this.properties.put(SeBootstrap.Configuration.SSL_CONTEXT, SSLContext.getDefault());
+            } catch (final NoSuchAlgorithmException e) {
+                throw new RuntimeException(e);
+            }
+            this.properties.put(SeBootstrap.Configuration.SSL_CLIENT_AUTHENTICATION,
+                    SeBootstrap.Configuration.SSLClientAuthentication.NONE);
+            this.properties.put(ServerProperties.WEBSERVER_AUTO_START, TRUE);
+        }
+
+        @Override
+        public JerseySeBootstrapConfiguration build() {
+            return JerseySeBootstrapConfiguration.from(this.properties::get);
+        }
+
+        @Override
+        public Builder property(String name, Object value) {
+            this.properties.put(name, value);
+            return this;
+        }
+
+        /**
+         * Set the the respective {@link WebServer} class to be used by the
+         * {@link org.glassfish.jersey.server.spi.WebServerProvider}.
+         * @param webServerClass the class implementing {@link WebServer}.
+         * @return the updated builder.
+         */
+        public Builder webServerClass(Class<? extends WebServer> webServerClass) {
+            return property(ServerProperties.WEBSERVER_CLASS, webServerClass);
+        }
+
+        /**
+         * Define if the {@link WebServer} should auto-start at bootstrap.
+         * @param autostart the auto-start flag.
+         * @return the updated builder.
+         */
+        public Builder autoStart(Boolean autostart) {
+            return property(ServerProperties.WEBSERVER_AUTO_START, autostart);
+        }
+
+        @Override
+        public <T> JerseySeBootstrapConfiguration.Builder from(BiFunction<String, Class<T>, Optional<T>> configProvider) {
+            PROPERTY_TYPES.forEach(
+                    (propertyName, propertyType) -> configProvider.apply(propertyName, (Class<T>) propertyType)
+                            .ifPresent(propertyValue -> this.properties.put(propertyName, propertyValue)));
+            return this;
+        }
+
+        @Override
+        public JerseySeBootstrapConfiguration.Builder from(Object externalConfig) {
+            if (SeBootstrap.Configuration.class.isInstance(externalConfig)) {
+                final SeBootstrap.Configuration other = (SeBootstrap.Configuration) externalConfig;
+                from((name, clazz) -> {
+                    final Object property = other.property(name);
+                    if (property != null) {
+                        if (clazz.equals(property.getClass())) {
+                            return Optional.of(property);
+                        } else {
+                            LOGGER.warning(LocalizationMessages.IGNORE_SEBOOTSTRAP_CONFIGURATION_PROPERTY(name, clazz));
+                        }
+                    }
+                    return Optional.empty();
+                });
+            }
+            return this;
+        }
+    }
+}
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ResourceConfig.java b/core-server/src/main/java/org/glassfish/jersey/server/ResourceConfig.java
index 291b76f..ae39a68 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/ResourceConfig.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/ResourceConfig.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2022 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
@@ -756,6 +756,11 @@
     }
 
     @Override
+    public final boolean hasProperty(final String name) {
+        return state.hasProperty(name);
+    }
+
+    @Override
     public final Object getProperty(final String name) {
         return state.getProperty(name);
     }
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ServerBinder.java b/core-server/src/main/java/org/glassfish/jersey/server/ServerBinder.java
index 03482f9..4cf65c8 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/ServerBinder.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/ServerBinder.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2021 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
@@ -16,6 +16,7 @@
 
 package org.glassfish.jersey.server;
 
+import jakarta.ws.rs.ext.ExceptionMapper;
 import jakarta.ws.rs.ext.MessageBodyWriter;
 import jakarta.ws.rs.ext.WriterInterceptor;
 
@@ -44,5 +45,8 @@
 
         // JSONP
         bind(JsonWithPaddingInterceptor.class).to(WriterInterceptor.class).in(Singleton.class);
+
+        //Default exception mapper
+        bind(DefaultExceptionMapper.class).to(ExceptionMapper.class).in(Singleton.class);
     }
 }
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ServerProperties.java b/core-server/src/main/java/org/glassfish/jersey/server/ServerProperties.java
index 4c06c5c..25b5e9e 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/ServerProperties.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/ServerProperties.java
@@ -24,6 +24,7 @@
 import org.glassfish.jersey.internal.util.PropertiesClass;
 import org.glassfish.jersey.internal.util.PropertiesHelper;
 import org.glassfish.jersey.internal.util.PropertyAlias;
+import org.glassfish.jersey.server.spi.WebServer;
 
 
 /**
@@ -38,6 +39,25 @@
 public final class ServerProperties {
 
     /**
+     * Defines the implementation of {@link WebServer} to bootstrap.
+     * <p>
+     * By default auto-selects the first server provider found.
+     * </p>
+     * @since 3.1.0
+     */
+    public static final String WEBSERVER_CLASS = "jersey.config.server.webserver.class";
+
+    /**
+     * Whether to automatically startup {@link WebServer} at bootstrap.
+     * <p>
+     * By default, servers are immediately listening to connections after bootstrap,
+     * so no explicit invocation of {@link WebServer#start()} is needed.
+     * </p>
+     * @since 3.1.0
+     */
+    public static final String WEBSERVER_AUTO_START = "jersey.config.server.webserver.autostart";
+
+    /**
      * Defines one or more packages that contain application-specific resources and
      * providers.
      *
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ServerRuntime.java b/core-server/src/main/java/org/glassfish/jersey/server/ServerRuntime.java
index 007cd0a..06e0f94 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/ServerRuntime.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/ServerRuntime.java
@@ -128,6 +128,11 @@
     /** Resolve relative URIs according to RFC7231 (not JAX-RS 2.0 compliant */
     private final boolean rfc7231LocationHeaderRelativeUriResolution;
 
+    /**
+     * Default exception mapper (@since 3.1.0 according to JAX-RS 3.1 spec)
+     */
+    private static final ExceptionMapper<Throwable> DEFAULT_EXCEPTION_MAPPER = new DefaultExceptionMapper();
+
     static ServerRuntime createServerRuntime(
             InjectionManager injectionManager,
             ServerBootstrapBag bootstrapBag,
@@ -378,6 +383,22 @@
             return response;
         }
 
+        private ContainerResponse preProcessResponse(Response exceptionResponse, ContainerRequest request) {
+            final ContainerResponse response;
+            try {
+                response = convertResponse(exceptionResponse);
+                if (!runtime.disableLocationHeaderRelativeUriResolution) {
+                    ensureAbsolute(response.getLocation(), response.getHeaders(), request,
+                            runtime.rfc7231LocationHeaderRelativeUriResolution);
+                }
+                processingContext.monitoringEventBuilder().setContainerResponse(response)
+                        .setResponseSuccessfullyMapped(true);
+            } finally {
+                processingContext.triggerEvent(RequestEvent.Type.EXCEPTION_MAPPING_FINISHED);
+            }
+            return response;
+        }
+
         /**
          * Process {@code throwable} by using exception mappers and generating the mapped
          * response if possible.
@@ -401,21 +422,11 @@
             processingContext.triggerEvent(RequestEvent.Type.ON_EXCEPTION);
 
             ContainerResponse response = null;
+            ContainerResponse defaultMapperResponse = null;
             try {
                 final Response exceptionResponse = mapException(throwable);
                 try {
-                    try {
-                        response = convertResponse(exceptionResponse);
-                        if (!runtime.disableLocationHeaderRelativeUriResolution) {
-                            ensureAbsolute(response.getLocation(), response.getHeaders(), request,
-                                    runtime.rfc7231LocationHeaderRelativeUriResolution);
-                        }
-                        processingContext.monitoringEventBuilder().setContainerResponse(response)
-                                .setResponseSuccessfullyMapped(true);
-                    } finally {
-                        processingContext.triggerEvent(RequestEvent.Type.EXCEPTION_MAPPING_FINISHED);
-                    }
-
+                    response = preProcessResponse(exceptionResponse, request);
                     processResponse(response);
                 } catch (final Throwable respError) {
                     LOGGER.log(Level.SEVERE, LocalizationMessages.ERROR_PROCESSING_RESPONSE_FROM_ALREADY_MAPPED_EXCEPTION());
@@ -432,16 +443,19 @@
 
                 if (!processResponseError(responseError)) {
                     // Pass the exception to the container.
-                    LOGGER.log(Level.FINE, LocalizationMessages.ERROR_EXCEPTION_MAPPING_THROWN_TO_CONTAINER(), responseError);
+                    LOGGER.log(Level.WARNING, LocalizationMessages.ERROR_EXCEPTION_MAPPING_THROWN_TO_CONTAINER(), responseError);
 
                     try {
                         request.getResponseWriter().failure(responseError);
                     } finally {
                         completionCallbackRunner.onComplete(responseError);
                     }
+
+                    defaultMapperResponse = processResponseWithDefaultExceptionMapper(responseError, request);
                 }
+
             } finally {
-                release(response);
+                release(response, defaultMapperResponse);
             }
         }
 
@@ -503,6 +517,7 @@
 
             do {
                 final Throwable throwable = wrap.getCurrent();
+
                 if (wrap.isInMappable() || throwable instanceof WebApplicationException) {
                     // in case ServerProperties.PROCESSING_RESPONSE_ERRORS_ENABLED is true, allow
                     // wrapped MessageBodyProviderNotFoundException to propagate
@@ -519,56 +534,22 @@
                         processingContext.routingContext().setMappedThrowable(throwable);
 
                         waeResponse = webApplicationException.getResponse();
-                        if (waeResponse.hasEntity()) {
-                            LOGGER.log(Level.FINE, LocalizationMessages
-                                    .EXCEPTION_MAPPING_WAE_ENTITY(waeResponse.getStatus()), throwable);
+                        if (waeResponse != null && waeResponse.hasEntity()) {
+                            LOGGER.log(Level.FINE,
+                                    LocalizationMessages.EXCEPTION_MAPPING_WAE_ENTITY(waeResponse.getStatus()),
+                                    throwable);
                             return waeResponse;
                         }
                     }
 
                     final long timestamp = tracingLogger.timestamp(ServerTraceEvent.EXCEPTION_MAPPING);
                     final ExceptionMapper mapper = runtime.exceptionMappers.findMapping(throwable);
-                    if (mapper != null) {
-                        processingContext.monitoringEventBuilder().setExceptionMapper(mapper);
-                        processingContext.triggerEvent(RequestEvent.Type.EXCEPTION_MAPPER_FOUND);
-                        try {
-                            final Response mappedResponse = mapper.toResponse(throwable);
-
-                            if (tracingLogger.isLogEnabled(ServerTraceEvent.EXCEPTION_MAPPING)) {
-                                tracingLogger.logDuration(ServerTraceEvent.EXCEPTION_MAPPING,
-                                        timestamp, mapper, throwable, throwable.getLocalizedMessage(),
-                                        mappedResponse != null ? mappedResponse.getStatusInfo() : "-no-response-");
-                            }
-
-                            // set mapped throwable
-                            processingContext.routingContext().setMappedThrowable(throwable);
-
-                            if (mappedResponse != null) {
-                                // response successfully mapped
-                                if (LOGGER.isLoggable(Level.FINER)) {
-                                    final String message = String.format(
-                                            "Exception '%s' has been mapped by '%s' to response '%s' (%s:%s).",
-                                            throwable.getLocalizedMessage(),
-                                            mapper.getClass().getName(),
-                                            mappedResponse.getStatusInfo().getReasonPhrase(),
-                                            mappedResponse.getStatusInfo().getStatusCode(),
-                                            mappedResponse.getStatusInfo().getFamily());
-                                    LOGGER.log(Level.FINER, message);
-                                }
-                                return mappedResponse;
-                            } else {
-                                return Response.noContent().build();
-                            }
-                        } catch (final Throwable mapperThrowable) {
-                            // spec: If the exception mapping provider throws an exception while creating a Response
-                            // then return a server error (status code 500) response to the client.
-                            LOGGER.log(Level.SEVERE, LocalizationMessages.EXCEPTION_MAPPER_THROWS_EXCEPTION(mapper.getClass()),
-                                    mapperThrowable);
-                            LOGGER.log(Level.SEVERE, LocalizationMessages.EXCEPTION_MAPPER_FAILED_FOR_EXCEPTION(), throwable);
-                            return Response.serverError().build();
-                        }
+                    if (mapper != null
+                            && !DefaultExceptionMapper.class.getName()
+                            .equals(mapper.getClass().getName())
+                    ) {
+                        return processExceptionWithMapper(mapper, throwable, timestamp);
                     }
-
                     if (waeResponse != null) {
                         LOGGER.log(Level.FINE, LocalizationMessages
                                 .EXCEPTION_MAPPING_WAE_NO_ENTITY(waeResponse.getStatus()), throwable);
@@ -582,7 +563,6 @@
                         return Response.status(Response.Status.BAD_REQUEST).build();
                     }
                 }
-
                 if (!wrap.isInMappable() || !wrap.isWrapped()) {
                     // user failures (thrown from Resource methods or provider methods)
 
@@ -598,6 +578,55 @@
             throw originalThrowable;
         }
 
+        private Response processExceptionWithMapper(ExceptionMapper mapper,
+                                                                      Throwable throwable, long timestamp) {
+            processingContext.monitoringEventBuilder().setExceptionMapper(mapper);
+            processingContext.triggerEvent(RequestEvent.Type.EXCEPTION_MAPPER_FOUND);
+            try {
+                final Response mappedResponse = mapper.toResponse(throwable);
+
+                if (isTracingLoggingEnabled(mapper, throwable, tracingLogger)) {
+                    tracingLogger.logDuration(ServerTraceEvent.EXCEPTION_MAPPING,
+                            timestamp, mapper, throwable, throwable.getLocalizedMessage(),
+                            mappedResponse != null ? mappedResponse.getStatusInfo() : "-no-response-");
+                }
+
+                // set mapped throwable
+                processingContext.routingContext().setMappedThrowable(throwable);
+
+                if (mappedResponse != null) {
+                    // response successfully mapped
+                    if (LOGGER.isLoggable(Level.FINER)) {
+                        final String message = String.format(
+                                "Exception '%s' has been mapped by '%s' to response '%s' (%s:%s).",
+                                throwable.getLocalizedMessage(),
+                                mapper.getClass().getName(),
+                                mappedResponse.getStatusInfo().getReasonPhrase(),
+                                mappedResponse.getStatusInfo().getStatusCode(),
+                                mappedResponse.getStatusInfo().getFamily());
+                        LOGGER.log(Level.FINER, message);
+                    }
+                    return mappedResponse;
+                } else {
+                    return Response.noContent().build();
+                }
+            } catch (final Throwable mapperThrowable) {
+                // spec: If the exception mapping provider throws an exception while creating a Response
+                // then return a server error (status code 500) response to the client.
+                LOGGER.log(Level.SEVERE, LocalizationMessages.EXCEPTION_MAPPER_THROWS_EXCEPTION(mapper.getClass()),
+                        mapperThrowable);
+                LOGGER.log(Level.SEVERE, LocalizationMessages.EXCEPTION_MAPPER_FAILED_FOR_EXCEPTION(), throwable);
+                return Response.serverError().build();
+            }
+        }
+
+        private ContainerResponse processResponseWithDefaultExceptionMapper(Throwable exception,
+                                                                            ContainerRequest request) {
+            long timestamp = tracingLogger.timestamp(ServerTraceEvent.EXCEPTION_MAPPING);
+            final Response response = processExceptionWithMapper(DEFAULT_EXCEPTION_MAPPER, exception, timestamp);
+            return processResponse(preProcessResponse(response, request));
+        }
+
         private ContainerResponse writeResponse(final ContainerResponse response) {
             final ContainerRequest request = processingContext.request();
             final ContainerResponseWriter writer = request.getResponseWriter();
@@ -729,16 +758,14 @@
                     .setResponseWritten(true);
         }
 
-        private void release(final ContainerResponse responseContext) {
+        private void release(final ContainerResponse... responseContexts) {
             try {
                 processingContext.closeableService().close();
 
                 // Commit the container response writer if not in chunked mode
                 // responseContext may be null in case the request processing was cancelled.
-                if (responseContext != null && !responseContext.isChunked()) {
-                    // responseContext.commitStream();
-                    responseContext.close();
-                }
+                Arrays.stream(responseContexts).filter(responseContext -> responseContext != null
+                        && !responseContext.isChunked()).forEach(ContainerResponse::close);
 
             } catch (final Throwable throwable) {
                 LOGGER.log(Level.WARNING, LocalizationMessages.RELEASING_REQUEST_PROCESSING_RESOURCES_FAILED(), throwable);
@@ -747,6 +774,14 @@
                 processingContext.triggerEvent(RequestEvent.Type.FINISHED);
             }
         }
+
+        private static boolean isTracingLoggingEnabled(ExceptionMapper mapper, Throwable throwable, TracingLogger tracingLogger) {
+            boolean defaultLoggingState = mapper instanceof DefaultExceptionMapper
+                    && throwable instanceof WebApplicationException;
+            return !defaultLoggingState
+                    && tracingLogger.isLogEnabled(ServerTraceEvent.EXCEPTION_MAPPING);
+
+        }
     }
 
     private static class AsyncResponder implements AsyncContext, ContainerResponseWriter.TimeoutHandler, CompletionCallback {
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/WebServerFactory.java b/core-server/src/main/java/org/glassfish/jersey/server/WebServerFactory.java
new file mode 100644
index 0000000..6454fc1
--- /dev/null
+++ b/core-server/src/main/java/org/glassfish/jersey/server/WebServerFactory.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018 Markus KARG. 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.server;
+
+import jakarta.ws.rs.ProcessingException;
+import jakarta.ws.rs.core.Application;
+
+import org.glassfish.jersey.internal.ServiceFinder;
+import org.glassfish.jersey.server.spi.WebServer;
+import org.glassfish.jersey.server.spi.WebServerProvider;
+
+/**
+ * Factory for creating specific HTTP servers.
+ *
+ * @author Markus KARG (markus@headcrashing.eu)
+ * @since 3.1.0
+ */
+public final class WebServerFactory {
+
+    /**
+     * Prevents instantiation.
+     */
+    private WebServerFactory() {
+    }
+
+    /**
+     * Creates a server of a given type which runs the given application using the
+     * given bootstrap configuration.
+     * <p>
+     * The list of service-providers supporting the {@link WebServerProvider}
+     * service-provider will be iterated over until one returns a non-null server
+     * instance.
+     * <p>
+     *
+     * @param <T>
+     *            the type of the server.
+     * @param type
+     *            the type of the server. Providers SHOULD support at least
+     *            {@link WebServer}.
+     * @param application
+     *            The application to host.
+     * @param configuration
+     *            The configuration (host, port, etc.) to be used for bootstrapping.
+     * @return the created server.
+     * @throws ProcessingException
+     *             if there is an error creating the server.
+     * @throws IllegalArgumentException
+     *             if no server provider supports the type.
+     */
+    public static <T extends WebServer> T createServer(final Class<T> type, final Application application,
+                                                       final JerseySeBootstrapConfiguration configuration) {
+        for (final WebServerProvider webServerProvider : ServiceFinder.find(WebServerProvider.class)) {
+            final T server = webServerProvider.createServer(type, application, configuration);
+            if (server != null) {
+                return server;
+            }
+        }
+
+        throw new IllegalArgumentException("No server provider supports the type " + type);
+    }
+
+    /**
+     * Creates a server of a given type which runs the given application using the
+     * given bootstrap configuration.
+     * <p>
+     * The list of service-providers supporting the {@link WebServerProvider}
+     * service-provider will be iterated over until one returns a non-null server
+     * instance.
+     * <p>
+     *
+     * @param <T>
+     *            the type of the server.
+     * @param type
+     *            the type of the server. Providers SHOULD support at least
+     *            {@link WebServer}.
+     * @param application
+     *            The application to host.
+     * @param configuration
+     *            The configuration (host, port, etc.) to be used for bootstrapping.
+     * @return the created server.
+     * @throws ProcessingException
+     *             if there is an error creating the server.
+     * @throws IllegalArgumentException
+     *             if no server provider supports the type.
+     */
+    public static <T extends WebServer> T createServer(final Class<T> type, final Class<? extends Application> application,
+                                                       final JerseySeBootstrapConfiguration configuration) {
+        for (final WebServerProvider webServerProvider : ServiceFinder.find(WebServerProvider.class)) {
+            final T server = webServerProvider.createServer(type, application, configuration);
+            if (server != null) {
+                return server;
+            }
+        }
+
+        throw new IllegalArgumentException("No server provider supports the type " + type);
+    }
+
+}
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/RuntimeDelegateImpl.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/RuntimeDelegateImpl.java
index ab70a02..1af9a40 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/RuntimeDelegateImpl.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/RuntimeDelegateImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2021 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
@@ -16,16 +16,26 @@
 
 package org.glassfish.jersey.server.internal;
 
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+
+import jakarta.ws.rs.SeBootstrap;
 import jakarta.ws.rs.core.Application;
 
 import org.glassfish.jersey.internal.AbstractRuntimeDelegate;
 import org.glassfish.jersey.message.internal.MessagingBinders;
 import org.glassfish.jersey.server.ContainerFactory;
+import org.glassfish.jersey.server.JerseySeBootstrapConfiguration;
+import org.glassfish.jersey.server.WebServerFactory;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.server.spi.WebServer;
 
 /**
  * Server-side implementation of JAX-RS {@link jakarta.ws.rs.ext.RuntimeDelegate}.
- * This overrides the default implementation of {@link jakarta.ws.rs.ext.RuntimeDelegate} from
- * jersey-common which does not implement {@link #createEndpoint(jakarta.ws.rs.core.Application, java.lang.Class)}
+ * This overrides the default implementation of
+ * {@link jakarta.ws.rs.ext.RuntimeDelegate} from jersey-common which does not
+ * implement
+ * {@link #createEndpoint(jakarta.ws.rs.core.Application, java.lang.Class)}
  * method.
  *
  * @author Jakub Podlesak
@@ -39,11 +49,88 @@
     }
 
     @Override
-    public <T> T createEndpoint(Application application, Class<T> endpointType)
+    public <T> T createEndpoint(final Application application, final Class<T> endpointType)
             throws IllegalArgumentException, UnsupportedOperationException {
         if (application == null) {
             throw new IllegalArgumentException("application is null.");
         }
         return ContainerFactory.createContainer(endpointType, application);
     }
+
+    @Override
+    public JerseySeBootstrapConfiguration.Builder createConfigurationBuilder() {
+        return JerseySeBootstrapConfiguration.builder();
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public CompletableFuture<SeBootstrap.Instance> bootstrap(final Application application,
+            final SeBootstrap.Configuration configuration) {
+
+        return CompletableFuture.supplyAsync(() -> {
+            final Class<WebServer> httpServerClass = configuration.hasProperty(ServerProperties.WEBSERVER_CLASS)
+                    ? (Class<WebServer>) configuration.property(ServerProperties.WEBSERVER_CLASS)
+                    : WebServer.class;
+
+            final JerseySeBootstrapConfiguration jerseySeConfiguration
+                    = JerseySeBootstrapConfiguration.from(configuration);
+            final WebServer webServer
+                    = WebServerFactory.createServer(httpServerClass, application, jerseySeConfiguration);
+            return instance(configuration, webServer);
+        });
+    }
+
+    @SuppressWarnings("unchecked")
+    public CompletableFuture<SeBootstrap.Instance> bootstrap(final Class<? extends Application> applicationClass,
+                                                             final SeBootstrap.Configuration configuration) {
+
+        return CompletableFuture.supplyAsync(() -> {
+            final Class<WebServer> httpServerClass = configuration.hasProperty(ServerProperties.WEBSERVER_CLASS)
+                    ? (Class<WebServer>) configuration.property(ServerProperties.WEBSERVER_CLASS)
+                    : WebServer.class;
+
+            final JerseySeBootstrapConfiguration jerseySeConfiguration
+                    = JerseySeBootstrapConfiguration.from(configuration);
+            final WebServer webServer
+                    = WebServerFactory.createServer(httpServerClass, applicationClass, jerseySeConfiguration);
+            return instance(configuration, webServer);
+        });
+    }
+
+    private SeBootstrap.Instance instance(final SeBootstrap.Configuration configuration,
+                                          final WebServer _webServer) {
+        return new SeBootstrap.Instance() {
+            final WebServer webServer = _webServer;
+            @Override
+            public final JerseySeBootstrapConfiguration configuration() {
+                return JerseySeBootstrapConfiguration.from(name -> {
+                    switch (name) {
+                        case SeBootstrap.Configuration.PORT:
+                            return webServer.port();
+                        case ServerProperties.WEBSERVER_CLASS:
+                            return webServer.getClass();
+                        default:
+                            return configuration.property(name);
+                    }
+                });
+            }
+
+            @Override
+            public final CompletionStage<StopResult> stop() {
+                return this.webServer.stop().thenApply(nativeResult -> new StopResult() {
+
+                    @Override
+                    public final <T> T unwrap(final Class<T> nativeClass) {
+                        return nativeClass.cast(nativeResult);
+                    }
+                });
+            }
+
+            @Override
+            public final <T> T unwrap(final Class<T> nativeClass) {
+                return nativeClass.isInstance(this.webServer) ? nativeClass.cast(this.webServer)
+                        : this.webServer.unwrap(nativeClass);
+            }
+        };
+    }
 }
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/FormParamValueParamProvider.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/FormParamValueParamProvider.java
index f1fcbeb..b6e428d 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/FormParamValueParamProvider.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/FormParamValueParamProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2021 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
@@ -16,19 +16,26 @@
 
 package org.glassfish.jersey.server.internal.inject;
 
+import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.lang.annotation.Annotation;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
 import java.net.URLDecoder;
 import java.net.URLEncoder;
+import java.security.AccessController;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.BiFunction;
 import java.util.function.Function;
 
 import jakarta.ws.rs.Encoded;
 import jakarta.ws.rs.FormParam;
 import jakarta.ws.rs.ProcessingException;
+import jakarta.ws.rs.core.EntityPart;
 import jakarta.ws.rs.core.Form;
+import jakarta.ws.rs.core.GenericType;
 import jakarta.ws.rs.core.HttpHeaders;
 import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.MultivaluedMap;
@@ -37,7 +44,13 @@
 import jakarta.inject.Singleton;
 
 import org.glassfish.jersey.internal.inject.ExtractorException;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.inject.Providers;
+import org.glassfish.jersey.internal.util.ReflectionHelper;
+import org.glassfish.jersey.internal.util.collection.LazyValue;
 import org.glassfish.jersey.internal.util.collection.NullableMultivaluedHashMap;
+import org.glassfish.jersey.internal.util.collection.Value;
+import org.glassfish.jersey.internal.util.collection.Values;
 import org.glassfish.jersey.message.internal.MediaTypes;
 import org.glassfish.jersey.message.internal.ReaderWriter;
 import org.glassfish.jersey.server.ContainerRequest;
@@ -45,6 +58,7 @@
 import org.glassfish.jersey.server.internal.InternalServerProperties;
 import org.glassfish.jersey.server.internal.LocalizationMessages;
 import org.glassfish.jersey.server.model.Parameter;
+import org.glassfish.jersey.server.spi.internal.ValueParamProvider;
 
 /**
  * Value factory provider supporting the {@link FormParam} injection annotation.
@@ -55,13 +69,17 @@
 @Singleton
 final class FormParamValueParamProvider extends AbstractValueParamProvider {
 
+    private final MultipartFormParamValueProvider multipartProvider;
     /**
      * Injection constructor.
      *
      * @param mpep extractor provider.
+     * @param injectionManager
      */
-    public FormParamValueParamProvider(Provider<MultivaluedParameterExtractorProvider> mpep) {
+    public FormParamValueParamProvider(Provider<MultivaluedParameterExtractorProvider> mpep,
+                                       InjectionManager injectionManager) {
         super(mpep, Parameter.Source.FORM);
+        this.multipartProvider = new MultipartFormParamValueProvider(injectionManager);
     }
 
     @Override
@@ -77,18 +95,24 @@
         if (e == null) {
             return null;
         }
-        return new FormParamValueProvider(e, !parameter.isEncoded());
+        return new FormParamValueProvider(e, multipartProvider, !parameter.isEncoded(), parameter);
     }
 
     private static final class FormParamValueProvider implements Function<ContainerRequest, Object> {
 
         private static final Annotation encodedAnnotation = getEncodedAnnotation();
         private final MultivaluedParameterExtractor<?> extractor;
+        private final MultipartFormParamValueProvider multipartProvider;
         private final boolean decode;
+        private final Parameter parameter;
 
-        FormParamValueProvider(MultivaluedParameterExtractor<?> extractor, boolean decode) {
+        FormParamValueProvider(MultivaluedParameterExtractor<?> extractor,
+                               MultipartFormParamValueProvider multipartProvider,
+                               boolean decode, Parameter parameter) {
             this.extractor = extractor;
+            this.multipartProvider = multipartProvider;
             this.decode = decode;
+            this.parameter = parameter;
         }
 
         private static Form getCachedForm(final ContainerRequest request, boolean decode) {
@@ -121,24 +145,27 @@
 
         @Override
         public Object apply(ContainerRequest request) {
-            Form form = getCachedForm(request, decode);
+            if (MediaTypes.typeEqual(MediaType.MULTIPART_FORM_DATA_TYPE, request.getMediaType())) {
+                return multipartProvider.apply(request, parameter);
+            } else {
+                Form form = getCachedForm(request, decode);
 
-            if (form == null) {
-                Form otherForm = getCachedForm(request, !decode);
-                if (otherForm != null) {
-                    form = switchUrlEncoding(request, otherForm);
-                    cacheForm(request, form);
-                } else {
-                    form = getForm(request);
+                if (form == null) {
+                    Form otherForm = getCachedForm(request, !decode);
+                    if (otherForm != null) {
+                        form = switchUrlEncoding(request, otherForm);
+                    } else {
+                        form = getForm(request);
+                    }
                     cacheForm(request, form);
                 }
-            }
 
-            try {
-                return extractor.extract(form.asMap());
-            } catch (ExtractorException e) {
-                throw new ParamException.FormParamException(e.getCause(),
-                        extractor.getName(), extractor.getDefaultValueString());
+                try {
+                    return extractor.extract(form.asMap());
+                } catch (ExtractorException e) {
+                    throw new ParamException.FormParamException(e.getCause(),
+                            extractor.getName(), extractor.getDefaultValueString());
+                }
             }
         }
 
@@ -199,4 +226,89 @@
             }
         }
     }
+
+    @Singleton
+    private static class MultipartFormParamValueProvider implements BiFunction<ContainerRequest, Parameter, Object> {
+        private static final class FormParamHolder {
+            @FormParam("name")
+            public static final Void dummy = null; // field to get an instance of FormParam annotation
+        }
+        private static Parameter entityPartParameter =
+                Parameter.create(
+                        EntityPart.class, EntityPart.class, false, EntityPart.class, EntityPart.class,
+                        AccessController.doPrivileged(ReflectionHelper.getDeclaredFieldsPA(FormParamHolder.class))[0]
+                            .getAnnotations()
+                );
+
+        private final InjectionManager injectionManager;
+        private final LazyValue<ValueParamProvider> entityPartProvider;
+
+        private MultipartFormParamValueProvider(InjectionManager injectionManager) {
+            this.injectionManager = injectionManager;
+
+            //Get the provider from jersey-media-multipart
+            entityPartProvider = Values.lazy((Value<ValueParamProvider>) () -> {
+                Set<ValueParamProvider> providers = Providers.getProviders(injectionManager, ValueParamProvider.class);
+                for (ValueParamProvider vfp : providers) {
+                    Function<ContainerRequest, ?> paramValueSupplier = vfp.getValueProvider(entityPartParameter);
+                    if (paramValueSupplier != null && !FormParamValueParamProvider.class.isInstance(vfp)) {
+                        return vfp;
+                    }
+                }
+                return null;
+            });
+        }
+
+
+        @Override
+        public Object apply(ContainerRequest containerRequest, Parameter parameter) {
+            Object entity = null;
+            if (entityPartProvider.get() != null) { // else jersey-multipart module is missing
+                final Function<ContainerRequest, ?> valueSupplier = entityPartProvider.get().getValueProvider(
+                        new WrappingFormParamParameter(entityPartParameter, parameter));
+                final EntityPart entityPart = (EntityPart) valueSupplier.apply(containerRequest);
+                try {
+                    entity = parameter.getType() != parameter.getRawType()
+                            ? entityPart.getContent(genericType(parameter.getRawType(), parameter.getType()))
+                            : entityPart.getContent(parameter.getRawType());
+                } catch (IOException e) {
+                    throw new ProcessingException(e);
+                }
+            }
+
+            return entity;
+        }
+
+        private GenericType genericType(Type rawType, Type genericType) {
+            return new GenericType(new ParameterizedType() {
+                @Override
+                public Type[] getActualTypeArguments() {
+                    return new Type[]{genericType};
+                }
+
+                @Override
+                public Type getRawType() {
+                    return rawType;
+                }
+
+                @Override
+                public Type getOwnerType() {
+                    return null;
+                }
+            });
+        }
+
+        private static class WrappingFormParamParameter extends Parameter {
+            protected WrappingFormParamParameter(Parameter entityPartDataParam, Parameter realDataParam) {
+                super(realDataParam.getAnnotations(),
+                        realDataParam.getSourceAnnotation(),
+                        realDataParam.getSource(),
+                        realDataParam.getSourceName(),
+                        entityPartDataParam.getRawType(),
+                        entityPartDataParam.getType(),
+                        realDataParam.isEncoded(),
+                        realDataParam.getDefaultValue());
+            }
+        }
+    }
 }
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/ValueParamProviderConfigurator.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/ValueParamProviderConfigurator.java
index b9ddc55..07337eb 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/ValueParamProviderConfigurator.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/ValueParamProviderConfigurator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2021 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
@@ -87,7 +87,7 @@
         EntityParamValueParamProvider entityProvider = new EntityParamValueParamProvider(paramExtractor);
         suppliers.add(entityProvider);
 
-        FormParamValueParamProvider formProvider = new FormParamValueParamProvider(paramExtractor);
+        FormParamValueParamProvider formProvider = new FormParamValueParamProvider(paramExtractor, injectionManager);
         suppliers.add(formProvider);
 
         HeaderParamValueParamProvider headerProvider = new HeaderParamValueParamProvider(paramExtractor);
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/spi/WebServer.java b/core-server/src/main/java/org/glassfish/jersey/server/spi/WebServer.java
new file mode 100644
index 0000000..1de9d68
--- /dev/null
+++ b/core-server/src/main/java/org/glassfish/jersey/server/spi/WebServer.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018 Markus KARG. 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.server.spi;
+
+import java.util.concurrent.CompletionStage;
+
+import jakarta.ws.rs.ConstrainedTo;
+import jakarta.ws.rs.RuntimeType;
+import jakarta.ws.rs.core.Application;
+
+import org.glassfish.jersey.spi.Contract;
+
+/**
+ * Jersey service contract for self-contained servers.
+ * <p>
+ * Runs a self-contained {@link Application} in a {@link Container} using a
+ * Web Server implicitly started and stopped together with the application.
+ * </p>
+ *
+ * @author Markus KARG (markus@headcrashing.eu)
+ * @since 3.1.0
+ */
+@Contract
+@ConstrainedTo(RuntimeType.SERVER)
+public interface WebServer {
+
+    /**
+     * @return container in which the application lives.
+     */
+    public Container container();
+
+    /**
+     * @return IP port the application listens to for requests.
+     */
+    public int port();
+
+    /**
+     * Initiates server bootstrap.
+     * <p>
+     * Startup happens in background. The completion stage produces a native startup
+     * result.
+     * </p>
+     * <p>
+     * Portable applications should not expect any particular result type, as it is
+     * implementation-specific.
+     * </p>
+     *
+     * @return A {@link CompletionStage} providing a native startup result of the
+     *         bootstrap process. The native result MAY be {@code null}.
+     */
+    public CompletionStage<?> start();
+
+    /**
+     * Initiates server shutdown.
+     * <p>
+     * Shutdown happens in background. The completion stage produces a native
+     * shutdown result.
+     * </p>
+     * </p>
+     * Portable applications should not expect any particular result type, as it is
+     * implementation-specific.
+     * </p>
+     *
+     * @return A {@link CompletionStage} providing a native shutdown result of the
+     *         shutdown process. The native result MAY be {@code null}.
+     */
+    public CompletionStage<?> stop();
+
+    /**
+     * Provides access to the native handle(s) of the server, if it holds at least
+     * one.
+     * <p>
+     * Implementations MAY use native handles to identify the server instance, and /
+     * or use those to communicate with and control the instance. Whether or not
+     * such handles exist, and their respective data types, is
+     * implementation-specific.
+     * </p>
+     * <p>
+     * Portable applications should not invoke this method, as the types of
+     * supported handles are implementation-specific.
+     * </p>
+     *
+     * @param nativeClass
+     *            The class of the native handle.
+     * @return The native handle, or {@code null} if no handle of this type exists.
+     */
+    public <T> T unwrap(Class<T> nativeClass);
+
+}
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/spi/WebServerProvider.java b/core-server/src/main/java/org/glassfish/jersey/server/spi/WebServerProvider.java
new file mode 100644
index 0000000..63453ee
--- /dev/null
+++ b/core-server/src/main/java/org/glassfish/jersey/server/spi/WebServerProvider.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018 Markus KARG. 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.server.spi;
+
+import jakarta.ws.rs.ConstrainedTo;
+import jakarta.ws.rs.SeBootstrap;
+import jakarta.ws.rs.ProcessingException;
+import jakarta.ws.rs.RuntimeType;
+import jakarta.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.ApplicationHandler;
+import org.glassfish.jersey.server.JerseySeBootstrapConfiguration;
+import org.glassfish.jersey.spi.Contract;
+
+/**
+ * Service-provider interface for creating server instances.
+ *
+ * If supported by the provider, a server instance of the requested Java type
+ * will be created.
+ * <p>
+ * The created server uses an internally created {@link Container} which is
+ * responsible for listening on a communication channel provided by the server
+ * for new client requests, dispatching these requests to the registered
+ * {@link ApplicationHandler Jersey application handler} using the handler's
+ * {@link ApplicationHandler#handle(org.glassfish.jersey.server.ContainerRequest)
+ * handle(requestContext)} method and sending the responses provided by the
+ * application back to the client.
+ * </p>
+ * <p>
+ * A provider shall support a one-to-one mapping between a type, provided the
+ * type is not {@link Object}. A provider may also support mapping of sub-types
+ * of a type (provided the type is not {@code Object}). It is expected that each
+ * provider supports mapping for distinct set of types and subtypes so that
+ * different providers do not conflict with each other. In addition, a provider
+ * SHOULD support the super type {@link WebServer} to participate in auto-selection
+ * of providers (in this case the <em>first</em> supporting provider found is
+ * used).
+ * </p>
+ * <p>
+ * An implementation can identify itself by placing a Java service provider
+ * configuration file (if not already present) -
+ * {@code org.glassfish.jersey.server.spi.WebServerProvider} - in the resource
+ * directory {@code META-INF/services}, and adding the fully qualified
+ * service-provider-class of the implementation in the file.
+ * </p>
+ *
+ * @author Markus KARG (markus@headcrashing.eu)
+ * @since 3.1.0
+ */
+@Contract
+@ConstrainedTo(RuntimeType.SERVER)
+public interface WebServerProvider {
+
+    /**
+     * Creates a server of a given type which runs the given application using the
+     * given bootstrap configuration.
+     *
+     * @param <T>
+     *            the type of the web server.
+     * @param type
+     *            the type of the web server. Providers SHOULD support at least
+     *            {@link WebServer}.
+     * @param application
+     *            The application to host.
+     * @param configuration
+     *            The configuration (host, port, etc.) to be used for bootstrapping.
+     * @return the server, otherwise {@code null} if the provider does not support
+     *         the requested {@code type}.
+     * @throws ProcessingException
+     *             if there is an error creating the server.
+     */
+    <T extends WebServer> T createServer(Class<T> type,
+                                         Application application,
+                                         JerseySeBootstrapConfiguration configuration) throws ProcessingException;
+
+    /**
+     * Creates a server of a given type which runs the given application using the
+     * given bootstrap configuration.
+     *
+     * @param <T>
+     *            the type of the web server.
+     * @param type
+     *            the type of the web server. Providers SHOULD support at least
+     *            {@link WebServer}.
+     * @param applicationClass
+     *            The class of application to host.
+     * @param configuration
+     *            The configuration (host, port, etc.) to be used for bootstrapping.
+     * @return the server, otherwise {@code null} if the provider does not support
+     *         the requested {@code type}.
+     * @throws ProcessingException
+     *             if there is an error creating the server.
+     */
+    <T extends WebServer> T createServer(Class<T> type,
+                                         Class<? extends Application> applicationClass,
+                                         JerseySeBootstrapConfiguration configuration) throws ProcessingException;
+}
diff --git a/core-server/src/main/resources/org/glassfish/jersey/server/internal/localization.properties b/core-server/src/main/resources/org/glassfish/jersey/server/internal/localization.properties
index 4311fee..a17fb8d 100644
--- a/core-server/src/main/resources/org/glassfish/jersey/server/internal/localization.properties
+++ b/core-server/src/main/resources/org/glassfish/jersey/server/internal/localization.properties
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2012, 2021 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
@@ -40,7 +40,7 @@
 error.closing.finder=Error while closing {0} resource finder.
 error.exception.mapping.original.exception=An exception mapping did not successfully produce and processed a response. Logging the original error.
 error.exception.mapping.processed.response.error=A response error mapping did not successfully produce and processed a response.
-error.exception.mapping.thrown.to.container=An exception mapping did not successfully produce and processed a response. Logging the exception propagated to the container.
+error.exception.mapping.thrown.to.container=An exception mapping did not successfully produce and processed a response. Logging the exception propagated to the default exception mapper.
 error.marshalling.jaxb=Error marshalling JAXB object of type "{0}".
 error.msg=ERROR: {0}
 error.monitoring.statistics.generation=Error generating monitoring statistics.
@@ -111,6 +111,7 @@
 event.sink.returns.type=A HTTP GET method {0} that is being injected with SseEventSink should return void. The output will propagate automatically.
 multiple.event.sink.injection=A HTTP GET method {0} defines to SseEventSink parameters to be injected. Only one of the injected event sinks will be connected to the output.
 chunked.output.closed=This chunked output has been closed.
+ignore.sebootstrap.configuration.property="SeBootstrap.Configuration property {0} is not of expected type {1} and it is ignored.
 illegal.client.config.class.property.value="{0}" property value ({1}) does not represent a valid client configuration class. Falling back to "{2}".
 init.msg=Initiating Jersey application, version {0}...
 injected.webtarget.uri.invalid="@Uri" annotation value is not a valid URI template: "{0}"
diff --git a/core-server/src/test/java/org/glassfish/jersey/server/ApplicationHandlerTest.java b/core-server/src/test/java/org/glassfish/jersey/server/ApplicationHandlerTest.java
index c14dbe3..225073b 100644
--- a/core-server/src/test/java/org/glassfish/jersey/server/ApplicationHandlerTest.java
+++ b/core-server/src/test/java/org/glassfish/jersey/server/ApplicationHandlerTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2021 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/core-server/src/test/java/org/glassfish/jersey/server/AsyncCallbackServerTest.java b/core-server/src/test/java/org/glassfish/jersey/server/AsyncCallbackServerTest.java
index ad7b02b..b707387 100644
--- a/core-server/src/test/java/org/glassfish/jersey/server/AsyncCallbackServerTest.java
+++ b/core-server/src/test/java/org/glassfish/jersey/server/AsyncCallbackServerTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2021 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/core-server/src/test/java/org/glassfish/jersey/server/filter/ApplicationFilterTest.java b/core-server/src/test/java/org/glassfish/jersey/server/filter/ApplicationFilterTest.java
index 220a72f..13a7f1f 100644
--- a/core-server/src/test/java/org/glassfish/jersey/server/filter/ApplicationFilterTest.java
+++ b/core-server/src/test/java/org/glassfish/jersey/server/filter/ApplicationFilterTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2021 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/core-server/src/test/java/org/glassfish/jersey/server/internal/RuntimeDelegateImplTest.java b/core-server/src/test/java/org/glassfish/jersey/server/internal/RuntimeDelegateImplTest.java
index 5a49cbb..3b42c6d 100644
--- a/core-server/src/test/java/org/glassfish/jersey/server/internal/RuntimeDelegateImplTest.java
+++ b/core-server/src/test/java/org/glassfish/jersey/server/internal/RuntimeDelegateImplTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2022 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
@@ -16,11 +16,19 @@
 
 package org.glassfish.jersey.server.internal;
 
+import jakarta.ws.rs.SeBootstrap;
 import jakarta.ws.rs.core.Application;
 import jakarta.ws.rs.ext.RuntimeDelegate;
 
 import org.junit.Test;
+
+import java.security.NoSuchAlgorithmException;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.notNullValue;
 import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 /**
@@ -43,8 +51,64 @@
         }
     }
 
+    /**
+     * Checks that the right RuntimeDelegateImpl is loaded by JAX-RS.
+     */
     @Test
     public void testRuntimeDelegateInstance() {
         assertSame(RuntimeDelegateImpl.class, RuntimeDelegate.getInstance().getClass());
     }
+
+    @Test
+    public final void shouldCreateConfigurationBuilder() {
+        // given
+        final RuntimeDelegate runtimeDelegate = new RuntimeDelegateImpl();
+        // when
+        final SeBootstrap.Configuration.Builder configurationBuilder = runtimeDelegate.createConfigurationBuilder();
+        // then
+        assertThat(configurationBuilder, is(notNullValue()));
+    }
+
+    @Test
+    public final void shouldBuildDefaultConfiguration() throws NoSuchAlgorithmException {
+        // given
+        final SeBootstrap.Configuration.Builder configurationBuilder = new RuntimeDelegateImpl().createConfigurationBuilder();
+        // when
+        final SeBootstrap.Configuration configuration = configurationBuilder.build();
+
+        // then
+        assertThat(configuration, is(notNullValue()));
+        assertTrue(configuration.hasProperty(SeBootstrap.Configuration.PROTOCOL));
+        assertTrue(configuration.hasProperty(SeBootstrap.Configuration.HOST));
+        assertTrue(configuration.hasProperty(SeBootstrap.Configuration.PORT));
+        assertTrue(configuration.hasProperty(SeBootstrap.Configuration.ROOT_PATH));
+        assertTrue(configuration.hasProperty(SeBootstrap.Configuration.SSL_CLIENT_AUTHENTICATION));
+        assertTrue(configuration.hasProperty(SeBootstrap.Configuration.SSL_CONTEXT));
+        assertThat(configuration.property(SeBootstrap.Configuration.PROTOCOL), is("HTTP"));
+        assertThat(configuration.property(SeBootstrap.Configuration.HOST), is("localhost"));
+        assertThat(configuration.property(SeBootstrap.Configuration.PORT), is(SeBootstrap.Configuration.DEFAULT_PORT));
+        assertThat(configuration.property(SeBootstrap.Configuration.ROOT_PATH), is("/"));
+        assertThat(configuration.property(SeBootstrap.Configuration.SSL_CLIENT_AUTHENTICATION),
+                is(SeBootstrap.Configuration.SSLClientAuthentication.NONE));
+//        assertThat(configuration.property(SeBootstrap.Configuration.SSL_CONTEXT), is(theInstance(SSLContext.getDefault())));
+        assertThat(configuration.protocol(), is("HTTP"));
+        assertThat(configuration.host(), is("localhost"));
+        assertThat(configuration.port(), is(SeBootstrap.Configuration.DEFAULT_PORT));
+        assertThat(configuration.rootPath(), is("/"));
+        assertThat(configuration.sslClientAuthentication(), is(SeBootstrap.Configuration.SSLClientAuthentication.NONE));
+//        assertThat(configuration.sslContext(), is(theInstance(SSLContext.getDefault())));
+    }
+
+    @Test
+    public final void shouldBuildConfigurationContainingCustomProperties() {
+        // given
+        final SeBootstrap.Configuration.Builder configurationBuilder = new RuntimeDelegateImpl().createConfigurationBuilder();
+        // when
+        final SeBootstrap.Configuration configuration = configurationBuilder.property("property", "value").build();
+
+        // then
+        assertThat(configuration, is(notNullValue()));
+        assertTrue(configuration.hasProperty("property"));
+        assertThat(configuration.property("property"), is("value"));
+    }
 }
diff --git a/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/ParamConverterInternalTest.java b/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/ParamConverterInternalTest.java
index 5cd8568..8ec946a 100644
--- a/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/ParamConverterInternalTest.java
+++ b/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/ParamConverterInternalTest.java
@@ -17,6 +17,8 @@
 
 package org.glassfish.jersey.server.internal.inject;
 
+import java.io.IOException;
+import java.io.InputStream;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
@@ -48,6 +50,7 @@
 import org.glassfish.jersey.server.RequestContextBuilder;
 import org.glassfish.jersey.server.ResourceConfig;
 
+import org.junit.Assert;
 import org.junit.Test;
 import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertEquals;
@@ -300,6 +303,24 @@
         }
     }
 
+    @Path("/")
+    public static class InpuStreamConverterTestResource {
+        @GET
+        public String inputStream(@QueryParam("param") InputStream inputStream) throws IOException {
+            return new String(inputStream.readAllBytes());
+        }
+    }
+
+    @Test
+    public void inputStreamTest() throws ExecutionException, InterruptedException {
+        initiateWebApplication(InpuStreamConverterTestResource.class);
+
+        final ContainerResponse responseContext = getResponseContext(UriBuilder.fromPath("/")
+                .queryParam("param", "Hello").build().toString());
+
+        Assert.assertEquals("Hello", responseContext.getEntity());
+    }
+
     public static class MyEagerParamProvider implements ParamConverterProvider {
 
         @Override
diff --git a/core-server/src/test/java/org/glassfish/jersey/server/model/MethodListTest.java b/core-server/src/test/java/org/glassfish/jersey/server/model/MethodListTest.java
index 5934f4a..275019e 100644
--- a/core-server/src/test/java/org/glassfish/jersey/server/model/MethodListTest.java
+++ b/core-server/src/test/java/org/glassfish/jersey/server/model/MethodListTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2021 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
@@ -23,6 +23,7 @@
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
 /**
@@ -105,7 +106,7 @@
 
     @Test
     public void testSyntheticMethods() {
-        assertTrue(CSynthetic.CWithField.class.getDeclaredMethods().length == 2);
+        // assertEquals(2, CSynthetic.CWithField.class.getDeclaredMethods().length);
 
         MethodList ml = new MethodList(CSynthetic.CWithField.class, true);
         assertTrue(!ml.iterator().hasNext());
diff --git a/docs/src/main/docbook/deployment.xml b/docs/src/main/docbook/deployment.xml
index 474fd63..55d8bb7 100644
--- a/docs/src/main/docbook/deployment.xml
+++ b/docs/src/main/docbook/deployment.xml
@@ -110,6 +110,9 @@
             classpath, unless explicitly stated otherwise in the documentation of each particular extension.
             Users are expected to explicitly register the extension &jaxrs.core.Feature;s using their
             &lit.jaxrs.core.Application; subclass.
+            Since Jersey 3.1.0 all features which extends &jaxrs.core.Feature; and are listed as SPI of the &lit.jaxrs.core.Feature;
+            interface are automatically registered (not required to be registered within &lit.jaxrs.core.Application; subclass).
+            For server site Jersey 3.1.0 also registers SPIs for &lit.jaxrs.container.DynamicFeature; interface.
             For a few Jersey provided modules however there is no need to explicitly register their extension
             &lit.jaxrs.core.Feature;s as these are discovered and registered in the &jaxrs.core.Configuration; (on client/server)
             automatically by Jersey runtime whenever the modules implementing these features are present on the classpath
@@ -193,6 +196,17 @@
         </section>
 
     </section>
+    <section  xml:id="deployment.feature.dynamicFeature">
+        <title>Feature and Dynamic Feature SPI automatic registration</title>
+        <para>
+            Since Jersey 3.1.0 there is a new specification requirement to automatically discover and register classes that
+            implement &jaxrs.core.Feature; and &jaxrs.container.DynamicFeature; interfaces. This is being done via SPI search
+            thus all those classes in order to be discovered automatically shall be listed as SPIs for given interfaces.
+            This automatic registration can also be disabled by <literal>jakarta.ws.rs.loadServices</literal>
+            (which is new specification property) to be set to false. This is also valid for
+            &jersey.common.CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE; lookup.
+        </para>
+    </section>
 
     <section xml:id="deployment.classpath-scanning">
         <title>Configuring the Classpath Scanning</title>
@@ -207,6 +221,12 @@
             <itemizedlist>
                 <title>List of SPIs recognized by Jersey</title>
                 <listitem>
+                    <para>&lit.jaxrs.core.Feature; (server, client) -
+                        Features are being registered similarly to AutoDiscoverable feature. In addition
+                        property jakarta.ws.rs.loadServices is checked before registration.
+                    </para>
+                </listitem>
+                <listitem>
                     <para><literal>AutoDiscoverable</literal> (server, client) -
                         it means if you disable service loading the AutoDiscoverable feature is automatically disabled too</para>
                 </listitem>
@@ -218,6 +238,10 @@
                     <para><literal>HeaderDelegateProvider</literal> (server, client)</para>
                 </listitem>
                 <listitem>
+                    <para>&lit.jaxrs.container.DynamicFeature; (server) -
+                    Similar to &lit.jaxrs.core.Feature; registration but only for server.</para>
+                </listitem>
+                <listitem>
                     <para><literal>ComponentProvider</literal> (server)</para>
                 </listitem>
                 <listitem>
diff --git a/docs/src/main/docbook/jaxrs-resources.xml b/docs/src/main/docbook/jaxrs-resources.xml
index d6cb100..884209d 100644
--- a/docs/src/main/docbook/jaxrs-resources.xml
+++ b/docs/src/main/docbook/jaxrs-resources.xml
@@ -480,6 +480,11 @@
             </example>
         </para>
 
+        <para>
+            For quicker getting of values for a known header name there is a shortcut for <literal>hh.getRequestHeaders().get(name)</literal>
+            which is <literal>hh.getRequestHeader(name)</literal>.
+        </para>
+
         <para>In general &jaxrs.core.Context; can be used to obtain contextual Java types related to the request or response.
         </para>
 
diff --git a/docs/src/main/docbook/jersey.ent b/docs/src/main/docbook/jersey.ent
index 7c7e824..144f7fd 100644
--- a/docs/src/main/docbook/jersey.ent
+++ b/docs/src/main/docbook/jersey.ent
@@ -28,6 +28,7 @@
 <!ENTITY jaxb-api-jar.version "$jaxb-api-jar-version">
 <!ENTITY jax-rs.version "$jax-rs-api-jar-version">
 <!ENTITY jax-rs21.version "2.1.6">
+<!ENTITY jax-rs31.spec.version "3.1">
 <!ENTITY jakarta.el.version "$jakarta.el-version">
 <!ENTITY jakarta.el-impl.version "$jakarta.el-impl-version">
 <!ENTITY jax-rs-api-jar.version "$jax-rs-api-jar-version">
@@ -60,6 +61,7 @@
 <!ENTITY jaxrs.release.uri "https://github.com/eclipse-ee4j/jaxrs-api">
 <!ENTITY jaxrs.javadoc.uri "https://jakartaee.github.io/rest/apidocs/&jax-rs.version;/jakarta/ws/rs">
 <!ENTITY jaxrs21.javadoc.uri "https://jakartaee.github.io/rest/apidocs/&jax-rs21.version;/javax/ws/rs">
+<!ENTITY jaxrs31.spec.uri "https://jakarta.ee/specifications/restful-ws/&jax-rs31.spec.version;/jakarta-restful-ws-spec-&jax-rs31.spec.version;.html">
 <!ENTITY jsonb.javadoc.uri "https://javaee.github.io/javaee-spec/javadocs/javax/json/bind">
 <!ENTITY jersey.documentation.uri "https://eclipse-ee4j.github.io/jersey.github.io">
 
@@ -213,6 +215,7 @@
 <!ENTITY jaxrs.core.Configuration "<link xlink:href='&jaxrs.javadoc.uri;/core/Configuration.html'>Configuration</link>">
 <!ENTITY jaxrs.core.Context "<link xlink:href='&jaxrs.javadoc.uri;/core/Context.html'>@Context</link>">
 <!ENTITY jaxrs.core.Cookie "<link xlink:href='&jaxrs.javadoc.uri;/core/Cookie.html'>Cookie</link>">
+<!ENTITY jaxrs.core.EntityPart "<link xlink:href='&jaxrs.javadoc.uri;/core/EntityPart.html'>EntityPart</link>">
 <!ENTITY jaxrs.core.EntityTag "<link xlink:href='&jaxrs.javadoc.uri;/core/EntityTag.html'>EntityTag</link>">
 <!ENTITY jaxrs.core.Feature "<link xlink:href='&jaxrs.javadoc.uri;/core/Feature.html'>Feature</link>">
 <!ENTITY jaxrs.core.Form "<link xlink:href='&jaxrs.javadoc.uri;/core/Form.html'>Form</link>">
@@ -248,6 +251,7 @@
 <!ENTITY jaxrs.ext.RuntimeDelegate.HeaderDelegate "<link xlink:href='&jaxrs.javadoc.uri;/ext/RuntimeDelegate.HeaderDelegate.html'>RuntimeDelegate.HeaderDelegate&lt;T&gt;</link>">
 <!ENTITY jaxrs21.sse.SseEventSink "<link xlink:href='&jaxrs21.javadoc.uri;/sse/SseEventSink.html'>SseEventSink</link>">
 <!ENTITY jaxrs21.sse.Sse "<link xlink:href='&jaxrs21.javadoc.uri;/sse/Sse.html'>Sse</link>">
+<!ENTITY jaxrs31.exceptionMapperProvider "<link xlink:href='&jaxrs31.spec.uri;#exceptionmapper'>Exception Mapping Providers</link>">
 
 <!ENTITY jdk6.Boolean "<link xlink:href='&jdk6.javadoc.uri;/java/lang/Boolean.html'>Boolean</link>">
 <!ENTITY jdk6.CountDownLatch "<link xlink:href='&jdk6.javadoc.uri;/java/util/concurrent/CountDownLatch.html'>CountDownLatch</link>">
@@ -758,6 +762,7 @@
 <!ENTITY lit.jaxrs.core.Context "<literal>@Context</literal>">
 <!ENTITY lit.jaxrs.core.Cookie "<literal>Cookie</literal>">
 <!ENTITY lit.jaxrs.core.EntityTag "<literal>EntityTag</literal>">
+<!ENTITY lit.jaxrs.core.EntityPart "<literal>EntityPart</literal>">
 <!ENTITY lit.jaxrs.core.Feature "<literal>Feature</literal>">
 <!ENTITY lit.jaxrs.core.Form "<literal>Form</literal>">
 <!ENTITY lit.jaxrs.core.GenericEntity "<literal>GenericEntity&lt;T&gt;</literal>">
@@ -803,6 +808,7 @@
 <!ENTITY lit.jdk6.InputStream "<literal>InputStream</literal>">
 <!ENTITY lit.jdk6.JAXBElement "<literal>JAXBElement</literal>">
 <!ENTITY lit.jdk6.KeyStore "<literal>KeyStore</literal>">
+<!ENTITY lit.jdk6.List "<literal>List</literal>">
 <!ENTITY lit.jdk6.Number "<literal>Number</literal>">
 <!ENTITY lit.jdk6.ObjectName "<literal>ObjectName</literal>">
 <!ENTITY lit.jdk6.ParameterizedType "<literal>ParameterizedType</literal>">
diff --git a/docs/src/main/docbook/media.xml b/docs/src/main/docbook/media.xml
index 95581df..3bd4013 100644
--- a/docs/src/main/docbook/media.xml
+++ b/docs/src/main/docbook/media.xml
@@ -23,6 +23,10 @@
         <!ENTITY link.jackson "<link linkend='json.jackson'>Jackson</link>" >
         <!ENTITY link.jettison "<link linkend='json.jettison'>Jettison</link>" >
         <!ENTITY link.json-b "<link linkend='json.json-b'>Java API for JSON Binding (JSON-B)</link>" >
+        <!ENTITY link.multipart.client.jersey "<link linkend='multipart.client.jersey'>Client using Jersey API</link>" >
+        <!ENTITY link.multipart.client.rest "<link linkend='multipart.client.rest'>Client using Jakarta REST API</link>" >
+        <!ENTITY link.multipart.server.jersey "<link linkend='multipart.server.jersey'>Server using Jersey API</link>" >
+        <!ENTITY link.multipart.server.rest "<link linkend='multipart.server.rest'>Server using Jakarta REST API</link>" >
 
         <!ENTITY % ents SYSTEM "jersey.ent" > %ents;
 ]>
@@ -1540,25 +1544,11 @@
                 <title>Registration</title>
 
                 <para>
-                    Before you can use capabilities of the &lit.jersey-media-multipart; module in your client/server code, you
-                    need to register &jersey.media.multipart.MultiPartFeature;.
+                    Prior to Jersey 3.1.0, before you can use the capabilities of the &lit.jersey-media-multipart;
+                    module in your client/server code, you need to register &jersey.media.multipart.MultiPartFeature;.
 
-                    <example>
-                        <title>Building client with MultiPart feature enabled.</title>
-
-                        <programlisting language="java">final Client client = ClientBuilder.newBuilder()
-    .register(MultiPartFeature.class)
-    .build();</programlisting>
-                    </example>
-
-                    <example>
-                        <title>Creating JAX-RS application with MultiPart feature enabled.</title>
-
-                        <programlisting language="java">// Create JAX-RS application.
-final Application application = new ResourceConfig()
-    .packages("org.glassfish.jersey.examples.multipart")
-    .register(MultiPartFeature.class)</programlisting>
-                    </example>
+                    The multipart feature is supported by Jakarta RESTful Web Services 3.1 multipart API. From Jersey 3.1.0 on,
+                    the &jersey.media.multipart.MultiPartFeature; is no longer required to be registered and it is registered automatically.
                 </para>
             </section>
 
@@ -1573,20 +1563,30 @@
 
         <section>
             <title>Client</title>
+            <itemizedlist>
+                <listitem>
+                    <para>&link.multipart.client.jersey;</para>
+                </listitem>
+                <listitem>
+                    <para>&link.multipart.client.rest;</para>
+                </listitem>
+            </itemizedlist>
+            <section xml:id="multipart.client.jersey">
+                <title>Client using Jersey API</title>
 
-            <para>
-                &jersey.media.multipart.MultiPart; class (or it's subclasses) can be used as an entry point to use
-                &lit.jersey-media-multipart; module on the client side. This class represents a
-                <link xlink:href='&wikipedia.uri;MIME#Multipart_messages'>MIME multipart message</link> and is able
-                to hold an arbitrary number of &jersey.media.multipart.BodyPart;s. Default media type is
-                <link xlink:href='&wikipedia.uri;MIME#Mixed'>multipart/mixed</link>
-                for &lit.jersey.media.multipart.MultiPart; entity and <literal>text/plain</literal> for
-                &lit.jersey.media.multipart.BodyPart;.
+                <para>
+                    &jersey.media.multipart.MultiPart; class (or it's subclasses) can be used as an entry point to use
+                    &lit.jersey-media-multipart; module on the client side. This class represents a
+                    <link xlink:href='&wikipedia.uri;MIME#Multipart_messages'>MIME multipart message</link> and is able
+                    to hold an arbitrary number of &jersey.media.multipart.BodyPart;s. Default media type is
+                    <link xlink:href='&wikipedia.uri;MIME#Mixed'>multipart/mixed</link>
+                    for &lit.jersey.media.multipart.MultiPart; entity and <literal>text/plain</literal> for
+                    &lit.jersey.media.multipart.BodyPart;.
 
-                <example>
-                    <title>&lit.jersey.media.multipart.MultiPart; entity</title>
+                    <example>
+                        <title>&lit.jersey.media.multipart.MultiPart; entity</title>
 
-                    <programlisting language="java">final MultiPart multiPartEntity = new MultiPart()
+                        <programlisting language="java">final MultiPart multiPartEntity = new MultiPart()
         .bodyPart(new BodyPart().entity("hello"))
         .bodyPart(new BodyPart(new JaxbBean("xml"), MediaType.APPLICATION_XML_TYPE))
         .bodyPart(new BodyPart(new JaxbBean("json"), MediaType.APPLICATION_JSON_TYPE));
@@ -1595,15 +1595,15 @@
 final Response response = target
         .request()
         .post(Entity.entity(multiPartEntity, multiPartEntity.getMediaType()));</programlisting>
-                </example>
+                    </example>
 
-                If you send a <literal>multiPartEntity</literal> to the server the entity with <literal>Content-Type</literal>
-                header in HTTP message would look like (don't forget to register a JSON provider):
+                    If you send a <literal>multiPartEntity</literal> to the server the entity with <literal>Content-Type</literal>
+                    header in HTTP message would look like:
 
-                <example>
-                    <title>&lit.jersey.media.multipart.MultiPart; entity in HTTP message.</title>
+                    <example>
+                        <title>&lit.jersey.media.multipart.MultiPart; entity in HTTP message.</title>
 
-                    <screen language="text" linenumbering="unnumbered"><emphasis>Content-Type: multipart/mixed; boundary=Boundary_1_829077776_1369128119878</emphasis>
+                        <screen language="text" linenumbering="unnumbered"><emphasis>Content-Type: multipart/mixed; boundary=Boundary_1_829077776_1369128119878</emphasis>
 
 --Boundary_1_829077776_1369128119878
 Content-Type: text/plain
@@ -1618,34 +1618,34 @@
 
 {"value":"json"}
 --Boundary_1_829077776_1369128119878--</screen>
-                </example>
-            </para>
-            <para>
-                When working with forms (e.g. media type <literal>multipart/form-data</literal>) and various fields in them,
-                there is a more convenient class to be used - &jersey.media.multipart.FormDataMultiPart;. It automatically sets
-                the media type for the &lit.jersey.media.multipart.FormDataMultiPart; entity to
-                <literal>multipart/form-data</literal> and <literal>Content-Disposition</literal> header to
-                &lit.jersey.media.multipart.FormDataBodyPart; body parts.
+                    </example>
+                </para>
+                <para>
+                    When working with forms (e.g. media type <literal>multipart/form-data</literal>) and various fields in them,
+                    there is a more convenient class to be used - &jersey.media.multipart.FormDataMultiPart;. It automatically sets
+                    the media type for the &lit.jersey.media.multipart.FormDataMultiPart; entity to
+                    <literal>multipart/form-data</literal> and <literal>Content-Disposition</literal> header to
+                    &lit.jersey.media.multipart.FormDataBodyPart; body parts.
 
-                <example>
-                    <title>&lit.jersey.media.multipart.FormDataMultiPart; entity</title>
-                    <programlisting language="java">final FormDataMultiPart multipart = new FormDataMultiPart()
+                    <example>
+                        <title>&lit.jersey.media.multipart.FormDataMultiPart; entity</title>
+                        <programlisting language="java">final FormDataMultiPart multipart = new FormDataMultiPart()
     .field("hello", "hello")
     .field("xml", new JaxbBean("xml"))
     .field("json", new JaxbBean("json"), MediaType.APPLICATION_JSON_TYPE);
 
 final WebTarget target = // Create WebTarget.
 final Response response = target.request().post(Entity.entity(multipart, multipart.getMediaType()));</programlisting>
-                </example>
+                    </example>
 
-                To illustrate the difference when using &lit.jersey.media.multipart.FormDataMultiPart; instead of
-                &lit.jersey.media.multipart.FormDataBodyPart; you can take a look at the
-                &lit.jersey.media.multipart.FormDataMultiPart; entity from HTML message:
+                    To illustrate the difference when using &lit.jersey.media.multipart.FormDataMultiPart; instead of
+                    &lit.jersey.media.multipart.FormDataBodyPart; you can take a look at the
+                    &lit.jersey.media.multipart.FormDataMultiPart; entity from HTML message:
 
-                <example>
-                    <title>&lit.jersey.media.multipart.FormDataMultiPart; entity in HTTP message.</title>
+                    <example>
+                        <title>&lit.jersey.media.multipart.FormDataMultiPart; entity in HTTP message.</title>
 
-                    <screen language="text" linenumbering="unnumbered"><emphasis>Content-Type: multipart/form-data; boundary=Boundary_1_511262261_1369143433608</emphasis>
+                        <screen language="text" linenumbering="unnumbered"><emphasis>Content-Type: multipart/form-data; boundary=Boundary_1_511262261_1369143433608</emphasis>
 
 --Boundary_1_511262261_1369143433608
 Content-Type: text/plain
@@ -1663,17 +1663,17 @@
 
 {"value":"json"}
 --Boundary_1_511262261_1369143433608--</screen>
-                </example>
-            </para>
-            <para>
-                A common use-case for many users is sending files from client to server. For this purpose you can use classes from
-                <literal>org.glassfish.jersey.jersey.media.multipart</literal> package, such as
-                &jersey.media.multipart.FileDataBodyPart; or &jersey.media.multipart.StreamDataBodyPart;.
+                    </example>
+                </para>
+                <para>
+                    A common use-case for many users is sending files from client to server. For this purpose you can use classes from
+                    <literal>org.glassfish.jersey.jersey.media.multipart</literal> package, such as
+                    &jersey.media.multipart.FileDataBodyPart; or &jersey.media.multipart.StreamDataBodyPart;.
 
-                <example>
-                    <title>Multipart - sending files.</title>
+                    <example>
+                        <title>Multipart - sending files.</title>
 
-                    <programlisting language="java">// MediaType of the body part will be derived from the file.
+                        <programlisting language="java">// MediaType of the body part will be derived from the file.
 final FileDataBodyPart filePart = new FileDataBodyPart("my_pom", new File("pom.xml"));
 
 final FormDataMultiPart multipart = new FormDataMultiPart()
@@ -1683,19 +1683,76 @@
 final WebTarget target = // Create WebTarget.
 final Response response = target.request()
     .post(Entity.entity(multipart, multipart.getMediaType()));</programlisting>
-                </example>
-            </para>
-            <warning>
-                <para>
-                    Do not use &lit.jersey.apache.ApacheConnectorProvider; nor &lit.jersey.grizzly.GrizzlyConnectorProvider;
-                    neither &lit.jersey.jetty.JettyConnectorProvider; connector implementations with Jersey Multipart
-                    features. See <xref linkend="connectors.warning"/> warning for more details.
+                    </example>
                 </para>
-            </warning>
+                <warning>
+                    <para>
+                        Do not use &lit.jersey.apache.ApacheConnectorProvider; nor &lit.jersey.grizzly.GrizzlyConnectorProvider;
+                        neither &lit.jersey.jetty.JettyConnectorProvider; connector implementations with Jersey Multipart
+                        features. See <xref linkend="connectors.warning"/> warning for more details.
+                    </para>
+                </warning>
+            </section>
+
+            <section xml:id="multipart.client.rest">
+                <title>Client using Jakarta REST API</title>
+
+                <para>
+                &jaxrs.core.EntityPart; interface can be used as an entry point to use
+                &lit.jersey-media-multipart; module on the client side. This class represents multipart message is able
+                to hold an arbitrary number of &jaxrs.core.EntityPart;s. Default media type is
+                <link xlink:href='&wikipedia.uri;MIME#Mixed'>multipart/form-data</link>.
+
+                <example>
+                    <title>Using <literal>EntityPart.Builder</literal> for building an Entity</title>
+
+                    <programlisting language="java">
+final List&lt;EntityPart&gt; multiPartEntity = new List&lt;&gt;();
+list.add(EntityPart.withName("part-01").content("hello").build());
+list.add(EntityPart.withName("part-01").content(new JaxbBean("xml")).mediaType(MediaType.APPLICATION_XML_TYPE).build()); //same name
+list.add(EntityPart.withName("part-02").content(new JaxbBean("json")).mediaType(MediaType.APPLICATION_JSON_TYPE).build()); //other name
+final GenericEntity&lt;List&lt;EntityPart&gt;&gt; genericEntity = new GenericEntity&lt;&gt;(list) {};
+final Entity entity = Entity.entity(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE);
+
+final WebTarget target = // Create WebTarget.
+final Response response = target.request().post(entity);
+                    </programlisting>
+                </example>
+                </para>
+                <para>
+                    The common use-case for many users is sending files from client to server. It is also covered by
+                    &jaxrs.core.EntityPart;.Builder.
+                    <example>
+                        <title>EntityPart - sending files.</title>
+
+                        <programlisting language="java">// MediaType of the body part will be derived from the file.
+final List&lt;EntityPart&gt; multiPartEntity = new List&lt;&gt;();
+list.add(EntityPart.withFileName("file001.txt").content(new FileInputStream("file001.txt")).build());
+list.add(EntityPart.withFileName("mypom.xml").content(new FileInputStream("pom.xml")).build());
+
+final GenericEntity&lt;List&lt;EntityPart&gt;&gt; genericEntity = new GenericEntity&lt;&gt;(list) {};
+final Entity entity = Entity.entity(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE);
+
+final WebTarget target = // Create WebTarget.
+final Response response = target.request().post(entity);
+                        </programlisting>
+                    </example>
+                </para>
+            </section>
         </section>
 
         <section>
             <title>Server</title>
+            <itemizedlist>
+                <listitem>
+                    <para>&link.multipart.server.jersey;</para>
+                </listitem>
+                <listitem>
+                    <para>&link.multipart.server.rest;</para>
+                </listitem>
+            </itemizedlist>
+            <section xml:id="multipart.server.jersey">
+            <title>Jersey Server API</title>
 
             <para>
                 Returning a multipart response from server to client is not much different from the parts described in the client
@@ -1852,5 +1909,60 @@
                 </tip>
             </section>
         </section>
+        <section xml:id="multipart.server.rest">
+                <title>Server using Jakarta REST API</title>
+                <para>
+                    Using &jaxrs.core.EntityPart; on the server side is similar to the client side.
+                    Jakarta REST specification allows for
+                    returning a &lit.jaxrs.core.Response; or a &lit.jdk6.List; of &lit.jaxrs.core.EntityPart;s.
+                </para>
+                <para>
+                    Receiving the &jaxrs.core.EntityPart;s can be done either using &lit.jaxrs.FormParam; annotations and
+                    &lit.jaxrs.core.EntityPart;, &lit.jdk6.InputStream; or &lit.jdk6.String; data-types, or using a
+                    &lit.jdk6.List; of &lit.jaxrs.core.EntityPart;s.
+                </para>
+
+                <example>
+                <title>Use of &lit.jaxrs.FormParam; annotation with &lit.jaxrs.core.EntityPart; &lit.jdk6.InputStream;
+                    and &lit.jdk6.String; types and returning a &lit.jaxrs.core.Response;</title>
+                <programlisting language="java">@POST
+@Path("/postFormVarious")
+public Response postFormVarious(@FormParam("name1") EntityPart part1,
+                @FormParam("name2") InputStream part2,
+                @FormParam("name3") String part3) throws IOException {
+    final List&lt;EntityPart&gt; list = new LinkedList&lt;&gt;();
+    list.add(EntityPart.withName(part1.getName())
+        .content(part1.getContent(String.class) + new String(part2.readAllBytes()) + part3)
+        .mediaType(MediaType.TEXT_PLAIN_TYPE)
+        .build());
+    final GenericEntity&lt;List&lt;EntityPart&gt;&gt; genericEntity = new GenericEntity&lt;&gt;(list) {};
+    return Response.ok(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE).build();
+}
+            </programlisting>
+            </example>
+            <example>
+                <title>Receiving a &lit.jdk6.List; of &lit.jaxrs.core.EntityPart;s</title>
+                <programlisting language="java">@POST
+@Path("/postListForm")
+public String postEntityPartForm(@FormParam("part-0x") List&lt;EntityPart&gt; part) throws IOException {
+    final String entity = part.get(0).getContent(String.class) + part.get(1).getContent(String.class);
+    return entity;
+}
+                </programlisting>
+            </example>
+            <example>
+                <title>Returning a &lit.jdk6.List; of &lit.jaxrs.core.EntityPart;s</title>
+                <programlisting language="java">@GET
+@Produces(MediaType.MULTIPART_FORM_DATA)
+@Path("/getList")
+public List&lt;EntityPart&gt; getList() throws IOException {
+    final List&lt;EntityPart&gt; list = new LinkedList&lt;&gt;();
+    list.add(EntityPart.withName("name1").content("data1").build());
+    return list;
+}
+                </programlisting>
+            </example>
+        </section>
+    </section>
     </section>
 </chapter>
diff --git a/docs/src/main/docbook/migration.xml b/docs/src/main/docbook/migration.xml
index 3b68582..9389341 100644
--- a/docs/src/main/docbook/migration.xml
+++ b/docs/src/main/docbook/migration.xml
@@ -102,6 +102,13 @@
                             <literal>true</literal>.
                         </para>
                     </listitem>
+                    <listitem>
+                        <para>
+                            Since Jersey 3.1.0+ the <literal>getRequestHeader(String name)</literal> method of the
+                            <literal>ClientRequest</literal> class returns NULL (instead of an empty List) in case if
+                            the specified header does not exist.
+                        </para>
+                    </listitem>
                 </itemizedlist>
             </para>
         </section>
diff --git a/docs/src/main/docbook/representations.xml b/docs/src/main/docbook/representations.xml
index 50515d6..12ef126 100644
--- a/docs/src/main/docbook/representations.xml
+++ b/docs/src/main/docbook/representations.xml
@@ -298,6 +298,19 @@
             and let other provider to be chosen for the exception mapping.
         </para>
 
+        <para>
+            Since Jersey 3.1.0 the default <literal>ExceptionMapper</literal> is implemented.
+            It is required by JAX-RS 3.1 specification (&jaxrs31.exceptionMapperProvider;).
+            The default behaviour of the mapper is to return a message from an exception caught and set the response status
+            to 500 (internal server error). In case of a &jaxrs.WebApplicationException; with a response that response is returned.
+            If response inside the &lit.jaxrs.WebApplicationException; is NULL the exception is being processed according to the
+            default behaviour.
+
+            The Default exception mapper is package private and can not be referenced. Its presence is required only by
+            JAX-RS 3.1 specification. Processing of the default Exception Mapper occurs at the very end of the exception
+            processing chain. It is invoked only if nothing else in the chain was invoked before.
+        </para>
+
     </section>
 
     <section>
diff --git a/etc/config/copyright-exclude b/etc/config/copyright-exclude
index 987175b..d5cff67 100644
--- a/etc/config/copyright-exclude
+++ b/etc/config/copyright-exclude
@@ -71,8 +71,6 @@
 /tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/entity/xxe.txt
 /core-server/src/main/java/com/sun/research/ws/wadl
 /core-common/src/main/java/org/glassfish/jersey/internal/jsr166
-/core-common/src/main/jsr166/org/glassfish/jersey/internal/jsr166
-/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166
 /tests/performance/etc/data/MEASUREMENT_DATA
 /core-common/src/main/java/org/glassfish/jersey/internal/guava/
 /core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/core/AbstractSlidingWindowTimeReservoir.java
diff --git a/etc/jenkins/Jenkinsfile_ci_build b/etc/jenkins/Jenkinsfile_ci_build
index f1b405b..59af68b 100644
--- a/etc/jenkins/Jenkinsfile_ci_build
+++ b/etc/jenkins/Jenkinsfile_ci_build
@@ -4,20 +4,6 @@
     stages {
         stage('Jersey build') {
             parallel {
-                stage('JDK 8 ') {
-                    agent {
-                        label 'centos-7'
-                    }
-                    tools {
-                        jdk 'oracle-jdk8-latest'
-                        maven 'apache-maven-latest'
-                    }
-                    steps {
-                        sh '''
-                                bash ${WORKSPACE}/etc/jenkins/jenkins_build.sh
-                            '''
-                    }
-                }
                 stage('JDK 11 ') {
                     agent {
                         label 'centos-7'
diff --git a/etc/jenkins/jenkins_build.sh b/etc/jenkins/jenkins_build.sh
index dbebc86..9983a05 100644
--- a/etc/jenkins/jenkins_build.sh
+++ b/etc/jenkins/jenkins_build.sh
@@ -2,5 +2,4 @@
 
 export DEBUG=true
 
-mvn -V -U -B -e clean install glassfish-copyright:check -Dcopyright.quiet=false
-
+mvn -V -U -B -e -Pstaging clean install glassfish-copyright:check -Dcopyright.quiet=false
\ No newline at end of file
diff --git a/examples/bookstore-webapp/pom.xml b/examples/bookstore-webapp/pom.xml
index 101a8dc..5cb6895 100644
--- a/examples/bookstore-webapp/pom.xml
+++ b/examples/bookstore-webapp/pom.xml
@@ -69,6 +69,11 @@
             <groupId>jakarta.xml.bind</groupId>
             <artifactId>jakarta.xml.bind-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.sun.xml.bind</groupId>
+            <artifactId>jaxb-osgi</artifactId>
+            <scope>runtime</scope>
+        </dependency>
     </dependencies>
 
     <build>
@@ -88,19 +93,6 @@
 
     <profiles>
         <profile>
-            <id>jdk11+</id>
-            <activation>
-                <jdk>[11,)</jdk>
-            </activation>
-            <dependencies>
-                <dependency>
-                    <groupId>com.sun.xml.bind</groupId>
-                    <artifactId>jaxb-osgi</artifactId>
-                    <scope>runtime</scope>
-                </dependency>
-            </dependencies>
-        </profile>
-        <profile>
             <!-- mvn test -Prun-external-tests -->
             <id>run-external-tests</id>
             <build>
diff --git a/examples/https-clientserver-grizzly/pom.xml b/examples/https-clientserver-grizzly/pom.xml
index ca1b4d6..14582e1 100644
--- a/examples/https-clientserver-grizzly/pom.xml
+++ b/examples/https-clientserver-grizzly/pom.xml
@@ -58,6 +58,12 @@
             <artifactId>junit</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>com.sun.xml.bind</groupId>
+            <artifactId>jaxb-osgi</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+
     </dependencies>
 
     <build>
@@ -74,23 +80,6 @@
 
     <profiles>
         <profile>
-            <id>jdk11+</id>
-            <activation>
-                <jdk>[11,)</jdk>
-            </activation>
-            <properties>
-                <!-- https://bugs.openjdk.java.net/browse/JDK-8211426 -->
-                <surefire.security.argline>-Djdk.tls.server.protocols=TLSv1.2</surefire.security.argline>
-            </properties>
-            <dependencies>
-                <dependency>
-                    <groupId>com.sun.xml.bind</groupId>
-                    <artifactId>jaxb-osgi</artifactId>
-                    <scope>runtime</scope>
-                </dependency>
-            </dependencies>
-        </profile>
-        <profile>
             <id>pre-release</id>
             <build>
                 <plugins>
@@ -103,4 +92,9 @@
         </profile>
     </profiles>
 
+    <properties>
+        <!-- https://bugs.openjdk.java.net/browse/JDK-8211426 -->
+        <surefire.security.argline>-Djdk.tls.server.protocols=TLSv1.2</surefire.security.argline>
+    </properties>
+
 </project>
diff --git a/examples/osgi-http-service/bundle/pom.xml b/examples/osgi-http-service/bundle/pom.xml
index 6c79fb4..efd62d4 100644
--- a/examples/osgi-http-service/bundle/pom.xml
+++ b/examples/osgi-http-service/bundle/pom.xml
@@ -72,7 +72,7 @@
                 <configuration>
                     <instructions>
                         <Export-Package>org.glassfish.jersey.examples.osgihttpservice</Export-Package>
-                        <Import-Package>jakarta.servlet.*;version="[5.0,6.0)",*</Import-Package>
+                        <Import-Package>jakarta.servlet.*;version="[5.0,7.0)",*</Import-Package>
                         <Bundle-Activator>org.glassfish.jersey.examples.osgihttpservice.Activator</Bundle-Activator>
                         <Implementation-Title>jersey-osgi-http-service-bundle</Implementation-Title>
                         <Implementation-Version>${project.version}</Implementation-Version>
diff --git a/ext/bean-validation/pom.xml b/ext/bean-validation/pom.xml
index 53ac3d1..be3a887 100644
--- a/ext/bean-validation/pom.xml
+++ b/ext/bean-validation/pom.xml
@@ -58,6 +58,8 @@
                         <Import-Package>
                             ${jakarta.annotation.osgi.version},
                             jakarta.validation.*;resolution:=optional;version="[3,4)",
+                            jakarta.enterprise.*;version="[3.0,5)",
+                            jakarta.decorator.*;version="[3.0,5)",
                             *
                         </Import-Package>
                     </instructions>
diff --git a/ext/cdi/jersey-cdi1x-servlet/pom.xml b/ext/cdi/jersey-cdi1x-servlet/pom.xml
index 17b9c91..057251d 100644
--- a/ext/cdi/jersey-cdi1x-servlet/pom.xml
+++ b/ext/cdi/jersey-cdi1x-servlet/pom.xml
@@ -86,6 +86,11 @@
                 <configuration>
                     <instructions>
                         <Export-Package>org.glassfish.jersey.ext.cdi1x.servlet.internal</Export-Package>
+                        <Import-Package>
+                            jakarta.enterprise.*;version="[3.0,5)",
+                            jakarta.decorator.*;version="[3.0,5)",
+                            *
+                        </Import-Package>
                     </instructions>
                 </configuration>
             </plugin>
diff --git a/ext/cdi/jersey-cdi1x-transaction/pom.xml b/ext/cdi/jersey-cdi1x-transaction/pom.xml
index 4ce8ff6..5c7b4cd 100644
--- a/ext/cdi/jersey-cdi1x-transaction/pom.xml
+++ b/ext/cdi/jersey-cdi1x-transaction/pom.xml
@@ -91,7 +91,12 @@
                 <configuration>
                     <instructions>
                         <Export-Package>org.glassfish.jersey.ext.cdi1x.transaction.internal</Export-Package>
-                        <Import-Package>${jakarta.annotation.osgi.version},*</Import-Package>
+                        <Import-Package>
+                            jakarta.enterprise.*;version="[3.0,5)",
+                            jakarta.decorator.*;version="[3.0,5)",
+                            ${jakarta.annotation.osgi.version},
+                            *
+                        </Import-Package>
                     </instructions>
                 </configuration>
             </plugin>
diff --git a/ext/cdi/jersey-cdi1x-validation/pom.xml b/ext/cdi/jersey-cdi1x-validation/pom.xml
index f176374..45701d1 100644
--- a/ext/cdi/jersey-cdi1x-validation/pom.xml
+++ b/ext/cdi/jersey-cdi1x-validation/pom.xml
@@ -103,7 +103,12 @@
                 <configuration>
                     <instructions>
                         <Export-Package>org.glassfish.jersey.ext.cdi1x.validation.internal;version=${project.version}</Export-Package>
-                        <Import-Package>${jakarta.annotation.osgi.version},*</Import-Package>
+                        <Import-Package>
+                            jakarta.enterprise.*;version="[3.0,5)",
+                            jakarta.decorator.*;version="[3.0,5)",
+                            ${jakarta.annotation.osgi.version},
+                            *
+                        </Import-Package>
                     </instructions>
                 </configuration>
             </plugin>
diff --git a/ext/cdi/jersey-cdi1x/pom.xml b/ext/cdi/jersey-cdi1x/pom.xml
index e31ca92..0c2425d 100644
--- a/ext/cdi/jersey-cdi1x/pom.xml
+++ b/ext/cdi/jersey-cdi1x/pom.xml
@@ -87,6 +87,8 @@
                         <Import-Package>
                             ${jakarta.annotation.osgi.version},
                             ${hk2.osgi.version},
+                            jakarta.enterprise.*;version="[3.0,5)",
+                            jakarta.decorator.*;version="[3.0,5)",
                             *
                         </Import-Package>
                     </instructions>
diff --git a/ext/microprofile/mp-config/pom.xml b/ext/microprofile/mp-config/pom.xml
index bffe705..12c3b1a 100644
--- a/ext/microprofile/mp-config/pom.xml
+++ b/ext/microprofile/mp-config/pom.xml
@@ -75,6 +75,13 @@
             <scope>test</scope>
         </dependency>
 
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-jetty</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+
     </dependencies>
     <build>
         <plugins>
@@ -99,40 +106,4 @@
         </plugins>
     </build>
 
-    <profiles>
-        <profile>
-            <id>JettyExclude</id>
-            <activation>
-                <jdk>1.8</jdk>
-            </activation>
-            <build>
-                <plugins>
-                    <plugin>
-                        <groupId>org.apache.maven.plugins</groupId>
-                        <artifactId>maven-compiler-plugin</artifactId>
-                        <configuration>
-                            <testExcludes>
-                                <testExclude>org/glassfish/jersey/microprofile/config/ExternalPropertiesConfigurationFactoryTest.java</testExclude>
-                            </testExcludes>
-                        </configuration>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-        <profile>
-            <id>Jetty11</id>
-            <activation>
-                <jdk>[11,)</jdk>
-            </activation>
-            <dependencies>
-                <dependency>
-                    <groupId>org.glassfish.jersey.test-framework.providers</groupId>
-                    <artifactId>jersey-test-framework-provider-jetty</artifactId>
-                    <version>${project.version}</version>
-                    <scope>test</scope>
-                </dependency>
-            </dependencies>
-        </profile>
-    </profiles>
-
 </project>
diff --git a/ext/microprofile/mp-rest-client/pom.xml b/ext/microprofile/mp-rest-client/pom.xml
index 553e1b2..c3551ec 100644
--- a/ext/microprofile/mp-rest-client/pom.xml
+++ b/ext/microprofile/mp-rest-client/pom.xml
@@ -78,7 +78,6 @@
         <dependency>
             <groupId>jakarta.json</groupId>
             <artifactId>jakarta.json-api</artifactId>
-            <version>${jsonp.ri.version}</version>
         </dependency>
         <dependency>
             <groupId>jakarta.enterprise</groupId>
@@ -86,8 +85,8 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
-            <groupId>org.glassfish</groupId>
-            <artifactId>jsonp-jaxrs</artifactId>
+            <groupId>org.eclipse.parsson</groupId>
+            <artifactId>parsson-media</artifactId>
         </dependency>
         <dependency>
             <groupId>org.reactivestreams</groupId>
diff --git a/ext/mvc-freemarker/pom.xml b/ext/mvc-freemarker/pom.xml
index 214c883..9477666 100644
--- a/ext/mvc-freemarker/pom.xml
+++ b/ext/mvc-freemarker/pom.xml
@@ -42,6 +42,7 @@
                 <extensions>true</extensions>
                 <configuration>
                     <instructions>
+                        <Import-Package>jakarta.servlet.*;version="[5.0,7.0)",*</Import-Package>
                         <Export-Package>org.glassfish.jersey.server.mvc.freemarker.*;version=${project.version}</Export-Package>
                     </instructions>
                     <unpackBundle>true</unpackBundle>
@@ -56,17 +57,9 @@
     </build>
 
     <dependencies>
-
-        <dependency>
-            <groupId>javax.servlet</groupId>
-            <artifactId>javax.servlet-api</artifactId>
-            <version>4.0.1</version>
-            <scope>provided</scope>
-        </dependency>
         <dependency>
             <groupId>jakarta.servlet</groupId>
             <artifactId>jakarta.servlet-api</artifactId>
-            <version>${servlet5.version}</version>
             <scope>provided</scope>
         </dependency>
 
diff --git a/ext/mvc-jsp/pom.xml b/ext/mvc-jsp/pom.xml
index ccdea70..6bf47a4 100644
--- a/ext/mvc-jsp/pom.xml
+++ b/ext/mvc-jsp/pom.xml
@@ -54,7 +54,7 @@
                     <instructions>
                         <Import-Package>
                             jakarta.servlet.jsp.*;version="[3.0,4.0)",
-                            jakarta.servlet.*;version="!",
+                            jakarta.servlet.*;version="[5.0,7.0)",
                             *
                         </Import-Package>
                         <Export-Package>org.glassfish.jersey.server.mvc.jsp.*;version=${project.version}</Export-Package>
@@ -84,7 +84,6 @@
         <dependency>
             <groupId>jakarta.servlet</groupId>
             <artifactId>jakarta.servlet-api</artifactId>
-            <version>${servlet5.version}</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
diff --git a/ext/mvc/pom.xml b/ext/mvc/pom.xml
index 68d79ad..3d2f274 100644
--- a/ext/mvc/pom.xml
+++ b/ext/mvc/pom.xml
@@ -37,7 +37,6 @@
         <dependency>
             <groupId>jakarta.servlet</groupId>
             <artifactId>jakarta.servlet-api</artifactId>
-            <version>${servlet5.version}</version>
         </dependency>
 
         <dependency>
@@ -67,7 +66,7 @@
                 <configuration>
                     <instructions>
                         <Export-Package>org.glassfish.jersey.server.mvc.*;version=${project.version}</Export-Package>
-                        <Import-Package>${jakarta.annotation.osgi.version},*</Import-Package>
+                        <Import-Package> jakarta.servlet.*;version="[5.0,7.0)",${jakarta.annotation.osgi.version},*</Import-Package>
                     </instructions>
                     <unpackBundle>true</unpackBundle>
                 </configuration>
diff --git a/ext/proxy-client/src/main/java/org/glassfish/jersey/client/proxy/RequestParameters.java b/ext/proxy-client/src/main/java/org/glassfish/jersey/client/proxy/RequestParameters.java
index 6457dd7..543e1c5 100644
--- a/ext/proxy-client/src/main/java/org/glassfish/jersey/client/proxy/RequestParameters.java
+++ b/ext/proxy-client/src/main/java/org/glassfish/jersey/client/proxy/RequestParameters.java
@@ -61,8 +61,7 @@
     RequestParameters(final WebTarget newTarget, final MultivaluedMap<String, Object> headers,
                              final List<Cookie> cookies, final Form form) {
 
-        this.headers = new MultivaluedHashMap<>();
-        this.headers.putAll(headers);
+        this.headers = new MultivaluedHashMap<>(headers);
         this.cookies = new LinkedList<>(cookies);
         this.form = new Form();
         this.form.asMap().putAll(form.asMap());
diff --git a/incubator/cdi-inject-weld/pom.xml b/incubator/cdi-inject-weld/pom.xml
index 999ecbb..2ed9f7d 100644
--- a/incubator/cdi-inject-weld/pom.xml
+++ b/incubator/cdi-inject-weld/pom.xml
@@ -59,7 +59,6 @@
         <dependency>
             <groupId>jakarta.servlet</groupId>
             <artifactId>jakarta.servlet-api</artifactId>
-            <version>${servlet5.version}</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
@@ -122,8 +121,11 @@
                             org.glassfish.jersey.inject.weld.managed.*;version=${project.version}
                         </Export-Package>
                         <Import-Package>
+                            jakarta.servlet.*;version="[5.0,7.0)",
                             sun.misc.*;resolution:=optional,
                             ${jakarta.annotation.osgi.version},
+                            jakarta.enterprise.*;version="[3.0,5)",
+                            jakarta.decorator.*;version="[3.0,5)",
                             *
                         </Import-Package>
                     </instructions>
diff --git a/incubator/declarative-linking/pom.xml b/incubator/declarative-linking/pom.xml
index 1c2d642..2a6c8a5 100644
--- a/incubator/declarative-linking/pom.xml
+++ b/incubator/declarative-linking/pom.xml
@@ -74,12 +74,12 @@
             <version>${project.version}</version>
             <scope>test</scope>
         </dependency>
-<!--        <dependency>-->
-<!--            <groupId>org.glassfish.jersey.media</groupId>-->
-<!--            <artifactId>jersey-media-json-jackson</artifactId>-->
-<!--            <version>${jersey.version}</version>-->
-<!--            <scope>test</scope>-->
-<!--        </dependency>-->
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jackson</artifactId>
+            <version>${jersey.version}</version>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-databind</artifactId>
@@ -174,22 +174,6 @@
                     </execution>
                 </executions>
             </plugin>
-            <plugin>
-                <!-- TODO remove after jakartification -->
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-surefire-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <id>default-test</id> <!-- jakartification-excluded-tests -->
-                        <configuration>
-                            <excludes>
-                                <exclude>org/glassfish/jersey/linking/integration/LinkingTest.java</exclude>
-                                <exclude>org/glassfish/jersey/linking/integration/LinkingManualTest.java</exclude>
-                            </excludes>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
         </plugins>
     </build>
 </project>
diff --git a/inject/cdi2-se/pom.xml b/inject/cdi2-se/pom.xml
index f01855d..d4d2c26 100644
--- a/inject/cdi2-se/pom.xml
+++ b/inject/cdi2-se/pom.xml
@@ -91,6 +91,8 @@
                         </Export-Package>
                         <Import-Package>
                             sun.misc.*;resolution:=optional,
+                            jakarta.enterprise.*;version="[3.0,5)",
+                            jakarta.decorator.*;version="[3.0,5)",
                             ${jakarta.annotation.osgi.version},
                             *
                         </Import-Package>
diff --git a/media/json-binding/pom.xml b/media/json-binding/pom.xml
index f5395b1..3ae3b87 100644
--- a/media/json-binding/pom.xml
+++ b/media/json-binding/pom.xml
@@ -78,9 +78,8 @@
             <artifactId>jakarta.json.bind-api</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.glassfish</groupId>
-            <artifactId>jakarta.json</artifactId>
-            <classifier>module</classifier>
+            <groupId>org.eclipse.parsson</groupId>
+            <artifactId>parsson</artifactId>
         </dependency>
 
         <dependency>
diff --git a/media/json-jettison/pom.xml b/media/json-jettison/pom.xml
index 9e69d15..6ff9a21 100644
--- a/media/json-jettison/pom.xml
+++ b/media/json-jettison/pom.xml
@@ -53,7 +53,14 @@
                 <extensions>true</extensions>
                 <configuration>
                     <instructions>
+                        <!-- Explicitly set versions for packages from GlassFish to allow future uptake of GlassFish 7.x-->
+                        <Import-Package>
+                            jakarta.xml.bind.*;version="[3.0,5)",
+                            *
+                        </Import-Package>
+                    
                         <Export-Package>org.glassfish.jersey.jettison.*</Export-Package>
+                        
                         <!--<Import-Package>
                             com.sun.xml.bind.annotation;resolution:=optional,com.sun.xml.bind.v2.*;resolution:=optional, *
                         </Import-Package>-->
diff --git a/media/json-processing/pom.xml b/media/json-processing/pom.xml
index 10dee3a..373b174 100644
--- a/media/json-processing/pom.xml
+++ b/media/json-processing/pom.xml
@@ -57,8 +57,7 @@
                         <!-- TODO: change to ${range;[==,+);${jsonp.api.version}} once the version is final-->
                         <Import-Package>
                             ${jakarta.annotation.osgi.version},
-                            jakarta.json.*;version="[0,3)",
-                            org.glassfish.json.*;version="[0,3)",
+                            jakarta.json.*;version="[2,3)",
                             *
                         </Import-Package>
                     </instructions>
@@ -81,13 +80,12 @@
             <artifactId>jakarta.json-api</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.glassfish</groupId>
-            <artifactId>jakarta.json</artifactId>
-            <classifier>module</classifier>
+            <groupId>org.eclipse.parsson</groupId>
+            <artifactId>parsson</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.glassfish</groupId>
-            <artifactId>jsonp-jaxrs</artifactId>
+            <groupId>org.eclipse.parsson</groupId>
+            <artifactId>parsson-media</artifactId>
         </dependency>
 
         <dependency>
diff --git a/media/json-processing/src/main/java/org/glassfish/jersey/jsonp/JsonProcessingFeature.java b/media/json-processing/src/main/java/org/glassfish/jersey/jsonp/JsonProcessingFeature.java
index cc1c913..3f23a73 100644
--- a/media/json-processing/src/main/java/org/glassfish/jersey/jsonp/JsonProcessingFeature.java
+++ b/media/json-processing/src/main/java/org/glassfish/jersey/jsonp/JsonProcessingFeature.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2021 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
@@ -20,10 +20,11 @@
 import jakarta.ws.rs.core.Feature;
 import jakarta.ws.rs.core.FeatureContext;
 
+import org.eclipse.parsson.media.JsonValueBodyReader;
+import org.eclipse.parsson.media.JsonValueBodyWriter;
+
 import org.glassfish.jersey.CommonProperties;
 
-import org.glassfish.json.jaxrs.JsonValueBodyReader;
-import org.glassfish.json.jaxrs.JsonValueBodyWriter;
 
 /**
  * {@link Feature} used to register JSON-P providers.
diff --git a/media/moxy/pom.xml b/media/moxy/pom.xml
index 2a36b2c..f590550 100644
--- a/media/moxy/pom.xml
+++ b/media/moxy/pom.xml
@@ -101,9 +101,8 @@
             <artifactId>jakarta.json.bind-api</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.glassfish</groupId>
-            <artifactId>jakarta.json</artifactId>
-            <classifier>module</classifier>
+            <groupId>org.eclipse.parsson</groupId>
+            <artifactId>parsson</artifactId>
         </dependency>
 
         <dependency>
diff --git a/media/multipart/pom.xml b/media/multipart/pom.xml
index be32746..bb340e5 100644
--- a/media/multipart/pom.xml
+++ b/media/multipart/pom.xml
@@ -92,6 +92,12 @@
             <version>${project.version}</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-jetty-connector</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
 
         <dependency>
             <groupId>junit</groupId>
@@ -99,40 +105,4 @@
         </dependency>
     </dependencies>
 
-    <profiles>
-        <profile>
-            <id>JettyExclude</id>
-            <activation>
-                <jdk>1.8</jdk>
-            </activation>
-            <build>
-                <plugins>
-                    <plugin>
-                        <groupId>org.apache.maven.plugins</groupId>
-                        <artifactId>maven-compiler-plugin</artifactId>
-                        <configuration>
-                            <testExcludes>
-                                <testExclude>org/glassfish/jersey/media/multipart/internal/MultiPartHeaderModificationTest.java</testExclude>
-                            </testExcludes>
-                        </configuration>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-        <profile>
-            <id>Jetty11</id>
-            <activation>
-                <jdk>[11,)</jdk>
-            </activation>
-            <dependencies>
-                <dependency>
-                    <groupId>org.glassfish.jersey.connectors</groupId>
-                    <artifactId>jersey-jetty-connector</artifactId>
-                    <version>${project.version}</version>
-                    <scope>test</scope>
-                </dependency>
-            </dependencies>
-        </profile>
-    </profiles>
-
 </project>
diff --git a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/BodyPart.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/BodyPart.java
index 0e20e25..7287759 100644
--- a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/BodyPart.java
+++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/BodyPart.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2021 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
@@ -18,9 +18,11 @@
 
 import java.io.IOException;
 import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
 import java.text.ParseException;
 
 import jakarta.ws.rs.ProcessingException;
+import jakarta.ws.rs.core.GenericType;
 import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.MultivaluedMap;
 import jakarta.ws.rs.ext.MessageBodyReader;
@@ -263,21 +265,42 @@
      * entity instance is not the unconverted content of the body part entity.
      */
     public <T> T getEntityAs(final Class<T> clazz) {
+        return getEntityAs(clazz, clazz);
+    }
+
+    /**
+     * Returns the entity after appropriate conversion to the requested type. This is useful only when the containing
+     * {@link MultiPart} instance has been received, which causes the {@code providers} property to have been set.
+     *
+     * @param genericEntity desired entity type into which the entity should be converted.
+     * @return entity after appropriate conversion to the requested type.
+     *
+     * @throws ProcessingException if an IO error arises during reading an entity.
+     * @throws IllegalArgumentException if no {@link MessageBodyReader} can be found to perform the requested conversion.
+     * @throws IllegalStateException if this method is called when the {@code providers} property has not been set or when the
+     * entity instance is not the unconverted content of the body part entity.
+     */
+    <T> T getEntityAs(final GenericType<T> genericEntity) {
+        return (T) getEntityAs(genericEntity.getRawType(), genericEntity.getType());
+    }
+
+    <T> T getEntityAs(final Class<T> type, Type genericType) {
         if (entity == null || !(entity instanceof BodyPartEntity)) {
             throw new IllegalStateException(LocalizationMessages.ENTITY_HAS_WRONG_TYPE());
         }
-        if (clazz == BodyPartEntity.class) {
-            return clazz.cast(entity);
+        if (type == BodyPartEntity.class) {
+            return type.cast(entity);
         }
 
         final Annotation[] annotations = new Annotation[0];
-        final MessageBodyReader<T> reader = messageBodyWorkers.getMessageBodyReader(clazz, clazz, annotations, mediaType);
+        final MessageBodyReader<T> reader = messageBodyWorkers.getMessageBodyReader(type, genericType, annotations, mediaType);
         if (reader == null) {
-            throw new IllegalArgumentException(LocalizationMessages.NO_AVAILABLE_MBR(clazz, mediaType));
+            throw new IllegalArgumentException(LocalizationMessages.NO_AVAILABLE_MBR(type, mediaType));
         }
 
         try {
-            return reader.readFrom(clazz, clazz, annotations, mediaType, headers, ((BodyPartEntity) entity).getInputStream());
+            return reader.readFrom(type, genericType, annotations, mediaType, headers,
+                    ((BodyPartEntity) entity).getInputStream());
         } catch (final IOException ioe) {
             throw new ProcessingException(LocalizationMessages.ERROR_READING_ENTITY(String.class), ioe);
         }
diff --git a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/FormDataBodyPart.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/FormDataBodyPart.java
index 2380c44..802cc02 100644
--- a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/FormDataBodyPart.java
+++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/FormDataBodyPart.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2021 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
@@ -16,9 +16,14 @@
 
 package org.glassfish.jersey.media.multipart;
 
+import java.io.InputStream;
 import java.text.ParseException;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import jakarta.ws.rs.ProcessingException;
+import jakarta.ws.rs.core.EntityPart;
+import jakarta.ws.rs.core.GenericType;
 import jakarta.ws.rs.core.MediaType;
 
 import org.glassfish.jersey.media.multipart.internal.LocalizationMessages;
@@ -59,9 +64,10 @@
  * @author Paul Sandoz
  * @author Michal Gajdos
  */
-public class FormDataBodyPart extends BodyPart {
+public class FormDataBodyPart extends BodyPart implements EntityPart {
 
     private final boolean fileNameFix;
+    protected final AtomicBoolean contentRead = new AtomicBoolean(false);
 
     /**
      * Instantiates an unnamed new {@link FormDataBodyPart} with a
@@ -231,6 +237,34 @@
         return formDataContentDisposition.getName();
     }
 
+    @Override
+    public Optional<String> getFileName() {
+        return Optional.ofNullable(getFormDataContentDisposition().getFileName());
+    }
+
+    @Override
+    public InputStream getContent() {
+        return getContent(InputStream.class);
+    }
+
+    @Override
+    public <T> T getContent(Class<T> type) {
+        if (contentRead.compareAndExchange(false, true)) {
+            throw new IllegalStateException(LocalizationMessages.CONTENT_HAS_BEEN_ALREADY_READ());
+        }
+        final Object entity = getEntity();
+        return type.isInstance(entity) ? type.cast(entity) : getEntityAs(type);
+    }
+
+    @Override
+    public <T> T getContent(GenericType<T> type) {
+        if (contentRead.compareAndExchange(false, true)) {
+            throw new IllegalStateException(LocalizationMessages.CONTENT_HAS_BEEN_ALREADY_READ());
+        }
+        final Object entity = getEntity();
+        return type.getRawType().isInstance(entity) ? (T) entity : getEntityAs(type);
+    }
+
     /**
      * Sets the control name.
      *
diff --git a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/JerseyEntityPartBuilderProvider.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/JerseyEntityPartBuilderProvider.java
new file mode 100644
index 0000000..61583d1
--- /dev/null
+++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/JerseyEntityPartBuilderProvider.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2021 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.media.multipart;
+
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.core.EntityPart;
+import jakarta.ws.rs.core.GenericEntity;
+import jakarta.ws.rs.core.GenericType;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.MultivaluedHashMap;
+import jakarta.ws.rs.core.MultivaluedMap;
+import org.glassfish.jersey.innate.spi.EntityPartBuilderProvider;
+import org.glassfish.jersey.media.multipart.file.FileDataBodyPart;
+import org.glassfish.jersey.media.multipart.file.StreamDataBodyPart;
+import org.glassfish.jersey.media.multipart.internal.LocalizationMessages;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Jersey implementation of {@link EntityPart.Builder}.
+ * @since 3.1.0
+ */
+public class JerseyEntityPartBuilderProvider implements EntityPartBuilderProvider {
+
+    @Override
+    public EntityPart.Builder withName(String partName) {
+        return new EnityPartBuilder().withName(partName);
+    }
+
+    private static class EnityPartBuilder implements EntityPart.Builder {
+
+        private String partName;
+        private String fileName = null;
+        private MultivaluedMap<String, String> headers = new MultivaluedHashMap<>();
+        private MediaType mediaType = null;
+        private MethodData methodData;
+
+        private EntityPart.Builder withName(String partName) {
+            this.partName = partName;
+            return this;
+        }
+
+        @Override
+        public EntityPart.Builder mediaType(MediaType mediaType) throws IllegalArgumentException {
+            this.mediaType = mediaType;
+            return this;
+        }
+
+        @Override
+        public EntityPart.Builder mediaType(String mediaTypeString) throws IllegalArgumentException {
+            this.mediaType = MediaType.valueOf(mediaTypeString);
+            return this;
+        }
+
+        @Override
+        public EntityPart.Builder header(String headerName, String... headerValues) throws IllegalArgumentException {
+            this.headers.addAll(headerName, headerValues);
+            return this;
+        }
+
+        @Override
+        public EntityPart.Builder headers(MultivaluedMap<String, String> newHeaders) throws IllegalArgumentException {
+            for (Map.Entry<String, List<String>> entry : newHeaders.entrySet()) {
+                header(entry.getKey(), entry.getValue().toArray(new String[0]));
+            }
+            return this;
+        }
+
+        @Override
+        public EntityPart.Builder fileName(String fileName) throws IllegalArgumentException {
+            this.fileName = fileName;
+            return this;
+        }
+
+        @Override
+        public EntityPart.Builder content(InputStream content) throws IllegalArgumentException {
+            methodData = new InputStreamMethodData(content);
+            return this;
+        }
+
+        @Override
+        public <T> EntityPart.Builder content(T content, Class<? extends T> type) throws IllegalArgumentException {
+            if (File.class.equals(type)) {
+                methodData = new FileMethodData((File) content);
+            } else if (InputStream.class.equals(type)) {
+                methodData = new InputStreamMethodData((InputStream) content);
+            } else {
+                methodData = new GenericData(content, null);
+            }
+            return this;
+        }
+
+        @Override
+        public <T> EntityPart.Builder content(T content, GenericType<T> type) throws IllegalArgumentException {
+            if (File.class.equals(type.getRawType())) {
+                methodData = new FileMethodData((File) content);
+            } else if (InputStream.class.equals(type.getRawType())) {
+                methodData = new InputStreamMethodData((InputStream) content);
+            } else {
+                methodData = new GenericData(content, type);
+            }
+            return this;
+        }
+
+        @Override
+        public EntityPart build() throws IllegalStateException, IOException, WebApplicationException {
+            if (methodData == null) {
+                throw new IllegalStateException(LocalizationMessages.ENTITY_CONTENT_NOT_SET());
+            }
+            final FormDataBodyPart bodyPart = methodData.build();
+            return bodyPart;
+        }
+
+
+        private abstract class MethodData<T> {
+            protected MethodData(T content) {
+                this.content = content;
+            }
+            protected final T content;
+            protected abstract FormDataBodyPart build();
+            protected void fillFormData(FormDataBodyPart bodyPart) {
+                FormDataContentDisposition contentDisposition =
+                        FormDataContentDisposition.name(partName).fileName(fileName).build();
+                bodyPart.setContentDisposition(contentDisposition);
+                if (mediaType != null) {
+                    bodyPart.setMediaType(mediaType);
+                }
+                for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
+                    bodyPart.getHeaders().addAll(entry.getKey(), entry.getValue().toArray(new String[0]));
+                }
+            }
+        }
+
+        private class InputStreamMethodData extends MethodData<InputStream> {
+            protected InputStreamMethodData(InputStream content) {
+                super(content);
+            }
+
+            @Override
+            protected FormDataBodyPart build() {
+                final StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart();
+                streamDataBodyPart.setFilename(fileName);
+                fillFormData(streamDataBodyPart);
+                streamDataBodyPart.setStreamEntity(content);
+                return streamDataBodyPart;
+            }
+        }
+
+        private class FileMethodData extends MethodData<File> {
+            protected FileMethodData(File content) {
+                super(content);
+            }
+
+            @Override
+            protected FormDataBodyPart build() {
+                final FileDataBodyPart fileDataBodyPart = new FileDataBodyPart();
+                fillFormData(fileDataBodyPart);
+                fileDataBodyPart.setFileEntity(content);
+                return fileDataBodyPart;
+            }
+        }
+
+        private class GenericData extends MethodData<Object> {
+            private final GenericType<?> genericEntity;
+
+            protected GenericData(Object content, GenericType<?> genericEntity) {
+                super(content);
+                this.genericEntity = genericEntity;
+            }
+
+            @Override
+            protected FormDataBodyPart build() {
+                final FormDataBodyPart formDataBodyPart = new FormDataBodyPart();
+                fillFormData(formDataBodyPart);
+                if (genericEntity != null && !GenericEntity.class.isInstance(content)) {
+                    GenericEntity entity = new GenericEntity(content, genericEntity.getType());
+                    formDataBodyPart.setEntity(entity);
+                } else {
+                    formDataBodyPart.setEntity(content);
+                }
+
+                return formDataBodyPart;
+            }
+        }
+    }
+}
diff --git a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/MultiPartFeature.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/MultiPartFeature.java
index 3224ec5..03bf661 100644
--- a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/MultiPartFeature.java
+++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/MultiPartFeature.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2021 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
@@ -20,6 +20,8 @@
 import jakarta.ws.rs.core.Feature;
 import jakarta.ws.rs.core.FeatureContext;
 
+import org.glassfish.jersey.media.multipart.internal.EntityPartReader;
+import org.glassfish.jersey.media.multipart.internal.EntityPartWriter;
 import org.glassfish.jersey.media.multipart.internal.FormDataParamInjectionFeature;
 import org.glassfish.jersey.media.multipart.internal.MultiPartReaderClientSide;
 import org.glassfish.jersey.media.multipart.internal.MultiPartReaderServerSide;
@@ -45,6 +47,9 @@
 
         context.register(MultiPartWriter.class);
 
+        context.register(EntityPartReader.class);
+        context.register(EntityPartWriter.class);
+
         return true;
     }
 }
diff --git a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/MultiPartFeatureAutodiscoverable.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/MultiPartFeatureAutodiscoverable.java
new file mode 100644
index 0000000..f9cfb9d
--- /dev/null
+++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/MultiPartFeatureAutodiscoverable.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2021 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.media.multipart;
+
+import jakarta.ws.rs.core.FeatureContext;
+import org.glassfish.jersey.internal.spi.AutoDiscoverable;
+
+/**
+ * Automatic registration of {@link MultiPartFeature}.
+ * @since 3.1.0
+ */
+public class MultiPartFeatureAutodiscoverable implements AutoDiscoverable {
+    @Override
+    public void configure(FeatureContext context) {
+        context.register(MultiPartFeature.class);
+    }
+}
diff --git a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/file/DefaultMediaTypePredictor.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/file/DefaultMediaTypePredictor.java
index f91c723..1493e8f 100644
--- a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/file/DefaultMediaTypePredictor.java
+++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/file/DefaultMediaTypePredictor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2021 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
@@ -38,6 +38,7 @@
      * Currently supported file extension and MIME Types are -
      * <ul>
      *   <li>".xml" - application/xml</li>
+     *   <li>".json - application/json</li>
      *   <li>".txt" - text/plain</li>
      *   <li>".pdf" - application/pdf</li>
      *   <li>".htm" - text/html</li>
@@ -59,6 +60,7 @@
     public enum CommonMediaTypes {
 
         XML(".xml", MediaType.APPLICATION_XML_TYPE),
+        JSON(".json", MediaType.APPLICATION_JSON_TYPE),
         TXT(".txt", MediaType.TEXT_PLAIN_TYPE),
         HTM(".htm", MediaType.TEXT_HTML_TYPE),
         HTML(".html", MediaType.TEXT_HTML_TYPE),
diff --git a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/file/StreamDataBodyPart.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/file/StreamDataBodyPart.java
index 3ddedf2..707bedf 100644
--- a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/file/StreamDataBodyPart.java
+++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/file/StreamDataBodyPart.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2021 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
@@ -18,6 +18,7 @@
 
 import java.io.InputStream;
 import java.text.MessageFormat;
+import java.util.Optional;
 
 import jakarta.ws.rs.core.MediaType;
 
@@ -275,4 +276,8 @@
         return filename;
     }
 
+    @Override
+    public Optional<String> getFileName() {
+        return Optional.ofNullable(getFilename());
+    }
 }
diff --git a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/EntityPartReader.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/EntityPartReader.java
new file mode 100644
index 0000000..864cec4
--- /dev/null
+++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/EntityPartReader.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2021 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.media.multipart.internal;
+
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.core.EntityPart;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.MultivaluedMap;
+import jakarta.ws.rs.ext.MessageBodyReader;
+import jakarta.ws.rs.ext.Providers;
+import org.glassfish.jersey.internal.util.ReflectionHelper;
+import org.glassfish.jersey.media.multipart.BodyPart;
+import org.glassfish.jersey.media.multipart.FormDataBodyPart;
+import org.glassfish.jersey.media.multipart.JerseyEntityPartBuilderProvider;
+import org.glassfish.jersey.media.multipart.MultiPart;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Reader supporting List&lt;EntityPart&gt; Make sure {@code GenericEntity} class is used when reading the
+ * {@link EntityPart}s.
+ * @since 3.1.0
+ */
+@Consumes(MediaType.MULTIPART_FORM_DATA)
+@Singleton
+public class EntityPartReader implements MessageBodyReader<List<EntityPart>> {
+
+    private MultiPartReaderClientSide multiPartReaderClientSide;
+
+    @Inject
+    Providers providers;
+
+    @Override
+    public boolean isReadable(Class<?> type, Type generic, Annotation[] annotations, MediaType mediaType) {
+        return List.class.isAssignableFrom(type)
+                && ParameterizedType.class.isInstance(generic)
+                && EntityPart.class.isAssignableFrom(ReflectionHelper.getGenericTypeArgumentClasses(generic).get(0));
+    }
+
+    @Override
+    public List<EntityPart> readFrom(Class<List<EntityPart>> type, Type genericType, Annotation[] annotations,
+                                     MediaType mediaType, MultivaluedMap<String, String> httpHeaders,
+                                     InputStream entityStream) throws IOException, WebApplicationException {
+
+        if (multiPartReaderClientSide == null) {
+            multiPartReaderClientSide = (MultiPartReaderClientSide) providers.getMessageBodyReader(
+                    MultiPart.class, MultiPart.class, new Annotation[0], MediaType.MULTIPART_FORM_DATA_TYPE);
+        }
+
+        final MultiPart multiPart = multiPartReaderClientSide.readFrom(
+                MultiPart.class, MultiPart.class, annotations, mediaType, httpHeaders, entityStream);
+        final List<BodyPart> bodyParts = multiPart.getBodyParts();
+        final List<EntityPart> entityParts = new LinkedList<>();
+
+        for (BodyPart bp : bodyParts) {
+            if (FormDataBodyPart.class.isInstance(bp)) {
+                entityParts.add((EntityPart) bp);
+            } else {
+                final EntityPart ep = new JerseyEntityPartBuilderProvider().withName("")
+                        .mediaType(bp.getMediaType()).content(bp.getEntity()).headers(bp.getHeaders()).build();
+                entityParts.add(ep);
+            }
+        }
+
+        return entityParts;
+    }
+}
diff --git a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/EntityPartWriter.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/EntityPartWriter.java
new file mode 100644
index 0000000..f5c3472
--- /dev/null
+++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/EntityPartWriter.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2021 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.media.multipart.internal;
+
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.core.EntityPart;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.MultivaluedMap;
+import jakarta.ws.rs.ext.MessageBodyWriter;
+import jakarta.ws.rs.ext.Providers;
+import org.glassfish.jersey.internal.util.ReflectionHelper;
+import org.glassfish.jersey.media.multipart.BodyPart;
+import org.glassfish.jersey.media.multipart.MultiPart;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.List;
+
+/**
+ * Writer supporting List&lt;EntityPart&gt; Make sure {@code GenericEntity} class is used when sending the
+ * {@link EntityPart}s.
+ * @since 3.1.0
+ */
+@Produces(MediaType.MULTIPART_FORM_DATA)
+@Singleton
+public class EntityPartWriter implements MessageBodyWriter<List<EntityPart>> {
+
+    private MultiPartWriter multiPartWriter;
+
+    @Inject
+    Providers providers;
+
+    @Override
+    public boolean isWriteable(Class<?> type, Type generic, Annotation[] annotations, MediaType mediaType) {
+        return List.class.isAssignableFrom(type)
+                && ParameterizedType.class.isInstance(generic)
+                && EntityPart.class.isAssignableFrom(ReflectionHelper.getGenericTypeArgumentClasses(generic).get(0));
+    }
+
+    @Override
+    public void writeTo(List<EntityPart> entityParts, Class<?> type, Type genericType, Annotation[] annotations,
+                        MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
+            throws IOException, WebApplicationException {
+        final MultiPart multiPart = new MultiPart();
+        multiPart.setMediaType(mediaType);
+        for (EntityPart ep : entityParts) {
+            multiPart.bodyPart((BodyPart) ep);
+        }
+
+        if (multiPartWriter == null) {
+            multiPartWriter = (MultiPartWriter) providers.getMessageBodyWriter(
+                    MultiPart.class, MultiPart.class, new Annotation[0], MediaType.MULTIPART_FORM_DATA_TYPE);
+        }
+
+        multiPartWriter.writeTo(multiPart, MultiPart.class, MultiPart.class,
+                annotations, multiPart.getMediaType(), httpHeaders, entityStream);
+    }
+}
diff --git a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/FormDataParamValueParamProvider.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/FormDataParamValueParamProvider.java
index d95b320..6c834d9 100644
--- a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/FormDataParamValueParamProvider.java
+++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/FormDataParamValueParamProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2021 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
@@ -31,6 +31,8 @@
 import java.util.stream.Collectors;
 
 import jakarta.ws.rs.BadRequestException;
+import jakarta.ws.rs.FormParam;
+import jakarta.ws.rs.core.EntityPart;
 import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.MultivaluedMap;
 import jakarta.ws.rs.ext.MessageBodyReader;
@@ -59,7 +61,7 @@
 
 /**
  * Value supplier provider supporting the {@link FormDataParam} injection annotation and entity ({@link FormDataMultiPart})
- * injection.
+ * injection. Also supports {@link FormParam} {@code EntityPart} annotation injection.
  *
  * @author Craig McClanahan
  * @author Paul Sandoz
@@ -311,6 +313,43 @@
         }
     }
 
+    /**
+     * Provider supplier for list of {@link EntityPart} types injected via
+     * {@link jakarta.ws.rs.FormParam} annotation.
+     */
+    private final class ListEntityPartValueProvider extends ValueProvider<List<EntityPart>> {
+
+        private final String name;
+
+        public ListEntityPartValueProvider(final String name) {
+            this.name = name;
+        }
+
+        @Override
+        public List<EntityPart> apply(ContainerRequest request) {
+            return (List<EntityPart>) (List<?>) getEntity(request).getFields(name);
+        }
+    }
+
+    /**
+     * Provider supplier for list of {@link EntityPart} types injected via
+     * {@link jakarta.ws.rs.FormParam} annotation.
+     */
+    private final class EntityPartValueProvider extends ValueProvider<EntityPart> {
+
+        private final String name;
+
+        public EntityPartValueProvider(final String name) {
+            this.name = name;
+        }
+
+        @Override
+        public EntityPart apply(ContainerRequest request) {
+            List<FormDataBodyPart> bodyParts = getEntity(request).getFields(name);
+            return bodyParts.size() != 0 ? bodyParts.get(0) : null;
+        }
+    }
+
     private static final Set<Class<?>> TYPES = initializeTypes();
 
     private static Set<Class<?>> initializeTypes() {
@@ -344,7 +383,7 @@
      * @param extractorProvider multi-valued map parameter extractor provider.
      */
     public FormDataParamValueParamProvider(Provider<MultivaluedParameterExtractorProvider> extractorProvider) {
-        super(extractorProvider, Parameter.Source.ENTITY, Parameter.Source.UNKNOWN);
+        super(extractorProvider, Parameter.Source.ENTITY, Parameter.Source.FORM, Parameter.Source.UNKNOWN);
     }
 
     @Override
@@ -386,6 +425,16 @@
             } else {
                 return new FormDataParamValueProvider(parameter, get(parameter));
             }
+        } else if (FormParam.class.equals(parameter.getSourceAnnotation().annotationType())) {
+            final String paramName = parameter.getSourceName();
+            if (Collection.class == rawType || List.class == rawType) {
+                final Class clazz = ReflectionHelper.getGenericTypeArgumentClasses(parameter.getType()).get(0);
+                if (EntityPart.class.equals(clazz)) {
+                    return new ListEntityPartValueProvider(paramName);
+                }
+            } else if (EntityPart.class.equals(rawType)) {
+                return new EntityPartValueProvider(paramName);
+            }
         }
 
         return null;
diff --git a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/MultiPartWriter.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/MultiPartWriter.java
index 4e155f8..011096a 100644
--- a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/MultiPartWriter.java
+++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/MultiPartWriter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2021 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
@@ -30,6 +30,7 @@
 import jakarta.ws.rs.Produces;
 import jakarta.ws.rs.WebApplicationException;
 import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.GenericEntity;
 import jakarta.ws.rs.core.HttpHeaders;
 import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.MultivaluedMap;
@@ -195,14 +196,21 @@
                 bodyEntity = ((BodyPartEntity) bodyEntity).getInputStream();
             }
 
+            Type bodyType = bodyClass;
+            if (GenericEntity.class.isInstance(bodyEntity)) {
+                bodyClass = ((GenericEntity<?>) bodyEntity).getRawType();
+                bodyType = ((GenericEntity) bodyEntity).getType();
+                bodyEntity = ((GenericEntity<?>) bodyEntity).getEntity();
+            }
+
             final MessageBodyWriter bodyWriter = providers.getMessageBodyWriter(
                     bodyClass,
-                    bodyClass,
+                    bodyType,
                     EMPTY_ANNOTATIONS,
                     bodyMediaType);
 
             if (bodyWriter == null) {
-                throw new IllegalArgumentException(LocalizationMessages.NO_AVAILABLE_MBW(bodyClass, mediaType));
+                throw new IllegalArgumentException(LocalizationMessages.NO_AVAILABLE_MBW(bodyClass, bodyMediaType));
             }
 
             bodyWriter.writeTo(
diff --git a/media/multipart/src/main/resources/META-INF/services/org.glassfish.jersey.innate.spi.EntityPartBuilderProvider b/media/multipart/src/main/resources/META-INF/services/org.glassfish.jersey.innate.spi.EntityPartBuilderProvider
new file mode 100644
index 0000000..32738ac
--- /dev/null
+++ b/media/multipart/src/main/resources/META-INF/services/org.glassfish.jersey.innate.spi.EntityPartBuilderProvider
@@ -0,0 +1 @@
+org.glassfish.jersey.media.multipart.JerseyEntityPartBuilderProvider
\ No newline at end of file
diff --git a/media/multipart/src/main/resources/META-INF/services/org.glassfish.jersey.internal.spi.AutoDiscoverable b/media/multipart/src/main/resources/META-INF/services/org.glassfish.jersey.internal.spi.AutoDiscoverable
new file mode 100644
index 0000000..04a6776
--- /dev/null
+++ b/media/multipart/src/main/resources/META-INF/services/org.glassfish.jersey.internal.spi.AutoDiscoverable
@@ -0,0 +1 @@
+org.glassfish.jersey.media.multipart.MultiPartFeatureAutodiscoverable
\ No newline at end of file
diff --git a/media/multipart/src/main/resources/org/glassfish/jersey/media/multipart/internal/localization.properties b/media/multipart/src/main/resources/org/glassfish/jersey/media/multipart/internal/localization.properties
index 851c026..904214d 100644
--- a/media/multipart/src/main/resources/org/glassfish/jersey/media/multipart/internal/localization.properties
+++ b/media/multipart/src/main/resources/org/glassfish/jersey/media/multipart/internal/localization.properties
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2012, 2021 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
@@ -15,6 +15,8 @@
 #
 
 cannot.inject.file=Cannot provide file for an entity body part.
+content.has.been.already.read=Content method has already been invoked.
+entity.content.not.set=EntityPart content is not set.
 entity.has.wrong.type=Entity instance does not contain the unconverted content.
 error.parsing.content.disposition=Error parsing content disposition: {0}
 error.reading.entity=Error reading entity as {0}.
diff --git a/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/file/FileDataBodyPartTest.java b/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/file/FileDataBodyPartTest.java
index d2acefb..ca040cb 100644
--- a/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/file/FileDataBodyPartTest.java
+++ b/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/file/FileDataBodyPartTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2021 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
@@ -86,6 +86,12 @@
         fdbp.setFileEntity(file, expectedType);
         checkEntityAttributes(name, fdbp, file, expectedType);
 
+        file = new File("pom.json");
+        name = "json";
+        fdbp = new FileDataBodyPart(name, file);
+        expectedType = DefaultMediaTypePredictor.CommonMediaTypes.JSON.getMediaType();
+        checkEntityAttributes(name, fdbp, file, expectedType);
+
         file = new File("pom.png");
         name = "png";
         fdbp = new FileDataBodyPart("png", file);
diff --git a/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/internal/EntityPartTest.java b/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/internal/EntityPartTest.java
new file mode 100644
index 0000000..91e3368
--- /dev/null
+++ b/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/internal/EntityPartTest.java
@@ -0,0 +1,372 @@
+/*
+ * Copyright (c) 2021 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.media.multipart.internal;
+
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.FormParam;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.EntityPart;
+import jakarta.ws.rs.core.GenericEntity;
+import jakarta.ws.rs.core.GenericType;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.MultivaluedMap;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.ext.MessageBodyReader;
+import jakarta.ws.rs.ext.MessageBodyWriter;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.internal.util.ReflectionHelper;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.nio.charset.StandardCharsets;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class EntityPartTest extends JerseyTest {
+
+    private static final GenericType<List<EntityPart>> LIST_ENTITY_PART_TYPE = new GenericType<List<EntityPart>>(){};
+    private static final GenericType<AtomicReference<String>> ATOMIC_REFERENCE_GENERIC_TYPE = new GenericType<>(){};
+
+    @Path("/")
+    public static class EntityPartTestResource {
+        @GET
+        public Response getResponse() throws IOException {
+            List<EntityPart> list = new LinkedList<>();
+            list.add(EntityPart.withName("part-01").content("TEST1").build());
+            list.add(EntityPart.withName("part-02").content("TEST2").build());
+            GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(list) {};
+            return Response.ok(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE).build();
+        }
+
+        @POST
+        @Path("/postList")
+        public String postEntityPartList(List<EntityPart> list) throws IOException {
+            String entity = list.get(0).getContent(String.class) + list.get(1).getContent(String.class);
+            return entity;
+        }
+
+        @POST
+        @Path("/postForm")
+        public String postEntityPartForm(@FormParam("part-01") EntityPart part1, @FormParam("part-02") EntityPart part2)
+                throws IOException {
+            String entity = part1.getContent(String.class) + part2.getContent(String.class);
+            return entity;
+        }
+
+        @POST
+        @Path("/postListForm")
+        public String postEntityPartForm(@FormParam("part-0x") List<EntityPart> part)
+                throws IOException {
+            String entity = part.get(0).getContent(String.class) + part.get(1).getContent(String.class);
+            return entity;
+        }
+
+        @POST
+        @Path("/postStreams")
+        public Response postEntityStreams(@FormParam("name1") EntityPart part1,
+                                        @FormParam("name2") EntityPart part2,
+                                        @FormParam("name3") EntityPart part3) throws IOException {
+            List<EntityPart> list = new LinkedList<>();
+            list.add(EntityPart.withName(part1.getName()).fileName(part1.getFileName().get()).content(
+                    new ByteArrayInputStream(part1.getContent(String.class).getBytes(StandardCharsets.UTF_8))).build());
+            list.add(EntityPart.withName(part2.getName()).fileName(part2.getFileName().get()).content(
+                    new ByteArrayInputStream(part2.getContent(String.class).getBytes(StandardCharsets.UTF_8))).build());
+            list.add(EntityPart.withName(part3.getName()).fileName(part3.getFileName().get())
+                    .content(part3.getContent(StringHolder.class), StringHolder.class)
+                    .mediaType(part3.getMediaType()).build());
+            GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(list) {};
+            return Response.ok(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE).build();
+        }
+
+        @POST
+        @Path("/postHeaders")
+        public Response postEntityStreams(@FormParam("name1") EntityPart part1) throws IOException {
+            List<EntityPart> list = new LinkedList<>();
+            list.add(EntityPart.withName(part1.getName()).content(part1.getContent(String.class))
+                    .headers(part1.getHeaders()).build());
+            GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(list) {};
+            return Response.ok(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE).build();
+        }
+
+        @POST
+        @Path("/postGeneric")
+        public Response postGeneric(@FormParam("name1") EntityPart part1) throws IOException {
+            List<EntityPart> list = new LinkedList<>();
+            list.add(EntityPart.withName(part1.getName())
+                    .content(part1.getContent(ATOMIC_REFERENCE_GENERIC_TYPE), ATOMIC_REFERENCE_GENERIC_TYPE)
+                    .mediaType(MediaType.TEXT_PLAIN_TYPE)
+                    .build());
+            GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(list) {};
+            return Response.ok(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE).build();
+        }
+
+        @POST
+        @Path("/postFormVarious")
+        public Response postFormVarious(@FormParam("name1") EntityPart part1,
+                                        @FormParam("name2") InputStream part2,
+                                        @FormParam("name3") String part3) throws IOException {
+            List<EntityPart> list = new LinkedList<>();
+            list.add(EntityPart.withName(part1.getName())
+                    .content(part1.getContent(String.class) + new String(part2.readAllBytes()) + part3)
+                    .mediaType(MediaType.TEXT_PLAIN_TYPE)
+                    .build());
+            GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(list) {};
+            return Response.ok(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE).build();
+        }
+
+        @GET
+        @Produces(MediaType.MULTIPART_FORM_DATA)
+        @Path("/getList")
+        public List<EntityPart> getList() throws IOException {
+            List<EntityPart> list = new LinkedList<>();
+            list.add(EntityPart.withName("name1").content("data1").build());
+            return list;
+        }
+    }
+
+    public static class StringHolder extends AtomicReference<String> {
+        StringHolder(String name) {
+            set(name);
+        }
+    }
+
+    @Consumes(MediaType.TEXT_PLAIN)
+    @Produces(MediaType.TEXT_PLAIN)
+    public static class StringHolderWorker implements MessageBodyReader<StringHolder>, MessageBodyWriter<StringHolder> {
+
+        @Override
+        public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return type == StringHolder.class;
+        }
+
+        @Override
+        public StringHolder readFrom(Class<StringHolder> type, Type genericType, Annotation[] annotations,
+                                     MediaType mediaType, MultivaluedMap<String, String> httpHeaders,
+                                     InputStream entityStream) throws IOException, WebApplicationException {
+            final StringHolder holder = new StringHolder(new String(entityStream.readAllBytes()));
+            return holder;
+        }
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return type == StringHolder.class;
+        }
+
+        @Override
+        public void writeTo(StringHolder s, Class<?> type, Type genericType, Annotation[] annotations,
+                            MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
+                            OutputStream entityStream) throws IOException, WebApplicationException {
+            entityStream.write(s.get().getBytes(StandardCharsets.UTF_8));
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(EntityPartTestResource.class,
+                StringHolderWorker.class, AtomicReferenceProvider.class)
+                .property(ServerProperties.WADL_FEATURE_DISABLE, true);
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(StringHolderWorker.class).register(AtomicReferenceProvider.class);
+    }
+
+    @Test
+    public void getEntityPartListTest() throws IOException {
+        try (Response response = target().request().get()) {
+            List<EntityPart> list = response.readEntity(LIST_ENTITY_PART_TYPE);
+            Assert.assertEquals("TEST1", list.get(0).getContent(String.class));
+            Assert.assertEquals("TEST2", list.get(1).getContent(String.class));
+        }
+    }
+
+    @Test
+    public void postEntityPartListTest() throws IOException {
+        List<EntityPart> list = new LinkedList<>();
+        list.add(EntityPart.withName("part-01").content("TEST").build());
+        list.add(EntityPart.withName("part-02").content("1").build());
+        GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(list) {};
+        Entity entity = Entity.entity(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE);
+        try (Response response = target("/postList").request().post(entity)) {
+            Assert.assertEquals("TEST1", response.readEntity(String.class));
+        }
+    }
+
+    @Test
+    public void postEntityPartFormParamTest() throws IOException {
+        List<EntityPart> list = new LinkedList<>();
+        list.add(EntityPart.withName("part-01").content("TEST").build());
+        list.add(EntityPart.withName("part-02").content("1").build());
+        GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(list) {};
+        Entity entity = Entity.entity(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE);
+        try (Response response = target("/postForm").request().post(entity)) {
+            Assert.assertEquals("TEST1", response.readEntity(String.class));
+        }
+    }
+
+    @Test
+    public void postListEntityPartFormParamTest() throws IOException {
+        List<EntityPart> list = new LinkedList<>();
+        list.add(EntityPart.withName("part-0x").content("TEST").build());
+        list.add(EntityPart.withName("part-0x").content("1").build());
+        GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(list) {};
+        Entity entity = Entity.entity(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE);
+        try (Response response = target("/postListForm").request().post(entity)) {
+            Assert.assertEquals("TEST1", response.readEntity(String.class));
+        }
+    }
+
+    @Test
+    public void postEntityPartStreamsTest() throws IOException {
+        List<EntityPart> list = new LinkedList<>();
+        list.add(EntityPart.withName("name1").fileName("file1.doc").content(
+                new ByteArrayInputStream("data1".getBytes(StandardCharsets.UTF_8))).build());
+        list.add(EntityPart.withName("name2").fileName("file2.doc").content(
+                new ByteArrayInputStream("data2".getBytes(StandardCharsets.UTF_8))).build());
+        list.add(EntityPart.withName("name3").fileName("file3.xml")
+                .content(new StringHolder("data3"), StringHolder.class)
+                .mediaType(MediaType.TEXT_PLAIN_TYPE).build());
+        GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(list) {
+        };
+        Entity entity = Entity.entity(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE);
+
+        try (Response response = target("/postStreams").request().post(entity)) {
+            List<EntityPart> result = response.readEntity(LIST_ENTITY_PART_TYPE);
+
+            EntityPart part1 = result.get(0);
+            Assert.assertEquals("name1", part1.getName());
+            Assert.assertEquals("file1.doc", part1.getFileName().get());
+            Assert.assertEquals("data1", part1.getContent(String.class));
+
+            EntityPart part2 = result.get(1);
+            Assert.assertEquals("name2", part2.getName());
+            Assert.assertEquals("file2.doc", part2.getFileName().get());
+            Assert.assertEquals("data2", part2.getContent(String.class));
+
+            EntityPart part3 = result.get(2);
+            Assert.assertEquals("name3", part3.getName());
+            Assert.assertEquals("file3.xml", part3.getFileName().get());
+            Assert.assertEquals("data3", part3.getContent(String.class));
+            Assert.assertEquals(MediaType.TEXT_PLAIN_TYPE, part3.getMediaType());
+        }
+    }
+
+    @Test
+    public void postHeaderTest() throws IOException {
+        List<EntityPart> list = new LinkedList<>();
+        list.add(EntityPart.withName("name1").content("data1")
+                .header("header-01", "value-01").build());
+        GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(list) {};
+        Entity entity = Entity.entity(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE);
+        try (Response response = target("/postHeaders").request().post(entity)) {
+            List<EntityPart> result = response.readEntity(LIST_ENTITY_PART_TYPE);
+            Assert.assertEquals("value-01", result.get(0).getHeaders().getFirst("header-01"));
+            Assert.assertEquals("data1", result.get(0).getContent(String.class));
+        }
+    }
+
+    @Consumes(MediaType.TEXT_PLAIN)
+    @Produces(MediaType.TEXT_PLAIN)
+    public static class AtomicReferenceProvider implements
+            MessageBodyReader<AtomicReference<String>>,
+            MessageBodyWriter<AtomicReference<String>> {
+
+        @Override
+        public boolean isReadable(Class<?> type, Type generic, Annotation[] annotations, MediaType mediaType) {
+            return type == AtomicReference.class
+                    && ParameterizedType.class.isInstance(generic)
+                    && String.class.isAssignableFrom(ReflectionHelper.getGenericTypeArgumentClasses(generic).get(0));
+        }
+
+        @Override
+        public AtomicReference<String> readFrom(Class<AtomicReference<String>> type, Type genericType,
+                                                Annotation[] annotations, MediaType mediaType,
+                                                MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
+                throws IOException, WebApplicationException {
+            return new AtomicReference<String>(new String(entityStream.readAllBytes(), StandardCharsets.UTF_8));
+        }
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return isReadable(type, genericType, annotations, mediaType);
+        }
+
+        @Override
+        public void writeTo(AtomicReference<String> stringAtomicReference, Class<?> type, Type genericType,
+                            Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
+                            OutputStream entityStream) throws IOException, WebApplicationException {
+            entityStream.write(stringAtomicReference.get().getBytes(StandardCharsets.UTF_8));
+        }
+    }
+
+    @Test
+    public void genericEntityTest() throws IOException {
+        List<EntityPart> list = new LinkedList<>();
+        list.add(EntityPart.withName("name1")
+                .content(new AtomicReference<String>("data1"), ATOMIC_REFERENCE_GENERIC_TYPE)
+                .mediaType(MediaType.TEXT_PLAIN_TYPE)
+                .build());
+        GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(list) {};
+        Entity entity = Entity.entity(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE);
+        try (Response response = target("/postGeneric").request().post(entity)) {
+            List<EntityPart> result = response.readEntity(LIST_ENTITY_PART_TYPE);
+            Assert.assertEquals("data1", result.get(0).getContent(String.class));
+        }
+    }
+
+    @Test
+    public void postVariousTest() throws IOException {
+        List<EntityPart> list = new LinkedList<>();
+        list.add(EntityPart.withName("name1").content("Hello ").build());
+        list.add(EntityPart.withName("name2").content("world").build());
+        list.add(EntityPart.withName("name3").content("!").build());
+        GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(list) {
+        };
+        Entity entity = Entity.entity(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE);
+
+        try (Response response = target("/postFormVarious").request().post(entity)) {
+            List<EntityPart> result = response.readEntity(LIST_ENTITY_PART_TYPE);
+            Assert.assertEquals("Hello world!", result.get(0).getContent(String.class));
+        }
+    }
+
+    @Test
+    public void getListTest() throws IOException {
+        try (Response response = target("/getList").request().get()) {
+            List<EntityPart> result = response.readEntity(LIST_ENTITY_PART_TYPE);
+            Assert.assertEquals("data1", result.get(0).getContent(String.class));
+        }
+    }
+}
diff --git a/pom.xml b/pom.xml
index e55a824..39b9360 100644
--- a/pom.xml
+++ b/pom.xml
@@ -23,7 +23,7 @@
     <parent>
         <groupId>org.eclipse.ee4j</groupId>
         <artifactId>project</artifactId>
-        <version>1.0.6</version>
+        <version>1.0.7</version>
     </parent>
 
     <groupId>org.glassfish.jersey</groupId>
@@ -1527,6 +1527,11 @@
                 <artifactId>jakarta.activation-api</artifactId>
                 <version>${jakarta.activation-api.version}</version>
             </dependency>
+            <dependency>
+                <groupId>jakarta.servlet</groupId>
+                <artifactId>jakarta.servlet-api</artifactId>
+                <version>${servlet.version}</version>
+            </dependency>
 
             <dependency>
                 <groupId>com.sun.activation</groupId>
@@ -1829,17 +1834,16 @@
             <dependency>
                 <groupId>jakarta.json</groupId>
                 <artifactId>jakarta.json-api</artifactId>
+                <version>${jakarta.jsonp.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.eclipse.parsson</groupId>
+                <artifactId>parsson</artifactId>
                 <version>${jsonp.ri.version}</version>
             </dependency>
             <dependency>
-                <groupId>org.glassfish</groupId>
-                <artifactId>jakarta.json</artifactId>
-                <classifier>module</classifier>
-                <version>${jsonp.ri.version}</version>
-            </dependency>
-            <dependency>
-                <groupId>org.glassfish</groupId>
-                <artifactId>jsonp-jaxrs</artifactId>
+                <groupId>org.eclipse.parsson</groupId>
+                <artifactId>parsson-media</artifactId>
                 <version>${jsonp.jaxrs.version}</version>
             </dependency>
 
@@ -1965,6 +1969,19 @@
                 <version>6.9.6</version>
                 <scope>test</scope>
             </dependency>
+            <dependency>
+                <groupId>org.assertj</groupId>
+                <artifactId>assertj-core</artifactId>
+                <version>3.21.0</version>
+                <scope>test</scope>
+            </dependency>
+
+            <dependency>
+                <groupId>org.awaitility</groupId>
+                <artifactId>awaitility</artifactId>
+                <version>4.1.1</version>
+                <scope>test</scope>
+            </dependency>
 
             <dependency>
                 <groupId>org.hamcrest</groupId>
@@ -2062,7 +2079,7 @@
         <findbugs.glassfish.logging.validLoggerPrefixes>
             javax.enterprise
         </findbugs.glassfish.logging.validLoggerPrefixes>
-        <java.version>1.8</java.version>
+        <java.version>11</java.version>
 <!--        <jersey.repackaged.prefix>jersey.repackaged</jersey.repackaged.prefix>-->
 <!--        <netbeans.hint.license>gf-cddl-gpl</netbeans.hint.license>-->
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -2103,8 +2120,8 @@
         <hamcrest.version>1.3</hamcrest.version>
         <helidon.version>1.0.3</helidon.version>
         <xmlunit.version>1.6</xmlunit.version>
-        <hk2.osgi.version>org.glassfish.hk2.*;version="[2.5,4)"</hk2.osgi.version>
-        <hk2.jvnet.osgi.version>org.jvnet.hk2.*;version="[2.5,4)"</hk2.jvnet.osgi.version>
+        <hk2.osgi.version>org.glassfish.hk2.*;version="[3.0,4)"</hk2.osgi.version>
+        <hk2.jvnet.osgi.version>org.jvnet.hk2.*;version="[3.0,4)"</hk2.jvnet.osgi.version>
         <hk2.config.version>6.0.0</hk2.config.version>
         <httpclient.version>4.5.13</httpclient.version>
         <jackson.version>2.13.0</jackson.version>
@@ -2138,12 +2155,12 @@
         <slf4j.version>1.7.21</slf4j.version>
         <spring4.version>4.3.20.RELEASE</spring4.version>
         <spring5.version>5.1.5.RELEASE</spring5.version>
-        <surefire.version>3.0.0-M3</surefire.version>
+        <surefire.version>3.0.0-M5</surefire.version>
 
         <!-- Jakartified, eligible for CQ -->
         <weld.version>4.0.2.Final</weld.version>
         <weld3.version>3.1.7.SP1</weld3.version>
-        <validation.impl.version>7.0.0.Final</validation.impl.version>
+        <validation.impl.version>7.0.1.Final</validation.impl.version>
         <!-- END of Jakartified, eligible for CQ -->
         <xerces.version>2.11.0</xerces.version>
 
@@ -2155,36 +2172,39 @@
         <!-- Jakartified -->
         <cdi.api.version>3.0.0</cdi.api.version>
         <ejb.version>4.0.0</ejb.version>
-        <grizzly2.version>3.0.0</grizzly2.version>
+        <grizzly2.version>3.0.1</grizzly2.version>
         <grizzly.npn.version>2.0.0</grizzly.npn.version>
-        <hk2.version>3.0.1</hk2.version>
+        <hk2.version>3.0.2</hk2.version>
         <jsp.version>3.0.0</jsp.version>
         <jstl.version>2.0.0</jstl.version>
         <jta.api.version>2.0.0</jta.api.version>
         <servlet5.version>5.0.0</servlet5.version>
+        <servlet6.version>6.0.0</servlet6.version>
+        <servlet.version>5.0.0</servlet.version> <!-- Keep this until Grizzly is Servlet 6 compatible -->
         <istack.commons.runtime.version>4.0.0</istack.commons.runtime.version>
-        <jakarta.activation-api.version>2.0.0</jakarta.activation-api.version>
+        <jakarta.activation-api.version>2.0.1</jakarta.activation-api.version>
         <jakarta.activation.version>2.0.1</jakarta.activation.version>
         <jakarta.el.version>4.0.0</jakarta.el.version>
-        <jakarta.el.impl.version>4.0.0</jakarta.el.impl.version>
+        <jakarta.el.impl.version>4.0.2</jakarta.el.impl.version>
         <jakarta.annotation.osgi.version>jakarta.annotation.*;version="[2.0,3)"</jakarta.annotation.osgi.version>
         <jakarta.annotation.version>2.0.0</jakarta.annotation.version>
-        <jakarta.inject.version>2.0.0</jakarta.inject.version>
+        <jakarta.inject.version>2.0.1</jakarta.inject.version>
         <jakarta.interceptor.version>2.0.0</jakarta.interceptor.version>
+        <jakarta.jsonp.version>2.1.0</jakarta.jsonp.version>
         <jakarta.persistence.version>3.0.0</jakarta.persistence.version>
-        <jakarta.validation.api.version>3.0.0</jakarta.validation.api.version>
+        <jakarta.validation.api.version>3.0.1</jakarta.validation.api.version>
         <jakarta.jaxb.api.version>3.0.1</jakarta.jaxb.api.version>
         <jaxb.ri.version>3.0.2</jaxb.ri.version>
-        <jaxrs.api.spec.version>3.0</jaxrs.api.spec.version>
-        <jaxrs.api.impl.version>3.0.0</jaxrs.api.impl.version>
-        <jetty.version>11.0.0</jetty.version>
-        <jetty.plugin.version>11.0.0</jetty.plugin.version>
+        <jaxrs.api.spec.version>3.1</jaxrs.api.spec.version>
+        <jaxrs.api.impl.version>3.1.0</jaxrs.api.impl.version>
+        <jetty.version>11.0.7</jetty.version>
+        <jetty.plugin.version>11.0.7</jetty.plugin.version>
         <jetty.servlet.api.25.version>6.1.14</jetty.servlet.api.25.version>
         <jsonb.api.version>2.0.0</jsonb.api.version>
-        <jsonp.ri.version>2.0.0</jsonp.ri.version>
-        <jsonp.jaxrs.version>2.0.0</jsonp.jaxrs.version>
-        <moxy.version>3.0.0</moxy.version>
-        <yasson.version>2.0.1</yasson.version>
+        <jsonp.ri.version>1.0.0</jsonp.ri.version>
+        <jsonp.jaxrs.version>1.0.0</jsonp.jaxrs.version>
+        <moxy.version>3.0.2</moxy.version>
+        <yasson.version>2.0.3</yasson.version>
         <!-- END of Jakartified -->
 
         <javax.annotation.version>1.3.2</javax.annotation.version> <!--Deprecated, used only for @generated annotation in perf tests -->
diff --git a/test-framework/providers/grizzly2/pom.xml b/test-framework/providers/grizzly2/pom.xml
index 75ce20d..b610451 100644
--- a/test-framework/providers/grizzly2/pom.xml
+++ b/test-framework/providers/grizzly2/pom.xml
@@ -36,7 +36,6 @@
         <dependency>
             <groupId>jakarta.servlet</groupId>
             <artifactId>jakarta.servlet-api</artifactId>
-            <version>${servlet5.version}</version>
         </dependency>
 
         <dependency>
diff --git a/test-framework/providers/jetty/pom.xml b/test-framework/providers/jetty/pom.xml
index 399d3de..e2c1af2 100644
--- a/test-framework/providers/jetty/pom.xml
+++ b/test-framework/providers/jetty/pom.xml
@@ -44,148 +44,4 @@
         </dependency>
     </dependencies>
 
-    <properties>
-        <java8.build.outputDirectory>${project.basedir}/target</java8.build.outputDirectory>
-        <java8.sourceDirectory>${project.basedir}/src/main/java8</java8.sourceDirectory>
-        <java11.build.outputDirectory>${project.basedir}/target11</java11.build.outputDirectory>
-        <java11.sourceDirectory>${project.basedir}/src/main/java11</java11.sourceDirectory>
-    </properties>
-
-    <profiles>
-        <profile>
-            <id>JettyExclude</id>
-            <activation>
-                <jdk>1.8</jdk>
-            </activation>
-            <build>
-                <directory>${java8.build.outputDirectory}</directory>
-                <plugins>
-                    <plugin>
-                        <groupId>org.codehaus.mojo</groupId>
-                        <artifactId>build-helper-maven-plugin</artifactId>
-                        <executions>
-                            <execution>
-                                <phase>generate-sources</phase>
-                                <goals>
-                                    <goal>add-source</goal>
-                                </goals>
-                                <configuration>
-                                    <sources>
-                                        <source>${java8.sourceDirectory}</source>
-                                    </sources>
-                                </configuration>
-                            </execution>
-                        </executions>
-                    </plugin>
-                    <plugin>
-                        <groupId>org.apache.maven.plugins</groupId>
-                        <artifactId>maven-compiler-plugin</artifactId>
-                        <configuration>
-                            <testExcludes>
-                                <testExclude>org/glassfish/jersey/test/jetty/*.java</testExclude>
-                            </testExcludes>
-                        </configuration>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-        <profile>
-            <id>Jetty11</id>
-            <activation>
-                <jdk>[11,)</jdk>
-            </activation>
-            <build>
-                <directory>${java11.build.outputDirectory}</directory>
-                <plugins>
-                    <plugin>
-                        <groupId>org.codehaus.mojo</groupId>
-                        <artifactId>build-helper-maven-plugin</artifactId>
-                        <executions>
-                            <execution>
-                                <phase>generate-sources</phase>
-                                <goals>
-                                    <goal>add-source</goal>
-                                </goals>
-                                <configuration>
-                                    <sources>
-                                        <source>${java11.sourceDirectory}</source>
-                                    </sources>
-                                </configuration>
-                            </execution>
-                        </executions>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-        <profile>
-            <id>copyJDK11FilesToMultiReleaseJar</id>
-            <activation>
-                <file>
-                    <!-- ${java11.build.outputDirectory} does not work here -->
-                    <exists>target11/classes/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.class</exists>
-                </file>
-                <jdk>1.8</jdk>
-            </activation>
-            <build>
-                <plugins>
-                    <plugin>
-                        <groupId>org.apache.felix</groupId>
-                        <artifactId>maven-bundle-plugin</artifactId>
-                        <inherited>true</inherited>
-                        <extensions>true</extensions>
-                        <configuration>
-                            <instructions>
-                                <Multi-Release>true</Multi-Release>
-                            </instructions>
-                        </configuration>
-                    </plugin>
-                    <plugin>
-                        <groupId>org.apache.maven.plugins</groupId>
-                        <artifactId>maven-resources-plugin</artifactId>
-                        <inherited>true</inherited>
-                        <executions>
-                            <execution>
-                                <id>copy-jdk11-classes</id>
-                                <phase>prepare-package</phase>
-                                <goals>
-                                    <goal>copy-resources</goal>
-                                </goals>
-                                <configuration>
-                                    <outputDirectory>${java8.build.outputDirectory}/classes/META-INF/versions/11</outputDirectory>
-                                    <resources>
-                                        <resource>
-                                            <directory>${java11.build.outputDirectory}/classes</directory>
-                                        </resource>
-                                    </resources>
-                                </configuration>
-                            </execution>
-                        </executions>
-                    </plugin>
-                    <plugin>
-                        <groupId>org.apache.maven.plugins</groupId>
-                        <artifactId>maven-antrun-plugin</artifactId>
-                        <executions>
-                            <execution>
-                                <id>copy-jdk11-sources</id>
-                                <phase>package</phase>
-                                <configuration>
-                                    <target>
-                                        <property name="sources-jar" value="${java8.build.outputDirectory}/${project.artifactId}-${project.version}-sources.jar"/>
-                                        <echo>sources-jar: ${sources-jar}</echo>
-                                        <zip destfile="${sources-jar}" update="true">
-                                            <zipfileset dir="${java11.sourceDirectory}" prefix="META-INF/versions/11"/>
-                                        </zip>
-                                    </target>
-                                </configuration>
-                                <goals>
-                                    <goal>run</goal>
-                                </goals>
-                            </execution>
-                        </executions>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-    </profiles>
-
 </project>
diff --git a/test-framework/providers/jetty/src/main/java11/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java b/test-framework/providers/jetty/src/main/java/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java
similarity index 98%
rename from test-framework/providers/jetty/src/main/java11/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java
rename to test-framework/providers/jetty/src/main/java/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java
index e5dba36..2ce4577 100644
--- a/test-framework/providers/jetty/src/main/java11/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java
+++ b/test-framework/providers/jetty/src/main/java/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2021 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/test-framework/providers/jetty/src/main/java8/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java b/test-framework/providers/jetty/src/main/java8/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java
deleted file mode 100644
index cd2e332..0000000
--- a/test-framework/providers/jetty/src/main/java8/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (c) 2020 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;
-
-import jakarta.ws.rs.ProcessingException;
-import org.glassfish.jersey.jetty.internal.LocalizationMessages;
-import org.glassfish.jersey.test.DeploymentContext;
-import org.glassfish.jersey.test.spi.TestContainer;
-import org.glassfish.jersey.test.spi.TestContainerFactory;
-
-import java.net.URI;
-
-/**
- * Jetty test factory stub for JDK 1.8 only
- *
- * since Jetty 11+ does not support JDKs below 11
- */
-public class JettyTestContainerFactory implements TestContainerFactory {
-
-    @Override
-    public TestContainer create(final URI baseUri, final DeploymentContext context) throws IllegalArgumentException {
-        throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
-    }
-}
diff --git a/tests/e2e-client/pom.xml b/tests/e2e-client/pom.xml
index 036b616..51ce01e 100644
--- a/tests/e2e-client/pom.xml
+++ b/tests/e2e-client/pom.xml
@@ -188,16 +188,6 @@
 
     <profiles>
         <profile>
-            <id>jdk11+</id>
-            <activation>
-                <jdk>[11,)</jdk>
-            </activation>
-            <properties>
-                <!-- https://bugs.openjdk.java.net/browse/JDK-8211426 -->
-                <surefire.security.argline>-Djdk.tls.server.protocols=TLSv1.2</surefire.security.argline>
-            </properties>
-        </profile>
-        <profile>
             <id>xdk</id>
             <properties>
                 <!-- do not use security manager for xdk -->
@@ -224,4 +214,9 @@
         </profile>
     </profiles>
 
+    <properties>
+        <!-- https://bugs.openjdk.java.net/browse/JDK-8211426 -->
+        <surefire.security.argline>-Djdk.tls.server.protocols=TLSv1.2</surefire.security.argline>
+    </properties>
+
 </project>
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/TestRuntimeDelegate.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/TestRuntimeDelegate.java
index 5a506a2..02343f8 100644
--- a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/TestRuntimeDelegate.java
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/TestRuntimeDelegate.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2021 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
@@ -16,8 +16,10 @@
 
 package org.glassfish.jersey.tests.e2e.common;
 
+import jakarta.ws.rs.SeBootstrap;
 import jakarta.ws.rs.WebApplicationException;
 import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.EntityPart;
 import jakarta.ws.rs.core.Link;
 import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.Response;
@@ -30,6 +32,8 @@
 
 import org.junit.Assert;
 
+import java.util.concurrent.CompletionStage;
+
 /**
  * Test runtime delegate.
  *
@@ -47,6 +51,22 @@
         throw new UnsupportedOperationException("Not supported yet.");
     }
 
+    @Override
+    public SeBootstrap.Configuration.Builder createConfigurationBuilder() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public CompletionStage<SeBootstrap.Instance> bootstrap(Application application, SeBootstrap.Configuration configuration) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public CompletionStage<SeBootstrap.Instance> bootstrap(Class<? extends Application> aClass,
+                                                           SeBootstrap.Configuration configuration) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
     public void testMediaType() {
         MediaType m = new MediaType("text", "plain");
         Assert.assertNotNull(m);
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/CookiesParserTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/CookiesParserTest.java
index 767b644..80b03e0 100644
--- a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/CookiesParserTest.java
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/CookiesParserTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2021 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
@@ -37,14 +37,18 @@
 
     @Test
     public void testCaseInsensitiveNewCookieParams() throws Exception {
-        _testCaseInsensitiveNewCookieParams("expires", "max-age", "path", "domain", "comment", "version", "secure", "httponly");
-        _testCaseInsensitiveNewCookieParams("Expires", "Max-Age", "Path", "Domain", "Comment", "Version", "Secure", "HttpOnly");
-        _testCaseInsensitiveNewCookieParams("exPires", "max-aGe", "patH", "doMAin", "Comment", "vErsion", "secuRe", "httPonly");
+        _testCaseInsensitiveNewCookieParams("expires", "max-age", "path", "domain",
+                "comment", "version", "secure", "httponly", "samesite");
+        _testCaseInsensitiveNewCookieParams("Expires", "Max-Age", "Path", "Domain",
+                "Comment", "Version", "Secure", "HttpOnly", "SameSite");
+        _testCaseInsensitiveNewCookieParams("exPires", "max-aGe", "patH", "doMAin",
+                "Comment", "vErsion", "secuRe", "httPonly", "samEsite");
     }
 
     private void _testCaseInsensitiveNewCookieParams(final String expires, final String maxAge, final String path,
                                                      final String domain, final String comment, final String version,
-                                                     final String secure, final String httpOnly) throws Exception {
+                                                     final String secure, final String httpOnly, final String sameSite)
+            throws Exception {
 
         final String header = "foo=bar;"
                 + expires + "=Tue, 15 Jan 2013 21:47:38 GMT;"
@@ -54,7 +58,8 @@
                 + comment + "=Testing;"
                 + version + "=1;"
                 + secure + ";"
-                + httpOnly;
+                + httpOnly + ";"
+                + sameSite + "=STRICT";
 
         final NewCookie cookie = CookiesParser.parseNewCookie(header);
 
@@ -69,5 +74,6 @@
         assertThat(cookie.getVersion(), equalTo(1));
         assertThat(cookie.isSecure(), is(true));
         assertThat(cookie.isHttpOnly(), is(true));
+        assertThat(cookie.getSameSite(), equalTo(NewCookie.SameSite.STRICT));
     }
 }
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/AsyncResponseTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/AsyncResponseTest.java
index 5d129ae..b58da21 100644
--- a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/AsyncResponseTest.java
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/AsyncResponseTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2021 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
@@ -46,6 +46,7 @@
 import org.glassfish.jersey.test.JerseyTest;
 import org.glassfish.jersey.test.TestProperties;
 
+import org.junit.Ignore;
 import org.junit.Test;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.instanceOf;
@@ -105,6 +106,7 @@
     }
 
     @Test
+    @Ignore("since 3.1 nothing is being thrown")
     public void testResumeRuntimeException() throws Exception {
         testResumeException("resumeRuntimeException", null);
 
@@ -112,6 +114,7 @@
     }
 
     @Test
+    @Ignore("since 3.1 nothing is being thrown")
     public void testResumeCheckedException() throws Exception {
         testResumeException("resumeCheckedException", null);
 
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExceptionLoggingTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExceptionLoggingTest.java
index 6b50666..2e93185 100644
--- a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExceptionLoggingTest.java
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExceptionLoggingTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2021 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
@@ -76,14 +76,12 @@
     public void testRuntime() throws Exception {
         final Response response = target().path("runtime").request().get();
         assertEquals(500, response.getStatus());
-        assertEquals(getLastLoggedRecord().getThrown().getClass(), MyRuntimeException.class);
     }
 
     @Test
     public void testChecked() throws Exception {
         final Response response = target().path("checked").request().get();
         assertEquals(500, response.getStatus());
-        assertEquals(getLastLoggedRecord().getThrown().getClass(), MyCheckedException.class);
     }
 
     @Provider
@@ -123,8 +121,6 @@
     public void testReaderFails() throws Exception {
         final Response response = target().path("resource/entity").request().get();
         assertEquals(500, response.getStatus());
-
-        assertEquals(getLastLoggedRecord().getThrown().getMessage(), "test");
     }
 
     static class ExceptionLoggingTestPOJO {
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExceptionMapperTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExceptionMapperTest.java
index 6e0389f..eecbfdb 100644
--- a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExceptionMapperTest.java
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExceptionMapperTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2021 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
@@ -137,6 +137,13 @@
         assertEquals(500, res.getStatus());
     }
 
+    @Test
+    public void testDefaultExceptionMapper() {
+        Response res = target().path("test/defaultExceptionMapper")
+                .request("test/test").get();
+        assertEquals(200, res.getStatus());
+    }
+
     @Path("test")
     public static class Resource {
 
@@ -174,6 +181,14 @@
                     new RuntimeException("runtime-exception",
                             new ClientErrorException("client-error", 499)));
         }
+
+        @GET
+        @Path("defaultExceptionMapper")
+        public Response isRegisteredDefaultExceptionTest(@Context Providers providers) {
+            ExceptionMapper<Throwable> em = providers
+                    .getExceptionMapper(Throwable.class);
+            return Response.status((em == null) ? 500 : 200).build();
+        }
     }
 
     public static class ClientErrorExceptionMapper implements ExceptionMapper<ClientErrorException> {
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExtendedExceptionMapperTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExtendedExceptionMapperTest.java
index 1d840e3..2b53c60 100644
--- a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExtendedExceptionMapperTest.java
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExtendedExceptionMapperTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2021 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
@@ -278,8 +278,8 @@
                     LocalizationMessages.ERROR_EXCEPTION_MAPPING_ORIGINAL_EXCEPTION(),
                     LocalizationMessages.ERROR_EXCEPTION_MAPPING_THROWN_TO_CONTAINER()}) {
 
-                if (logRecord.getMessage().contains(message) && logRecord.getLevel().intValue() > Level.FINE.intValue()) {
-                    fail("Log message should be logged at lower (FINE) level: " + message);
+                if (logRecord.getMessage().contains(message) && logRecord.getLevel().intValue() > Level.WARNING.intValue()) {
+                    fail("Log message should be logged at lower (WARNING) level: " + message);
                 }
             }
         }
diff --git a/tests/e2e/pom.xml b/tests/e2e/pom.xml
index a6ecdd3..85d2e40 100644
--- a/tests/e2e/pom.xml
+++ b/tests/e2e/pom.xml
@@ -205,26 +205,6 @@
             </properties>
         </profile>
         <profile>
-            <id>JettyExclude</id>
-            <activation>
-                <jdk>1.8</jdk>
-            </activation>
-            <build>
-                <plugins>
-                    <plugin>
-                        <groupId>org.apache.maven.plugins</groupId>
-                        <artifactId>maven-compiler-plugin</artifactId>
-                        <configuration>
-                            <testExcludes>
-                                <testExclude>org/glassfish/jersey/tests/e2e/container/Jersey2462Test.java</testExclude>
-                                <testExclude>org/glassfish/jersey/tests/e2e/container/JettyEmptyHeaderParamTest.java</testExclude>
-                            </testExcludes>
-                        </configuration>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-        <profile>
             <id>sonar</id>
             <build>
                 <pluginManagement>
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/CookieImplTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/CookieImplTest.java
index 1d8c7de..a5f04a7 100644
--- a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/CookieImplTest.java
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/CookieImplTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2021 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
@@ -187,6 +187,12 @@
         cookie = new NewCookie("fred", "flintstone", null, null, "a modern stonage family", 60, false);
         expResult = "fred=flintstone;Version=1;Comment=\"a modern stonage family\";Max-Age=60";
         assertEquals(expResult, cookie.toString());
+
+        cookie = new NewCookie("fred", "flintstone", null, null, 1,
+                "a modern stonage family", 60, null, false, false,
+                NewCookie.SameSite.STRICT);
+        expResult = "fred=flintstone;Version=1;Comment=\"a modern stonage family\";Max-Age=60;SameSite=STRICT";
+        assertEquals(expResult, cookie.toString());
     }
 
     @Test
@@ -209,6 +215,16 @@
         assertEquals(1, cookie.getVersion());
         assertEquals(60, cookie.getMaxAge());
         assertTrue(cookie.isSecure());
+
+        cookie = NewCookie.valueOf(
+                "fred=flintstone;Version=1;Comment=\"a modern stonage family\";Max-Age=60;Secure;SameSite=NONE");
+        assertEquals("fred", cookie.getName());
+        assertEquals("flintstone", cookie.getValue());
+        assertEquals("a modern stonage family", cookie.getComment());
+        assertEquals(1, cookie.getVersion());
+        assertEquals(60, cookie.getMaxAge());
+        assertTrue(cookie.isSecure());
+        assertEquals(NewCookie.SameSite.NONE, cookie.getSameSite());
     }
 
 }
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/LeadingSlashesTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/LeadingSlashesTest.java
index 18de2d6..9af6cb5 100644
--- a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/LeadingSlashesTest.java
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/LeadingSlashesTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2021 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
@@ -31,6 +31,7 @@
 import org.glassfish.jersey.server.ResourceConfig;
 import org.glassfish.jersey.server.ServerProperties;
 
+import org.glassfish.jersey.test.jetty.JettyTestContainerFactory;
 import org.junit.Test;
 import static org.junit.Assert.assertNotEquals;
 
@@ -118,12 +119,19 @@
 
     @Test
     public void testSlashesWithBeginningEmptyPathParam() {
+        if (JettyTestContainerFactory.class.isInstance(factory)) {
+            return; // since Jetty 11.0.5
+        }
         Response result = call("///test");
         assertEquals("-", result.readEntity(String.class));
     }
 
     @Test
     public void testSlashesWithBeginningEmptyPathParamWithQueryParams() {
+        if (JettyTestContainerFactory.class.isInstance(factory)) {
+            return; // since Jetty 11.0.5
+        }
+
         URI hostPort = UriBuilder.fromUri("http://localhost/").port(getPort()).build();
         WebTarget target = client().target(hostPort).path("///testParams")
                 .queryParam("bar", "Container")
@@ -135,6 +143,9 @@
 
     @Test
     public void testEncodedQueryParams() {
+        if (JettyTestContainerFactory.class.isInstance(factory)) {
+            return; // Jetty 11.0.5
+        }
         URI hostPort = UriBuilder.fromUri("http://localhost/").port(getPort()).build();
         WebTarget target = client().target(hostPort).path("///encoded")
                 .queryParam("query", "%dummy23+a");
diff --git a/tests/integration/async-jersey-filter/pom.xml b/tests/integration/async-jersey-filter/pom.xml
index 1c46a01..4db6da5 100644
--- a/tests/integration/async-jersey-filter/pom.xml
+++ b/tests/integration/async-jersey-filter/pom.xml
@@ -49,7 +49,6 @@
         <dependency>
             <groupId>jakarta.servlet</groupId>
             <artifactId>jakarta.servlet-api</artifactId>
-            <version>${servlet5.version}</version>
             <scope>provided</scope>
         </dependency>
     </dependencies>
diff --git a/tests/integration/cdi-integration/cdi-multimodule/war1/pom.xml b/tests/integration/cdi-integration/cdi-multimodule/war1/pom.xml
index 3e155e6..a15f2f5 100644
--- a/tests/integration/cdi-integration/cdi-multimodule/war1/pom.xml
+++ b/tests/integration/cdi-integration/cdi-multimodule/war1/pom.xml
@@ -40,7 +40,6 @@
         <dependency>
             <groupId>jakarta.servlet</groupId>
             <artifactId>jakarta.servlet-api</artifactId>
-            <version>${servlet5.version}</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
diff --git a/tests/integration/cdi-integration/cdi-resource-with-at-context/pom.xml b/tests/integration/cdi-integration/cdi-resource-with-at-context/pom.xml
index 190ca83..4ebaa21 100644
--- a/tests/integration/cdi-integration/cdi-resource-with-at-context/pom.xml
+++ b/tests/integration/cdi-integration/cdi-resource-with-at-context/pom.xml
@@ -51,7 +51,6 @@
         <dependency>
             <groupId>jakarta.servlet</groupId>
             <artifactId>jakarta.servlet-api</artifactId>
-            <version>${servlet5.version}</version>
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.test-framework.providers</groupId>
diff --git a/tests/integration/cdi-integration/context-inject-on-server/pom.xml b/tests/integration/cdi-integration/context-inject-on-server/pom.xml
index 07b0452..5fd8561 100644
--- a/tests/integration/cdi-integration/context-inject-on-server/pom.xml
+++ b/tests/integration/cdi-integration/context-inject-on-server/pom.xml
@@ -49,7 +49,6 @@
         <dependency>
             <groupId>jakarta.servlet</groupId>
             <artifactId>jakarta.servlet-api</artifactId>
-            <version>${servlet5.version}</version>
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.test-framework.providers</groupId>
diff --git a/tests/integration/ejb-test-webapp/pom.xml b/tests/integration/ejb-test-webapp/pom.xml
index 756b2e8..42cd393 100644
--- a/tests/integration/ejb-test-webapp/pom.xml
+++ b/tests/integration/ejb-test-webapp/pom.xml
@@ -40,7 +40,6 @@
         <dependency>
             <groupId>jakarta.servlet</groupId>
             <artifactId>jakarta.servlet-api</artifactId>
-            <version>${servlet5.version}</version>
             <scope>provided</scope>
         </dependency>
          <dependency>
diff --git a/tests/integration/j-59/war/pom.xml b/tests/integration/j-59/war/pom.xml
index 8dfc6fc..51914ce 100644
--- a/tests/integration/j-59/war/pom.xml
+++ b/tests/integration/j-59/war/pom.xml
@@ -65,6 +65,11 @@
             <artifactId>hamcrest-library</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>jakarta.jws</groupId>
+            <artifactId>jakarta.jws-api</artifactId>
+            <version>1.1.1</version>
+        </dependency>
     </dependencies>
 
     <build>
@@ -96,22 +101,6 @@
         </plugins>
     </build>
 
-    <profiles>
-        <profile>
-            <id>jdk11+</id>
-            <activation>
-                <jdk>[11,)</jdk>
-            </activation>
-            <dependencies>
-              <dependency>
-                  <groupId>jakarta.jws</groupId>
-                  <artifactId>jakarta.jws-api</artifactId>
-                  <version>1.1.1</version>
-              </dependency>
-            </dependencies>
-        </profile>
-    </profiles>
-
     <properties>
         <failOnMissingWebXml>false</failOnMissingWebXml>
         <skipTests>true</skipTests>
diff --git a/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176App.java b/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176App.java
index 4bc3f0c..bff0fe2 100644
--- a/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176App.java
+++ b/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176App.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2021 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
@@ -34,6 +34,7 @@
         property(ServerProperties.RESPONSE_SET_STATUS_OVER_SEND_ERROR, setStatusOverSendError);
         register(MyWriterInterceptor.class);
         register(Issue2176ReproducerResource.class);
+        register(Jersey2176ExceptionMapper.class);
     }
 
     public boolean isSetStatusOverSendError() {
diff --git a/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176ExceptionMapper.java b/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176ExceptionMapper.java
new file mode 100644
index 0000000..3a97c3d
--- /dev/null
+++ b/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176ExceptionMapper.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2021 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.tests.integration.jersey2176;
+
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.ext.ExceptionMapper;
+
+public class Jersey2176ExceptionMapper implements ExceptionMapper<Throwable> {
+    @Override
+    public Response toResponse(Throwable exception) {
+        if (exception instanceof WebApplicationException) {
+            return ((WebApplicationException) exception).getResponse();
+        }
+        return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(exception).build();
+    }
+}
diff --git a/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/TraceResponseFilter.java b/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/TraceResponseFilter.java
index ddb6b60..dadc0cd 100644
--- a/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/TraceResponseFilter.java
+++ b/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/TraceResponseFilter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2021 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
@@ -61,6 +61,9 @@
         } catch (final Throwable th) {
             status = "FAIL";
         } finally {
+            if (((HttpServletResponse) response).getStatus() == 500) {
+                status = "FAIL";
+            }
             final long duration = System.nanoTime() - startTime;
             ((HttpServletResponse) response).addHeader(X_SERVER_DURATION_HEADER, String.valueOf(duration));
             ((HttpServletResponse) response).addHeader(X_STATUS_HEADER, status);
diff --git a/tests/integration/jersey-2322/pom.xml b/tests/integration/jersey-2322/pom.xml
index 4b9713d..309f71f 100644
--- a/tests/integration/jersey-2322/pom.xml
+++ b/tests/integration/jersey-2322/pom.xml
@@ -58,14 +58,6 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
-                <configuration>
-                    <excludes>
-                        <exclude>**/*</exclude>
-                    </excludes>
-                    <testExcludes>
-                        <testExclude>**/*</testExclude>
-                    </testExcludes>
-                </configuration>
             </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
diff --git a/tests/integration/pom.xml b/tests/integration/pom.xml
index 19aa781..dc72108 100644
--- a/tests/integration/pom.xml
+++ b/tests/integration/pom.xml
@@ -34,21 +34,55 @@
 
     <modules>
         <module>asm</module>
+        <module>async-jersey-filter</module>
         <module>cdi-integration</module>
         <module>client-connector-provider</module>
         <module>ejb-multimodule</module>
         <module>ejb-multimodule-reload</module>
         <module>ejb-test-webapp</module>
+        <module>externalproperties</module>
+        <module>jaxrs-component-inject</module>
         <module>j-376</module>
         <module>j-441</module>
         <module>j-59</module>
+        <module>jersey-780</module>
+        <module>jersey-1107</module>
+        <module>jersey-1223</module>
+        <module>jersey-1604</module>
+        <module>jersey-1667</module>
+        <!--                <module>jersey-1829</module> Jakartification-->
+        <module>jersey-1883</module>
+        <module>jersey-1928</module>
+        <module>jersey-1960</module>
+        <module>jersey-1964</module>
+        <module>jersey-2031</module>
         <module>jersey-2136</module>
         <module>jersey-2137</module>
         <module>jersey-2154</module>
+        <module>jersey-2160</module>
+        <module>jersey-2164</module>
+        <module>jersey-2167</module>
+        <module>jersey-2176</module>
+        <module>jersey-2184</module>
+        <module>jersey-2255</module>
+        <module>jersey-2322</module>
+        <module>jersey-2335</module>
         <module>jersey-2421</module>
+        <module>jersey-2551</module>
+        <module>jersey-2612</module>
+        <module>jersey-2637</module>
+        <module>jersey-2654</module>
+        <module>jersey-2673</module>
+        <module>jersey-2689</module>
+        <module>jersey-2704</module>
         <module>jersey-2776</module>
+        <module>jersey-2794</module>
+        <module>jersey-2846</module>
+        <module>jersey-2878</module>
+        <module>jersey-2892</module>
         <module>jersey-3662</module>
         <module>jersey-3670</module>
+        <module>jersey-3796</module>
         <module>jersey-3992</module>
         <module>jersey-4003</module>
         <module>jersey-4099</module>
@@ -57,31 +91,64 @@
         <module>jersey-4542</module>
         <module>jersey-4697</module>
         <module>jersey-4722</module>
+        <module>jersey-4949</module>
         <module>microprofile</module>
-<!--        <module>spring4</module>-->
-<!--        <module>spring5</module>-->
+        <module>property-check</module>
         <module>reactive-streams</module>
+        <module>security-digest</module>
+        <module>servlet-2.5-autodiscovery-1</module>
+        <module>servlet-2.5-autodiscovery-2</module>
+        <module>servlet-2.5-filter</module>
+        <module>servlet-2.5-inflector-1</module>
+        <module>servlet-2.5-init-1</module>
+        <module>servlet-2.5-init-2</module>
+        <module>servlet-2.5-init-3</module>
+        <module>servlet-2.5-init-4</module>
+        <module>servlet-2.5-init-5</module>
+        <module>servlet-2.5-init-6</module>
+        <module>servlet-2.5-init-7</module>
+        <module>servlet-2.5-init-8</module>
+        <module>servlet-2.5-mvc-1</module>
+        <module>servlet-2.5-mvc-2</module>
+        <module>servlet-2.5-mvc-3</module>
+        <module>servlet-2.5-reload</module>
+        <module>servlet-3-async</module>
+        <module>servlet-3-chunked-io</module>
+        <module>servlet-3-filter</module>
+        <module>servlet-3-gf-async</module>
+        <module>servlet-3-inflector-1</module>
+        <module>servlet-3-init-1</module>
+        <module>servlet-3-init-2</module>
+        <module>servlet-3-init-3</module>
+        <module>servlet-3-init-4</module>
+        <module>servlet-3-init-5</module>
+        <module>servlet-3-init-6</module>
+        <module>servlet-3-init-7</module>
+        <module>servlet-3-init-8</module>
+        <module>servlet-3-init-9</module>
+        <module>servlet-3-init-provider</module>
+        <module>servlet-3-params</module>
+        <module>servlet-3-sse-1</module>
+        <module>servlet-4.0-mvc-1</module>
+        <module>servlet-tests</module>
+        <module>servlet-request-wrapper-binding</module>
+        <module>servlet-request-wrapper-binding-2</module>
+        <!--        <module>spring4</module>-->
+        <!--        <module>spring5</module>-->
+        <module>sonar-test</module>
+        <module>thin-server</module>
+        <module>tracing-support</module>
     </modules>
 
+    <properties>
+        <env>default</env>
+        <jersey.config.test.container.port>9998</jersey.config.test.container.port>
+        <jetty.log.file>${project.build.directory}/jetty-out.log</jetty.log.file>
+    </properties>
+
     <profiles>
         <profile>
-            <id>default</id>
-            <properties>
-                <env>default</env>
-                <jersey.config.test.container.port>9998</jersey.config.test.container.port>
-            </properties>
-            <activation>
-                <jdk>[1.8,)</jdk>
-<!--                <activeByDefault>true</activeByDefault> does not work ?!-->
-            </activation>
-        </profile>
-        <profile>
             <id>sonar</id>
-            <properties>
-                <env>default</env>
-                <jersey.config.test.container.port>9998</jersey.config.test.container.port>
-                <jetty.log.file>${project.build.directory}/jetty-out.log</jetty.log.file>
-            </properties>
             <build>
                 <pluginManagement>
                     <plugins>
@@ -122,90 +189,6 @@
                 </pluginManagement>
             </build>
         </profile>
-        <profile>
-            <id>Jetty11</id>
-            <activation>
-                <jdk>[11,)</jdk>
-            </activation>
-            <modules>
-                <module>async-jersey-filter</module>
-                <module>externalproperties</module>
-                <module>jaxrs-component-inject</module>
-                <module>jersey-780</module>
-                <module>jersey-1107</module>
-                <module>jersey-1223</module>
-                <module>jersey-1604</module>
-                <module>jersey-1667</module>
-<!--                <module>jersey-1829</module> Jakartification-->
-                <module>jersey-1883</module>
-                <module>jersey-1928</module>
-                <module>jersey-1960</module>
-                <module>jersey-1964</module>
-                <module>jersey-2031</module>
-                <module>jersey-2160</module>
-                <module>jersey-2164</module>
-                <module>jersey-2167</module>
-                <module>jersey-2176</module>
-                <module>jersey-2184</module>
-                <module>jersey-2255</module>
-                <module>jersey-2322</module>
-                <module>jersey-2335</module>
-                <module>jersey-2551</module>
-                <module>jersey-2612</module>
-                <module>jersey-2637</module>
-                <module>jersey-2654</module>
-                <module>jersey-2673</module>
-                <module>jersey-2689</module>
-                <module>jersey-2704</module>
-                <module>jersey-2794</module>
-                <module>jersey-2846</module>
-                <module>jersey-2878</module>
-                <module>jersey-2892</module>
-                <module>jersey-3796</module>
-                <module>jersey-4949</module>
-                <module>property-check</module>
-                <module>security-digest</module>
-                <module>servlet-2.5-autodiscovery-1</module>
-                <module>servlet-2.5-autodiscovery-2</module>
-                <module>servlet-2.5-filter</module>
-                <module>servlet-2.5-inflector-1</module>
-                <module>servlet-2.5-init-1</module>
-                <module>servlet-2.5-init-2</module>
-                <module>servlet-2.5-init-3</module>
-                <module>servlet-2.5-init-4</module>
-                <module>servlet-2.5-init-5</module>
-                <module>servlet-2.5-init-6</module>
-                <module>servlet-2.5-init-7</module>
-                <module>servlet-2.5-init-8</module>
-                <module>servlet-2.5-mvc-1</module>
-                <module>servlet-2.5-mvc-2</module>
-                <module>servlet-2.5-mvc-3</module>
-                <module>servlet-2.5-reload</module>
-                <module>servlet-3-async</module>
-                <module>servlet-3-chunked-io</module>
-                <module>servlet-3-filter</module>
-                <module>servlet-3-gf-async</module>
-                <module>servlet-3-inflector-1</module>
-                <module>servlet-3-init-1</module>
-                <module>servlet-3-init-2</module>
-                <module>servlet-3-init-3</module>
-                <module>servlet-3-init-4</module>
-                <module>servlet-3-init-5</module>
-                <module>servlet-3-init-6</module>
-                <module>servlet-3-init-7</module>
-                <module>servlet-3-init-8</module>
-                <module>servlet-3-init-9</module>
-                <module>servlet-3-init-provider</module>
-                <module>servlet-3-params</module>
-                <module>servlet-3-sse-1</module>
-                <module>servlet-4.0-mvc-1</module>
-                <module>servlet-tests</module>
-                <module>servlet-request-wrapper-binding</module>
-                <module>servlet-request-wrapper-binding-2</module>
-                <module>sonar-test</module>
-                <module>tracing-support</module>
-            </modules>
-        </profile>
     </profiles>
 
     <build>
diff --git a/tests/integration/servlet-4.0-mvc-1/pom.xml b/tests/integration/servlet-4.0-mvc-1/pom.xml
index 7cca8ac..ec38922 100644
--- a/tests/integration/servlet-4.0-mvc-1/pom.xml
+++ b/tests/integration/servlet-4.0-mvc-1/pom.xml
@@ -51,6 +51,14 @@
             <artifactId>jakarta.servlet-api</artifactId>
             <version>${servlet5.version}</version>
         </dependency>
+        <dependency>
+            <groupId>jakarta.xml.bind</groupId>
+            <artifactId>jakarta.xml.bind-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.xml.bind</groupId>
+            <artifactId>jaxb-osgi</artifactId>
+        </dependency>
 
         <dependency>
             <groupId>org.glassfish.jersey.test-framework.providers</groupId>
@@ -91,24 +99,4 @@
             </plugin>
         </plugins>
     </build>
-
-    <profiles>
-        <profile>
-            <id>jdk11+</id>
-            <activation>
-                <jdk>[11,)</jdk>
-            </activation>
-            <dependencies>
-                <dependency>
-                    <groupId>jakarta.xml.bind</groupId>
-                    <artifactId>jakarta.xml.bind-api</artifactId>
-                </dependency>
-                <dependency>
-                    <groupId>com.sun.xml.bind</groupId>
-                    <artifactId>jaxb-osgi</artifactId>
-                </dependency>
-            </dependencies>
-        </profile>
-    </profiles>
-
 </project>
diff --git a/tests/integration/thin-server/pom.xml b/tests/integration/thin-server/pom.xml
new file mode 100644
index 0000000..4a26ef0
--- /dev/null
+++ b/tests/integration/thin-server/pom.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2022 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>project</artifactId>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <version>3.1.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>thin-server</artifactId>
+    <name>jersey-thin-server</name>
+    <description>
+        Run server without HTTP stack in tests.
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-server</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-client</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework</groupId>
+            <artifactId>jersey-test-framework-util</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-library</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java b/tests/integration/thin-server/src/main/java/org.glassfish.jersey.integration.thinserver/ThinServerResource.java
similarity index 65%
copy from core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java
copy to tests/integration/thin-server/src/main/java/org.glassfish.jersey.integration.thinserver/ThinServerResource.java
index dd25372..9cb3cb7 100644
--- a/core-common/src/main/java8/org/glassfish/jersey/internal/jsr166/JerseyFlowSubscriber.java
+++ b/tests/integration/thin-server/src/main/java/org.glassfish.jersey.integration.thinserver/ThinServerResource.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022 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
@@ -14,7 +14,16 @@
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
  */
 
-package org.glassfish.jersey.internal.jsr166;
+package org.glassfish.jersey.integration.thinserver;
 
-public interface JerseyFlowSubscriber<T> extends Flow.Subscriber<T> {
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+
+@Path("/")
+public class ThinServerResource {
+    @GET
+    @Path("someget")
+    public String get() {
+        return ThinServerResource.class.getName();
+    }
 }
diff --git a/tests/integration/thin-server/src/test/java/org/glassfish/jersey/integration/thinserver/ThinServerTest.java b/tests/integration/thin-server/src/test/java/org/glassfish/jersey/integration/thinserver/ThinServerTest.java
new file mode 100644
index 0000000..c64bce5
--- /dev/null
+++ b/tests/integration/thin-server/src/test/java/org/glassfish/jersey/integration/thinserver/ThinServerTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2022 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.integration.thinserver;
+
+import jakarta.ws.rs.HttpMethod;
+import jakarta.ws.rs.core.Response;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.message.internal.OutboundJaxrsResponse;
+import org.glassfish.jersey.message.internal.OutboundMessageContext;
+import org.glassfish.jersey.server.ApplicationHandler;
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.ContainerResponse;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.util.server.ContainerRequestBuilder;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.net.URI;
+import java.util.concurrent.ExecutionException;
+
+public class ThinServerTest {
+    @Test
+    public void testGet() throws ExecutionException, InterruptedException {
+        ContainerRequest request =
+                ContainerRequestBuilder.from(URI.create("/someget"), HttpMethod.GET, new ClientConfig()).build();
+
+        ApplicationHandler applicationHandler = new ApplicationHandler(new ResourceConfig(ThinServerResource.class));
+        ContainerResponse containerResponse = applicationHandler.apply(request).get();
+        OutboundMessageContext outboundMessageContext = containerResponse.getWrappedMessageContext();
+        Response response = new OutboundJaxrsResponse(containerResponse.getStatusInfo(), outboundMessageContext);
+        Assert.assertEquals(ThinServerResource.class.getName(), response.getEntity());
+    }
+}
diff --git a/tests/jmockit/src/test/java/org/glassfish/jersey/tests/jmockit/server/WebServerFactoryTest.java b/tests/jmockit/src/test/java/org/glassfish/jersey/tests/jmockit/server/WebServerFactoryTest.java
new file mode 100644
index 0000000..2831134
--- /dev/null
+++ b/tests/jmockit/src/test/java/org/glassfish/jersey/tests/jmockit/server/WebServerFactoryTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018 Markus KARG. 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.tests.jmockit.server;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.theInstance;
+import static org.junit.Assert.assertThat;
+
+import java.util.Iterator;
+
+import jakarta.ws.rs.core.Application;
+
+import org.glassfish.jersey.internal.ServiceFinder;
+import org.glassfish.jersey.internal.ServiceFinder.ServiceIteratorProvider;
+import org.glassfish.jersey.internal.guava.Iterators;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.inject.InjectionManagerFactory;
+import org.glassfish.jersey.server.JerseySeBootstrapConfiguration;
+import org.glassfish.jersey.server.WebServerFactory;
+import org.glassfish.jersey.server.spi.WebServer;
+import org.glassfish.jersey.server.spi.WebServerProvider;
+import org.junit.After;
+import org.junit.Test;
+
+import mockit.Mocked;
+
+/**
+ * Unit tests for {@link WebServerFactory}.
+ *
+ * @author Markus KARG (markus@headcrashing.eu)
+ * @since 3.1.0
+ */
+public final class WebServerFactoryTest {
+
+    @Test
+    public final void shouldBuildServer(@Mocked final Application mockApplication,
+                                        @Mocked final WebServer mockServer,
+                                        @Mocked final JerseySeBootstrapConfiguration mockConfiguration,
+                                        @Mocked final InjectionManager mockInjectionManager) {
+        // given
+        ServiceFinder.setIteratorProvider(new ServiceIteratorProvider() {
+            @Override
+            public final <T> Iterator<T> createIterator(final Class<T> service, final String serviceName,
+                    final ClassLoader loader, final boolean ignoreOnClassNotFound) {
+                return Iterators.singletonIterator(service.cast(
+                        service == WebServerProvider.class ? new WebServerProvider() {
+                            @Override
+                            public final <U extends WebServer> U createServer(
+                                    final Class<U> type,
+                                    final Application application,
+                                    final JerseySeBootstrapConfiguration configuration) {
+                                return application == mockApplication && configuration == mockConfiguration
+                                        ? type.cast(mockServer)
+                                        : null;
+                            }
+
+                            @Override
+                            public <T extends WebServer> T createServer(
+                                    final Class<T> type,
+                                    final Class<? extends Application> applicationClass,
+                                    final JerseySeBootstrapConfiguration configuration) {
+                                return null;
+                            }
+                        }
+                                : service == InjectionManagerFactory.class ? new InjectionManagerFactory() {
+                            @Override
+                            public final InjectionManager create(final Object parent) {
+                                return mockInjectionManager;
+                            }
+                        }
+                                : null));
+            }
+
+            @Override
+            public final <T> Iterator<Class<T>> createClassIterator(final Class<T> service, final String serviceName,
+                    final ClassLoader loader, final boolean ignoreOnClassNotFound) {
+                return null;
+            }
+        });
+
+        // when
+        final WebServer server = WebServerFactory.createServer(WebServer.class, mockApplication, mockConfiguration);
+
+        // then
+        assertThat(server, is(theInstance(mockServer)));
+    }
+
+    @After
+    public final void resetServiceFinder() {
+        ServiceFinder.setIteratorProvider(null);
+    }
+
+}
diff --git a/tests/osgi/functional/pom.xml b/tests/osgi/functional/pom.xml
index 23317ba..b72e906 100644
--- a/tests/osgi/functional/pom.xml
+++ b/tests/osgi/functional/pom.xml
@@ -207,7 +207,6 @@
             <scope>test</scope>
             <version>${httpclient.version}</version>
         </dependency>
-
         <dependency>
             <groupId>org.glassfish.jersey.media</groupId>
             <artifactId>jersey-media-json-jackson</artifactId>
@@ -309,14 +308,13 @@
             <artifactId>jakarta.json-api</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.glassfish</groupId>
-            <artifactId>jakarta.json</artifactId>
-            <classifier>module</classifier>
+            <groupId>org.eclipse.parsson</groupId>
+            <artifactId>parsson</artifactId>
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>org.glassfish</groupId>
-            <artifactId>jsonp-jaxrs</artifactId>
+            <groupId>org.eclipse.parsson</groupId>
+            <artifactId>parsson-media</artifactId>
             <scope>test</scope>
         </dependency>
         <dependency>
diff --git a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonProcessingTest.java b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonProcessingTest.java
index ca3a7a0..f40cafe 100644
--- a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonProcessingTest.java
+++ b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonProcessingTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2021 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
@@ -69,8 +69,9 @@
                 // JSON processing.
                 mavenBundle().groupId("org.glassfish.jersey.media").artifactId("jersey-media-json-processing")
                         .versionAsInProject(),
-                mavenBundle().groupId("org.glassfish").artifactId("jakarta.json").versionAsInProject(),
-                mavenBundle().groupId("org.glassfish").artifactId("jsonp-jaxrs").versionAsInProject()
+                mavenBundle().groupId("jakarta.json").artifactId("jakarta.json-api").versionAsInProject(),
+                mavenBundle().groupId("org.eclipse.parsson").artifactId("parsson").versionAsInProject(),
+                mavenBundle().groupId("org.eclipse.parsson").artifactId("parsson-media").versionAsInProject()
         ));
 
         options = Helper.addPaxExamMavenLocalRepositoryProperty(options);
diff --git a/tests/performance/test-cases/mbw-xml-jaxb/pom.xml b/tests/performance/test-cases/mbw-xml-jaxb/pom.xml
index 9f94dd8..b046d56 100644
--- a/tests/performance/test-cases/mbw-xml-jaxb/pom.xml
+++ b/tests/performance/test-cases/mbw-xml-jaxb/pom.xml
@@ -46,6 +46,11 @@
             <artifactId>jakarta.ws.rs-api</artifactId>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>com.sun.xml.bind</groupId>
+            <artifactId>jaxb-osgi</artifactId>
+        </dependency>
+
 
         <dependency>
             <groupId>org.glassfish.jersey.test-framework.providers</groupId>
@@ -63,42 +68,4 @@
         </plugins>
     </build>
 
-    <profiles>
-        <profile>
-            <id>jdk11+</id>
-            <activation>
-                <jdk>[11,)</jdk>
-            </activation>
-            <dependencies>
-                <dependency>
-                    <groupId>jakarta.xml.bind</groupId>
-                    <artifactId>jakarta.xml.bind-api</artifactId>
-                </dependency>
-                <dependency>
-                    <groupId>com.sun.xml.bind</groupId>
-                    <artifactId>jaxb-osgi</artifactId>
-                </dependency>
-            </dependencies>
-        </profile>
-        <profile>
-            <id>jdk8</id>
-            <activation>
-                <jdk>1.8</jdk>
-            </activation>
-            <build>
-                <plugins>
-                    <plugin>
-                        <groupId>org.apache.maven.plugins</groupId>
-                        <artifactId>maven-surefire-plugin</artifactId>
-                        <configuration>
-                            <excludes>
-                                <exclude>org/glassfish/jersey/tests/performance/mbw/xml/XmlEntityTest.java</exclude>
-                            </excludes>
-                        </configuration>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-    </profiles>
-
 </project>