Merge remote-tracking branch 'upstream/3.1' into 'upstream/3.1_JPMS'

Signed-off-by: Maxim Nesen <maxim.nesen@oracle.com>
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 6e6046c..b047d80 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation
+# Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation
 #
 # This program and the accompanying materials are made available under the
 # terms of the Eclipse Public License v. 2.0 which is available at
@@ -10,7 +10,7 @@
 # SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 #
 
-name: Jersey
+name: Eclipse Required License Check
 
 on: [push, pull_request]
 
@@ -29,11 +29,11 @@
 
     steps:
     - name: Checkout for build
-      uses: actions/checkout@v3
+      uses: actions/checkout@v4
       with:
         fetch-depth: 0
     - name: Set up JDK
-      uses: actions/setup-java@v3
+      uses: actions/setup-java@v4.1.0
       with:
         distribution: 'zulu'
         java-version: ${{ matrix.java_version }}
@@ -44,7 +44,7 @@
     - name: Build
       run: mvn -V -U -B ${{matrix.verify_profiles}} org.eclipse.dash:license-tool-plugin:license-check -DexcludeArtifactIds=bsh,jmh-core,jmh-generator-annprocess,swing-layout -pl '!:version-agnostic'
     - name: Upload license-check info
-      uses: actions/upload-artifact@v3
+      uses: actions/upload-artifact@v4
       with:
         name: license-summary.txt
         path: target/dash/summary
diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml
new file mode 100644
index 0000000..ddf4e77
--- /dev/null
+++ b/.github/workflows/validate.yml
@@ -0,0 +1,99 @@
+#
+# Copyright (c) 2024 Contributors to the Eclipse Foundation
+#
+# 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,
+# or the Eclipse Distribution License v. 1.0 which is available at
+# http://www.eclipse.org/org/documents/edl-v10.php.
+#
+# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+#
+
+name: "Validate"
+
+on: [pull_request, push]
+
+env:
+  JAVA_VERSION: '21'
+  JAVA_DISTRO: 'temurin'
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
+
+jobs:
+  copyright:
+    timeout-minutes: 10
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+      - name: Set up JDK ${{ env.JAVA_VERSION }}
+        uses: actions/setup-java@v4.1.0
+        with:
+          distribution: ${{ env.JAVA_DISTRO }}
+          java-version: ${{ env.JAVA_VERSION }}
+          cache: maven
+      - name: Copyright
+        run: etc/scripts/copyright.sh
+      - name: Upload copyright info
+        uses: actions/upload-artifact@v4
+        with:
+          name: copyright_results
+          path: copyright.log
+          retention-days: 5
+  checkstyle:
+    timeout-minutes: 10
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+      - name: Set up JDK ${{ env.JAVA_VERSION }}
+        uses: actions/setup-java@v4.1.0
+        with:
+          distribution: ${{ env.JAVA_DISTRO }}
+          java-version: ${{ env.JAVA_VERSION }}
+          cache: maven
+      - name: Checkstyle
+        run: etc/scripts/checkstyle.sh
+      - name: Upload checkstyle info
+        uses: actions/upload-artifact@v4
+        with:
+          name: checkstyle_results
+          path: target/checkstyle/checkstyle-result.xml
+          retention-days: 5
+  apidocs:
+    timeout-minutes: 30
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+      - name: Set up JDK 17
+        uses: actions/setup-java@v4.1.0
+        with:
+          distribution: ${{ env.JAVA_DISTRO }}
+          java-version: 17
+          cache: maven
+      - name: Build JDK17+ required modules
+        run: mvn -B -U -V clean install -DskipTests -pl :jersey-helidon-connector -am
+      - name: Set up JDK ${{ env.JAVA_VERSION }}
+        uses: actions/setup-java@v4.1.0
+        with:
+          distribution: ${{ env.JAVA_DISTRO }}
+          java-version: ${{ env.JAVA_VERSION }}
+          cache: maven
+      - name: Build ApiDocs
+        run: etc/scripts/apidocs.sh
+  archetypes:
+    timeout-minutes: 45
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+      - name: Set up JDK ${{ env.JAVA_VERSION }}
+        uses: actions/setup-java@v4.1.0
+        with:
+          distribution: ${{ env.JAVA_DISTRO }}
+          java-version: ${{ env.JAVA_VERSION }}
+          cache: maven
+      - name: Test archetypes
+        run: etc/scripts/test-archetypes.sh
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f42b9db..9dcb3de 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,4 +1,4 @@
-[//]: # " Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. "

+[//]: # " Copyright (c) 2018, 2024 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,13 +18,14 @@
 

 ## Project description

 

-Eclipse Jersey is a REST framework that provides a JAX-RS (JSR-370) implementation and more. 

+Eclipse Jersey is a REST framework that provides a <a href="https://jcp.org/en/jsr/detail?id=370">JAX-RS (JSR-370)</a>, 

+now mainly <a href="https://projects.eclipse.org/projects/ee4j.rest">Jakarta REST</a>, implementation and more. 

 Jersey provides its own APIs that extend the JAX-RS toolkit with additional features and utilities 

 to further simplify RESTful service and client development. Jersey also exposes numerous extension 

 SPIs so that developers may extend Jersey to best suit their needs.

  

 Goals of Jersey project can be summarized in the following points:

-* Track the JAX-RS API and provide regular releases of production quality implementations that ships with GlassFish;

+* Track the JAX-RS/Jakarta REST API and provide regular releases of production quality implementations that ships with GlassFish;

 

 * Provide APIs to extend Jersey & Build a community of users and developers; and finally

 

@@ -41,6 +42,21 @@
 

 * https://github.com/eclipse-ee4j/jersey

 

+## Eclipse Development Process

+

+This Eclipse Foundation open project is governed by the Eclipse Foundation

+Development Process and operates under the terms of the Eclipse IP Policy.

+

+## Specifications

+

+This specification project operates under the terms of the Eclipse Foundation

+Specification process.

+

+* https://eclipse.org/projects/dev_process

+* https://www.eclipse.org/org/documents/Eclipse_IP_Policy.pdf

+* https://www.eclipse.org/projects/efsp/

+* https://www.eclipse.org/legal/efsp_non_assert.php

+

 ## Eclipse Contributor Agreement

 

 Before your contribution can be accepted by the project team contributors must

diff --git a/NOTICE.md b/NOTICE.md
index 724bef4..bd12480 100644
--- a/NOTICE.md
+++ b/NOTICE.md
@@ -1,4 +1,4 @@
-# Notice for Jersey 

+# Notice for Jersey

 This content is produced and maintained by the Eclipse Jersey project.

 

 *  Project home: https://projects.eclipse.org/projects/ee4j.jersey

@@ -57,25 +57,25 @@
 * Project: http://getbootstrap.com

 * Copyright: 2011-2016 Twitter, Inc

 

-Google Guava Version 18.0

+Google Guava Version 33.3.0-jre

 * License: Apache License, 2.0

-* Copyright (C) 2009 The Guava Authors

+* Copyright (C) 2009, 2024 The Guava Authors

 

-jakarta.inject Version: 1

+jakarta.inject Version: 2.0.1

 * License: Apache License, 2.0

-* Copyright (C) 2009 The JSR-330 Expert Group

+* Copyright (C) 2009, 2021 The JSR-330 Expert Group

 

-Javassist Version 3.29.2-GA

+Javassist Version 3.30.2-GA

 * License: Apache License, 2.0

 * Project: http://www.javassist.org/

 * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.

 

-Jackson JAX-RS Providers Version 2.15.3

+Jackson JAX-RS Providers Version 2.17.2

 * License: Apache License, 2.0

 * Project: https://github.com/FasterXML/jackson-jaxrs-providers

-* Copyright: (c) 2009-2023 FasterXML, LLC. All rights reserved unless otherwise indicated.

+* Copyright: (c) 2009-2024 FasterXML, LLC. All rights reserved unless otherwise indicated.

 

-jQuery v1.12.4

+jQuery v3.7.1

 * License: jquery.org/license

 * Project: jquery.org

 * Copyright: (c) jQuery Foundation

@@ -95,7 +95,7 @@
 * Project: http://www.kineticjs.com, https://github.com/ericdrowell/KineticJS

 * Copyright: Eric Rowell

 

-org.objectweb.asm Version 9.6

+org.objectweb.asm Version 9.7

 * License: Modified BSD (https://asm.ow2.io/license.html)

 * Copyright (c) 2000-2011 INRIA, France Telecom. All rights reserved.

 

diff --git a/archetypes/jersey-example-java8-webapp/src/main/resources/archetype-resources/pom.xml b/archetypes/jersey-example-java8-webapp/src/main/resources/archetype-resources/pom.xml
index af97394..678b7e2 100644
--- a/archetypes/jersey-example-java8-webapp/src/main/resources/archetype-resources/pom.xml
+++ b/archetypes/jersey-example-java8-webapp/src/main/resources/archetype-resources/pom.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
 
-    Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2015, 2024 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
@@ -125,6 +125,5 @@
     <properties>
         <java.version>11</java.version>
         <jersey.config.test.container.port>8080</jersey.config.test.container.port>
-        <war.mvn.plugin.version>3.4.0</war.mvn.plugin.version>
     </properties>
 </project>
diff --git a/archetypes/jersey-heroku-webapp/pom.xml b/archetypes/jersey-heroku-webapp/pom.xml
index 5c30a45..ca84605 100644
--- a/archetypes/jersey-heroku-webapp/pom.xml
+++ b/archetypes/jersey-heroku-webapp/pom.xml
@@ -37,7 +37,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-resources-plugin</artifactId>
-                <version>2.5</version>
+                <version>${resources.mvn.plugin.version}</version>
                 <configuration>
                     <escapeString>\</escapeString>
                 </configuration>
diff --git a/archetypes/jersey-heroku-webapp/src/main/resources/archetype-resources/pom.xml b/archetypes/jersey-heroku-webapp/src/main/resources/archetype-resources/pom.xml
index 3b60fd3..edfed3d 100644
--- a/archetypes/jersey-heroku-webapp/src/main/resources/archetype-resources/pom.xml
+++ b/archetypes/jersey-heroku-webapp/src/main/resources/archetype-resources/pom.xml
@@ -64,7 +64,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
-                <version>3.8.1</version>
+                <version>${compiler.mvn.plugin.version}</version>
                 <inherited>true</inherited>
                 <configuration>
                     <source>11</source>
@@ -117,9 +117,6 @@
 
     <properties>
         <jersey.version>${project.version}</jersey.version>
-        <jetty.version>12.0.3</jetty.version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-        <surefire.mvn.plugin.version>3.2.1</surefire.mvn.plugin.version>
-        <war.mvn.plugin.version>3.4.0</war.mvn.plugin.version>
     </properties>
 </project>
diff --git a/archetypes/jersey-quickstart-grizzly2/pom.xml b/archetypes/jersey-quickstart-grizzly2/pom.xml
index 4964159..9bbefa0 100644
--- a/archetypes/jersey-quickstart-grizzly2/pom.xml
+++ b/archetypes/jersey-quickstart-grizzly2/pom.xml
@@ -45,7 +45,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-resources-plugin</artifactId>
-                <version>${maven.resources.plugin.version}</version>
+                <version>${resources.mvn.plugin.version}</version>
                 <configuration>
                     <escapeString>\</escapeString>
                 </configuration>
diff --git a/archetypes/jersey-quickstart-grizzly2/src/main/resources/META-INF/maven/archetype-metadata.xml b/archetypes/jersey-quickstart-grizzly2/src/main/resources/META-INF/maven/archetype-metadata.xml
index e932741..58e9ba4 100644
--- a/archetypes/jersey-quickstart-grizzly2/src/main/resources/META-INF/maven/archetype-metadata.xml
+++ b/archetypes/jersey-quickstart-grizzly2/src/main/resources/META-INF/maven/archetype-metadata.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
 
-    Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2024 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/archetypes/jersey-quickstart-grizzly2/src/main/resources/archetype-resources/pom.xml b/archetypes/jersey-quickstart-grizzly2/src/main/resources/archetype-resources/pom.xml
index 682a44b..2690b43 100644
--- a/archetypes/jersey-quickstart-grizzly2/src/main/resources/archetype-resources/pom.xml
+++ b/archetypes/jersey-quickstart-grizzly2/src/main/resources/archetype-resources/pom.xml
@@ -40,7 +40,7 @@
         <dependency>
             <groupId>org.junit.jupiter</groupId>
             <artifactId>junit-jupiter</artifactId>
-            <version>\${junit-jupiter.version}</version>
+            <version>\${junit5.version}</version>
             <scope>test</scope>
         </dependency>
     </dependencies>
@@ -50,13 +50,13 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
-                <version>3.10.1</version>
+                <version>${compiler.mvn.plugin.version}</version>
                 <inherited>true</inherited>
             </plugin>
             <plugin>
                 <groupId>org.codehaus.mojo</groupId>
                 <artifactId>exec-maven-plugin</artifactId>
-                <version>3.0.0</version>
+                <version>${exec.mvn.plugin.version}</version>
                 <executions>
                     <execution>
                         <goals>
@@ -79,8 +79,7 @@
 
     <properties>
         <jersey.version>${project.version}</jersey.version>
-        <junit-jupiter.version>5.10.0</junit-jupiter.version>
+        <junit5.version>${junit5.version}</junit5.version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-        <surefire.mvn.plugin.version>3.2.1</surefire.mvn.plugin.version>
     </properties>
 </project>
diff --git a/archetypes/jersey-quickstart-webapp/pom.xml b/archetypes/jersey-quickstart-webapp/pom.xml
index 27228bc..77a762c 100644
--- a/archetypes/jersey-quickstart-webapp/pom.xml
+++ b/archetypes/jersey-quickstart-webapp/pom.xml
@@ -36,7 +36,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-resources-plugin</artifactId>
-                <version>${maven.resources.plugin.version}</version>
+                <version>${resources.mvn.plugin.version}</version>
                 <configuration>
                     <escapeString>\</escapeString>
                 </configuration>
diff --git a/archetypes/jersey-quickstart-webapp/src/main/resources/META-INF/maven/archetype-metadata.xml b/archetypes/jersey-quickstart-webapp/src/main/resources/META-INF/maven/archetype-metadata.xml
index 53d7ba6..97e49d3 100644
--- a/archetypes/jersey-quickstart-webapp/src/main/resources/META-INF/maven/archetype-metadata.xml
+++ b/archetypes/jersey-quickstart-webapp/src/main/resources/META-INF/maven/archetype-metadata.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
 
-    Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2024 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/archetypes/jersey-quickstart-webapp/src/main/resources/archetype-resources/pom.xml b/archetypes/jersey-quickstart-webapp/src/main/resources/archetype-resources/pom.xml
index 8de4e4d..0db4b81 100644
--- a/archetypes/jersey-quickstart-webapp/src/main/resources/archetype-resources/pom.xml
+++ b/archetypes/jersey-quickstart-webapp/src/main/resources/archetype-resources/pom.xml
@@ -15,7 +15,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
-                <version>3.10.1</version>
+                <version>${compiler.mvn.plugin.version}</version>
                 <inherited>true</inherited>
             </plugin>
             <plugin>
@@ -59,6 +59,5 @@
     <properties>
         <jersey.version>${project.version}</jersey.version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-        <war.mvn.plugin.version>3.4.0</war.mvn.plugin.version>
     </properties>
 </project>
diff --git a/bom/pom.xml b/bom/pom.xml
index 91ce899..92dbe37 100644
--- a/bom/pom.xml
+++ b/bom/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.eclipse.ee4j</groupId>
         <artifactId>project</artifactId>
-        <version>1.0.8</version>
+        <version>1.0.9</version>
         <relativePath/>
     </parent>
 
@@ -420,6 +420,11 @@
                 <version>${project.version}</version>
             </dependency>
             <dependency>
+                <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+                <artifactId>jersey-test-framework-provider-netty</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
                 <groupId>org.glassfish.jersey.test-framework</groupId>
                 <artifactId>jersey-test-framework-util</artifactId>
                 <version>${project.version}</version>
@@ -435,11 +440,6 @@
                 <version>2.4</version>
                 <inherited>true</inherited>
             </plugin>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-site-plugin</artifactId>
-                <version>3.9.1</version>
-            </plugin>
         </plugins>
     </build>
 
diff --git a/bundles/apidocs/pom.xml b/bundles/apidocs/pom.xml
index c7bb57e..58c4485 100644
--- a/bundles/apidocs/pom.xml
+++ b/bundles/apidocs/pom.xml
@@ -63,12 +63,6 @@
             <groupId>org.glassfish.jersey.connectors</groupId>
             <artifactId>jersey-apache-connector</artifactId>
             <version>${project.version}</version>
-            <!--<exclusions>
-                <exclusion>
-                    <groupId>commons-codec</groupId>
-                    <artifactId>commons-codec</artifactId>
-                </exclusion>
-            </exclusions>-->
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.connectors</groupId>
@@ -131,11 +125,7 @@
         <dependency>
             <groupId>org.glassfish</groupId>
             <artifactId>javax.servlet</artifactId>
-            <version>3.1</version>
-        </dependency>
-        <dependency>
-            <groupId>jakarta.persistence</groupId>
-            <artifactId>jakarta.persistence-api</artifactId>
+            <version>3.1.1</version>
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
@@ -299,12 +289,14 @@
                                 <fileExclude>META-INF/versions/17/org/glassfish/jersey/jetty/connector/*.java</fileExclude>
                                 <fileExclude>META-INF/versions/17/org/glassfish/jersey/helidon/connector/*.java</fileExclude>
                                 <fileExclude>org/glassfish/jersey/helidon/connector/*.java</fileExclude>
+                                <fileExclude>org/glassfish/jersey/jetty/*.java</fileExclude>
+                                <fileExclude>org/glassfish/jersey/jetty/connector/*.java</fileExclude>
                                 <fileExclude>org/glassfish/jersey/wadl/doclet/*.java</fileExclude>
                             </sourceFileExcludes>
                             <dependencySourceIncludes>
                                 <dependencySourceInclude>org.glassfish.jersey.*:*</dependencySourceInclude>
                             </dependencySourceIncludes>
-                            <excludePackageNames>*.internal.*:*.innate.*</excludePackageNames>
+                            <excludePackageNames>*.innate:*.innate.*</excludePackageNames>
                         </configuration>
                     </execution>
                 </executions>
diff --git a/bundles/jaxrs-ri/pom.xml b/bundles/jaxrs-ri/pom.xml
index 9b4d43f..5d9b962 100644
--- a/bundles/jaxrs-ri/pom.xml
+++ b/bundles/jaxrs-ri/pom.xml
@@ -208,8 +208,7 @@
                 <artifactId>maven-compiler-plugin</artifactId>
                 <inherited>false</inherited>
                 <configuration>
-                    <source>${java.version}</source>
-                    <target>${java.version}</target>
+                    <release>${java.version}</release>
                     <compilerArguments>
                         <!-- Do not warn about using sun.misc.Unsafe -->
                         <XDignore.symbol.file />
@@ -217,7 +216,9 @@
                     <showWarnings>false</showWarnings>
                     <fork>false</fork>
                     <excludes>
-                        <exclude>module-info.java</exclude><exclude>META-INF/versions/11/**</exclude>
+                        <exclude>module-info.java</exclude>
+                        <exclude>META-INF/versions/11/**</exclude>
+                        <exclude>META-INF/versions/21/**</exclude>
                     </excludes>
                 </configuration>
             </plugin>
@@ -314,7 +315,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-shade-plugin</artifactId>
-                <version>3.2.4</version>
+                <version>${shade.mvn.plugin.version}</version>
                 <executions>
                     <execution>
                         <phase>package</phase>
@@ -421,7 +422,7 @@
                     <plugin>
                         <groupId>org.codehaus.mojo</groupId>
                         <artifactId>wagon-maven-plugin</artifactId>
-                        <version>1.0-beta-4</version>
+                        <version>2.0.2</version>
                         <inherited>false</inherited>
                         <executions>
                             <execution>
@@ -463,6 +464,15 @@
                 </plugins>
             </build>
         </profile>
+        <profile>
+            <id>java_version</id>
+            <activation>
+                <jdk>21</jdk>
+            </activation>
+            <properties>
+                <java.version>21</java.version>
+            </properties>
+        </profile>
     </profiles>
 
     <properties>
diff --git a/connectors/apache-connector/pom.xml b/connectors/apache-connector/pom.xml
index b429cd2..95048b8 100644
--- a/connectors/apache-connector/pom.xml
+++ b/connectors/apache-connector/pom.xml
@@ -40,6 +40,16 @@
         <dependency>
             <groupId>org.apache.httpcomponents</groupId>
             <artifactId>httpclient</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>commons-codec</groupId>
+                    <artifactId>commons-codec</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>commons-logging</groupId>
+                    <artifactId>commons-logging</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
 
         <dependency>
@@ -55,6 +65,17 @@
             <version>${project.version}</version>
             <scope>test</scope>
         </dependency>
+
+        <dependency>
+            <groupId>commons-codec</groupId>
+            <artifactId>commons-codec</artifactId>
+            <version>${commons.codec.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-logging</groupId>
+            <artifactId>commons-logging</artifactId>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/connectors/apache-connector/src/main/java/org/glassfish/jersey/apache/connector/ApacheConnector.java b/connectors/apache-connector/src/main/java/org/glassfish/jersey/apache/connector/ApacheConnector.java
index 264c453..8af222a 100644
--- a/connectors/apache-connector/src/main/java/org/glassfish/jersey/apache/connector/ApacheConnector.java
+++ b/connectors/apache-connector/src/main/java/org/glassfish/jersey/apache/connector/ApacheConnector.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 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,9 +31,11 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.concurrent.CancellationException;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Future;
 import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.stream.Collectors;
@@ -58,6 +60,7 @@
 import org.glassfish.jersey.client.innate.http.SSLParamConfigurator;
 import org.glassfish.jersey.client.spi.AsyncConnectorCallback;
 import org.glassfish.jersey.client.spi.Connector;
+import org.glassfish.jersey.innate.io.InputStreamWrapper;
 import org.glassfish.jersey.internal.util.PropertiesHelper;
 import org.glassfish.jersey.message.internal.HeaderUtils;
 import org.glassfish.jersey.message.internal.OutboundMessageContext;
@@ -521,7 +524,7 @@
 
             try {
                 final ConnectionClosingMechanism closingMechanism = new ConnectionClosingMechanism(clientRequest, request);
-                responseContext.setEntityStream(getInputStream(response, closingMechanism));
+                responseContext.setEntityStream(getInputStream(response, closingMechanism, () -> clientRequest.isCancelled()));
             } catch (final IOException e) {
                 LOGGER.log(Level.SEVERE, null, e);
             }
@@ -730,17 +733,19 @@
     }
 
     private static InputStream getInputStream(final CloseableHttpResponse response,
-                                              final ConnectionClosingMechanism closingMechanism) throws IOException {
+                                              final ConnectionClosingMechanism closingMechanism,
+                                              final Supplier<Boolean> isCancelled) throws IOException {
         final InputStream inputStream;
 
         if (response.getEntity() == null) {
             inputStream = new ByteArrayInputStream(new byte[0]);
         } else {
-            final InputStream i = response.getEntity().getContent();
+            final InputStream i = new CancellableInputStream(response.getEntity().getContent(), isCancelled);
             if (i.markSupported()) {
                 inputStream = i;
             } else {
-                inputStream = new BufferedInputStream(i, ReaderWriter.BUFFER_SIZE);
+                inputStream = ReaderWriter.AUTOSIZE_BUFFER ? new BufferedInputStream(i)
+                        : new BufferedInputStream(i, ReaderWriter.BUFFER_SIZE);
             }
         }
 
@@ -879,10 +884,34 @@
                 Object objectRequest = context.getAttribute(JERSEY_REQUEST_ATTR_NAME);
                 if (objectRequest != null) {
                     ClientRequest clientRequest = (ClientRequest) objectRequest;
-                    SSLParamConfigurator sniConfig = SSLParamConfigurator.builder().request(clientRequest).build();
+                    SSLParamConfigurator sniConfig = SSLParamConfigurator.builder().request(clientRequest)
+                            .setSNIHostName(clientRequest).build();
                     sniConfig.setSNIServerName(socket);
                 }
             }
         }
     }
+
+    private static class CancellableInputStream extends InputStreamWrapper {
+        private final InputStream in;
+        private final Supplier<Boolean> isCancelled;
+
+        private CancellableInputStream(InputStream in, Supplier<Boolean> isCancelled) {
+            this.in = in;
+            this.isCancelled = isCancelled;
+        }
+
+        @Override
+        protected InputStream getWrapped() {
+            return in;
+        }
+
+        @Override
+        protected InputStream getWrappedIOE() throws IOException {
+            if (isCancelled.get()) {
+                throw new IOException(new CancellationException());
+            }
+            return in;
+        }
+    }
 }
diff --git a/connectors/apache5-connector/src/main/java/org/glassfish/jersey/apache5/connector/Apache5Connector.java b/connectors/apache5-connector/src/main/java/org/glassfish/jersey/apache5/connector/Apache5Connector.java
index f7c30a9..99e5c7a 100644
--- a/connectors/apache5-connector/src/main/java/org/glassfish/jersey/apache5/connector/Apache5Connector.java
+++ b/connectors/apache5-connector/src/main/java/org/glassfish/jersey/apache5/connector/Apache5Connector.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2024 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
@@ -32,8 +32,10 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.concurrent.CancellationException;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Future;
+import java.util.function.Supplier;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.stream.Collectors;
@@ -103,6 +105,7 @@
 import org.glassfish.jersey.client.innate.http.SSLParamConfigurator;
 import org.glassfish.jersey.client.spi.AsyncConnectorCallback;
 import org.glassfish.jersey.client.spi.Connector;
+import org.glassfish.jersey.innate.io.InputStreamWrapper;
 import org.glassfish.jersey.internal.util.PropertiesHelper;
 import org.glassfish.jersey.message.internal.HeaderUtils;
 import org.glassfish.jersey.message.internal.OutboundMessageContext;
@@ -519,7 +522,7 @@
             final HttpEntity entity = response.getEntity();
 
             if (entity != null) {
-                if (headers.get(HttpHeaders.CONTENT_LENGTH) == null) {
+                if (headers.get(HttpHeaders.CONTENT_LENGTH) == null && entity.getContentLength() >= 0) {
                     headers.add(HttpHeaders.CONTENT_LENGTH, String.valueOf(entity.getContentLength()));
                 }
 
@@ -531,7 +534,7 @@
 
             try {
                 final ConnectionClosingMechanism closingMechanism = new ConnectionClosingMechanism(clientRequest, request);
-                responseContext.setEntityStream(getInputStream(response, closingMechanism));
+                responseContext.setEntityStream(getInputStream(response, closingMechanism, () -> clientRequest.isCancelled()));
             } catch (final IOException e) {
                 LOGGER.log(Level.SEVERE, null, e);
             }
@@ -741,17 +744,19 @@
     }
 
     private static InputStream getInputStream(final CloseableHttpResponse response,
-                                              final ConnectionClosingMechanism closingMechanism) throws IOException {
+                                              final ConnectionClosingMechanism closingMechanism,
+                                              final Supplier<Boolean> isCancelled) throws IOException {
         final InputStream inputStream;
 
         if (response.getEntity() == null) {
             inputStream = new ByteArrayInputStream(new byte[0]);
         } else {
-            final InputStream i = response.getEntity().getContent();
+            final InputStream i = new CancellableInputStream(response.getEntity().getContent(), isCancelled);
             if (i.markSupported()) {
                 inputStream = i;
             } else {
-                inputStream = new BufferedInputStream(i, ReaderWriter.BUFFER_SIZE);
+                inputStream = ReaderWriter.AUTOSIZE_BUFFER ? new BufferedInputStream(i)
+                        : new BufferedInputStream(i, ReaderWriter.BUFFER_SIZE);
             }
         }
 
@@ -878,7 +883,8 @@
                 Object objectRequest = context.getAttribute(JERSEY_REQUEST_ATTR_NAME);
                 if (objectRequest != null) {
                     ClientRequest clientRequest = (ClientRequest) objectRequest;
-                    SSLParamConfigurator sniConfig = SSLParamConfigurator.builder().request(clientRequest).build();
+                    SSLParamConfigurator sniConfig = SSLParamConfigurator.builder().request(clientRequest)
+                            .setSNIHostName(clientRequest).build();
                     sniConfig.setSNIServerName(socket);
 
                     final int socketTimeout = ((ClientRequest) objectRequest).resolveProperty(ClientProperties.READ_TIMEOUT, -1);
@@ -889,4 +895,28 @@
             }
         }
     }
+
+    private static class CancellableInputStream extends InputStreamWrapper {
+        private final InputStream in;
+        private final Supplier<Boolean> isCancelled;
+
+        private CancellableInputStream(InputStream in, Supplier<Boolean> isCancelled) {
+            this.in = in;
+            this.isCancelled = isCancelled;
+        }
+
+        @Override
+        protected InputStream getWrapped() {
+            return in;
+        }
+
+        @Override
+        protected InputStream getWrappedIOE() throws IOException {
+            if (isCancelled.get()) {
+                throw new IOException(new CancellationException());
+            }
+            return in;
+        }
+    }
+
 }
diff --git a/connectors/helidon-connector/pom.xml b/connectors/helidon-connector/pom.xml
index daaf815..7e1838c 100644
--- a/connectors/helidon-connector/pom.xml
+++ b/connectors/helidon-connector/pom.xml
@@ -42,7 +42,7 @@
         <dependency>
             <groupId>io.helidon.jersey</groupId>
             <artifactId>helidon-jersey-connector</artifactId>
-            <version>${helidon.jersey.connector.version}</version>
+            <version>${helidon.connector.version}</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
diff --git a/connectors/jdk-connector/pom.xml b/connectors/jdk-connector/pom.xml
index 39b1bd7..84b8324 100644
--- a/connectors/jdk-connector/pom.xml
+++ b/connectors/jdk-connector/pom.xml
@@ -92,6 +92,27 @@
     </build>
 
     <profiles>
+        <profile> <!-- we still require JDK 1.8 TLS 1.3 limitations -->
+            <id>jdk8</id>
+            <activation>
+                <jdk>(,9)</jdk>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <reuseForks>false</reuseForks>
+                            <excludes>
+                                <exclude>**/SslFilterTLS13Test.java</exclude>
+                                <exclude>**/SslFilterTLS13UrlStoresTest.java</exclude>
+                            </excludes>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
         <profile>
             <id>disable_tls1and11</id>
             <!-- TLS 1 and TLS 1.1 are disabled for JDK 16 -->
diff --git a/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/ConnectorConfiguration.java b/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/ConnectorConfiguration.java
index 422d919..9b92517 100644
--- a/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/ConnectorConfiguration.java
+++ b/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/ConnectorConfiguration.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2024 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
@@ -60,8 +60,10 @@
     private final int connectTimeout;
     private final ProxyConfiguration proxyConfiguration;
     private final AtomicReference<SSLParamConfigurator> sniConfigs = new AtomicReference<>(null);
+    private final Configuration configuration;
 
     ConnectorConfiguration(Client client, Configuration config) {
+        configuration = config;
         final Map<String, Object> properties = config.getProperties();
 
         int proposedChunkSize = JdkConnectorProperties.getValue(properties, ClientProperties.CHUNKED_ENCODING_SIZE,
@@ -181,6 +183,10 @@
         return sniConfigs.get();
     }
 
+    Configuration getConfiguration() {
+        return configuration;
+    }
+
     @Override
     public String toString() {
         return "ConnectorConfiguration{"
diff --git a/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/HttpConnectionPool.java b/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/HttpConnectionPool.java
index b20f00c..7413caf 100644
--- a/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/HttpConnectionPool.java
+++ b/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/HttpConnectionPool.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2024 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
@@ -51,7 +51,8 @@
     void send(HttpRequest httpRequest, CompletionHandler<HttpResponse> completionHandler) {
         final Map<String, List<Object>> headers = new HashMap<>();
         httpRequest.getHeaders().forEach((k, v) -> headers.put(k, (List) v));
-        final SSLParamConfigurator sniConfig = SSLParamConfigurator.builder().uri(httpRequest.getUri()).headers(headers).build();
+        final SSLParamConfigurator sniConfig = SSLParamConfigurator.builder().uri(httpRequest.getUri())
+                .headers(headers).setSNIHostName(connectorConfiguration.getConfiguration()).build();
         connectorConfiguration.setSniConfig(sniConfig);
 
         final DestinationConnectionPool.DestinationKey destinationKey = new DestinationConnectionPool.DestinationKey(
diff --git a/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/HttpParser.java b/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/HttpParser.java
index 15725e7..6099e8e 100644
--- a/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/HttpParser.java
+++ b/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/HttpParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2024 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
@@ -496,7 +496,7 @@
 
         if (contentLengths != null) {
             try {
-                int bodyLength = Integer.parseInt(contentLengths.get(0));
+                long bodyLength = Long.parseLong(contentLengths.get(0));
                 if (bodyLength == 0) {
                     expectContent = false;
                     return;
@@ -514,8 +514,15 @@
             }
 
             return;
+        } else if (httpResponse.getHasContent()) {
+            // missing Content-Length
+            transferEncodingParser = TransferEncodingParser
+                    .createFixedLengthParser(httpResponse.getBodyStream(), Long.MAX_VALUE);
+            return;
         }
 
+
+
         // TODO what now? Expect no content or fail loudly?
     }
 
diff --git a/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/TransferEncodingParser.java b/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/TransferEncodingParser.java
index 0a3ede0..1a94ff7 100644
--- a/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/TransferEncodingParser.java
+++ b/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/TransferEncodingParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2024 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,7 +31,7 @@
 
     abstract boolean parse(ByteBuffer input) throws ParseException;
 
-    static TransferEncodingParser createFixedLengthParser(AsynchronousBodyInputStream responseBody, int expectedLength) {
+    static TransferEncodingParser createFixedLengthParser(AsynchronousBodyInputStream responseBody, long expectedLength) {
         return new FixedLengthEncodingParser(responseBody, expectedLength);
     }
 
@@ -42,11 +42,11 @@
 
     private static class FixedLengthEncodingParser extends TransferEncodingParser {
 
-        private final int expectedLength;
+        private final long expectedLength;
         private final AsynchronousBodyInputStream responseBody;
-        private volatile int consumedLength = 0;
+        private volatile long consumedLength = 0;
 
-        FixedLengthEncodingParser(AsynchronousBodyInputStream responseBody, int expectedLength) {
+        FixedLengthEncodingParser(AsynchronousBodyInputStream responseBody, long expectedLength) {
             this.expectedLength = expectedLength;
             this.responseBody = responseBody;
         }
@@ -63,7 +63,7 @@
             responseBody.notifyDataAvailable(parsed);
             consumedLength += data.length;
 
-            return consumedLength == expectedLength;
+            return consumedLength == expectedLength || expectedLength == Long.MAX_VALUE /* unknown at the beginning */;
         }
     }
 
diff --git a/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/internal/SslFilterTLS13UrlStoresTest.java b/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/internal/SslFilterTLS13UrlStoresTest.java
new file mode 100644
index 0000000..b8e96c0
--- /dev/null
+++ b/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/internal/SslFilterTLS13UrlStoresTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2024 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.jdk.connector.internal;
+
+import org.glassfish.jersey.SslConfigurator;
+
+public class SslFilterTLS13UrlStoresTest extends SslFilterTest {
+
+    public SslFilterTLS13UrlStoresTest() {
+        System.setProperty("jdk.tls.server.protocols", "TLSv1.3");
+        System.setProperty("jdk.tls.client.protocols", "TLSv1.3");
+    }
+
+    @Override
+    protected SslConfigurator getSslConfigurator() {
+        return SslConfigurator.newInstance()
+                .trustStoreUrl(this.getClass().getResource("/truststore_client"))
+                .trustStorePassword("asdfgh")
+                .keyStoreUrl(this.getClass().getResource("/keystore_client"))
+                .keyStorePassword("asdfgh");
+    }
+}
diff --git a/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/internal/SslFilterTest.java b/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/internal/SslFilterTest.java
index ef13997..7f3b00a 100644
--- a/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/internal/SslFilterTest.java
+++ b/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/internal/SslFilterTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2024 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
@@ -388,11 +388,7 @@
                                                                                     final CountDownLatch completionLatch,
                                                                                     HostnameVerifier customHostnameVerifier)
             throws Throwable {
-        SslConfigurator sslConfig = SslConfigurator.newInstance()
-                .trustStoreFile(this.getClass().getResource("/truststore_client").getPath())
-                .trustStorePassword("asdfgh")
-                .keyStoreFile(this.getClass().getResource("/keystore_client").getPath())
-                .keyStorePassword("asdfgh");
+        SslConfigurator sslConfig = getSslConfigurator();
 
         TransportFilter transportFilter = new TransportFilter(17_000, ThreadPoolConfig.defaultConfig(), 100_000);
         final SSLParamConfigurator sslParamConfigurator = SSLParamConfigurator.builder()
@@ -481,6 +477,14 @@
         return clientSocket;
     }
 
+    protected SslConfigurator getSslConfigurator() {
+        return SslConfigurator.newInstance()
+                .trustStoreFile(this.getClass().getResource("/truststore_client").getPath())
+                .trustStorePassword("asdfgh")
+                .keyStoreFile(this.getClass().getResource("/keystore_client").getPath())
+                .keyStorePassword("asdfgh");
+    }
+
     /**
      * SSL echo server. It expects a message to be terminated with \n.
      */
diff --git a/connectors/jetty-connector/pom.xml b/connectors/jetty-connector/pom.xml
index cdd1f0e..d8436b5 100644
--- a/connectors/jetty-connector/pom.xml
+++ b/connectors/jetty-connector/pom.xml
@@ -107,6 +107,11 @@
             <version>${jetty.servlet.api.version}</version>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <version>${slf4j.version}</version>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/connectors/jetty-connector/src/main/java/module-info.java b/connectors/jetty-connector/src/main/java17/module-info.java
similarity index 97%
rename from connectors/jetty-connector/src/main/java/module-info.java
rename to connectors/jetty-connector/src/main/java17/module-info.java
index 1c9c76f..67f8b4b 100644
--- a/connectors/jetty-connector/src/main/java/module-info.java
+++ b/connectors/jetty-connector/src/main/java17/module-info.java
@@ -16,6 +16,7 @@
 
 module org.glassfish.jersey.jetty.connector {
     requires java.logging;
+    requires org.slf4j;
 
     requires jakarta.ws.rs;
 
diff --git a/connectors/jetty-http2-connector/pom.xml b/connectors/jetty-http2-connector/pom.xml
index adc7d54..c8f5a85 100644
--- a/connectors/jetty-http2-connector/pom.xml
+++ b/connectors/jetty-http2-connector/pom.xml
@@ -48,6 +48,12 @@
         <dependency>
             <groupId>org.eclipse.jetty</groupId>
             <artifactId>jetty-util</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-api</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
 
         <dependency>
@@ -159,10 +165,22 @@
                 <dependency>
                     <groupId>org.eclipse.jetty.http2</groupId>
                     <artifactId>jetty-http2-client</artifactId>
+                    <exclusions>
+                        <exclusion>
+                            <groupId>org.slf4j</groupId>
+                            <artifactId>slf4j-api</artifactId>
+                        </exclusion>
+                    </exclusions>
                 </dependency>
                 <dependency>
                     <groupId>org.eclipse.jetty.http2</groupId>
                     <artifactId>jetty-http2-client-transport</artifactId>
+                    <exclusions>
+                        <exclusion>
+                            <groupId>org.slf4j</groupId>
+                            <artifactId>slf4j-api</artifactId>
+                        </exclusion>
+                    </exclusions>
                 </dependency>
                 <dependency>
                     <groupId>org.glassfish.jersey.containers</groupId>
diff --git a/connectors/jetty11-connector/pom.xml b/connectors/jetty11-connector/pom.xml
index a57b94f..2fec1f1 100644
--- a/connectors/jetty11-connector/pom.xml
+++ b/connectors/jetty11-connector/pom.xml
@@ -151,6 +151,13 @@
                     </instructions>
                 </configuration>
             </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-enforcer-plugin</artifactId>
+                <configuration>
+                    <rulesToSkip>dependencyConvergence</rulesToSkip>
+                </configuration>
+            </plugin>
         </plugins>
     </build>
 </project>
\ No newline at end of file
diff --git a/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpConnector.java b/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpConnector.java
index 69d25eb..57296c6 100644
--- a/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpConnector.java
+++ b/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpConnector.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2024 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
@@ -360,24 +360,30 @@
         @Override
         public int read() throws IOException {
             lock.lock();
-            final int r = zero != -1 ? zero : inner.read();
-            zero = -1;
-            lock.unlock();
-            return r;
+            try {
+                final int r = zero != -1 ? zero : inner.read();
+                zero = -1;
+                return r;
+            } finally {
+                lock.unlock();
+            }
         }
 
         @Override
         public int read(byte[] b, int off, int len) throws IOException {
             lock.lock();
             int r;
-            if (zero != -1) {
-                b[off] = (byte) (zero & 0xFF);
-                r = inner.read(b, off + 1, len - 1);
-            } else {
-                r = inner.read(b, off, len);
+            try {
+                if (zero != -1) {
+                    b[off] = (byte) (zero & 0xFF);
+                    r = inner.read(b, off + 1, len - 1);
+                } else {
+                    r = inner.read(b, off, len);
+                }
+                zero = -1;
+            } finally {
+                lock.unlock();
             }
-            zero = -1;
-            lock.unlock();
             return r;
 
         }
@@ -385,23 +391,24 @@
         @Override
         public int available() throws IOException {
             lock.lock();
-            if (zero != -1) {
-                lock.unlock();
-                return 1;
-            }
+            try {
+                if (zero != -1) {
+                    return 1;
+                }
 
-            int available = inner.available();
-            if (available != 1) {
-                lock.unlock();
+                int available = inner.available();
+                if (available != 1) {
+                    return available;
+                }
+
+                zero = inner.read();
+                if (zero == -1) {
+                    available = 0;
+                }
                 return available;
+            } finally {
+                lock.unlock();
             }
-
-            zero = inner.read();
-            if (zero == -1) {
-                available = 0;
-            }
-            lock.unlock();
-            return available;
         }
 
         @Override
@@ -418,10 +425,14 @@
         }
 
         @Override
-        public synchronized void mark(int readlimit) {
+        public void mark(int readlimit) {
             inner.mark(readlimit);
         }
 
+        @Override
+        public void reset() throws IOException {
+            inner.reset();
+        }
     }
 
 }
diff --git a/connectors/netty-connector/pom.xml b/connectors/netty-connector/pom.xml
index 92c0896..4432bc9 100644
--- a/connectors/netty-connector/pom.xml
+++ b/connectors/netty-connector/pom.xml
@@ -34,6 +34,11 @@
 
     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <surefire.coverage.argline>
+            --add-opens org.glassfish.jersey.core.common/org.glassfish.jersey.innate=ALL-UNNAMED
+            --add-opens org.glassfish.jersey.core.common/org.glassfish.jersey.innate.virtual=ALL-UNNAMED
+            --add-modules=ALL-MODULE-PATH
+        </surefire.coverage.argline>
     </properties>
 
     <dependencies>
@@ -55,6 +60,17 @@
             <version>${project.version}</version>
 <!--            <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>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
@@ -81,4 +97,26 @@
         </plugins>
     </build>
 
+    <profiles>
+        <profile>
+            <id>JettyExclude</id>
+            <activation>
+                <jdk>(1.8,17)</jdk>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-compiler-plugin</artifactId>
+                        <configuration>
+                            <testExcludes>
+                                <testExclude>org/glassfish/jersey/netty/connector/HugeHeaderTest.java</testExclude>
+                            </testExcludes>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
 </project>
diff --git a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyClientHandler.java b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyClientHandler.java
index 87551dd..ec4b81c 100644
--- a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyClientHandler.java
+++ b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyClientHandler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2024 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
@@ -24,6 +24,7 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.CancellationException;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.TimeoutException;
 import java.util.function.Predicate;
@@ -97,6 +98,8 @@
 
        if (readTimedOut) {
           responseDone.completeExceptionally(new TimeoutException("Stream closed: read timeout"));
+       } else if (jerseyRequest.isCancelled()) {
+          responseDone.completeExceptionally(new CancellationException());
        } else {
           responseDone.completeExceptionally(new IOException("Stream closed"));
        }
@@ -157,6 +160,10 @@
 
     @Override
     public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) {
+        if (jerseyRequest.isCancelled()) {
+            responseAvailable.completeExceptionally(new CancellationException());
+            return;
+        }
         if (msg instanceof HttpResponse) {
             final HttpResponse response = (HttpResponse) msg;
             jerseyResponse = new ClientResponse(new Response.StatusType() {
@@ -181,21 +188,10 @@
             }
 
             // request entity handling.
-            if ((response.headers().contains(HttpHeaders.CONTENT_LENGTH) && HttpUtil.getContentLength(response) > 0)
-                    || HttpUtil.isTransferEncodingChunked(response)) {
+            nis = new NettyInputStream();
+            responseDone.whenComplete((_r, th) -> nis.complete(th));
 
-                nis = new NettyInputStream();
-                responseDone.whenComplete((_r, th) -> nis.complete(th));
-
-                jerseyResponse.setEntityStream(nis);
-            } else {
-                jerseyResponse.setEntityStream(new InputStream() {
-                    @Override
-                    public int read() throws IOException {
-                        return -1;
-                    }
-                });
-            }
+            jerseyResponse.setEntityStream(nis);
         }
         if (msg instanceof HttpContent) {
 
diff --git a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyExpectContinueHandler.java b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyExpectContinueHandler.java
index 8bceac6..bdede02 100644
--- a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyExpectContinueHandler.java
+++ b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyExpectContinueHandler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2024 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,7 +59,8 @@
                 ctx.pipeline().remove(JerseyExpectContinueHandler.class);
             }
         } else {
-            if (!isExpected) {
+            if (!isExpected
+                    && ctx.pipeline().context(JerseyExpectContinueHandler.class) != null) {
                 ctx.pipeline().remove(JerseyExpectContinueHandler.class);
             }
             ctx.fireChannelRead(msg); //bypass the message to the next handler in line
diff --git a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyClientProperties.java b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyClientProperties.java
index 9c79d12..562edad 100644
--- a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyClientProperties.java
+++ b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyClientProperties.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2024 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
@@ -129,4 +129,56 @@
     public static final Integer
             DEFAULT_EXPECT_100_CONTINUE_TIMEOUT_VALUE = 500;
 
+
+    /**
+     * Parameter which allows extending of the header size for the Netty connector
+     *
+     * @since 2.44
+     */
+    public static final String
+            MAX_HEADER_SIZE = "jersey.config.client.netty.maxHeaderSize";
+
+    /**
+     * Default header size for Netty Connector.
+     * Taken from {@link io.netty.handler.codec.http.HttpClientCodec#HttpClientCodec(int, int, int)}
+     *
+     * @since 2.44
+     */
+    public static final Integer
+        DEFAULT_HEADER_SIZE = 8192;
+
+    /**
+     * Parameter which allows extending of the initial line length for the Netty connector
+     *
+     * @since 2.44
+     */
+    public static final String
+            MAX_INITIAL_LINE_LENGTH = "jersey.config.client.netty.maxInitialLineLength";
+
+    /**
+     * Default initial line length for Netty Connector.
+     * Taken from {@link io.netty.handler.codec.http.HttpClientCodec#HttpClientCodec(int, int, int)}
+     *
+     * @since 2.44
+     */
+    public static final Integer
+        DEFAULT_INITIAL_LINE_LENGTH = 4096;
+
+    /**
+     * Parameter which allows extending of the chunk size for the Netty connector
+     *
+     * @since 2.44
+     */
+    public static final String
+            MAX_CHUNK_SIZE = "jersey.config.client.netty.maxChunkSize";
+
+    /**
+     * Default chunk size for Netty Connector.
+     * Taken from {@link io.netty.handler.codec.http.HttpClientCodec#HttpClientCodec(int, int, int)}
+     *
+     * @since 2.44
+     */
+    public static final Integer
+        DEFAULT_CHUNK_SIZE = 8192;
+
 }
diff --git a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java
index ab71e1f..cec1348 100644
--- a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java
+++ b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2024 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,7 +34,6 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
@@ -89,6 +88,10 @@
 import org.glassfish.jersey.client.innate.http.SSLParamConfigurator;
 import org.glassfish.jersey.client.spi.AsyncConnectorCallback;
 import org.glassfish.jersey.client.spi.Connector;
+import org.glassfish.jersey.innate.VirtualThreadUtil;
+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.OutboundMessageContext;
 import org.glassfish.jersey.netty.connector.internal.NettyEntityWriter;
 
@@ -104,6 +107,17 @@
     final Client client;
     final HashMap<String, ArrayList<Channel>> connections = new HashMap<>();
 
+    private static final LazyValue<String> NETTY_VERSION = Values.lazy(
+        (Value<String>) () -> {
+            String nettyVersion = null;
+            try {
+                nettyVersion = io.netty.util.Version.identify().values().iterator().next().artifactVersion();
+            } catch (Throwable t) {
+                nettyVersion = "4.1.x";
+            }
+            return "Netty " + nettyVersion;
+        });
+
     // If HTTP keepalive is enabled the value of "http.maxConnections" determines the maximum number
     // of idle connections that will be simultaneously kept alive, per destination.
     private static final String HTTP_KEEPALIVE_STRING = System.getProperty("http.keepAlive");
@@ -130,14 +144,15 @@
 
     NettyConnector(Client client) {
 
-        final Map<String, Object> properties = client.getConfiguration().getProperties();
+        final Configuration configuration = client.getConfiguration();
+        final Map<String, Object> properties = configuration.getProperties();
         final Object threadPoolSize = properties.get(ClientProperties.ASYNC_THREADPOOL_SIZE);
 
         if (threadPoolSize != null && threadPoolSize instanceof Integer && (Integer) threadPoolSize > 0) {
-            executorService = Executors.newFixedThreadPool((Integer) threadPoolSize);
+            executorService = VirtualThreadUtil.withConfig(configuration).newFixedThreadPool((Integer) threadPoolSize);
             this.group = new NioEventLoopGroup((Integer) threadPoolSize);
         } else {
-            executorService = Executors.newCachedThreadPool();
+            executorService = VirtualThreadUtil.withConfig(configuration).newCachedThreadPool();
             this.group = new NioEventLoopGroup();
         }
 
@@ -208,7 +223,7 @@
 
         try {
             final SSLParamConfigurator sslConfig = SSLParamConfigurator.builder()
-                    .request(jerseyRequest).setSNIAlways(true).build();
+                    .request(jerseyRequest).setSNIAlways(true).setSNIHostName(jerseyRequest).build();
 
             String key = requestUri.getScheme() + "://" + sslConfig.getSNIHostName() + ":" + port;
             ArrayList<Channel> conns;
@@ -303,7 +318,17 @@
                          p.addLast(sslHandler);
                      }
 
-                     p.addLast(new HttpClientCodec());
+                     final Integer maxHeaderSize = ClientProperties.getValue(config.getProperties(),
+                                NettyClientProperties.MAX_HEADER_SIZE,
+                                NettyClientProperties.DEFAULT_HEADER_SIZE);
+                     final Integer maxChunkSize = ClientProperties.getValue(config.getProperties(),
+                                NettyClientProperties.MAX_CHUNK_SIZE,
+                                NettyClientProperties.DEFAULT_CHUNK_SIZE);
+                     final Integer maxInitialLineLength = ClientProperties.getValue(config.getProperties(),
+                                NettyClientProperties.MAX_INITIAL_LINE_LENGTH,
+                                NettyClientProperties.DEFAULT_INITIAL_LINE_LENGTH);
+
+                     p.addLast(new HttpClientCodec(maxInitialLineLength, maxHeaderSize, maxChunkSize));
                      p.addLast(new ChunkedWriteHandler());
                      p.addLast(new HttpContentDecompressor());
                     }
@@ -514,7 +539,7 @@
 
     @Override
     public String getName() {
-        return "Netty 4.1.x";
+        return NETTY_VERSION.get();
     }
 
     @Override
diff --git a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/internal/NettyEntityWriter.java b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/internal/NettyEntityWriter.java
index a9e7040..bcd3fd8 100644
--- a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/internal/NettyEntityWriter.java
+++ b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/internal/NettyEntityWriter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2024 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
@@ -24,8 +24,10 @@
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * The Entity Writer is used to write entity in Netty. One implementation is delayed,
@@ -196,10 +198,7 @@
                 for (Runnable runnable : delayedOps) {
                     runnable.run();
                 }
-
-                if (outputStream.b != null) {
-                    writer.getOutputStream().write(outputStream.b, outputStream.off, outputStream.len);
-                }
+                outputStream._flush();
             }
         }
 
@@ -216,7 +215,7 @@
 
         @Override
         public long getLength() {
-            return outputStream.len - outputStream.off;
+            return outputStream.writeLen;
         }
 
         @Override
@@ -225,9 +224,9 @@
         }
 
         private class DelayedOutputStream extends OutputStream {
-            private byte[] b;
-            private int off;
-            private int len;
+            private final List<WriteAction> actions = new ArrayList<>();
+            private int writeLen = 0;
+            private AtomicBoolean streamFlushed = new AtomicBoolean(false);
 
             @Override
             public void write(int b) throws IOException {
@@ -241,15 +240,39 @@
 
             @Override
             public void write(byte[] b, int off, int len) throws IOException {
-                if (!flushed && this.b == null) {
-                    this.b = b;
-                    this.off = off;
-                    this.len = len;
+                if (!flushed) {
+                    actions.add(new WriteAction(b, off, len));
+                    writeLen += len;
                 } else {
-                    DelayedEntityWriter.this._flush();
+                    _flush();
                     writer.getOutputStream().write(b, off, len);
+                    writer.getOutputStream().flush();
                 }
             }
+
+            public void _flush() throws IOException {
+                if (streamFlushed.compareAndSet(false, true)) {
+                    DelayedEntityWriter.this._flush();
+                    for (WriteAction action : actions) {
+                        action.run();
+                    }
+                    actions.clear();
+                }
+            }
+        }
+
+        private class WriteAction {
+            private final byte[] b;
+
+            private WriteAction(byte[] b, int off, int len) {
+                this.b = new byte[len]; // b passed in can be reused
+                System.arraycopy(b, off, this.b, 0, len);
+            }
+
+            public void run() throws IOException {
+                writer.getOutputStream().write(b, 0, b.length);
+                writer.getOutputStream().flush();
+            }
         }
     }
 }
diff --git a/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/HugeHeaderTest.java b/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/HugeHeaderTest.java
new file mode 100644
index 0000000..dddf8d9
--- /dev/null
+++ b/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/HugeHeaderTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2024 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.netty.connector;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.jetty.JettyTestContainerFactory;
+import org.glassfish.jersey.test.jetty.JettyTestContainerProperties;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.HeaderParam;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Response;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+public class HugeHeaderTest extends JerseyTest {
+
+    private static final int SERVER_HEADER_SIZE = 1234567;
+
+    private static final String hugeHeader =
+              "abcdefghijklmnopqrstuvwxyz"
+            + "abcdefghijklmnopqrstuvwxyz"
+            + "abcdefghijklmnopqrstuvwxyz"
+            + "abcdefghijklmnopqrstuvwxyz"
+            + "abcdefghijklmnopqrstuvwxyz"
+            + "abcdefghijklmnopqrstuvwxyz"
+            + "abcdefghijklmnopqrstuvwxyz"
+            + "abcdefghijklmnopqrstuvwxyz"
+            + "abcdefghijklmnopqrstuvwxyz"
+            + "abcdefghijklmnopqrstuvwxyz";
+
+    @Path("/test")
+    public static class HttpMethodResource {
+        @POST
+        public Response post(
+                @HeaderParam("X-HUGE-HEADER") String hugeHeader,
+                String entity) {
+
+            return Response.noContent()
+                    .header("X-HUGE-HEADER", hugeHeader)
+                    .header("X-HUGE-HEADER-SIZE", hugeHeader.length())
+                    .build();
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(HugeHeaderTest.HttpMethodResource.class);
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        final Map<String, Object> configurationProperties = new HashMap<>();
+        configurationProperties.put(JettyTestContainerProperties.HEADER_SIZE, SERVER_HEADER_SIZE);
+        return new JettyTestContainerFactory(configurationProperties);
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.connectorProvider(new NettyConnectorProvider());
+    }
+
+    @Test
+    public void testContentHeaderTrunked() {
+        final StringBuffer veryHugeHeader = new StringBuffer();
+        for (int i = 1; i < 33; i++) {
+            veryHugeHeader.append(hugeHeader);
+        }
+        final Response response = target("test").request()
+                .header("X-HUGE-HEADER", veryHugeHeader.toString())
+                .post(null);
+
+        assertNull(response.getHeaderString("X-HUGE-HEADER-SIZE"));
+        assertNull(response.getHeaderString("X-HUGE-HEADER"));
+        response.close();
+    }
+
+    @Test
+    public void testConnectorHeaderSize() {
+        final StringBuffer veryHugeHeader = new StringBuffer();
+        for (int i = 1; i < 35; i++) {
+            veryHugeHeader.append(hugeHeader);
+        }
+        int headerSize = veryHugeHeader.length();
+        Response response = target("test")
+                .property(NettyClientProperties.MAX_HEADER_SIZE, 77750)
+                        .request()
+
+
+                        .header("X-HUGE-HEADER", veryHugeHeader.toString())
+                        .post(null);
+        assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
+
+        assertEquals(String.valueOf(headerSize), response.getHeaderString("X-HUGE-HEADER-SIZE"));
+        assertEquals(veryHugeHeader.toString(), response.getHeaderString("X-HUGE-HEADER"));
+        response.close();
+    }
+}
diff --git a/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerFactory.java b/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerFactory.java
index 28d03d4..11d74a6 100644
--- a/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerFactory.java
+++ b/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 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,10 +18,15 @@
 
 import java.io.IOException;
 import java.net.URI;
+import java.util.concurrent.ThreadFactory;
 
 import jakarta.ws.rs.ProcessingException;
+import jakarta.ws.rs.core.Configuration;
 
 import org.glassfish.jersey.grizzly2.httpserver.internal.LocalizationMessages;
+import org.glassfish.jersey.innate.VirtualThreadSupport;
+import org.glassfish.jersey.innate.VirtualThreadUtil;
+import org.glassfish.jersey.innate.virtual.LoomishExecutors;
 import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;
 import org.glassfish.jersey.process.JerseyProcessingUncaughtExceptionHandler;
 import org.glassfish.jersey.server.ApplicationHandler;
@@ -281,11 +286,20 @@
                 : uri.getPort();
 
         final NetworkListener listener = new NetworkListener("grizzly", host, port);
+        final Configuration configuration = handler != null ? handler.getConfiguration().getConfiguration() : null;
 
-        listener.getTransport().getWorkerThreadPoolConfig().setThreadFactory(new ThreadFactoryBuilder()
+        final LoomishExecutors executors = VirtualThreadUtil.withConfig(configuration, false);
+        final ThreadFactory threadFactory = new ThreadFactoryBuilder()
                 .setNameFormat("grizzly-http-server-%d")
                 .setUncaughtExceptionHandler(new JerseyProcessingUncaughtExceptionHandler())
-                .build());
+                .setThreadFactory(executors.getThreadFactory())
+                .build();
+
+        if (executors.isVirtual()) {
+            listener.getTransport().setWorkerThreadPool(executors.newCachedThreadPool());
+        } else {
+            listener.getTransport().getWorkerThreadPoolConfig().setThreadFactory(threadFactory);
+        }
 
         listener.setSecure(secure);
         if (sslEngineConfigurator != null) {
diff --git a/containers/grizzly2-servlet/src/main/java/module-info.java b/containers/grizzly2-servlet/src/main/java/module-info.java
index 2acb16f..07712cd 100644
--- a/containers/grizzly2-servlet/src/main/java/module-info.java
+++ b/containers/grizzly2-servlet/src/main/java/module-info.java
@@ -22,6 +22,7 @@
     requires org.glassfish.grizzly.http.server;
 
     requires org.glassfish.jersey.core.common;
+    requires org.glassfish.jersey.core.server;
     requires org.glassfish.jersey.container.servlet.core;
     requires org.glassfish.jersey.container.grizzly2.http;
 
diff --git a/containers/grizzly2-servlet/src/main/java/org/glassfish/jersey/grizzly2/servlet/GrizzlyWebContainerFactory.java b/containers/grizzly2-servlet/src/main/java/org/glassfish/jersey/grizzly2/servlet/GrizzlyWebContainerFactory.java
index a70ee7f..d513d0a 100644
--- a/containers/grizzly2-servlet/src/main/java/org/glassfish/jersey/grizzly2/servlet/GrizzlyWebContainerFactory.java
+++ b/containers/grizzly2-servlet/src/main/java/org/glassfish/jersey/grizzly2/servlet/GrizzlyWebContainerFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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 jakarta.servlet.Servlet;
 
 import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
 import org.glassfish.jersey.servlet.ServletContainer;
 import org.glassfish.jersey.uri.UriComponent;
 
@@ -251,11 +252,13 @@
             }
         }
 
+        ResourceConfig configuration = new ResourceConfig();
         if (initParams != null) {
             registration.setInitParameters(initParams);
+            configuration.addProperties((Map) initParams);
         }
 
-        HttpServer server = GrizzlyHttpServerFactory.createHttpServer(u);
+        HttpServer server = GrizzlyHttpServerFactory.createHttpServer(u, configuration);
         context.deploy(server);
         return server;
     }
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 56cf0ea..19810fa 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
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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
@@ -294,6 +294,7 @@
         final URI baseUri;
         final URI requestUri;
         try {
+            LOGGER.debugLog("ServletContainer.service(...) started");
             baseUri = absoluteUriBuilder.replacePath(encodedBasePath).build();
             String queryParameters = ContainerUtils.encodeUnsafeCharacters(request.getQueryString());
             if (queryParameters == null) {
@@ -530,6 +531,7 @@
         final URI baseUri;
         final URI requestUri;
         try {
+            LOGGER.debugLog("ServletContainer.doFilter(...) started");
             final UriBuilder absoluteUriBuilder = UriBuilder.fromUri(request.getRequestURL().toString());
 
             // depending on circumstances, use the correct path to replace in the absolute request URI
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 584aef0..e6f5155 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, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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,8 @@
 package org.glassfish.jersey.servlet;
 
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
 import java.lang.reflect.Type;
 import java.net.URI;
 import java.security.AccessController;
@@ -54,6 +56,7 @@
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 
+import org.glassfish.jersey.innate.io.InputStreamWrapper;
 import org.glassfish.jersey.internal.ServiceFinderBinder;
 import org.glassfish.jersey.internal.inject.AbstractBinder;
 import org.glassfish.jersey.internal.inject.InjectionManager;
@@ -412,7 +415,7 @@
     }
 
     /**
-     * Initialize {@code ContainerRequest} instance to used used to handle {@code servletRequest}.
+     * Initialize {@code ContainerRequest} instance to handle {@code servletRequest}.
      */
     private void initContainerRequest(
             final ContainerRequest requestContext,
@@ -420,7 +423,21 @@
             final HttpServletResponse servletResponse,
             final ResponseWriter responseWriter) throws IOException {
 
-        requestContext.setEntityStream(servletRequest.getInputStream());
+        try {
+            requestContext.setEntityStream(new InputStreamWrapper() {
+                @Override
+                protected InputStream getWrapped() {
+                    try {
+                        return servletRequest.getInputStream();
+                    } catch (IOException e) {
+                        throw new UncheckedIOException(e);
+                    }
+                }
+            });
+        } catch (UncheckedIOException e) {
+            throw e.getCause();
+        }
+
         requestContext.setRequestScopedInitializer(requestScopedInitializer.get(new RequestContextProvider() {
             @Override
             public HttpServletRequest getHttpServletRequest() {
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 2335932..5cf6812 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, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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
@@ -142,12 +142,7 @@
             }
         }
 
-        final String reasonPhrase = responseContext.getStatusInfo().getReasonPhrase();
-        if (reasonPhrase != null) {
-            response.setStatus(responseContext.getStatus());
-        } else {
-            response.setStatus(responseContext.getStatus());
-        }
+        response.setStatus(responseContext.getStatus());
 
         if (!responseContext.hasEntity()) {
             return null;
@@ -214,12 +209,13 @@
         try {
             if (!response.isCommitted()) {
                 try {
+                    final int statusCode = Response.Status.INTERNAL_SERVER_ERROR.getStatusCode();
                     if (configSetStatusOverSendError) {
                         response.reset();
                         //noinspection deprecation
-                        response.setStatus(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode());
+                        response.setStatus(statusCode);
                     } else {
-                        response.sendError(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()/*, "Request failed."*/);
+                        response.sendError(statusCode, "Request failed.");
                     }
                 } catch (final IllegalStateException ex) {
                     // a race condition externally committing the response can still occur...
diff --git a/containers/jersey-servlet-core/src/test/java/org/glassfish/jersey/servlet/internal/RequestInputStreamTest.java b/containers/jersey-servlet-core/src/test/java/org/glassfish/jersey/servlet/internal/RequestInputStreamTest.java
new file mode 100644
index 0000000..feb899c
--- /dev/null
+++ b/containers/jersey-servlet-core/src/test/java/org/glassfish/jersey/servlet/internal/RequestInputStreamTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2024 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.servlet.internal;
+
+import org.glassfish.jersey.CommonProperties;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.servlet.ServletProperties;
+import org.glassfish.jersey.servlet.WebComponent;
+import org.glassfish.jersey.servlet.WebConfig;
+import org.glassfish.jersey.servlet.WebFilterConfig;
+import org.junit.jupiter.api.Test;
+
+import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.net.URI;
+import java.util.Collections;
+import java.util.Enumeration;
+
+public class RequestInputStreamTest {
+    @Test
+    public void test404RequestInputStream() throws ServletException, IOException {
+        InvocationHandler handler = new InvocationHandler() {
+            @Override
+            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                switch (method.getName()) {
+                    case "getHeaderNames":
+                        return Collections.emptyEnumeration();
+                    case "getInputStream":
+                        throw new IllegalStateException("ServletRequest#getInputStream clashes with ServletRequest#getReader");
+                }
+                return null;
+            }
+        };
+
+        FilterConfig filterConfig = new FilterConfig() {
+            @Override
+            public String getFilterName() {
+                return null;
+            }
+
+            @Override
+            public ServletContext getServletContext() {
+                return (ServletContext) Proxy.newProxyInstance(getClass().getClassLoader(),
+                        new Class[]{ServletContext.class},
+                        handler);
+            }
+
+            @Override
+            public String getInitParameter(String name) {
+                return null;
+            }
+
+            @Override
+            public Enumeration<String> getInitParameterNames() {
+                return null;
+            }
+        };
+        WebConfig dummyWebConfig = new WebFilterConfig(filterConfig);
+        ResourceConfig resourceConfig = new ResourceConfig()
+                .property(CommonProperties.PROVIDER_DEFAULT_DISABLE, "ALL")
+                .property(ServerProperties.WADL_FEATURE_DISABLE, true)
+                .property(ServletProperties.FILTER_FORWARD_ON_404, true)
+                .property(ServerProperties.RESPONSE_SET_STATUS_OVER_SEND_ERROR, true);
+        WebComponent component = new WebComponent(dummyWebConfig, resourceConfig);
+        component.service(URI.create("http://localhost"), URI.create("http://localhost"),
+                (HttpServletRequest) Proxy.newProxyInstance(getClass().getClassLoader(),
+                        new Class[] {HttpServletRequest.class},
+                        handler
+                        ),
+                (HttpServletResponse) Proxy.newProxyInstance(getClass().getClassLoader(),
+                        new Class[]{HttpServletResponse.class},
+                        handler)
+                );
+    }
+}
diff --git a/containers/jetty-http/src/main/java11/org/glassfish/jersey/jetty/JettyHttpContainer.java b/containers/jetty-http/src/main/java11/org/glassfish/jersey/jetty/JettyHttpContainer.java
index 55258ea..3d519b6 100644
--- a/containers/jetty-http/src/main/java11/org/glassfish/jersey/jetty/JettyHttpContainer.java
+++ b/containers/jetty-http/src/main/java11/org/glassfish/jersey/jetty/JettyHttpContainer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2024 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/java17/org/glassfish/jersey/jetty/JettyHttpContainer.java b/containers/jetty-http/src/main/java17/org/glassfish/jersey/jetty/JettyHttpContainer.java
index dfe20f2..c555e92 100644
--- a/containers/jetty-http/src/main/java17/org/glassfish/jersey/jetty/JettyHttpContainer.java
+++ b/containers/jetty-http/src/main/java17/org/glassfish/jersey/jetty/JettyHttpContainer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2024 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,10 +23,13 @@
 import java.security.Principal;
 import java.util.List;
 import java.util.Map;
-import java.util.Timer;
-import java.util.TimerTask;
 import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -43,11 +46,13 @@
 import org.eclipse.jetty.security.AuthenticationState;
 import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.thread.Scheduler;
 import org.glassfish.jersey.internal.MapPropertiesDelegate;
 import org.glassfish.jersey.internal.inject.AbstractBinder;
 import org.glassfish.jersey.internal.inject.ReferencingFactory;
 import org.glassfish.jersey.internal.util.ExtendedLogger;
 import org.glassfish.jersey.internal.util.collection.Ref;
+import org.glassfish.jersey.jetty.internal.LocalizationMessages;
 import org.glassfish.jersey.process.internal.RequestScoped;
 import org.glassfish.jersey.server.ApplicationHandler;
 import org.glassfish.jersey.server.ContainerException;
@@ -137,6 +142,7 @@
 
         final ResponseWriter responseWriter = new ResponseWriter(request, response, callback, configSetStatusOverSendError);
         try {
+            LOGGER.debugLog(LocalizationMessages.CONTAINER_STARTED());
             final URI baseUri = getBaseUri(request);
             final URI requestUri = getRequestUri(request, baseUri);
             final ContainerRequest requestContext = new ContainerRequest(
@@ -246,38 +252,51 @@
         }
     }
 
-    private static final class ResponseWriter implements ContainerResponseWriter {
+    private static class ResponseWriter implements ContainerResponseWriter {
 
         private final Request request;
         private final Response response;
         private final Callback callback;
         private final boolean configSetStatusOverSendError;
-        private final Timer timer = new Timer();
-        private final long asyncStartTimeMillis;
+        private final long asyncStartTimeNanos;
+        private final Scheduler scheduler;
         private final ConcurrentLinkedQueue<TimeoutHandler> timeoutHandlerQueue = new ConcurrentLinkedQueue<>();
-        private TimerTask currentTimerTask;
+        private Scheduler.Task currentTimerTask;
 
-        ResponseWriter(final Request request, final Response response, final Callback callback,
-                       final boolean configSetStatusOverSendError) {
+        ResponseWriter(final Request request, final Response response,
+                       final Callback callback, final boolean configSetStatusOverSendError) {
             this.request = request;
             this.response = response;
             this.callback = callback;
-            this.asyncStartTimeMillis = System.currentTimeMillis();
+            this.asyncStartTimeNanos = System.nanoTime();
             this.configSetStatusOverSendError = configSetStatusOverSendError;
+
+            this.scheduler = request.getComponents().getScheduler();
         }
 
         private synchronized void setNewTimeout(long timeOut, TimeUnit timeUnit) {
-            long timeOutMillis = timeUnit.toMillis(timeOut);
+            long timeOutNanos = timeUnit.toNanos(timeOut);
             if (currentTimerTask != null) {
+                // Do not interrupt, see callTimeoutHandlers()
                 currentTimerTask.cancel();
             }
-            currentTimerTask = new TimerTask() {
-                @Override
-                public void run() {
-                    timeoutHandlerQueue.forEach(timeoutHandler -> timeoutHandler.onTimeout(ResponseWriter.this));
+            // Use System.nanoTime() as the clock source here, because the returned value is not prone to wall-clock
+            // drift - unlike System.currentTimeMillis().
+            long delayNanos = Math.max(asyncStartTimeNanos - System.nanoTime() + timeOutNanos, 0L);
+            currentTimerTask = scheduler.schedule(this::callTimeoutHandlers, delayNanos, TimeUnit.NANOSECONDS);
+        }
+
+        private void callTimeoutHandlers() {
+            // Note: Although it might not happen in practice, it is in theory possible that this function is
+            // called multiple times concurrently. To prevent any timeout handler being called twice, we poll()
+            // timeout handlers from the queue, instead of iterating over the queue.
+            while (true) {
+                TimeoutHandler handler = timeoutHandlerQueue.poll();
+                if (handler == null) {
+                    break;
                 }
-            };
-            timer.schedule(currentTimerTask, Math.max(0, timeOutMillis + asyncStartTimeMillis - System.currentTimeMillis()));
+                handler.onTimeout(ResponseWriter.this);
+            }
         }
 
         @Override
@@ -417,8 +436,15 @@
         super.doStop();
         appHandler.onShutdown(this);
         appHandler = null;
+
+        boolean needInterrupt = false;
+        if (needInterrupt) {
+            Thread.currentThread().interrupt();
+        }
     }
 
+    private static final AtomicInteger TIMEOUT_HANDLER_ID_GEN = new AtomicInteger();
+
     /**
      * Create a new Jetty HTTP container.
      *
diff --git a/containers/jetty-http/src/main/java17/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java b/containers/jetty-http/src/main/java17/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java
index 26a4b79..cb66f21 100644
--- a/containers/jetty-http/src/main/java17/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java
+++ b/containers/jetty-http/src/main/java17/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2024 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,7 +20,10 @@
 import java.util.concurrent.ThreadFactory;
 
 import jakarta.ws.rs.ProcessingException;
+import jakarta.ws.rs.core.Configuration;
 
+import org.glassfish.jersey.innate.VirtualThreadUtil;
+import org.glassfish.jersey.innate.virtual.LoomishExecutors;
 import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;
 import org.glassfish.jersey.jetty.internal.LocalizationMessages;
 import org.glassfish.jersey.process.JerseyProcessingUncaughtExceptionHandler;
@@ -254,7 +257,8 @@
         }
         final int port = (uri.getPort() == -1) ? defaultPort : uri.getPort();
 
-        final Server server = new Server(new JettyConnectorThreadPool());
+        final Configuration configuration = handler != null ? handler.getConfiguration() : null;
+        final Server server = new Server(new JettyConnectorThreadPool(configuration));
         final HttpConfiguration config = new HttpConfiguration();
         if (sslContextFactory != null) {
             config.setSecureScheme("https");
@@ -292,10 +296,20 @@
     //
     //  Keeping this for backwards compatibility for the time being
     private static final class JettyConnectorThreadPool extends QueuedThreadPool {
-        private final ThreadFactory threadFactory = new ThreadFactoryBuilder()
-                .setNameFormat("jetty-http-server-%d")
-                .setUncaughtExceptionHandler(new JerseyProcessingUncaughtExceptionHandler())
-                .build();
+        private final ThreadFactory threadFactory;
+
+        private JettyConnectorThreadPool(Configuration configuration) {
+            final LoomishExecutors executors = VirtualThreadUtil.withConfig(configuration, false);
+            if (executors.isVirtual()) {
+                super.setMaxThreads(Integer.MAX_VALUE - 1);
+            }
+
+            this.threadFactory = new ThreadFactoryBuilder()
+                    .setNameFormat("jetty-http-server-%d")
+                    .setUncaughtExceptionHandler(new JerseyProcessingUncaughtExceptionHandler())
+                    .setThreadFactory(executors.getThreadFactory())
+                    .build();
+        }
 
         @Override
         public Thread newThread(Runnable runnable) {
diff --git a/containers/jetty-http/src/main/resources/org/glassfish/jersey/jetty/internal/localization.properties b/containers/jetty-http/src/main/resources/org/glassfish/jersey/jetty/internal/localization.properties
index 8d507d5..2fb2350 100644
--- a/containers/jetty-http/src/main/resources/org/glassfish/jersey/jetty/internal/localization.properties
+++ b/containers/jetty-http/src/main/resources/org/glassfish/jersey/jetty/internal/localization.properties
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2013, 2023 Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2013, 2024 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,7 @@
 #
 
 # {0} - status code; {1} - status reason message
+container.started=JettyHttpContainer.handle(...) started.
 exception.sending.error.response=I/O exception occurred while sending "{0}/{1}" error response.
 error.when.creating.server=Exception thrown when trying to create jetty server.
 unable.to.close.response=Unable to close response output.
diff --git a/containers/jetty11-http/pom.xml b/containers/jetty11-http/pom.xml
index 0eea308..66e8e74 100644
--- a/containers/jetty11-http/pom.xml
+++ b/containers/jetty11-http/pom.xml
@@ -114,6 +114,13 @@
                     </instructions>
                 </configuration>
             </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-enforcer-plugin</artifactId>
+                <configuration>
+                    <rulesToSkip>dependencyConvergence</rulesToSkip>
+                </configuration>
+            </plugin>
         </plugins>
 
         <resources>
diff --git a/containers/jetty11-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainer.java b/containers/jetty11-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainer.java
index 36be72d..dc680a3 100644
--- a/containers/jetty11-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainer.java
+++ b/containers/jetty11-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2024 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
@@ -147,6 +147,7 @@
         final Response response = request.getResponse();
         final ResponseWriter responseWriter = new ResponseWriter(request, response, configSetStatusOverSendError);
         try {
+            LOGGER.debugLog(LocalizationMessages.CONTAINER_STARTED());
             final URI baseUri = getBaseUri(request);
             final URI requestUri = getRequestUri(request, baseUri);
             final ContainerRequest requestContext = new ContainerRequest(
diff --git a/containers/jetty11-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java b/containers/jetty11-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java
index 26a4b79..cb66f21 100644
--- a/containers/jetty11-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java
+++ b/containers/jetty11-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2024 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,7 +20,10 @@
 import java.util.concurrent.ThreadFactory;
 
 import jakarta.ws.rs.ProcessingException;
+import jakarta.ws.rs.core.Configuration;
 
+import org.glassfish.jersey.innate.VirtualThreadUtil;
+import org.glassfish.jersey.innate.virtual.LoomishExecutors;
 import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;
 import org.glassfish.jersey.jetty.internal.LocalizationMessages;
 import org.glassfish.jersey.process.JerseyProcessingUncaughtExceptionHandler;
@@ -254,7 +257,8 @@
         }
         final int port = (uri.getPort() == -1) ? defaultPort : uri.getPort();
 
-        final Server server = new Server(new JettyConnectorThreadPool());
+        final Configuration configuration = handler != null ? handler.getConfiguration() : null;
+        final Server server = new Server(new JettyConnectorThreadPool(configuration));
         final HttpConfiguration config = new HttpConfiguration();
         if (sslContextFactory != null) {
             config.setSecureScheme("https");
@@ -292,10 +296,20 @@
     //
     //  Keeping this for backwards compatibility for the time being
     private static final class JettyConnectorThreadPool extends QueuedThreadPool {
-        private final ThreadFactory threadFactory = new ThreadFactoryBuilder()
-                .setNameFormat("jetty-http-server-%d")
-                .setUncaughtExceptionHandler(new JerseyProcessingUncaughtExceptionHandler())
-                .build();
+        private final ThreadFactory threadFactory;
+
+        private JettyConnectorThreadPool(Configuration configuration) {
+            final LoomishExecutors executors = VirtualThreadUtil.withConfig(configuration, false);
+            if (executors.isVirtual()) {
+                super.setMaxThreads(Integer.MAX_VALUE - 1);
+            }
+
+            this.threadFactory = new ThreadFactoryBuilder()
+                    .setNameFormat("jetty-http-server-%d")
+                    .setUncaughtExceptionHandler(new JerseyProcessingUncaughtExceptionHandler())
+                    .setThreadFactory(executors.getThreadFactory())
+                    .build();
+        }
 
         @Override
         public Thread newThread(Runnable runnable) {
diff --git a/containers/jetty11-http/src/main/resources/org/glassfish/jersey/jetty/internal/localization.properties b/containers/jetty11-http/src/main/resources/org/glassfish/jersey/jetty/internal/localization.properties
index b3094ba..f6be7cf 100644
--- a/containers/jetty11-http/src/main/resources/org/glassfish/jersey/jetty/internal/localization.properties
+++ b/containers/jetty11-http/src/main/resources/org/glassfish/jersey/jetty/internal/localization.properties
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2013, 2023 Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2013, 2024 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,7 @@
 #
 
 # {0} - status code; {1} - status reason message
+container.started=JettyHttpContainer.handle(...) started.
 exception.sending.error.response=I/O exception occurred while sending "{0}/{1}" error response.
 error.when.creating.server=Exception thrown when trying to create jetty server.
 unable.to.close.response=Unable to close response output.
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/ChunkedInputReader.java b/core-client/src/main/java/org/glassfish/jersey/client/ChunkedInputReader.java
index 74c8cfb..fa020cf 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/ChunkedInputReader.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/ChunkedInputReader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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
@@ -35,6 +35,7 @@
 import org.glassfish.jersey.internal.PropertiesDelegate;
 import org.glassfish.jersey.internal.util.ReflectionHelper;
 import org.glassfish.jersey.message.MessageBodyWorkers;
+import org.glassfish.jersey.message.internal.ReaderInterceptorExecutor;
 
 /**
  * {@link jakarta.ws.rs.ext.MessageBodyWriter} for {@link ChunkedInput}.
@@ -71,7 +72,7 @@
 
         return new ChunkedInput(
                 chunkType,
-                inputStream,
+                ReaderInterceptorExecutor.closeableInputStream(inputStream),
                 annotations,
                 mediaType,
                 headers,
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/ClientExecutorProvidersConfigurator.java b/core-client/src/main/java/org/glassfish/jersey/client/ClientExecutorProvidersConfigurator.java
index df26b22..ab5b9f5 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/ClientExecutorProvidersConfigurator.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/ClientExecutorProvidersConfigurator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2024 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
@@ -32,11 +32,12 @@
 import org.glassfish.jersey.internal.util.collection.Value;
 import org.glassfish.jersey.internal.util.collection.Values;
 import org.glassfish.jersey.model.internal.ComponentBag;
-import org.glassfish.jersey.model.internal.ManagedObjectsFinalizer;
 import org.glassfish.jersey.process.internal.AbstractExecutorProvidersConfigurator;
 import org.glassfish.jersey.spi.ExecutorServiceProvider;
 import org.glassfish.jersey.spi.ScheduledExecutorServiceProvider;
 
+import jakarta.ws.rs.core.Configuration;
+
 /**
  * Configurator which initializes and register {@link ExecutorServiceProvider} and
  * {@link ScheduledExecutorServiceProvider}.
@@ -64,7 +65,8 @@
 
     @Override
     public void init(InjectionManager injectionManager, BootstrapBag bootstrapBag) {
-        Map<String, Object> runtimeProperties = bootstrapBag.getConfiguration().getProperties();
+        final Configuration configuration = bootstrapBag.getConfiguration();
+        Map<String, Object> runtimeProperties = configuration.getProperties();
 
         ExecutorServiceProvider defaultAsyncExecutorProvider;
         ScheduledExecutorServiceProvider defaultScheduledExecutorProvider;
@@ -94,12 +96,12 @@
                         .named("ClientAsyncThreadPoolSize");
                 injectionManager.register(asyncThreadPoolSizeBinding);
 
-                defaultAsyncExecutorProvider = new DefaultClientAsyncExecutorProvider(asyncThreadPoolSize);
+                defaultAsyncExecutorProvider = new DefaultClientAsyncExecutorProvider(asyncThreadPoolSize, configuration);
             } else {
                 if (MANAGED_EXECUTOR_SERVICE != null) {
                     defaultAsyncExecutorProvider = new ClientExecutorServiceProvider(MANAGED_EXECUTOR_SERVICE);
                 } else {
-                    defaultAsyncExecutorProvider = new DefaultClientAsyncExecutorProvider(0);
+                    defaultAsyncExecutorProvider = new DefaultClientAsyncExecutorProvider(0, configuration);
                 }
             }
         }
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java b/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java
index aa0676b..364bff0 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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
@@ -241,10 +241,33 @@
     public static final String OUTBOUND_CONTENT_LENGTH_BUFFER = CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER_CLIENT;
 
     /**
+     * If {@code true} then disable configuration of Json Binding (JSR-367)
+     * feature on client.
+     * <p>
+     * By default, Json Binding on client is automatically enabled if global
+     * property
+     * {@value org.glassfish.jersey.CommonProperties#JSON_BINDING_FEATURE_DISABLE}
+     * is not disabled. If set then the client property value overrides the
+     * global property value.
+     * <p>
+     * The default value is {@code false}.
+     * </p>
+     * <p>
+     * The name of the configuration property is <tt>{@value}</tt>.
+     * </p>
+     * <p>This constant is an alias for {@link CommonProperties#JSON_BINDING_FEATURE_DISABLE_CLIENT}.</p>
+     *
+     * @see org.glassfish.jersey.CommonProperties#JSON_BINDING_FEATURE_DISABLE
+     * @since 2.45
+     */
+    @PropertyAlias
+    public static final String JSON_BINDING_FEATURE_DISABLE = CommonProperties.JSON_BINDING_FEATURE_DISABLE_CLIENT;
+
+    /**
      * If {@code true} then disable configuration of Json Processing (JSR-353)
      * feature on client.
      * <p>
-     * By default Json Processing on client is automatically enabled if global
+     * By default, Json Processing on client is automatically enabled if global
      * property
      * {@value org.glassfish.jersey.CommonProperties#JSON_PROCESSING_FEATURE_DISABLE}
      * is not disabled. If set then the client property value overrides the
@@ -265,7 +288,7 @@
     /**
      * If {@code true} then disable META-INF/services lookup on client.
      * <p>
-     * By default Jersey looks up SPI implementations described by {@code META-INF/services/*} files.
+     * By default,  Jersey looks up SPI implementations described by {@code META-INF/services/*} files.
      * Then you can register appropriate provider  classes by {@link jakarta.ws.rs.core.Application}.
      * </p>
      * <p>
@@ -467,24 +490,6 @@
     public static final String QUERY_PARAM_STYLE = "jersey.config.client.uri.query.param.style";
 
     /**
-     * <p>
-     *     Most connectors support HOST header value to be used as an SNIHostName. However, the HOST header is restricted in JDK.
-     *     {@code HttpUrlConnector} and {@code JavaNetHttpConnector} need
-     *     to have an extra System Property set to allow HOST header.
-     *     As an option to HOST header, this property allows the HOST name to be pre-set on a Client and does not need to
-     *     be set on each request.
-     * </p>
-     * <p>
-     *     The value MUST be an instance of {@link java.lang.String}.
-     * </p>
-     * <p>
-     *     The name of the configuration property is <tt>{@value}</tt>.
-     * </p>
-     * @since 3.1.2
-     */
-    public static final String SNI_HOST_NAME = "jersey.config.client.sniHostName";
-
-    /**
      * Sets the {@link org.glassfish.jersey.client.spi.ConnectorProvider} class. Overrides the value from META-INF/services.
      *
      * <p>
@@ -501,6 +506,31 @@
     public static final String CONNECTOR_PROVIDER = "jersey.config.client.connector.provider";
 
     /**
+     * <p>
+     *     Sets the {@code hostName} to be used for calculating the {@link javax.net.ssl.SNIHostName} during the HTTPS request.
+     *     Takes precedence over the HTTP HOST header, if set.
+     * </p>
+     * <p>
+     *     By default, the {@code SNIHostName} is set when the HOST HTTP header differs from the HTTP request host.
+     *     When the {@code hostName} matches the HTTPS request host, the {@code SNIHostName} is not set,
+     *     and the HTTP HOST header is not used for setting the {@code SNIHostName}. This allows for Domain Fronting.
+     * </p>
+     * <p>
+     *    Most connectors support HOST header value to be used as an SNIHostName. However, the HOST header is restricted in JDK.
+     *    {@code HttpUrlConnector} and {@code JavaNetHttpConnector} need
+     *    to have an extra System Property set to allow HOST header.
+     * </p>
+     * <p>
+     *     The value MUST be an instance of {@link java.lang.String}.
+     * </p>
+     * <p>
+     *     The name of the configuration property is <tt>{@value}</tt>.
+     * </p>
+     * @since 3.1.2
+     */
+    public static final String SNI_HOST_NAME = "jersey.config.client.snihostname";
+
+    /**
      * <p>The {@link javax.net.ssl.SSLContext} {@link java.util.function.Supplier} to be used to set ssl context in the current
      * HTTP request. Has precedence over the {@link Client#getSslContext()}.
      * </p>
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 9b0e9c1..9896c7a 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, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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
@@ -25,6 +25,10 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -90,6 +94,8 @@
     private  LazyValue<PropertiesResolver> propertiesResolver = Values.lazy(
             (Value<PropertiesResolver>) () -> PropertiesResolver.create(getConfiguration(), getPropertiesDelegate())
     );
+    // by default nothing to be cancelled.
+    private Future cancellable = NotCancellable.INSTANCE;
 
     private static final Logger LOGGER = Logger.getLogger(ClientRequest.class.getName());
 
@@ -126,6 +132,7 @@
         this.writerInterceptors = original.writerInterceptors;
         this.propertiesDelegate = new MapPropertiesDelegate(original.propertiesDelegate);
         this.ignoreUserAgent = original.ignoreUserAgent;
+        this.cancellable = original.cancellable;
     }
 
     @Override
@@ -599,4 +606,66 @@
     public void ignoreUserAgent(final boolean ignore) {
         this.ignoreUserAgent = ignore;
     }
+
+    /**
+     * Sets the new {@code Future} that may cancel this {@link ClientRequest}.
+     * @param cancellable
+     */
+    void setCancellable(Future cancellable) {
+        this.cancellable = cancellable;
+    }
+
+    /**
+     * Cancels this {@link ClientRequest}. May result in {@link java.util.concurrent.CancellationException} later in this
+     * request processing if this {@link ClientRequest} is backed by a {@link Future} provided to
+     * {@link JerseyInvocation.Builder#setCancellable(Future)}.
+     * @param mayInterruptIfRunning may have no effect or {@code true} if the thread executing this task should be interrupted
+     *                              (if the thread is known to the implementation);
+     *                              otherwise, in-progress tasks are allowed to complete
+     */
+    public void cancel(boolean mayInterruptIfRunning) {
+        cancellable.cancel(mayInterruptIfRunning);
+    }
+
+    /**
+     * Returns {@code true} if this {@link ClientRequest} was cancelled
+     * before it completed normally.
+     *
+     * @return {@code true} if this {@link ClientRequest} was cancelled
+     * before it completed normally
+     */
+    public boolean isCancelled() {
+        return cancellable.isCancelled();
+    }
+
+    private static class NotCancellable implements Future {
+        public static final Future INSTANCE = new NotCancellable();
+        private boolean isCancelled = false;
+
+        @Override
+        public boolean cancel(boolean mayInterruptIfRunning) {
+            isCancelled = true;
+            return isCancelled;
+        }
+
+        @Override
+        public boolean isCancelled() {
+            return isCancelled;
+        }
+
+        @Override
+        public boolean isDone() {
+            return false;
+        }
+
+        @Override
+        public Object get() throws InterruptedException, ExecutionException {
+            return null;
+        }
+
+        @Override
+        public Object get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+            return null;
+        }
+    }
 }
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/DefaultClientAsyncExecutorProvider.java b/core-client/src/main/java/org/glassfish/jersey/client/DefaultClientAsyncExecutorProvider.java
index 2f9fa71..2a19b0d 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/DefaultClientAsyncExecutorProvider.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/DefaultClientAsyncExecutorProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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,9 @@
 
 import jakarta.inject.Inject;
 import jakarta.inject.Named;
+import jakarta.ws.rs.core.Configuration;
+import jakarta.ws.rs.core.Context;
+
 
 import org.glassfish.jersey.client.internal.LocalizationMessages;
 import org.glassfish.jersey.internal.util.collection.LazyValue;
@@ -46,8 +49,9 @@
      *                 See also {@link org.glassfish.jersey.client.ClientProperties#ASYNC_THREADPOOL_SIZE}.
      */
     @Inject
-    public DefaultClientAsyncExecutorProvider(@Named("ClientAsyncThreadPoolSize") final int poolSize) {
-        super("jersey-client-async-executor");
+    public DefaultClientAsyncExecutorProvider(@Named("ClientAsyncThreadPoolSize") final int poolSize,
+                                              @Context Configuration configuration) {
+        super("jersey-client-async-executor", configuration);
 
         this.asyncThreadPoolSize = Values.lazy(new Value<Integer>() {
             @Override
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/HttpUrlConnectorProvider.java b/core-client/src/main/java/org/glassfish/jersey/client/HttpUrlConnectorProvider.java
index e0446d2..306e336 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/HttpUrlConnectorProvider.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/HttpUrlConnectorProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2024 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,9 @@
 import java.net.Proxy;
 import java.net.URL;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 import java.util.logging.Logger;
 
 import jakarta.ws.rs.client.Client;
@@ -94,7 +97,7 @@
     /**
      * A value of {@code true} declares that the client will try to set
      * unsupported HTTP method to {@link java.net.HttpURLConnection} via
-     * reflection.
+     * reflection as a workaround for a missing HTTP method.
      * <p>
      * NOTE: Enabling this property may cause security related warnings/errors
      * and it may break when other JDK implementation is used. <b>Use only
@@ -103,6 +106,10 @@
      * <p>The value MUST be an instance of {@link java.lang.Boolean}.</p>
      * <p>The default value is {@code false}.</p>
      * <p>The name of the configuration property is <tt>{@value}</tt>.</p>
+     * <p>Since JDK 16 the JDK internal classes are not opened for reflection and the workaround method does not work,
+     * unless {@code --add-opens java.base/java.net=ALL-UNNAMED} for HTTP requests and additional
+     * {@code --add-opens java.base/sun.net.www.protocol.https=ALL-UNNAMED} for HTTPS (HttpsUrlConnection) options are set.
+     * </p>
      */
     public static final String SET_METHOD_WORKAROUND =
             "jersey.config.client.httpUrlConnection.setMethodWorkaround";
@@ -291,9 +298,26 @@
 
     private static class DefaultConnectionFactory implements ConnectionFactory {
 
+        private final ConcurrentHashMap<URL, Lock> locks = new ConcurrentHashMap<>();
+
         @Override
         public HttpURLConnection getConnection(final URL url) throws IOException {
-            return (HttpURLConnection) url.openConnection();
+            return connect(url, null);
+        }
+
+        @Override
+        public HttpURLConnection getConnection(URL url, Proxy proxy) throws IOException {
+            return connect(url, proxy);
+        }
+
+        private HttpURLConnection connect(URL url, Proxy proxy) throws IOException {
+            Lock lock = locks.computeIfAbsent(url, u -> new ReentrantLock());
+            lock.lock();
+            try {
+                return (proxy == null) ? (HttpURLConnection) url.openConnection() : (HttpURLConnection) url.openConnection(proxy);
+            } finally {
+                lock.unlock();
+            }
         }
     }
 
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/JerseyInvocation.java b/core-client/src/main/java/org/glassfish/jersey/client/JerseyInvocation.java
index 222c2a9..c120856 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/JerseyInvocation.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/JerseyInvocation.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2024 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
@@ -573,6 +573,18 @@
             throw new IllegalStateException(
                     LocalizationMessages.CLIENT_RX_PROVIDER_NOT_REGISTERED(clazz.getSimpleName()));
         }
+
+        /**
+         * Sets Future that backs {@link ClientRequest} {@link ClientRequest#isCancelled()} method. Can be used for instance
+         * by {@link CompletionStageRxInvoker} to pass the created {@link CompletableFuture} to the provided {@link SyncInvoker}.
+         * @param cancellable the {@link Future} whose result of {@link Future#cancel(boolean)} will be available by
+         * {@link ClientRequest#isCancelled()}.
+         * @return the updated builder.
+         */
+        public Builder setCancellable(Future cancellable) {
+            requestContext.setCancellable(cancellable);
+            return this;
+        }
     }
 
     /* package */ static class AsyncInvoker extends CompletableFutureAsyncInvoker implements jakarta.ws.rs.client.AsyncInvoker {
@@ -711,6 +723,8 @@
     public Future<Response> submit() {
         final CompletableFuture<Response> responseFuture = new CompletableFuture<>();
         final ClientRuntime runtime = request().getClientRuntime();
+
+        requestContext.setCancellable(responseFuture);
         runtime.submit(runtime.createRunnableForAsyncProcessing(requestForCall(requestContext),
                 new InvocationResponseCallback<>(responseFuture, (request, scope) -> translate(request, scope, Response.class))));
 
@@ -725,6 +739,7 @@
         final CompletableFuture<T> responseFuture = new CompletableFuture<>();
         final ClientRuntime runtime = request().getClientRuntime();
 
+        requestContext.setCancellable(responseFuture);
         runtime.submit(runtime.createRunnableForAsyncProcessing(requestForCall(requestContext),
                 new InvocationResponseCallback<T>(responseFuture, (request, scope) -> translate(request, scope, responseType))));
 
@@ -764,6 +779,7 @@
         final CompletableFuture<T> responseFuture = new CompletableFuture<>();
         final ClientRuntime runtime = request().getClientRuntime();
 
+        requestContext.setCancellable(responseFuture);
         runtime.submit(runtime.createRunnableForAsyncProcessing(requestForCall(requestContext),
                 new InvocationResponseCallback<T>(responseFuture, (request, scope) -> translate(request, scope, responseType))));
 
@@ -888,6 +904,7 @@
                 }
             };
             final ClientRuntime runtime = request().getClientRuntime();
+            requestContext.setCancellable(responseFuture);
             runtime.submit(runtime.createRunnableForAsyncProcessing(requestForCall(requestContext), responseCallback));
         } catch (final Throwable error) {
             final ProcessingException ce;
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SSLParamConfigurator.java b/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SSLParamConfigurator.java
index cb32a5c..79dc60e 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SSLParamConfigurator.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SSLParamConfigurator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2024 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,25 +16,23 @@
 
 package org.glassfish.jersey.client.innate.http;
 
-import jakarta.ws.rs.core.Configuration;
-import jakarta.ws.rs.core.HttpHeaders;
 import org.glassfish.jersey.client.ClientProperties;
 import org.glassfish.jersey.client.ClientRequest;
+import org.glassfish.jersey.http.HttpHeaders;
+import org.glassfish.jersey.internal.PropertiesResolver;
 
 import javax.net.ssl.SSLEngine;
 import javax.net.ssl.SSLParameters;
 import javax.net.ssl.SSLSocket;
+import jakarta.ws.rs.core.Configuration;
 import jakarta.ws.rs.core.UriBuilder;
-import org.glassfish.jersey.internal.PropertiesResolver;
-import org.glassfish.jersey.internal.util.PropertiesHelper;
-
 import java.net.InetAddress;
 import java.net.URI;
 import java.net.UnknownHostException;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Optional;
-import java.util.Properties;
 
 /**
  * A unified routines to configure {@link SSLParameters}.
@@ -48,11 +46,12 @@
      * Builder of the {@link SSLParamConfigurator} instance.
      */
     public static final class Builder {
-        private URI uri = null;
+        private URI uri;
         private String sniHostNameHeader = null;
-        private String sniHostNameProperty = null;
+        private String sniHostPrecedence = null;
         private boolean setAlways = false;
 
+
         /**
          * Sets the SNIHostName and {@link URI} from the {@link ClientRequest} instance.
          * @param clientRequest the {@link ClientRequest}
@@ -60,7 +59,7 @@
          */
         public Builder request(ClientRequest clientRequest) {
             this.sniHostNameHeader = getSniHostNameHeader(clientRequest.getHeaders());
-            this.sniHostNameProperty = clientRequest.resolveProperty(ClientProperties.SNI_HOST_NAME, String.class);
+            this.sniHostPrecedence = resolveSniHostNameProperty(clientRequest);
             this.uri = clientRequest.getUri();
             return this;
         }
@@ -71,7 +70,7 @@
          * @return the builder instance
          */
         public Builder configuration(Configuration configuration) {
-            this.sniHostNameProperty = (String) configuration.getProperty(ClientProperties.SNI_HOST_NAME);
+            this.sniHostPrecedence = getSniHostNameProperty(configuration);
             return this;
         }
 
@@ -107,6 +106,62 @@
         }
 
         /**
+         * <p>
+         *     Sets the {@code hostName} to be used for calculating the  {@link javax.net.ssl.SNIHostName}.
+         *     Takes precedence over the HTTP HOST header, if set.
+         * </p>
+         * <p>
+         *     By default, the {@code SNIHostName} is set when the HOST HTTP header differs from the HTTP request host.
+         *     When the {@code hostName} matches the HTTP request host, the {@code SNIHostName} is not set,
+         *     and the HTTP HOST header is not used for setting the {@code SNIHostName}. This allows Domain Fronting.
+         * </p>
+         * @param hostName the host the {@code SNIHostName} should be set for.
+         * @return the builder instance.
+         */
+        public Builder setSNIHostName(String hostName) {
+            sniHostPrecedence = hostName;
+            return this;
+        }
+
+        /**
+         * <p>
+         *     Sets the {@code hostName} to be used for calculating the  {@link javax.net.ssl.SNIHostName}.
+         *     The {@code hostName} value is taken from the {@link Configuration} if the property
+         *     {@link ClientProperties#SNI_HOST_NAME} is set.
+         *     Takes precedence over the HTTP HOST header, if set.
+         * </p>
+         * <p>
+         *     By default, the {@code SNIHostName} is set when the HOST HTTP header differs from the HTTP request host.
+         *     When the {@code hostName} matches the HTTP request host, the {@code SNIHostName} is not set,
+         *     and the HTTP HOST header is not used for setting the {@code SNIHostName}. This allows for Domain Fronting.
+         * </p>
+         * @param configuration the host the {@code SNIHostName} should be set for.
+         * @return the builder instance.
+         */
+        public Builder setSNIHostName(Configuration configuration) {
+            return setSNIHostName(getSniHostNameProperty(configuration));
+        }
+
+        /**
+         * <p>
+         *     Sets the {@code hostName} to be used for calculating the  {@link javax.net.ssl.SNIHostName}.
+         *     The {@code hostName} value is taken from the {@link PropertiesResolver} if the property
+         *     {@link ClientProperties#SNI_HOST_NAME} is set.
+         *     Takes precedence over the HTTP HOST header, if set.
+         * </p>
+         * <p>
+         *     By default, the {@code SNIHostName} is set when the HOST HTTP header differs from the HTTP request host.
+         *     When the {@code hostName} matches the HTTPS request host, the {@code SNIHostName} is not set,
+         *     and the HTTP HOST header is not used for setting the {@code SNIHostName}. This allows for Domain Fronting.
+         * </p>
+         * @param resolver the host the {@code SNIHostName} should be set for.
+         * @return the builder instance.
+         */
+        public Builder setSNIHostName(PropertiesResolver resolver) {
+            return setSNIHostName(resolveSniHostNameProperty(resolver));
+        }
+
+        /**
          * Builds the {@link SSLParamConfigurator} instance.
          * @return the configured {@link SSLParamConfigurator} instance.
          */
@@ -121,23 +176,34 @@
             }
 
             final String hostHeader = hostHeaders.get(0).toString();
-            final String trimmedHeader;
-            if (hostHeader != null) {
-                int index = hostHeader.indexOf(':'); // RFC 7230  Host = uri-host [ ":" port ] ;
-                final String trimmedHeader0 = index != -1 ? hostHeader.substring(0, index).trim() : hostHeader.trim();
-                trimmedHeader = trimmedHeader0.isEmpty() ? hostHeader : trimmedHeader0;
-            } else {
-                trimmedHeader = null;
-            }
+            return hostHeader;
+        }
 
-            return trimmedHeader;
+        private static String resolveSniHostNameProperty(PropertiesResolver resolver) {
+            String property = resolver.resolveProperty(ClientProperties.SNI_HOST_NAME, String.class);
+            if (property == null) {
+                property = resolver.resolveProperty(ClientProperties.SNI_HOST_NAME.toLowerCase(Locale.ROOT), String.class);
+            }
+            return property;
+        }
+
+        private static String getSniHostNameProperty(Configuration configuration) {
+            Object property = configuration.getProperty(ClientProperties.SNI_HOST_NAME);
+            if (property == null) {
+                property = configuration.getProperty(ClientProperties.SNI_HOST_NAME.toLowerCase(Locale.ROOT));
+            }
+            return (String) property;
         }
     }
 
     private SSLParamConfigurator(SSLParamConfigurator.Builder builder) {
-        String sniHostName = builder.sniHostNameHeader == null ? builder.sniHostNameProperty : builder.sniHostNameHeader;
         uri = builder.uri;
-        sniConfigurator = SniConfigurator.createWhenHostHeader(uri, sniHostName, builder.setAlways);
+        if (builder.sniHostPrecedence == null) {
+            sniConfigurator = SniConfigurator.createWhenHostHeader(uri, builder.sniHostNameHeader, builder.setAlways);
+        } else {
+            // Do not set SNI always, the property can be used to turn the SNI off
+            sniConfigurator = SniConfigurator.createWhenHostHeader(uri, builder.sniHostPrecedence, false);
+        }
     }
 
     /**
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SniConfigurator.java b/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SniConfigurator.java
index 9e766f5..baf1c28 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SniConfigurator.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SniConfigurator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2024 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
@@ -50,26 +50,31 @@
     }
 
     /**
-     * Create ClientSNI when {@link HttpHeaders#HOST} is set different from the request URI host (or {@code whenDiffer}.is false).
+     * Create {@link SniConfigurator} when {@code sniHost} is set different from the request URI host
+     * (or {@code whenDiffer}.is false).
      * @param hostUri the Uri of the HTTP request
-     * @param sniHostName the SniHostName either from HttpHeaders or the
-     *      {@link org.glassfish.jersey.client.ClientProperties#SNI_HOST_NAME} property from Configuration object.
+     * @param sniHost the preferred host name to create the {@link SNIHostName}
      * @param whenDiffer create {@SniConfigurator only when different from the request URI host}
-     * @return ClientSNI or empty when {@link HttpHeaders#HOST}
+     * @return Optional {@link SniConfigurator} or empty when {@code sniHost} is equal to the requestHost
      */
-    static Optional<SniConfigurator> createWhenHostHeader(URI hostUri, String sniHostName, boolean whenDiffer) {
-        if (sniHostName == null) {
+    static Optional<SniConfigurator> createWhenHostHeader(URI hostUri, String sniHost, boolean whenDiffer) {
+        final String trimmedHeader;
+        if (sniHost != null) {
+            int index = sniHost.indexOf(':'); // RFC 7230  Host = uri-host [ ":" port ] ;
+            final String trimmedHeader0 = index != -1 ? sniHost.substring(0, index).trim() : sniHost.trim();
+            trimmedHeader = trimmedHeader0.isEmpty() ? sniHost : trimmedHeader0;
+        } else {
             return Optional.empty();
         }
 
         if (hostUri != null) {
             final String hostUriString = hostUri.getHost();
-            if (!whenDiffer && hostUriString.equals(sniHostName)) {
+            if (!whenDiffer && hostUriString.equals(trimmedHeader)) {
                 return Optional.empty();
             }
         }
 
-        return Optional.of(new SniConfigurator(sniHostName));
+        return Optional.of(new SniConfigurator(trimmedHeader));
     }
 
     /**
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/innate/inject/NonInjectionManager.java b/core-client/src/main/java/org/glassfish/jersey/client/innate/inject/NonInjectionManager.java
index 047bba8..8fb151a 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/innate/inject/NonInjectionManager.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/innate/inject/NonInjectionManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2024 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
@@ -56,6 +56,7 @@
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.concurrent.locks.ReentrantLock;
 import java.util.function.Supplier;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -90,20 +91,25 @@
      */
     private class TypedInstances<TYPE> {
         private final MultivaluedMap<TYPE, InstanceContext<?>> singletonInstances = new MultivaluedHashMap<>();
-        private final ThreadLocal<MultivaluedMap<TYPE, InstanceContext<?>>> threadInstances = new ThreadLocal<>();
+        private ThreadLocal<MultivaluedMap<TYPE, InstanceContext<?>>> threadInstances = new ThreadLocal<>();
         private final List<Object> threadPredestroyables = Collections.synchronizedList(new LinkedList<>());
+        private final ReentrantLock singletonInstancesLock = new ReentrantLock();
 
         private <T> List<InstanceContext<?>> _getSingletons(TYPE clazz) {
             List<InstanceContext<?>> si;
-            synchronized (singletonInstances) {
+            singletonInstancesLock.lock();
+            try {
                 si = singletonInstances.get(clazz);
+            } finally {
+                singletonInstancesLock.unlock();
             }
             return si;
         }
 
         @SuppressWarnings("unchecked")
         <T> T _addSingleton(TYPE clazz, T instance, Binding<?, ?> binding, Annotation[] qualifiers) {
-            synchronized (singletonInstances) {
+            singletonInstancesLock.lock();
+            try {
                 // check existing singleton with a qualifier already created by another thread io a meantime
                 List<InstanceContext<?>> values = singletonInstances.get(clazz);
                 if (values != null) {
@@ -118,6 +124,8 @@
                 singletonInstances.add(clazz, new InstanceContext<>(instance, binding, qualifiers));
                 threadPredestroyables.add(instance);
                 return instance;
+            } finally {
+                singletonInstancesLock.unlock();
             }
         }
 
@@ -195,6 +203,8 @@
         void dispose() {
             singletonInstances.forEach((clazz, instances) -> instances.forEach(instance -> preDestroy(instance.getInstance())));
             threadPredestroyables.forEach(NonInjectionManager.this::preDestroy);
+            /* The java.lang.ThreadLocal$ThreadLocalMap$Entry[] keeps references to this NonInjectionManager */
+            threadInstances = null;
         }
     }
 
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java b/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java
index f5a3409..ca73f4c 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2024 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,6 +37,7 @@
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
+import java.util.concurrent.CancellationException;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Future;
 import java.util.function.Supplier;
@@ -65,6 +66,7 @@
 import org.glassfish.jersey.client.spi.AsyncConnectorCallback;
 import org.glassfish.jersey.client.spi.Connector;
 import org.glassfish.jersey.internal.util.PropertiesHelper;
+import org.glassfish.jersey.internal.util.collection.LRU;
 import org.glassfish.jersey.internal.util.collection.LazyValue;
 import org.glassfish.jersey.internal.util.collection.UnsafeValue;
 import org.glassfish.jersey.internal.util.collection.Value;
@@ -82,7 +84,7 @@
     private static final String ALLOW_RESTRICTED_HEADERS_SYSTEM_PROPERTY = "sun.net.http.allowRestrictedHeaders";
     // Avoid multi-thread uses of HttpsURLConnection.getDefaultSSLSocketFactory() because it does not implement a
     // proper lazy-initialization. See https://github.com/jersey/jersey/issues/3293
-    private static final LazyValue<SSLSocketFactory> DEFAULT_SSL_SOCKET_FACTORY =
+    private static final Value<SSLSocketFactory> DEFAULT_SSL_SOCKET_FACTORY =
             Values.lazy((Value<SSLSocketFactory>) () -> HttpsURLConnection.getDefaultSSLSocketFactory());
     // The list of restricted headers is extracted from sun.net.www.protocol.http.HttpURLConnection
     private static final String[] restrictedHeaders = {
@@ -113,7 +115,12 @@
     private final boolean fixLengthStreaming;
     private final boolean setMethodWorkaround;
     private final boolean isRestrictedHeaderPropertySet;
-    private LazyValue<SSLSocketFactory> sslSocketFactory;
+    private Value<SSLSocketFactory> sslSocketFactory;
+
+    // SSLContext#getSocketFactory not idempotent
+    // JDK KeepAliveCache keeps connections per Factory
+    // SSLContext set per request blows that -> keep factory in LRU
+    private final LRU<SSLContext, SSLSocketFactory> sslSocketFactoryCache = LRU.create();
 
     private final ConnectorExtension<HttpURLConnection, IOException> connectorExtension
             = new HttpUrlExpect100ContinueConnectorExtension();
@@ -142,6 +149,13 @@
         this.fixLengthStreaming = fixLengthStreaming;
         this.setMethodWorkaround = setMethodWorkaround;
 
+        this.sslSocketFactory = Values.lazy(new Value<SSLSocketFactory>() {
+            @Override
+            public SSLSocketFactory get() {
+                return client.getSslContext().getSocketFactory();
+            }
+        });
+
         // check if sun.net.http.allowRestrictedHeaders system property has been set and log the result
         // the property is being cached in the HttpURLConnection, so this is only informative - there might
         // already be some connection(s), that existed before the property was set/changed.
@@ -155,7 +169,7 @@
         );
     }
 
-    private static InputStream getInputStream(final HttpURLConnection uc) throws IOException {
+    private static InputStream getInputStream(final HttpURLConnection uc, final ClientRequest clientRequest) throws IOException {
         return new InputStream() {
             private final UnsafeValue<InputStream, IOException> in = Values.lazy(new UnsafeValue<InputStream, IOException>() {
                 @Override
@@ -190,6 +204,10 @@
                 if (closed) {
                     throw new IOException("Stream closed");
                 }
+                if (clientRequest.isCancelled()) {
+                    close();
+                    throw new IOException(new CancellationException());
+                }
             }
 
             @Override
@@ -311,7 +329,7 @@
             if (DEFAULT_SSL_SOCKET_FACTORY.get() == suc.getSSLSocketFactory()) {
                 // indicates that the custom socket factory was not set
                 suc.setSSLSocketFactory(sslSocketFactory.get());
-                }
+            }
         }
     }
 
@@ -337,22 +355,30 @@
         }
     }
 
-    private void setSslContextFactory(Client client, ClientRequest request) {
+    protected void setSslContextFactory(Client client, ClientRequest request) {
         final Supplier<SSLContext> supplier = request.resolveProperty(ClientProperties.SSL_CONTEXT_SUPPLIER, Supplier.class);
 
-        sslSocketFactory = Values.lazy(new Value<SSLSocketFactory>() {
-            @Override
-            public SSLSocketFactory get() {
-                final SSLContext ctx = supplier == null ? client.getSslContext() : supplier.get();
-                return ctx.getSocketFactory();
-            }
-        });
+        if (supplier != null) {
+            sslSocketFactory = Values.lazy(new Value<SSLSocketFactory>() { // lazy for double-check locking if multiple requests
+                @Override
+                public SSLSocketFactory get() {
+                    SSLContext sslContext = supplier.get();
+                    SSLSocketFactory factory = sslSocketFactoryCache.getIfPresent(sslContext);
+                    if (factory == null) {
+                        factory = sslContext.getSocketFactory();
+                        sslSocketFactoryCache.put(sslContext, factory);
+                    }
+                    return factory;
+                }
+            });
+        }
     }
 
     private ClientResponse _apply(final ClientRequest request) throws IOException {
         final HttpURLConnection uc;
         final Optional<ClientProxy> proxy = ClientProxy.proxyFromRequest(request);
-        final SSLParamConfigurator sniConfig = SSLParamConfigurator.builder().request(request).build();
+        final SSLParamConfigurator sniConfig = SSLParamConfigurator.builder().request(request)
+                .setSNIHostName(request).build();
         final URI sniUri;
         if (sniConfig.isSNIRequired()) {
             sniUri = sniConfig.toIPRequestUri();
@@ -448,7 +474,7 @@
         );
 
         try {
-            InputStream inputStream = getInputStream(uc);
+            InputStream inputStream = getInputStream(uc, request);
             responseContext.setEntityStream(inputStream);
         } catch (IOException ioe) {
             // allow at least a partial response in a ResponseProcessingException
diff --git a/core-client/src/main/resources/org/glassfish/jersey/client/internal/localization.properties b/core-client/src/main/resources/org/glassfish/jersey/client/internal/localization.properties
index 291ac98..8940d72 100644
--- a/core-client/src/main/resources/org/glassfish/jersey/client/internal/localization.properties
+++ b/core-client/src/main/resources/org/glassfish/jersey/client/internal/localization.properties
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2012, 2024 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
@@ -50,7 +50,7 @@
   Reverting to programmatically set default: [{1}]
 negative.input.parameter="Input parameter {0} must not be negative1."
 noninject.ambiguous.services=Ambiguous providing services ${0}.
-noninject.fallback=Falling back to injection-less client.
+noninject.fallback=Jersey-HK2 module is missing. Falling back to injection-less client. Injection may not be supported on the client.
 noninject.no.constructor=No applicable constructor for ${0} found.
 noninject.no.binding=No binding found for ${0}.
 noninject.requestscope.created=RequestScope already created.
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 1f7de34..c075af9 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, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2024 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,8 +46,8 @@
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.same;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.same;
 
 /**
  * {@code ClientRequest} unit tests.
diff --git a/core-client/src/test/java/org/glassfish/jersey/client/SSLSocketFactoryTest.java b/core-client/src/test/java/org/glassfish/jersey/client/SSLSocketFactoryTest.java
new file mode 100644
index 0000000..362bbe6
--- /dev/null
+++ b/core-client/src/test/java/org/glassfish/jersey/client/SSLSocketFactoryTest.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2024 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.client;
+
+import org.glassfish.jersey.client.internal.HttpUrlConnector;
+import org.glassfish.jersey.client.spi.Connector;
+import org.glassfish.jersey.internal.MapPropertiesDelegate;
+import org.glassfish.jersey.internal.PropertiesDelegate;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.Test;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.core.Response;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+
+public class SSLSocketFactoryTest {
+    static final AtomicReference<SSLSocketFactory> factoryHolder = new AtomicReference<>();
+    static SSLSocketFactory defaultSocketFactory = HttpsURLConnection.getDefaultSSLSocketFactory();
+
+    // @Test
+    // Alternative test
+    // Check KeepAliveCache#get(URL url, Object obj)
+    public void testSingleConnection() throws InterruptedException, IOException {
+        Client client = ClientBuilder.newClient();
+
+        for (int i = 0; i < 3; i++) {
+            try (Response response = client.target("https://www.spiegel.de")
+                    .request()
+                    .get()) {
+
+                response.readEntity(String.class);
+                System.out.println(String.format("response = %s", response));
+                Thread.sleep(1000);
+            }
+        }
+
+        System.in.read();
+    }
+
+    @Test
+    public void testSslContextFactoryOnClientIsSameForConsecutiveRequests() throws IOException, URISyntaxException {
+        int firstRequestFactory, secondRequestFactory = 0;
+        Client client = ClientBuilder.newClient();
+        HttpUrlConnectorProvider.ConnectionFactory connectionFactory = (url) -> (HttpURLConnection) url.openConnection();
+        SSLSocketFactoryConnector connector = (SSLSocketFactoryConnector) new SSlSocketFactoryUrlConnectorProvider()
+                .createHttpUrlConnector(client, connectionFactory, 4096, true, false);
+        URL url = new URL("https://somewhere.whereever:8080");
+        URLConnection urlConnection = url.openConnection();
+
+        // First Request
+        connector.setSslContextFactory(client, new ClientRequest(url.toURI(),
+                (ClientConfig) client.getConfiguration(), new MapPropertiesDelegate()));
+        connector.secureConnection((JerseyClient) client, (HttpURLConnection) urlConnection);
+        firstRequestFactory = factoryHolder.get().hashCode();
+
+        // reset to the default socketFactory
+        ((HttpsURLConnection) urlConnection).setSSLSocketFactory(defaultSocketFactory);
+
+        // Second Request
+        connector.setSslContextFactory(client, new ClientRequest(url.toURI(),
+                (ClientConfig) client.getConfiguration(), new MapPropertiesDelegate()));
+        connector.secureConnection((JerseyClient) client, (HttpURLConnection) urlConnection);
+        secondRequestFactory = factoryHolder.get().hashCode();
+
+        MatcherAssert.assertThat(firstRequestFactory, Matchers.equalTo(secondRequestFactory));
+    }
+
+    @Test
+    public void testSslContextFactoryOnRequestIsSameForConsecutiveRequests() throws IOException, URISyntaxException {
+        SSLSocketFactory firstRequestFactory, secondRequestFactory = null;
+        Client client = ClientBuilder.newClient();
+        SSLContext sslContext = new SslContextClientBuilder().build();
+        HttpUrlConnectorProvider.ConnectionFactory connectionFactory = (url) -> (HttpURLConnection) url.openConnection();
+        SSLSocketFactoryConnector connector = (SSLSocketFactoryConnector) new SSlSocketFactoryUrlConnectorProvider()
+                .createHttpUrlConnector(client, connectionFactory, 4096, true, false);
+        URL url = new URL("https://somewhere.whereever:8080");
+        URLConnection urlConnection = url.openConnection();
+        PropertiesDelegate propertiesDelegate = new MapPropertiesDelegate();
+        propertiesDelegate.setProperty(ClientProperties.SSL_CONTEXT_SUPPLIER, (Supplier<SSLContext>) () -> sslContext);
+
+        // First Request
+        connector.setSslContextFactory(client, new ClientRequest(url.toURI(),
+                (ClientConfig) client.getConfiguration(), propertiesDelegate));
+        connector.secureConnection((JerseyClient) client, (HttpURLConnection) urlConnection);
+        firstRequestFactory = factoryHolder.get();
+
+        // reset to the default socketFactory
+        ((HttpsURLConnection) urlConnection).setSSLSocketFactory(defaultSocketFactory);
+
+        // Second Request
+        connector.setSslContextFactory(client, new ClientRequest(url.toURI(),
+                (ClientConfig) client.getConfiguration(), propertiesDelegate));
+        connector.secureConnection((JerseyClient) client, (HttpURLConnection) urlConnection);
+        secondRequestFactory = factoryHolder.get();
+
+        MatcherAssert.assertThat(firstRequestFactory, Matchers.equalTo(secondRequestFactory));
+    }
+
+    private static class SSLSocketFactoryConnector extends HttpUrlConnector {
+        public SSLSocketFactoryConnector(Client client, HttpUrlConnectorProvider.ConnectionFactory connectionFactory,
+                                         int chunkSize, boolean fixLengthStreaming, boolean setMethodWorkaround) {
+            super(client, connectionFactory, chunkSize, fixLengthStreaming, setMethodWorkaround);
+        }
+
+        @Override
+        protected void secureConnection(JerseyClient client, HttpURLConnection uc) {
+            super.secureConnection(client, uc);
+            if (HttpsURLConnection.class.isInstance(uc)) {
+                SSLSocketFactory factory = ((HttpsURLConnection) uc).getSSLSocketFactory();
+                factoryHolder.set(factory);
+            }
+        }
+
+        @Override
+        protected void setSslContextFactory(Client client, ClientRequest request) {
+            super.setSslContextFactory(client, request);
+        }
+    }
+
+    private static class SSlSocketFactoryUrlConnectorProvider extends HttpUrlConnectorProvider {
+        @Override
+        protected Connector createHttpUrlConnector(Client client, ConnectionFactory connectionFactory, int chunkSize,
+                                                   boolean fixLengthStreaming, boolean setMethodWorkaround) {
+            return new SSLSocketFactoryConnector(
+                    client,
+                    connectionFactory,
+                    chunkSize,
+                    fixLengthStreaming,
+                    setMethodWorkaround);
+        }
+    }
+}
diff --git a/core-client/src/test/java/org/glassfish/jersey/client/innate/http/SSLParamConfiguratorTest.java b/core-client/src/test/java/org/glassfish/jersey/client/innate/http/SSLParamConfiguratorTest.java
new file mode 100644
index 0000000..06dcdec
--- /dev/null
+++ b/core-client/src/test/java/org/glassfish/jersey/client/innate/http/SSLParamConfiguratorTest.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2024 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.client.innate.http;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.client.ClientRequest;
+import org.glassfish.jersey.client.JerseyClient;
+import org.glassfish.jersey.http.HttpHeaders;
+import org.glassfish.jersey.internal.MapPropertiesDelegate;
+import org.glassfish.jersey.internal.PropertiesDelegate;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.core.MultivaluedHashMap;
+
+import java.net.URI;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+public class SSLParamConfiguratorTest {
+    @Test
+    public void testNoHost() {
+        final URI uri = URI.create("http://xxx.com:8080");
+        final JerseyClient client = (JerseyClient) ClientBuilder.newClient();
+        final ClientConfig config = client.getConfiguration();
+        final PropertiesDelegate delegate = new MapPropertiesDelegate();
+        ClientRequest request = new ClientRequest(uri, config, delegate) {};
+        SSLParamConfigurator configurator = SSLParamConfigurator.builder().request(request).build();
+        MatcherAssert.assertThat(configurator.isSNIRequired(), Matchers.is(false));
+    }
+
+    @Test
+    public void testHostHeaderHasPrecedence() {
+        final URI uri = URI.create("http://xxx.com:8080");
+        final JerseyClient client = (JerseyClient) ClientBuilder.newClient();
+        final ClientConfig config = client.getConfiguration();
+        final PropertiesDelegate delegate = new MapPropertiesDelegate();
+        ClientRequest request = new ClientRequest(uri, config, delegate) {};
+        request.getHeaders().add(HttpHeaders.HOST, "yyy.com");
+        SSLParamConfigurator configurator = SSLParamConfigurator.builder().request(request).build();
+        MatcherAssert.assertThat(configurator.isSNIRequired(), Matchers.is(true));
+        MatcherAssert.assertThat(configurator.getSNIHostName(), Matchers.is("yyy.com"));
+    }
+
+    @Test
+    public void testPropertyOnClientHasPrecedence() {
+        final URI uri = URI.create("http://xxx.com:8080");
+        final JerseyClient client = (JerseyClient) ClientBuilder.newClient();
+        final ClientConfig config = client.getConfiguration();
+        final PropertiesDelegate delegate = new MapPropertiesDelegate();
+        client.property(ClientProperties.SNI_HOST_NAME, "yyy.com");
+        ClientRequest request = new ClientRequest(uri, config, delegate) {};
+        SSLParamConfigurator configurator = SSLParamConfigurator.builder().request(request).build();
+        MatcherAssert.assertThat(configurator.isSNIRequired(), Matchers.is(true));
+        MatcherAssert.assertThat(configurator.getSNIHostName(), Matchers.is("yyy.com"));
+    }
+
+    @Test
+    public void testPropertyOnDelegateHasPrecedence() {
+        final URI uri = URI.create("http://xxx.com:8080");
+        final JerseyClient client = (JerseyClient) ClientBuilder.newClient();
+        final ClientConfig config = client.getConfiguration();
+        final PropertiesDelegate delegate = new MapPropertiesDelegate();
+        client.property(ClientProperties.SNI_HOST_NAME, "yyy.com");
+        delegate.setProperty(ClientProperties.SNI_HOST_NAME, "zzz.com");
+        ClientRequest request = new ClientRequest(uri, config, delegate) {};
+        SSLParamConfigurator configurator = SSLParamConfigurator.builder().request(request).build();
+        MatcherAssert.assertThat(configurator.isSNIRequired(), Matchers.is(true));
+        MatcherAssert.assertThat(configurator.getSNIHostName(), Matchers.is("zzz.com"));
+    }
+
+    @Test
+    public void testPropertyOnDelegateHasPrecedenceOverHost() {
+        final URI uri = URI.create("http://xxx.com:8080");
+        final JerseyClient client = (JerseyClient) ClientBuilder.newClient();
+        final ClientConfig config = client.getConfiguration();
+        final PropertiesDelegate delegate = new MapPropertiesDelegate();
+        client.property(ClientProperties.SNI_HOST_NAME, "yyy.com");
+        delegate.setProperty(ClientProperties.SNI_HOST_NAME, "zzz.com");
+        ClientRequest request = new ClientRequest(uri, config, delegate) {};
+        request.getHeaders().add(HttpHeaders.HOST, "www.com");
+        SSLParamConfigurator configurator = SSLParamConfigurator.builder().request(request).build();
+        MatcherAssert.assertThat(configurator.isSNIRequired(), Matchers.is(true));
+        MatcherAssert.assertThat(configurator.getSNIHostName(), Matchers.is("zzz.com"));
+    }
+
+    @Test
+    public void testDisableSni() {
+        final URI uri = URI.create("http://xxx.com:8080");
+        final JerseyClient client = (JerseyClient) ClientBuilder.newClient();
+        final ClientConfig config = client.getConfiguration();
+        final PropertiesDelegate delegate = new MapPropertiesDelegate();
+        client.property(ClientProperties.SNI_HOST_NAME, "yyy.com");
+        delegate.setProperty(ClientProperties.SNI_HOST_NAME, "xxx.com");
+        ClientRequest request = new ClientRequest(uri, config, delegate) {};
+        request.getHeaders().add(HttpHeaders.HOST, "www.com");
+        SSLParamConfigurator configurator = SSLParamConfigurator.builder().request(request).build();
+        MatcherAssert.assertThat(configurator.isSNIRequired(), Matchers.is(false));
+        MatcherAssert.assertThat(configurator.getSNIHostName(), Matchers.is("xxx.com"));
+    }
+
+    @Test
+    public void testLowerCasePropertyOnClientHasPrecedence() {
+        final URI uri = URI.create("http://xxx.com:8080");
+        final JerseyClient client = (JerseyClient) ClientBuilder.newClient();
+        final ClientConfig config = client.getConfiguration();
+        final PropertiesDelegate delegate = new MapPropertiesDelegate();
+        client.property(ClientProperties.SNI_HOST_NAME.toLowerCase(Locale.ROOT), "yyy.com");
+        ClientRequest request = new ClientRequest(uri, config, delegate) {};
+        request.getHeaders().add(HttpHeaders.HOST, "www.com");
+        SSLParamConfigurator configurator = SSLParamConfigurator.builder().request(request).build();
+        MatcherAssert.assertThat(configurator.isSNIRequired(), Matchers.is(true));
+        MatcherAssert.assertThat(configurator.getSNIHostName(), Matchers.is("yyy.com"));
+    }
+
+    @Test
+    public void testUriAndHeadersAndConfig() {
+        final URI uri = URI.create("http://xxx.com:8080");
+        final JerseyClient client = (JerseyClient) ClientBuilder.newClient();
+        Map<String, List<Object>> httpHeaders = new MultivaluedHashMap<>();
+        httpHeaders.put(HttpHeaders.HOST, Collections.singletonList("www.com"));
+        SSLParamConfigurator configurator = SSLParamConfigurator.builder()
+                .uri(uri)
+                .headers(httpHeaders)
+                .configuration(client.getConfiguration())
+                .build();
+        MatcherAssert.assertThat(configurator.isSNIRequired(), Matchers.is(true));
+        MatcherAssert.assertThat(configurator.getSNIHostName(), Matchers.is("www.com"));
+
+        client.property(ClientProperties.SNI_HOST_NAME, "yyy.com");
+        configurator = SSLParamConfigurator.builder()
+                .uri(uri)
+                .headers(httpHeaders)
+                .configuration(client.getConfiguration())
+                .build();
+        MatcherAssert.assertThat(configurator.isSNIRequired(), Matchers.is(true));
+        MatcherAssert.assertThat(configurator.getSNIHostName(), Matchers.is("yyy.com"));
+    }
+}
diff --git a/core-common/pom.xml b/core-common/pom.xml
index 0d431f0..47de5b9 100644
--- a/core-common/pom.xml
+++ b/core-common/pom.xml
@@ -71,10 +71,10 @@
         </resources>
 
         <testResources>
-          <testResource>
-            <directory>${basedir}/src/test/resources</directory>
-            <filtering>true</filtering>
-          </testResource>
+            <testResource>
+                <directory>${basedir}/src/test/resources</directory>
+                <filtering>true</filtering>
+            </testResource>
         </testResources>
 
         <plugins>
@@ -84,6 +84,8 @@
                 <version>${compiler.common.mvn.plugin.version}</version>
                 <inherited>false</inherited>
                 <configuration>
+                    <source>${java.version}</source>
+                    <target>${java.version}</target>
                     <compilerArguments>
                         <!-- Do not warn about using sun.misc.Unsafe -->
                         <XDignore.symbol.file />
@@ -213,13 +215,12 @@
             <groupId>jakarta.annotation</groupId>
             <artifactId>jakarta.annotation-api</artifactId>
         </dependency>
-<!--
         <dependency>
             <groupId>org.eclipse.angus</groupId>
             <artifactId>angus-activation</artifactId>
             <scope>provided</scope>
             <optional>true</optional>
-        </dependency> -->
+        </dependency>
         <dependency>
             <groupId>org.osgi</groupId>
             <artifactId>org.osgi.core</artifactId>
@@ -260,9 +261,172 @@
 
     <profiles>
         <profile>
+            <id>build.jdk20-</id>
+            <activation>
+                <jdk>[11,21)</jdk>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>build-helper-maven-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>add.java20-</id>
+                                <phase>generate-sources</phase>
+                                <goals>
+                                    <goal>add-source</goal>
+                                </goals>
+                                <configuration>
+                                    <sources>
+                                        <source>src/main/java20-</source>
+                                    </sources>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>build.jdk21</id>
+            <activation>
+                <jdk>[21,)</jdk>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>build-helper-maven-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>add.java20-</id>
+                                <phase>generate-sources</phase>
+                                <goals>
+                                    <goal>add-source</goal>
+                                </goals>
+                                <configuration>
+                                    <sources>
+                                        <source>src/main/java21</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>21</release>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                    <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 21+</echo>
+                                    </target>
+                                </configuration>
+                            </execution>
+                            <execution>
+                                <id>compile-java21</id>
+                                <phase>process-resources</phase>
+                                <configuration>
+                                    <target>
+                                        <mkdir dir="${java21.build.outputDirectory}" />
+                                        <javac srcdir="${java21.sourceDirectory}${path.separator}${project.basedir}/src/main/java/org/glassfish/jersey/innate/virtual" destdir="${java21.build.outputDirectory}"
+                                               classpath="${project.build.outputDirectory}" includeantruntime="false" release="21"/>
+                                    </target>
+                                </configuration>
+                                <goals>
+                                    <goal>run</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>copyJDK21FilesToMultiReleaseJar</id>
+            <activation>
+                <file>
+                    <!-- ${java21.build.outputDirectory} does not work here -->
+                    <exists>target/classes-java21/org/glassfish/jersey/innate/VirtualThreadSupport.class</exists>
+                </file>
+                <jdk>[11,21)</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-jdk21-sources</id>
+                                <phase>prepare-package</phase>
+                                <goals>
+                                    <goal>copy-resources</goal>
+                                </goals>
+                                <configuration>
+                                    <outputDirectory>${project.build.directory}/generated-sources/rsrc-gen/META-INF/versions/21/org/glassfish/jersey/innate</outputDirectory>
+                                    <resources>
+                                        <resource>
+                                            <directory>${java21.sourceDirectory}/org/glassfish/jersey/innate</directory>
+                                        </resource>
+                                    </resources>
+                                </configuration>
+                            </execution>
+                            <execution>
+                                <id>copy-jdk21-classes-to-meta-inf</id>
+                                <phase>prepare-package</phase>
+                                <goals>
+                                    <goal>copy-resources</goal>
+                                </goals>
+                                <configuration>
+                                    <outputDirectory>${project.build.outputDirectory}/META-INF/versions/21</outputDirectory>
+                                    <resources>
+                                        <resource>
+                                            <directory>${java21.build.outputDirectory}</directory>
+                                        </resource>
+                                    </resources>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+
+        <profile>
             <id>securityOff</id>
             <properties>
-               <surefire.security.argline />
+                <surefire.security.argline />
             </properties>
             <build>
                 <plugins>
@@ -299,6 +463,8 @@
 
     <properties>
         <surefire.security.argline>-Djava.security.manager -Djava.security.policy=${project.build.directory}/test-classes/surefire.policy</surefire.security.argline>
+        <java21.build.outputDirectory>${project.build.directory}/classes-java21</java21.build.outputDirectory>
+        <java21.sourceDirectory>${project.basedir}/src/main/java21</java21.sourceDirectory>
         <surefire.coverage.argline>
             --add-reads org.glassfish.jersey.core.common=ALL-UNNAMED
             --add-modules=ALL-MODULE-PATH
diff --git a/core-common/src/main/java/module-info.java b/core-common/src/main/java/module-info.java
index 3a3f1f5..7af8951 100644
--- a/core-common/src/main/java/module-info.java
+++ b/core-common/src/main/java/module-info.java
@@ -53,7 +53,26 @@
     exports org.glassfish.jersey.uri;
     exports org.glassfish.jersey.uri.internal;
 
+
+    exports org.glassfish.jersey.innate to org.glassfish.jersey.container.grizzly2.http,
+                                           org.glassfish.jersey.container.servlet.core,
+                                           org.glassfish.jersey.container.jetty.http,
+                                           org.glassfish.jersey.netty.connector,
+                                           org.glassfish.jersey.ext.mp.rest.client;
+    exports org.glassfish.jersey.innate.virtual to org.glassfish.jersey.container.grizzly2.http,
+                                                   org.glassfish.jersey.container.jetty.http,
+                                                   org.glassfish.jersey.netty.connector,
+                                                   org.glassfish.jersey.ext.mp.rest.client;
+
+    opens org.glassfish.jersey.innate.virtual to org.glassfish.jersey.container.grizzly2.http,
+                                                 org.glassfish.jersey.container.jetty.http;
+    opens org.glassfish.jersey.innate to org.glassfish.jersey.container.servlet.core;
+
+    exports org.glassfish.jersey.innate.io to org.glassfish.jersey.core.server,
+                                              org.glassfish.jersey.container.servlet.core,
+                                              org.glassfish.jersey.apache.connector;
     exports org.glassfish.jersey.innate.spi to org.glassfish.jersey.media.multipart;
+    exports org.glassfish.jersey.innate.inject.spi to org.glassfish.jersey.inject.hk2;
 
     opens org.glassfish.jersey.innate.spi to org.glassfish.jersey.media.multipart;
     opens org.glassfish.jersey.internal;
diff --git a/core-common/src/main/java/org/glassfish/jersey/ApplicationSupplier.java b/core-common/src/main/java/org/glassfish/jersey/ApplicationSupplier.java
new file mode 100644
index 0000000..6ef17b9
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/ApplicationSupplier.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2024 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;
+
+import jakarta.ws.rs.core.Application;
+
+/**
+ * Implementation of this interface is capable of returning {@link Application}.
+ */
+public interface ApplicationSupplier {
+    /**
+     * Get Application.
+     *
+     * @return Application.
+     */
+    Application getApplication();
+
+}
diff --git a/core-common/src/main/java/org/glassfish/jersey/CommonProperties.java b/core-common/src/main/java/org/glassfish/jersey/CommonProperties.java
index e71e5e1..308ff94 100644
--- a/core-common/src/main/java/org/glassfish/jersey/CommonProperties.java
+++ b/core-common/src/main/java/org/glassfish/jersey/CommonProperties.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2024 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
@@ -71,7 +71,7 @@
     /**
      * If {@code true} then disable feature auto discovery globally on client/server.
      * <p>
-     * By default auto discovery is automatically enabled. The value of this property may be overridden by the client/server
+     * By default, auto discovery is automatically enabled. The value of this property may be overridden by the client/server
      * variant of this property.
      * <p>
      * The default value is {@code false}.
@@ -98,10 +98,55 @@
      */
     public static final String FEATURE_AUTO_DISCOVERY_DISABLE_SERVER = "jersey.config.server.disableAutoDiscovery";
 
+
+    /**
+     * If {@code true} then disable configuration of Json Binding (JSR-367) feature.
+     * <p>
+     * By default, Json Binding is automatically enabled. The value of this property may be overridden by the client/server
+     * variant of this property.
+     * <p>
+     * The default value is {@code false}.
+     * </p>
+     * <p>
+     * The name of the configuration property is <tt>{@value}</tt>.
+     * </p>
+     * @since 2.45
+     */
+    public static final String JSON_BINDING_FEATURE_DISABLE = "jersey.config.disableJsonBinding";
+
+    /**
+     * Client-specific version of {@link CommonProperties#JSON_BINDING_FEATURE_DISABLE}.
+     *
+     * If present, it overrides the generic one for the client environment.
+     * @since 2.45
+     */
+    public static final String JSON_BINDING_FEATURE_DISABLE_CLIENT = "jersey.config.client.disableJsonBinding";
+
+    /**
+     * Server-specific version of {@link CommonProperties#JSON_BINDING_FEATURE_DISABLE}.
+     *
+     * If present, it overrides the generic one for the server environment.
+     * @since 2.45
+     */
+    public static final String JSON_BINDING_FEATURE_DISABLE_SERVER = "jersey.config.server.disableJsonBinding";
+
+    /**
+     * Disables configuration of Json Binding (JSR-367) feature for {@link jakarta.ws.rs.core.Application} subclasses whose
+     * package names are specified as a value. The value is comma-separated string defining prefixes of the application
+     * package names.
+     * <p>
+     * By default, Json Binding is automatically enabled.
+     * <p>
+     * The name of the configuration property is <tt>{@value}</tt>.
+     * </p>
+     * @since 2.45
+     */
+    public static final String JSON_BINDING_FEATURE_DISABLE_APPLICATION = "jersey.config.application.disableJsonBinding";
+
     /**
      * If {@code true} then disable configuration of Json Processing (JSR-353) feature.
      * <p>
-     * By default Json Processing is automatically enabled. The value of this property may be overridden by the client/server
+     * By default, Json Processing is automatically enabled. The value of this property may be overridden by the client/server
      * variant of this property.
      * <p>
      * The default value is {@code false}.
@@ -131,7 +176,7 @@
     /**
      * If {@code true} then disable META-INF/services lookup globally on client/server.
      * <p>
-     * By default Jersey looks up SPI implementations described by META-INF/services/* files.
+     * By default, Jersey looks up SPI implementations described by META-INF/services/* files.
      * Then you can register appropriate provider classes by {@link jakarta.ws.rs.core.Application}.
      * </p>
      * <p>
@@ -164,7 +209,7 @@
     /**
      * If {@code true} then disable configuration of MOXy Json feature.
      * <p>
-     * By default MOXy Json is automatically enabled. The value of this property may be overridden by the client/server
+     * By default, MOXy Json is automatically enabled. The value of this property may be overridden by the client/server
      * variant of this property.
      * <p>
      * The default value is {@code false}.
@@ -321,6 +366,30 @@
     public static final String PARAM_CONVERTERS_THROW_IAE = "jersey.config.paramconverters.throw.iae";
 
     /**
+     * <p>
+     *     Defines the {@link java.util.concurrent.ThreadFactory} to be used by internal default Executor Services.
+     * </p>
+     * <p>
+     *     The default is {@link  java.util.concurrent.Executors#defaultThreadFactory()} on platform threads and
+     *     {@code Thread.ofVirtual().factory()} on virtual threads.
+     * </p>
+     * @since 3.1.7
+     */
+    public static String THREAD_FACTORY = "jersey.config.threads.factory";
+
+    /**
+     * <p>
+     *     Defines whether the virtual threads should be used by Jersey on JDK 21+ when not using an exact number
+     *     of threads by {@code FixedThreadPool}.
+     * </p>
+     * <p>
+     *     The default is {@code false} for this version of Jersey.
+     * </p>
+     * @since 3.1.7
+     */
+    public static String USE_VIRTUAL_THREADS = "jersey.config.threads.use.virtual";
+
+    /**
      * Prevent instantiation.
      */
     private CommonProperties() {
diff --git a/core-common/src/main/java/org/glassfish/jersey/SslConfigurator.java b/core-common/src/main/java/org/glassfish/jersey/SslConfigurator.java
index b3befea..2bef140 100644
--- a/core-common/src/main/java/org/glassfish/jersey/SslConfigurator.java
+++ b/core-common/src/main/java/org/glassfish/jersey/SslConfigurator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2024 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.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
+import java.net.URL;
 import java.nio.file.Files;
 import java.security.AccessController;
 import java.security.KeyManagementException;
@@ -31,6 +32,7 @@
 import java.security.UnrecoverableKeyException;
 import java.security.cert.CertificateException;
 import java.util.Arrays;
+import java.util.Objects;
 import java.util.Properties;
 import java.util.logging.Logger;
 
@@ -241,6 +243,9 @@
     private String trustStoreFile;
     private String keyStoreFile;
 
+    private URL trustStoreUrl;
+    private URL keyStoreUrl;
+
     private byte[] trustStoreBytes;
     private byte[] keyStoreBytes;
 
@@ -321,6 +326,8 @@
         this.keyPass = that.keyPass;
         this.trustStoreFile = that.trustStoreFile;
         this.keyStoreFile = that.keyStoreFile;
+        this.keyStoreUrl = that.keyStoreUrl;
+        this.trustStoreUrl = that.trustStoreUrl;
         this.trustStoreBytes = that.trustStoreBytes;
         this.keyStoreBytes = that.keyStoreBytes;
         this.trustManagerFactoryAlgorithm = that.trustManagerFactoryAlgorithm;
@@ -441,8 +448,9 @@
     /**
      * Set the <em>trust</em> store file name.
      * <p>
-     * Setting a trust store instance resets any {@link #trustStore(java.security.KeyStore) trust store instance}
-     * or {@link #trustStoreBytes(byte[]) trust store payload} value previously set.
+     * Setting a trust store instance resets any {@link #trustStore(java.security.KeyStore) trust store instance},
+     * {@link #trustStoreBytes(byte[]) trust store payload} or {@link #trustStoreUrl(URL) trust store url}
+     * value previously set.
      * </p>
      *
      * @param fileName {@link java.io.File file} name of the <em>trust</em> store.
@@ -451,6 +459,26 @@
     public SslConfigurator trustStoreFile(String fileName) {
         this.trustStoreFile = fileName;
         this.trustStoreBytes = null;
+        this.trustStoreUrl = null;
+        this.trustStore = null;
+        return this;
+    }
+
+    /**
+     * Set the <em>trust</em> store file url.
+     * <p>
+     * Setting a trust store instance resets any {@link #trustStore(java.security.KeyStore) trust store instance},
+     * {@link #trustStoreBytes(byte[]) trust store payload} or {@link #trustStoreUrl(URL) trust store url}
+     * value previously set.
+     * </p>
+     *
+     * @param url {@link java.net.URL url} link of the <em>trust</em> store.
+     * @return updated SSL configurator instance.
+     */
+    public SslConfigurator trustStoreUrl(URL url) {
+        this.trustStoreFile = null;
+        this.trustStoreBytes = null;
+        this.trustStoreUrl = url;
         this.trustStore = null;
         return this;
     }
@@ -458,8 +486,9 @@
     /**
      * Set the <em>trust</em> store payload as byte array.
      * <p>
-     * Setting a trust store instance resets any {@link #trustStoreFile(String) trust store file}
-     * or {@link #trustStore(java.security.KeyStore) trust store instance} value previously set.
+     * Setting a trust store instance resets any {@link #trustStoreFile(String) trust store file},
+     * {@link #trustStore(java.security.KeyStore) trust store instance} or {@link #trustStoreUrl(URL) trust store url}
+     * value previously set.
      * </p>
      *
      * @param payload <em>trust</em> store payload.
@@ -468,6 +497,7 @@
     public SslConfigurator trustStoreBytes(byte[] payload) {
         this.trustStoreBytes = payload.clone();
         this.trustStoreFile = null;
+        this.trustStoreUrl = null;
         this.trustStore = null;
         return this;
     }
@@ -475,8 +505,8 @@
     /**
      * Set the <em>key</em> store file name.
      * <p>
-     * Setting a key store instance resets any {@link #keyStore(java.security.KeyStore) key store instance}
-     * or {@link #keyStoreBytes(byte[]) key store payload} value previously set.
+     * Setting a key store instance resets any {@link #keyStore(java.security.KeyStore) key store instance},
+     * {@link #keyStoreBytes(byte[]) key store payload} or {@link #keyStoreUrl(URL) key store url} value previously set.
      * </p>
      *
      * @param fileName {@link java.io.File file} name of the <em>key</em> store.
@@ -484,6 +514,26 @@
      */
     public SslConfigurator keyStoreFile(String fileName) {
         this.keyStoreFile = fileName;
+        this.keyStoreUrl = null;
+        this.keyStoreBytes = null;
+        this.keyStore = null;
+        return this;
+    }
+
+    /**
+     * Set the <em>key</em> store url.
+     * <p>
+     * Setting a key store instance resets any {@link #keyStore(java.security.KeyStore) key store instance},
+     * {@link #keyStoreBytes(byte[]) key store payload} or {@link #keyStoreFile(String) key store file}
+     * value previously set.
+     * </p>
+     *
+     * @param url {@link java.net.URL url} of the <em>key</em> store.
+     * @return updated SSL configurator instance.
+     */
+    public SslConfigurator keyStoreUrl(URL url) {
+        this.keyStoreFile = null;
+        this.keyStoreUrl = url;
         this.keyStoreBytes = null;
         this.keyStore = null;
         return this;
@@ -492,8 +542,9 @@
     /**
      * Set the <em>key</em> store payload as byte array.
      * <p>
-     * Setting a key store instance resets any {@link #keyStoreFile(String) key store file}
-     * or {@link #keyStore(java.security.KeyStore) key store instance} value previously set.
+     * Setting a key store instance resets any {@link #keyStoreFile(String) key store file},
+     * {@link #keyStore(java.security.KeyStore) key store instance} or {@link #keyStoreUrl(URL) key store url}
+     * value previously set.
      * </p>
      *
      * @param payload <em>key</em> store payload.
@@ -501,6 +552,7 @@
      */
     public SslConfigurator keyStoreBytes(byte[] payload) {
         this.keyStoreBytes = payload.clone();
+        this.keyStoreUrl = null;
         this.keyStoreFile = null;
         this.keyStore = null;
         return this;
@@ -573,8 +625,8 @@
     /**
      * Set the <em>key</em> store instance.
      * <p>
-     * Setting a key store instance resets any {@link #keyStoreFile(String) key store file}
-     * or {@link #keyStoreBytes(byte[]) key store payload} value previously set.
+     * Setting a key store instance resets any {@link #keyStoreFile(String) key store file},
+     * {@link #keyStoreBytes(byte[]) key store payload} or {@link #keyStoreUrl(URL) key store url} value previously set.
      * </p>
      *
      * @param keyStore <em>key</em> store instance.
@@ -584,15 +636,12 @@
         this.keyStore = keyStore;
         this.keyStoreFile = null;
         this.keyStoreBytes = null;
+        this.keyStoreUrl = null;
         return this;
     }
 
     /**
      * Get the <em>trust</em> store instance.
-     * <p>
-     * Setting a trust store instance resets any {@link #trustStoreFile(String) trust store file}
-     * or {@link #trustStoreBytes(byte[]) trust store payload} value previously set.
-     * </p>
      *
      * @return <em>trust</em> store instance or {@code null} if not explicitly set.
      */
@@ -602,12 +651,16 @@
 
     /**
      * Set the <em>trust</em> store instance.
-     *
+     * <p>
+     * Setting a trust store instance resets any {@link #trustStoreFile(String) trust store file},
+     * {@link #trustStoreBytes(byte[]) trust store payload} or {@link #trustStoreUrl(URL) trust store url} value previously set.
+     * </p>
      * @param trustStore <em>trust</em> store instance.
      * @return updated SSL configurator instance.
      */
     public SslConfigurator trustStore(KeyStore trustStore) {
         this.trustStore = trustStore;
+        this.trustStoreUrl = null;
         this.trustStoreFile = null;
         this.trustStoreBytes = null;
         return this;
@@ -623,7 +676,7 @@
         KeyManagerFactory keyManagerFactory = null;
 
         KeyStore _keyStore = keyStore;
-        if (_keyStore == null && (keyStoreBytes != null || keyStoreFile != null)) {
+        if (_keyStore == null && (keyStoreBytes != null || keyStoreFile != null || keyStoreUrl != null)) {
             try {
                 if (keyStoreProvider != null) {
                     _keyStore = KeyStore.getInstance(
@@ -635,6 +688,8 @@
                 try {
                     if (keyStoreBytes != null) {
                         keyStoreInputStream = new ByteArrayInputStream(keyStoreBytes);
+                    } else if (keyStoreUrl != null) {
+                      keyStoreInputStream = keyStoreUrl.openStream();
                     } else if (!keyStoreFile.equals("NONE")) {
                         keyStoreInputStream = Files.newInputStream(new File(keyStoreFile).toPath());
                     }
@@ -697,7 +752,7 @@
         }
 
         KeyStore _trustStore = trustStore;
-        if (_trustStore == null && (trustStoreBytes != null || trustStoreFile != null)) {
+        if (_trustStore == null && (trustStoreBytes != null || trustStoreFile != null || trustStoreUrl != null)) {
             try {
                 if (trustStoreProvider != null) {
                     _trustStore = KeyStore.getInstance(
@@ -710,6 +765,8 @@
                 try {
                     if (trustStoreBytes != null) {
                         trustStoreInputStream = new ByteArrayInputStream(trustStoreBytes);
+                    } else if (trustStoreUrl != null) {
+                      trustStoreInputStream = trustStoreUrl.openStream();
                     } else if (!trustStoreFile.equals("NONE")) {
                         trustStoreInputStream = Files.newInputStream(new File(trustStoreFile).toPath());
                     }
@@ -808,6 +865,9 @@
         trustStoreFile = props.getProperty(TRUST_STORE_FILE);
         keyStoreFile = props.getProperty(KEY_STORE_FILE);
 
+        keyStoreUrl = null;
+        trustStoreUrl = null;
+
         trustStoreBytes = null;
         keyStoreBytes = null;
 
@@ -857,6 +917,9 @@
         trustStoreFile = AccessController.doPrivileged(PropertiesHelper.getSystemProperty(TRUST_STORE_FILE));
         keyStoreFile = AccessController.doPrivileged(PropertiesHelper.getSystemProperty(KEY_STORE_FILE));
 
+        trustStoreUrl = null;
+        keyStoreUrl = null;
+
         trustStoreBytes = null;
         keyStoreBytes = null;
 
@@ -876,91 +939,35 @@
         if (o == null || getClass() != o.getClass()) {
             return false;
         }
-
         SslConfigurator that = (SslConfigurator) o;
-
-        if (keyManagerFactoryAlgorithm != null
-                ? !keyManagerFactoryAlgorithm.equals(that.keyManagerFactoryAlgorithm) : that.keyManagerFactoryAlgorithm != null) {
-            return false;
-        }
-        if (keyManagerFactoryProvider != null
-                ? !keyManagerFactoryProvider.equals(that.keyManagerFactoryProvider) : that.keyManagerFactoryProvider != null) {
-            return false;
-        }
-        if (!Arrays.equals(keyPass, that.keyPass)) {
-            return false;
-        }
-        if (keyStore != null ? !keyStore.equals(that.keyStore) : that.keyStore != null) {
-            return false;
-        }
-        if (!Arrays.equals(keyStoreBytes, that.keyStoreBytes)) {
-            return false;
-        }
-        if (keyStoreFile != null ? !keyStoreFile.equals(that.keyStoreFile) : that.keyStoreFile != null) {
-            return false;
-        }
-        if (!Arrays.equals(keyStorePass, that.keyStorePass)) {
-            return false;
-        }
-        if (keyStoreProvider != null ? !keyStoreProvider.equals(that.keyStoreProvider) : that.keyStoreProvider != null) {
-            return false;
-        }
-        if (keyStoreType != null ? !keyStoreType.equals(that.keyStoreType) : that.keyStoreType != null) {
-            return false;
-        }
-        if (securityProtocol != null ? !securityProtocol.equals(that.securityProtocol) : that.securityProtocol != null) {
-            return false;
-        }
-        if (trustManagerFactoryAlgorithm != null ? !trustManagerFactoryAlgorithm.equals(that.trustManagerFactoryAlgorithm)
-                : that.trustManagerFactoryAlgorithm != null) {
-            return false;
-        }
-        if (trustManagerFactoryProvider != null ? !trustManagerFactoryProvider.equals(that.trustManagerFactoryProvider)
-                : that.trustManagerFactoryProvider != null) {
-            return false;
-        }
-        if (trustStore != null ? !trustStore.equals(that.trustStore) : that.trustStore != null) {
-            return false;
-        }
-        if (!Arrays.equals(trustStoreBytes, that.trustStoreBytes)) {
-            return false;
-        }
-        if (trustStoreFile != null ? !trustStoreFile.equals(that.trustStoreFile) : that.trustStoreFile != null) {
-            return false;
-        }
-        if (!Arrays.equals(trustStorePass, that.trustStorePass)) {
-            return false;
-        }
-        if (trustStoreProvider != null ? !trustStoreProvider.equals(that.trustStoreProvider) : that.trustStoreProvider != null) {
-            return false;
-        }
-        if (trustStoreType != null ? !trustStoreType.equals(that.trustStoreType) : that.trustStoreType != null) {
-            return false;
-        }
-
-        return true;
+        return Objects.equals(keyStore, that.keyStore)
+                && Objects.equals(trustStore, that.trustStore)
+                && Objects.equals(trustStoreProvider, that.trustStoreProvider)
+                && Objects.equals(keyStoreProvider, that.keyStoreProvider)
+                && Objects.equals(trustStoreType, that.trustStoreType)
+                && Objects.equals(keyStoreType, that.keyStoreType)
+                && Arrays.equals(trustStorePass, that.trustStorePass)
+                && Arrays.equals(keyStorePass, that.keyStorePass)
+                && Arrays.equals(keyPass, that.keyPass)
+                && Objects.equals(trustStoreFile, that.trustStoreFile)
+                && Objects.equals(keyStoreFile, that.keyStoreFile)
+                && Objects.equals(trustStoreUrl, that.trustStoreUrl)
+                && Objects.equals(keyStoreUrl, that.keyStoreUrl)
+                && Arrays.equals(trustStoreBytes, that.trustStoreBytes)
+                && Arrays.equals(keyStoreBytes, that.keyStoreBytes)
+                && Objects.equals(trustManagerFactoryAlgorithm, that.trustManagerFactoryAlgorithm)
+                && Objects.equals(keyManagerFactoryAlgorithm, that.keyManagerFactoryAlgorithm)
+                && Objects.equals(trustManagerFactoryProvider, that.trustManagerFactoryProvider)
+                && Objects.equals(keyManagerFactoryProvider, that.keyManagerFactoryProvider)
+                && Objects.equals(securityProtocol, that.securityProtocol);
     }
 
     @Override
     public int hashCode() {
-        int result = keyStore != null ? keyStore.hashCode() : 0;
-        result = 31 * result + (trustStore != null ? trustStore.hashCode() : 0);
-        result = 31 * result + (trustStoreProvider != null ? trustStoreProvider.hashCode() : 0);
-        result = 31 * result + (keyStoreProvider != null ? keyStoreProvider.hashCode() : 0);
-        result = 31 * result + (trustStoreType != null ? trustStoreType.hashCode() : 0);
-        result = 31 * result + (keyStoreType != null ? keyStoreType.hashCode() : 0);
-        result = 31 * result + (trustStorePass != null ? Arrays.hashCode(trustStorePass) : 0);
-        result = 31 * result + (keyStorePass != null ? Arrays.hashCode(keyStorePass) : 0);
-        result = 31 * result + (keyPass != null ? Arrays.hashCode(keyPass) : 0);
-        result = 31 * result + (trustStoreFile != null ? trustStoreFile.hashCode() : 0);
-        result = 31 * result + (keyStoreFile != null ? keyStoreFile.hashCode() : 0);
-        result = 31 * result + (trustStoreBytes != null ? Arrays.hashCode(trustStoreBytes) : 0);
-        result = 31 * result + (keyStoreBytes != null ? Arrays.hashCode(keyStoreBytes) : 0);
-        result = 31 * result + (trustManagerFactoryAlgorithm != null ? trustManagerFactoryAlgorithm.hashCode() : 0);
-        result = 31 * result + (keyManagerFactoryAlgorithm != null ? keyManagerFactoryAlgorithm.hashCode() : 0);
-        result = 31 * result + (trustManagerFactoryProvider != null ? trustManagerFactoryProvider.hashCode() : 0);
-        result = 31 * result + (keyManagerFactoryProvider != null ? keyManagerFactoryProvider.hashCode() : 0);
-        result = 31 * result + (securityProtocol != null ? securityProtocol.hashCode() : 0);
+        int result = Objects.hash(keyStore, trustStore, trustStoreProvider, keyStoreProvider, trustStoreType, keyStoreType,
+                trustStoreFile, keyStoreFile, trustStoreUrl, keyStoreUrl, trustManagerFactoryAlgorithm,
+                keyManagerFactoryAlgorithm, trustManagerFactoryProvider, keyManagerFactoryProvider, securityProtocol,
+                trustStorePass, keyStorePass, keyPass, trustStoreBytes, keyStoreBytes);
         return result;
     }
 }
diff --git a/core-common/src/main/java/org/glassfish/jersey/innate/VirtualThreadUtil.java b/core-common/src/main/java/org/glassfish/jersey/innate/VirtualThreadUtil.java
new file mode 100644
index 0000000..3511d8e
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/innate/VirtualThreadUtil.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2024 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;
+
+import org.glassfish.jersey.CommonProperties;
+import org.glassfish.jersey.innate.virtual.LoomishExecutors;
+
+import jakarta.ws.rs.core.Configuration;
+import java.util.concurrent.ThreadFactory;
+
+/**
+ * Factory class to provide JDK specific implementation of bits related to the virtual thread support.
+ */
+public final class VirtualThreadUtil {
+
+    private static final boolean USE_VIRTUAL_THREADS_BY_DEFAULT = false;
+
+    /**
+     * Do not instantiate.
+     */
+    private VirtualThreadUtil() {
+        throw new IllegalStateException();
+    }
+
+    /**
+     * Return an instance of {@link LoomishExecutors} based on a configuration property.
+     * @param config the {@link Configuration}
+     * @return the {@link LoomishExecutors} instance.
+     */
+    public static LoomishExecutors withConfig(Configuration config) {
+        return withConfig(config, USE_VIRTUAL_THREADS_BY_DEFAULT);
+    }
+
+    /**
+     * Return an instance of {@link LoomishExecutors} based on a configuration property.
+     * @param config the {@link Configuration}
+     * @param useVirtualByDefault the default use if not said otherwise by property
+     * @return the {@link LoomishExecutors} instance.
+     */
+    public static LoomishExecutors withConfig(Configuration config, boolean useVirtualByDefault) {
+        ThreadFactory tfThreadFactory = null;
+        boolean useVirtualThreads = useVirtualThreads(config, useVirtualByDefault);
+
+        if (config != null) {
+            Object threadFactory = config.getProperty(CommonProperties.THREAD_FACTORY);
+            if (threadFactory != null && ThreadFactory.class.isInstance(threadFactory)) {
+                tfThreadFactory = (ThreadFactory) threadFactory;
+            }
+        }
+
+        return tfThreadFactory == null
+                ? VirtualThreadSupport.allowVirtual(useVirtualThreads)
+                : VirtualThreadSupport.allowVirtual(useVirtualThreads, tfThreadFactory);
+    }
+
+    /**
+     * Check configuration if the use of the virtual threads is expected or return the default value if not.
+     * @param config the {@link Configuration}
+     * @param useByDefault the default expectation
+     * @return the expected
+     */
+    private static boolean useVirtualThreads(Configuration config, boolean useByDefault) {
+        boolean bUseVirtualThreads = useByDefault;
+        if (config != null) {
+            Object useVirtualThread = config.getProperty(CommonProperties.USE_VIRTUAL_THREADS);
+            if (useVirtualThread != null && Boolean.class.isInstance(useVirtualThread)) {
+                bUseVirtualThreads = (boolean) useVirtualThread;
+            }
+            if (useVirtualThread != null && String.class.isInstance(useVirtualThread)) {
+                bUseVirtualThreads = Boolean.parseBoolean(useVirtualThread.toString());
+            }
+        }
+        return bUseVirtualThreads;
+    }
+}
diff --git a/core-common/src/main/java/org/glassfish/jersey/innate/inject/package-info.java b/core-common/src/main/java/org/glassfish/jersey/innate/inject/package-info.java
new file mode 100644
index 0000000..b276f3c
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/innate/inject/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/**
+ * Jersey innate injection related packages. The innate packages will not be opened by JPMS outside of Jersey.
+ * Not for public use.
+ */
+package org.glassfish.jersey.innate.inject;
diff --git a/core-common/src/main/java/org/glassfish/jersey/innate/inject/spi/ExternalRegistrables.java b/core-common/src/main/java/org/glassfish/jersey/innate/inject/spi/ExternalRegistrables.java
new file mode 100644
index 0000000..f5c5f8f
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/innate/inject/spi/ExternalRegistrables.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2024 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.inject.spi;
+
+import jakarta.ws.rs.RuntimeType;
+import java.util.List;
+
+/**
+ * Provide a list of classes or interfaces the InjectionManager can support.
+ */
+public interface ExternalRegistrables {
+
+    /**
+     * Contract - RuntimeType pair. For a contract applicable on both client and server, use {@code null} as RuntimeType.
+     */
+    public static final class ClassRuntimeTypePair {
+        private final Class<?> contract;
+        private final RuntimeType runtimeType;
+
+        public ClassRuntimeTypePair(Class<?> contract, RuntimeType runtimeType) {
+            this.contract = contract;
+            this.runtimeType = runtimeType;
+        }
+
+        public Class<?> getContract() {
+            return contract;
+        }
+
+        public RuntimeType getRuntimeType() {
+            return runtimeType;
+        }
+    }
+
+    /**
+     * List of contracts that can be registered into Jersey to be passed by the external injection framework.
+     * @return list of contracts allowed to be registered in Jersey.
+     */
+    List<ClassRuntimeTypePair> registrableContracts();
+}
diff --git a/core-common/src/main/java/org/glassfish/jersey/innate/inject/spi/package-info.java b/core-common/src/main/java/org/glassfish/jersey/innate/inject/spi/package-info.java
new file mode 100644
index 0000000..5ad36dd
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/innate/inject/spi/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/**
+ * Jersey innate injection related packages. The innate packages will not be opened by JPMS outside of Jersey.
+ * Not for public use.
+ */
+package org.glassfish.jersey.innate.inject.spi;
diff --git a/core-common/src/main/java/org/glassfish/jersey/innate/io/InputStreamWrapper.java b/core-common/src/main/java/org/glassfish/jersey/innate/io/InputStreamWrapper.java
new file mode 100644
index 0000000..c2109fe
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/innate/io/InputStreamWrapper.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2024 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.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Generic wrapper template for InputStream.
+ */
+public abstract class InputStreamWrapper extends InputStream {
+
+    /**
+     * Return the wrapped stream
+     * @return
+     */
+    protected abstract InputStream getWrapped();
+
+    /**
+     * Get wrapped stream that can throw {@link IOException}
+     * @return the wrapped InputStream.
+     * @throws IOException
+     */
+    protected InputStream getWrappedIOE() throws IOException {
+        return getWrapped();
+    }
+
+    @Override
+    public int read() throws IOException {
+        return getWrappedIOE().read();
+    }
+
+    @Override
+    public int read(byte[] b) throws IOException {
+        return getWrappedIOE().read(b);
+    }
+
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+        return getWrappedIOE().read(b, off, len);
+    }
+
+    @Override
+    public long skip(long n) throws IOException {
+        return getWrappedIOE().skip(n);
+    }
+
+    @Override
+    public int available() throws IOException {
+        return getWrappedIOE().available();
+    }
+
+    @Override
+    public void close() throws IOException {
+        getWrappedIOE().close();
+    }
+
+    @Override
+    public void mark(int readlimit) {
+        getWrapped().mark(readlimit);
+    }
+
+    @Override
+    public void reset() throws IOException {
+        getWrappedIOE().reset();
+    }
+
+    @Override
+    public boolean markSupported() {
+        return getWrapped().markSupported();
+    }
+}
diff --git a/core-common/src/main/java/org/glassfish/jersey/innate/io/package-info.java b/core-common/src/main/java/org/glassfish/jersey/innate/io/package-info.java
new file mode 100644
index 0000000..e569f79
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/innate/io/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/**
+ * Jersey innate io related packages. The innate packages will not be opened by JPMS outside of Jersey.
+ * Not for public use.
+ */
+package org.glassfish.jersey.innate.io;
diff --git a/core-common/src/main/java/org/glassfish/jersey/innate/package-info.java b/core-common/src/main/java/org/glassfish/jersey/innate/package-info.java
index b0648e7..a1835bb 100644
--- a/core-common/src/main/java/org/glassfish/jersey/innate/package-info.java
+++ b/core-common/src/main/java/org/glassfish/jersey/innate/package-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024 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,5 +16,6 @@
 
 /**
  * Jersey innate packages. The innate packages will not be opened by JPMS outside of Jersey.
+ * Not for public use.
  */
 package org.glassfish.jersey.innate;
diff --git a/core-common/src/main/java/org/glassfish/jersey/innate/virtual/LoomishExecutors.java b/core-common/src/main/java/org/glassfish/jersey/innate/virtual/LoomishExecutors.java
new file mode 100644
index 0000000..9065746
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/innate/virtual/LoomishExecutors.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2024 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.virtual;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+
+/**
+ * {@link Executors} facade to support virtual threads.
+ */
+public interface LoomishExecutors {
+    /**
+     * Creates a thread pool that creates new threads as needed and uses virtual threads if available.
+     * @return the newly created thread pool
+     */
+    ExecutorService newCachedThreadPool();
+
+    /**
+     * Creates a thread pool that reuses a fixed number of threads operating off a shared unbounded queue
+     * and uses virtual threads if available
+     * @param nThreads – the number of threads in the pool
+     * @return the newly created thread pool
+     */
+    ExecutorService newFixedThreadPool(int nThreads);
+
+    /**
+     * Returns thread factory used to create new threads
+     * @return thread factory used to create new threads
+     * @see Executors#defaultThreadFactory()
+     */
+    ThreadFactory getThreadFactory();
+
+    /**
+     * Return true if the virtual thread use is requested.
+     * @return whether the virtual thread use is requested.
+     */
+    boolean isVirtual();
+}
diff --git a/core-common/src/main/java/org/glassfish/jersey/innate/virtual/package-info.java b/core-common/src/main/java/org/glassfish/jersey/innate/virtual/package-info.java
new file mode 100644
index 0000000..c56f91b
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/innate/virtual/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/**
+ * Jersey innate packages. The innate packages will not be opened by JPMS outside of Jersey.
+ * Not for public use.
+ * This virtual package should contain only classes that do not have dependencies on Jersey, or the REST API to be buildable with
+ * ant for multi-release.
+ */
+package org.glassfish.jersey.innate.virtual;
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/OsgiRegistry.java b/core-common/src/main/java/org/glassfish/jersey/internal/OsgiRegistry.java
index cb7545f..77ab1e1 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/OsgiRegistry.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/OsgiRegistry.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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,7 +37,9 @@
 import java.util.ResourceBundle;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 import java.util.jar.JarEntry;
 import java.util.jar.JarInputStream;
@@ -79,6 +81,7 @@
     private final Map<Long, Map<String, Callable<List<Class<?>>>>> factories =
             new HashMap<Long, Map<String, Callable<List<Class<?>>>>>();
     private final ReadWriteLock lock = new ReentrantReadWriteLock();
+    private static final Lock INSTANCE_LOCK = new ReentrantLock();
 
     private static OsgiRegistry instance;
 
@@ -90,16 +93,21 @@
      *
      * @return an {@code OsgiRegistry} instance.
      */
-    public static synchronized OsgiRegistry getInstance() {
-        if (instance == null) {
-            final ClassLoader classLoader = AccessController
-                    .doPrivileged(ReflectionHelper.getClassLoaderPA(ReflectionHelper.class));
-            if (classLoader instanceof BundleReference) {
-                final BundleContext context = FrameworkUtil.getBundle(OsgiRegistry.class).getBundleContext();
-                if (context != null) { // context could be still null if the current bundle has not been started
-                    instance = new OsgiRegistry(context);
+    public static OsgiRegistry getInstance() {
+        INSTANCE_LOCK.lock();
+        try {
+            if (instance == null) {
+                final ClassLoader classLoader = AccessController
+                        .doPrivileged(ReflectionHelper.getClassLoaderPA(ReflectionHelper.class));
+                if (classLoader instanceof BundleReference) {
+                    final BundleContext context = FrameworkUtil.getBundle(OsgiRegistry.class).getBundleContext();
+                    if (context != null) { // context could be still null if the current bundle has not been started
+                        instance = new OsgiRegistry(context);
+                    }
                 }
             }
+        } finally {
+            INSTANCE_LOCK.unlock();
         }
         return instance;
     }
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/ServiceFinder.java b/core-common/src/main/java/org/glassfish/jersey/internal/ServiceFinder.java
index 46184d8..03d15d3 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/ServiceFinder.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/ServiceFinder.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 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
@@ -33,6 +33,8 @@
 import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.TreeSet;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -797,17 +799,20 @@
     public abstract static class ServiceIteratorProvider {
 
         private static volatile ServiceIteratorProvider sip;
-        private static final Object sipLock = new Object();
+        private static final Lock sipLock = new ReentrantLock();
 
         private static ServiceIteratorProvider getInstance() {
             // TODO: check the following is a good practice: Double-check idiom for lazy initialization of fields.
             ServiceIteratorProvider result = sip;
             if (result == null) { // First check (no locking)
-                synchronized (sipLock) {
+                sipLock.lock();
+                try {
                     result = sip;
                     if (result == null) { // Second check (with locking)
                         sip = result = new DefaultServiceIteratorProvider();
                     }
+                } finally {
+                    sipLock.unlock();
                 }
             }
             return result;
@@ -819,8 +824,11 @@
                 final ReflectPermission rp = new ReflectPermission("suppressAccessChecks");
                 security.checkPermission(rp);
             }
-            synchronized (sipLock) {
+            sipLock.lock();
+            try {
                 ServiceIteratorProvider.sip = sip;
+            } finally {
+                sipLock.unlock();
             }
         }
 
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/config/JerseySystemPropertiesConfigurationModel.java b/core-common/src/main/java/org/glassfish/jersey/internal/config/JerseySystemPropertiesConfigurationModel.java
index 4d9606b..a42a860 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/config/JerseySystemPropertiesConfigurationModel.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/config/JerseySystemPropertiesConfigurationModel.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2024 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
@@ -29,9 +29,11 @@
             "org.glassfish.jersey.servlet.ServletProperties",
             "org.glassfish.jersey.message.MessageProperties",
             "org.glassfish.jersey.apache.connector.ApacheClientProperties",
+            "org.glassfish.jersey.apache5.connector.Apache5ClientProperties",
             "org.glassfish.jersey.helidon.connector.HelidonClientProperties",
             "org.glassfish.jersey.jdk.connector.JdkConnectorProperties",
             "org.glassfish.jersey.jetty.connector.JettyClientProperties",
+            "org.glassfish.jersey.jnh.connector.JavaNetHttpClientProperties",
             "org.glassfish.jersey.netty.connector.NettyClientProperties",
             "org.glassfish.jersey.media.multipart.MultiPartProperties",
             "org.glassfish.jersey.server.oauth1.OAuth1ServerProperties");
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/inject/Providers.java b/core-common/src/main/java/org/glassfish/jersey/internal/inject/Providers.java
index 4bbab3e..f5c467a 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/inject/Providers.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/inject/Providers.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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,7 +43,9 @@
 import jakarta.annotation.Priority;
 
 import org.glassfish.jersey.JerseyPriorities;
+import org.glassfish.jersey.innate.inject.spi.ExternalRegistrables;
 import org.glassfish.jersey.internal.LocalizationMessages;
+import org.glassfish.jersey.internal.ServiceFinder;
 import org.glassfish.jersey.model.ContractProvider;
 import org.glassfish.jersey.model.internal.RankedComparator;
 import org.glassfish.jersey.model.internal.RankedProvider;
@@ -103,6 +105,14 @@
         interfaces.putAll(JAX_RS_PROVIDER_INTERFACE_WHITELIST);
         interfaces.put(jakarta.ws.rs.core.Feature.class, ProviderRuntime.BOTH);
         interfaces.put(Binder.class, ProviderRuntime.BOTH);
+
+        try {
+            ServiceFinder<ExternalRegistrables> registerables = ServiceFinder.find(ExternalRegistrables.class, true);
+            registerables.forEach(regs -> regs.registrableContracts()
+                    .forEach(pair -> interfaces.put(pair.getContract(), ProviderRuntime.fromRuntimeType(pair.getRuntimeType()))));
+        } catch (Throwable t) {
+            LOGGER.warning(LocalizationMessages.ERROR_EXTERNAL_REGISTERABLES_IGNORED(t.getMessage()));
+        }
         return interfaces;
     }
 
@@ -119,6 +129,10 @@
         public RuntimeType getRuntime() {
             return runtime;
         }
+
+        private static ProviderRuntime fromRuntimeType(RuntimeType type) {
+            return type == null ? BOTH : (type == RuntimeType.SERVER ? SERVER : CLIENT);
+        }
     }
 
     /**
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/util/PropertiesHelper.java b/core-common/src/main/java/org/glassfish/jersey/internal/util/PropertiesHelper.java
index 5500e54..97dee37 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/util/PropertiesHelper.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/util/PropertiesHelper.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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
@@ -390,6 +390,27 @@
     }
 
     /**
+     * Converts the property value to {@code boolean} and checks it is {@code true} or empty.
+     * Returns {@code true} if the value is {@code true} or empty but not {@code null}.
+     *
+     * <p>
+     *     The rationale behind this is that system property {@code -Dprop=true} is the same as {@code -Dprop}.
+     *     The property {@code -Dprop=false} behaves as if the {@code -Dprop} is not set at all.
+     * </p>
+     *
+     * @param value property value.
+     * @return {@code boolean} property value or {@code true} if the property value is not set or {@code false} if the property
+     *         is otherwise not convertible.
+     */
+    public static boolean isPropertyOrNotSet(final Object value) {
+        if (value instanceof Boolean) {
+            return Boolean.class.cast(value);
+        } else {
+            return value != null && ("".equals(value.toString()) || Boolean.parseBoolean(value.toString()));
+        }
+    }
+
+    /**
      * Faster replacement of {@code RuntimeType#name().toLowerCase(Locale.ROOT)}
      * @param runtimeType The runtime type to lower case
      * @return the lower-cased variant of the {@link RuntimeType}.
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Values.java b/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Values.java
index 4761644..5c3a747 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Values.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Values.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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,9 @@
 
 package org.glassfish.jersey.internal.util.collection;
 
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
 /**
  * A collection of {@link Value Value provider} factory & utility methods.
  *
@@ -297,25 +300,28 @@
 
     private static class LazyValueImpl<T> implements LazyValue<T> {
 
-        private final Object lock;
+        private final Lock lock;
         private final Value<T> delegate;
 
         private volatile Value<T> value;
 
         public LazyValueImpl(final Value<T> delegate) {
             this.delegate = delegate;
-            this.lock = new Object();
+            this.lock = new ReentrantLock();
         }
 
         @Override
         public T get() {
             Value<T> result = value;
             if (result == null) {
-                synchronized (lock) {
+                lock.lock();
+                try {
                     result = value;
                     if (result == null) {
                         value = result = Values.of(delegate.get());
                     }
+                } finally {
+                    lock.unlock();
                 }
             }
             return result.get();
@@ -380,21 +386,22 @@
 
     private static class LazyUnsafeValueImpl<T, E extends Throwable> implements LazyUnsafeValue<T, E> {
 
-        private final Object lock;
+        private final Lock lock;
         private final UnsafeValue<T, E> delegate;
 
         private volatile UnsafeValue<T, E> value;
 
         public LazyUnsafeValueImpl(final UnsafeValue<T, E> delegate) {
             this.delegate = delegate;
-            this.lock = new Object();
+            this.lock = new ReentrantLock();
         }
 
         @Override
         public T get() throws E {
             UnsafeValue<T, E> result = value;
             if (result == null) {
-                synchronized (lock) {
+                lock.lock();
+                try {
                     result = value;
                     //noinspection ConstantConditions
                     if (result == null) {
@@ -406,6 +413,8 @@
                         }
                         value = result;
                     }
+                } finally {
+                    lock.unlock();
                 }
             }
             return result.get();
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Views.java b/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Views.java
index 32e2640..4765056 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Views.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Views.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2024 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
@@ -24,13 +24,12 @@
 import java.util.List;
 import java.util.ListIterator;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
-import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull;
-
 /**
  * Collections utils, which provide transforming views for {@link List} and {@link Map}.
  *
@@ -197,8 +196,8 @@
      * @return union view of given sets.
      */
     public static <E> Set<E> setUnionView(final Set<? extends E> set1, final Set<? extends E> set2) {
-        checkNotNull(set1, "set1");
-        checkNotNull(set2, "set2");
+        Objects.requireNonNull(set1, "set1");
+        Objects.requireNonNull(set2, "set2");
 
         return new AbstractSet<E>() {
             @Override
@@ -220,18 +219,19 @@
     }
 
     /**
-     * Create a view of a difference of provided sets.
+     * Create a view of a difference of provided sets, i.e. the diff filters out from the first set the items included
+     * in the second set.
      * <p>
      * View is updated whenever any of the provided set changes.
      *
      * @param set1 first set.
      * @param set2 second set.
      * @param <E>  set item type.
-     * @return union view of given sets.
+     * @return view that is a difference of given sets.
      */
     public static <E> Set<E> setDiffView(final Set<? extends E> set1, final Set<? extends E> set2) {
-        checkNotNull(set1, "set1");
-        checkNotNull(set2, "set2");
+        Objects.requireNonNull(set1, "set1");
+        Objects.requireNonNull(set2, "set2");
 
         return new AbstractSet<E>() {
             @Override
diff --git a/core-common/src/main/java/org/glassfish/jersey/io/package-info.java b/core-common/src/main/java/org/glassfish/jersey/io/package-info.java
new file mode 100644
index 0000000..f913ae6
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/io/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2024 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
+ */
+
+/**
+ * Common Jersey core io classes.
+ */
+package org.glassfish.jersey.io;
diff --git a/core-common/src/main/java/org/glassfish/jersey/io/spi/FlushedCloseable.java b/core-common/src/main/java/org/glassfish/jersey/io/spi/FlushedCloseable.java
new file mode 100644
index 0000000..12aa714
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/io/spi/FlushedCloseable.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2024 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.io.spi;
+
+import java.io.Closeable;
+import java.io.Flushable;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * A marker interface that the stream provided to Jersey can implement,
+ * noting that the stream does not need to call {@link #flush()} prior to {@link #close()}.
+ * That way, {@link #flush()} method is not called twice.
+ *
+ * <p>
+ *     Usable by {@link javax.ws.rs.client.ClientRequestContext#setEntityStream(OutputStream)}.
+ *     Usable by {@link javax.ws.rs.container.ContainerResponseContext#setEntityStream(OutputStream)}.
+ * </p>
+ *
+ * <p>
+ *     This marker interface can be useful for the customer OutputStream to know the {@code flush} did not come from
+ *     Jersey before close. By default, when the entity stream is to be closed by Jersey, {@code flush} is called first.
+ * </p>
+ */
+public interface FlushedCloseable extends Flushable, Closeable {
+    /**
+     * Flushes this stream by writing any buffered output to the underlying stream.
+     * Then closes this stream and releases any system resources associated
+     * with it. If the stream is already closed then invoking this
+     * method has no effect.
+     *
+     * <p> As noted in {@link AutoCloseable#close()}, cases where the
+     * close may fail require careful attention. It is strongly advised
+     * to relinquish the underlying resources and to internally
+     * <em>mark</em> the {@code Closeable} as closed, prior to throwing
+     * the {@code IOException}.
+     *
+     * @throws IOException if an I/O error occurs
+     */
+    public void close() throws IOException;
+}
diff --git a/core-common/src/main/java/org/glassfish/jersey/io/spi/package-info.java b/core-common/src/main/java/org/glassfish/jersey/io/spi/package-info.java
new file mode 100644
index 0000000..7a70945
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/io/spi/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2024 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
+ */
+
+/**
+ * Common Jersey core io SPI classes.
+ */
+package org.glassfish.jersey.io.spi;
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/AbstractMessageReaderWriterProvider.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/AbstractMessageReaderWriterProvider.java
index f7e2673..466eaaa 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/AbstractMessageReaderWriterProvider.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/AbstractMessageReaderWriterProvider.java
@@ -46,7 +46,7 @@
      *
      * @deprecated use {@code StandardCharsets.UTF_8} instead.
      */
-    @Deprecated
+    @Deprecated(forRemoval = true)
     public static final Charset UTF8 = StandardCharsets.UTF_8;
 
     /**
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/CommittingOutputStream.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/CommittingOutputStream.java
index 39f74fb..e9f1bc2 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/CommittingOutputStream.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/CommittingOutputStream.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 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.logging.Level;
 import java.util.logging.Logger;
 
+import org.glassfish.jersey.innate.VirtualThreadSupport;
 import org.glassfish.jersey.internal.LocalizationMessages;
 import org.glassfish.jersey.internal.guava.Preconditions;
 
@@ -58,11 +59,13 @@
 public final class CommittingOutputStream extends OutputStream {
 
     private static final Logger LOGGER = Logger.getLogger(CommittingOutputStream.class.getName());
+    private final boolean isVirtualThread = VirtualThreadSupport.isVirtualThread();
+
     /**
      * Null stream provider.
      */
     private static final OutboundMessageContext.StreamProvider NULL_STREAM_PROVIDER =
-            contentLength -> new NullOutputStream();
+            contentLength -> OutputStream.nullOutputStream();
     /**
      * Default size of the buffer which will be used if no user defined size is specified.
      */
@@ -170,7 +173,7 @@
             Preconditions.checkState(streamProvider != null, STREAM_PROVIDER_NULL);
             adaptedOutput = streamProvider.getOutputStream(currentSize);
             if (adaptedOutput == null) {
-                adaptedOutput = new NullOutputStream();
+                adaptedOutput = OutputStream.nullOutputStream();
             }
 
             directWrite = true;
@@ -275,7 +278,13 @@
 
             commitStream(currentSize);
             if (buffer != null) {
-                buffer.writeTo(adaptedOutput);
+                if (isVirtualThread && adaptedOutput != null) {
+                    adaptedOutput.write(buffer.toByteArray());
+                } else {
+                    // Virtual thread in JDK 21 are blocked by synchronized writeTo
+                    // but about 10% faster than ^ without virtual threads.
+                    buffer.writeTo(adaptedOutput);
+                }
             }
         }
     }
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 bcd7e5f..14a008d 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, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 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
@@ -168,7 +168,7 @@
                     cookie.sameSite = NewCookie.SameSite.valueOf(value.toUpperCase());
                 }  else if (param.startsWith("expires")) {
                     try {
-                        cookie.expiry = HttpDateFormat.readDate(value + ", " + bites[++i]);
+                        cookie.expiry = HttpDateFormat.readDate(value + ", " + bites[++i].trim());
                     } catch (ParseException e) {
                         LOGGER.log(Level.FINE, LocalizationMessages.ERROR_NEWCOOKIE_EXPIRES(value), e);
                     }
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/DateProvider.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/DateProvider.java
index 09e7553..8417fb5 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/DateProvider.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/DateProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 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,7 +43,7 @@
     @Override
     public String toString(final Date header) {
         throwIllegalArgumentExceptionIfNull(header, LocalizationMessages.DATE_IS_NULL());
-        return HttpDateFormat.getPreferredDateFormat().format(header);
+        return HttpDateFormat.getPreferredDateFormatter().format(header);
     }
 
     @Override
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpDateFormat.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpDateFormat.java
index 75479e6..9cf2abf 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpDateFormat.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpDateFormat.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 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,12 +18,18 @@
 
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Date;
 import java.util.List;
 import java.util.Locale;
+import java.util.Queue;
 import java.util.TimeZone;
+import java.util.concurrent.ConcurrentLinkedQueue;
 
 /**
  * Helper class for HTTP specified date formats.
@@ -33,6 +39,46 @@
  */
 public final class HttpDateFormat {
 
+    private static final boolean USE_SIMPLE_DATE_FORMAT_OVER_DATE_TIME_FORMATTER = true;
+
+    /**
+     * <p>
+     *     A minimum formatter for converting java {@link Date} and {@link LocalDateTime} to {@code String} and vice-versa.
+     * </p>
+     * <p>
+     *     Works as a facade for implementation backed by {@link SimpleDateFormat} and {@link DateTimeFormatter}.
+     * </p>
+     */
+    public static interface HttpDateFormatter {
+        /**
+         *
+         * @param date
+         * @return
+         */
+        Date toDate(String date);
+
+        /**
+         *
+         * @param date
+         * @return
+         */
+        LocalDateTime toDateTime(String date);
+        /**
+         * Formats a {@link Date} into a date-time string.
+         *
+         * @param date the time value to be formatted into a date-time string.
+         * @return the formatted date-time string.
+         */
+        String format(Date date);
+        /**
+         * Formats a {@link LocalDateTime} into a date-time string.
+         *
+         * @param dateTime the time value to be formatted into a date-time string.
+         * @return the formatted date-time string.
+         */
+        String format(LocalDateTime dateTime);
+    }
+
     private HttpDateFormat() {
     }
     /**
@@ -50,39 +96,40 @@
 
     private static final TimeZone GMT_TIME_ZONE = TimeZone.getTimeZone("GMT");
 
-    private static final ThreadLocal<List<SimpleDateFormat>> dateFormats = new ThreadLocal<List<SimpleDateFormat>>() {
+    private static final List<HttpDateFormatter> dateFormats = createDateFormats();
+    private static final Queue<List<HttpDateFormatter>> simpleDateFormats = new ConcurrentLinkedQueue<>();
 
-        @Override
-        protected synchronized List<SimpleDateFormat> initialValue() {
-            return createDateFormats();
-        }
-    };
-
-    private static List<SimpleDateFormat> createDateFormats() {
-        final SimpleDateFormat[] formats = new SimpleDateFormat[]{
-            new SimpleDateFormat(RFC1123_DATE_FORMAT_PATTERN, Locale.US),
-            new SimpleDateFormat(RFC1036_DATE_FORMAT_PATTERN, Locale.US),
-            new SimpleDateFormat(ANSI_C_ASCTIME_DATE_FORMAT_PATTERN, Locale.US)
+    private static List<HttpDateFormatter> createDateFormats() {
+        final HttpDateFormatter[] formats = new HttpDateFormatter[]{
+                new HttpDateFormatterFromDateTimeFormatter(
+                        DateTimeFormatter.ofPattern(RFC1123_DATE_FORMAT_PATTERN, Locale.US).withZone(GMT_TIME_ZONE.toZoneId())),
+                new HttpDateFormatterFromDateTimeFormatter(
+                        DateTimeFormatter.ofPattern(RFC1123_DATE_FORMAT_PATTERN.replace("zzz", "ZZZ"), Locale.US)
+                                .withZone(GMT_TIME_ZONE.toZoneId())),
+                new HttpDateFormatterFromDateTimeFormatter(
+                        DateTimeFormatter.ofPattern(RFC1036_DATE_FORMAT_PATTERN, Locale.US).withZone(GMT_TIME_ZONE.toZoneId())),
+                new HttpDateFormatterFromDateTimeFormatter(
+                        DateTimeFormatter.ofPattern(RFC1036_DATE_FORMAT_PATTERN.replace("zzz", "ZZZ"), Locale.US)
+                                .withZone(GMT_TIME_ZONE.toZoneId())),
+                new HttpDateFormatterFromDateTimeFormatter(
+                        DateTimeFormatter.ofPattern(ANSI_C_ASCTIME_DATE_FORMAT_PATTERN, Locale.US)
+                                .withZone(GMT_TIME_ZONE.toZoneId()))
         };
-        formats[0].setTimeZone(GMT_TIME_ZONE);
-        formats[1].setTimeZone(GMT_TIME_ZONE);
-        formats[2].setTimeZone(GMT_TIME_ZONE);
 
         return Collections.unmodifiableList(Arrays.asList(formats));
     }
 
-    /**
-     * Return an unmodifiable list of HTTP specified date formats to use for
-     * parsing or formatting {@link Date}.
-     * <p>
-     * The list of date formats are scoped to the current thread and may be
-     * used without requiring to synchronize access to the instances when
-     * parsing or formatting.
-     *
-     * @return the list of data formats.
-     */
-    private static List<SimpleDateFormat> getDateFormats() {
-        return dateFormats.get();
+    private static List<HttpDateFormatter> createSimpleDateFormats() {
+        final HttpDateFormatterFromSimpleDateTimeFormat[] formats = new HttpDateFormatterFromSimpleDateTimeFormat[]{
+                new HttpDateFormatterFromSimpleDateTimeFormat(new SimpleDateFormat(RFC1123_DATE_FORMAT_PATTERN, Locale.US)),
+                new HttpDateFormatterFromSimpleDateTimeFormat(new SimpleDateFormat(RFC1036_DATE_FORMAT_PATTERN, Locale.US)),
+                new HttpDateFormatterFromSimpleDateTimeFormat(new SimpleDateFormat(ANSI_C_ASCTIME_DATE_FORMAT_PATTERN, Locale.US))
+        };
+        formats[0].simpleDateFormat.setTimeZone(GMT_TIME_ZONE);
+        formats[1].simpleDateFormat.setTimeZone(GMT_TIME_ZONE);
+        formats[2].simpleDateFormat.setTimeZone(GMT_TIME_ZONE);
+
+        return Collections.unmodifiableList(Arrays.asList(formats));
     }
 
     /**
@@ -94,9 +141,44 @@
      *
      * @return the preferred of data format.
      */
+    public static HttpDateFormatter getPreferredDateFormatter() {
+        if (USE_SIMPLE_DATE_FORMAT_OVER_DATE_TIME_FORMATTER) {
+            List<HttpDateFormatter> list = simpleDateFormats.poll();
+            if (list == null) {
+                list = createSimpleDateFormats();
+            }
+            // returns clone because calling SDF.parse(...) can change time zone
+            final SimpleDateFormat sdf = (SimpleDateFormat)
+                    ((HttpDateFormatterFromSimpleDateTimeFormat) list.get(0)).simpleDateFormat.clone();
+            simpleDateFormats.add(list);
+            return new HttpDateFormatterFromSimpleDateTimeFormat(sdf);
+        } else {
+            return dateFormats.get(0);
+        }
+    }
+
+    /**
+     * Get the preferred HTTP specified date format (RFC 1123).
+     * <p>
+     * The date format is scoped to the current thread and may be
+     * used without requiring to synchronize access to the instance when
+     * parsing or formatting.
+     *
+     * @return the preferred of data format.
+     * @deprecated Use getPreferredDateFormatter instead
+     */
+    // Unused in Jersey
+    @Deprecated(forRemoval = true)
     public static SimpleDateFormat getPreferredDateFormat() {
+        List<HttpDateFormatter> list = simpleDateFormats.poll();
+        if (list == null) {
+            list = createSimpleDateFormats();
+        }
         // returns clone because calling SDF.parse(...) can change time zone
-        return (SimpleDateFormat) dateFormats.get().get(0).clone();
+        final SimpleDateFormat sdf = (SimpleDateFormat)
+                ((HttpDateFormatterFromSimpleDateTimeFormat) list.get(0)).simpleDateFormat.clone();
+        simpleDateFormats.add(list);
+        return sdf;
     }
 
     /**
@@ -108,18 +190,106 @@
      * @throws java.text.ParseException in case the date string cannot be parsed.
      */
     public static Date readDate(final String date) throws ParseException {
-        ParseException pe = null;
-        for (final SimpleDateFormat f : HttpDateFormat.getDateFormats()) {
+        return USE_SIMPLE_DATE_FORMAT_OVER_DATE_TIME_FORMATTER
+                ? readDateSDF(date)
+                : readDateDTF(date);
+    }
+
+    private static Date readDateDTF(final String date) throws ParseException {
+        final List<HttpDateFormatter> list = dateFormats;
+        return readDate(date, list);
+    }
+
+    private static Date readDateSDF(final String date) throws ParseException {
+        List<HttpDateFormatter> list = simpleDateFormats.poll();
+        if (list == null) {
+            list = createSimpleDateFormats();
+        }
+        final Date ret = readDate(date, list);
+        simpleDateFormats.add(list);
+        return ret;
+    }
+
+    private static Date readDate(final String date, List<HttpDateFormatter> formatters) throws ParseException {
+        Exception pe = null;
+        for (final HttpDateFormatter f : formatters) {
             try {
-                Date result = f.parse(date);
-                // parse can change time zone -> set it back to GMT
-                f.setTimeZone(GMT_TIME_ZONE);
-                return result;
-            } catch (final ParseException e) {
+                return f.toDate(date);
+            } catch (final Exception e) {
                 pe = (pe == null) ? e : pe;
             }
         }
 
-        throw pe;
+        throw ParseException.class.isInstance(pe) ? (ParseException) pe
+                : new ParseException(pe.getMessage(),
+                DateTimeParseException.class.isInstance(pe) ? ((DateTimeParseException) pe).getErrorIndex() : 0);
+    }
+
+    /**
+     * Warning! DateTimeFormatter is incompatible with SimpleDateFormat for two digits year, since SimpleDateFormat uses
+     * 80 years before now and 20 years after, whereas DateTimeFormatter uses years starting with 2000.
+     */
+    private static class HttpDateFormatterFromDateTimeFormatter implements HttpDateFormatter {
+        private final DateTimeFormatter dateTimeFormatter;
+
+        private HttpDateFormatterFromDateTimeFormatter(DateTimeFormatter dateTimeFormatter) {
+            this.dateTimeFormatter = dateTimeFormatter;
+        }
+
+        @Override
+        public Date toDate(String date) {
+            return new Date(Instant.from(dateTimeFormatter.parse(date)).toEpochMilli());
+        }
+
+        @Override
+        public LocalDateTime toDateTime(String date) {
+            return Instant.from(dateTimeFormatter.parse(date)).atZone(GMT_TIME_ZONE.toZoneId()).toLocalDateTime();
+        }
+
+        @Override
+        public String format(Date date) {
+            return dateTimeFormatter.format(date.toInstant());
+        }
+
+        @Override
+        public String format(LocalDateTime dateTime) {
+            return dateTimeFormatter.format(dateTime);
+        }
+    }
+
+    private static class HttpDateFormatterFromSimpleDateTimeFormat implements HttpDateFormatter {
+        private final SimpleDateFormat simpleDateFormat;
+
+        private HttpDateFormatterFromSimpleDateTimeFormat(SimpleDateFormat simpleDateFormat) {
+            this.simpleDateFormat = simpleDateFormat;
+        }
+
+        @Override
+        public Date toDate(String date) {
+            final Date result;
+            try {
+                result = simpleDateFormat.parse(date);
+            } catch (ParseException e) {
+                throw new RuntimeException(e);
+            }
+            // parse can change time zone -> set it back to GMT
+            simpleDateFormat.setTimeZone(GMT_TIME_ZONE);
+            return result;
+        }
+
+        @Override
+        public LocalDateTime toDateTime(String date) {
+            return Instant.from(toDate(date).toInstant()).atZone(GMT_TIME_ZONE.toZoneId()).toLocalDateTime();
+        }
+
+        @Override
+        public String format(Date date) {
+            return simpleDateFormat.format(date);
+        }
+
+        @Override
+        public String format(LocalDateTime dateTime) {
+            return simpleDateFormat.format(Date.from(dateTime.atZone(GMT_TIME_ZONE.toZoneId()).toInstant()));
+        }
     }
 }
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpHeaderReader.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpHeaderReader.java
index fe5a249..62df83f 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpHeaderReader.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpHeaderReader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 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,8 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 
 import jakarta.ws.rs.core.Cookie;
 import jakarta.ws.rs.core.MediaType;
@@ -622,6 +624,7 @@
 
     private abstract static class ListReader<T> {
         private final LRU<String, List<T>> LIST_CACHE = LRU.create();
+        private final Lock lock = new ReentrantLock();
         protected final ListElementCreator<T> creator;
 
         protected ListReader(ListElementCreator<T> creator) {
@@ -639,7 +642,8 @@
             List<T> list = LIST_CACHE.getIfPresent(header);
 
             if (list == null) {
-                synchronized (LIST_CACHE) {
+                lock.lock();
+                try {
                     list = LIST_CACHE.getIfPresent(header);
                     if (list == null) {
                         HttpHeaderReader reader = new HttpHeaderReaderImpl(header);
@@ -655,6 +659,8 @@
                         }
                         LIST_CACHE.put(header, list);
                     }
+                } finally {
+                    lock.unlock();
                 }
             }
 
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/InboundMessageContext.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/InboundMessageContext.java
index 9eb4a40..8f31f74 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/InboundMessageContext.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/InboundMessageContext.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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,17 +23,13 @@
 import java.io.InputStream;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
-import java.net.URI;
 import java.text.ParseException;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 import java.util.StringTokenizer;
@@ -41,21 +37,16 @@
 
 import jakarta.ws.rs.ProcessingException;
 import jakarta.ws.rs.core.Configuration;
-import jakarta.ws.rs.core.Cookie;
-import jakarta.ws.rs.core.EntityTag;
 import jakarta.ws.rs.core.HttpHeaders;
 import jakarta.ws.rs.core.Link;
 import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.MultivaluedMap;
-import jakarta.ws.rs.core.NewCookie;
 import jakarta.ws.rs.ext.ReaderInterceptor;
 
-import jakarta.ws.rs.ext.RuntimeDelegate;
 import javax.xml.transform.Source;
 
 import org.glassfish.jersey.internal.LocalizationMessages;
 import org.glassfish.jersey.internal.PropertiesDelegate;
-import org.glassfish.jersey.internal.RuntimeDelegateDecorator;
 import org.glassfish.jersey.internal.util.collection.GuardianStringKeyMultivaluedMap;
 import org.glassfish.jersey.internal.util.collection.LazyValue;
 import org.glassfish.jersey.internal.util.collection.Value;
@@ -67,7 +58,7 @@
  *
  * @author Marek Potociar
  */
-public abstract class InboundMessageContext {
+public abstract class InboundMessageContext extends MessageHeaderMethods {
 
     private static final InputStream EMPTY = new InputStream() {
 
@@ -100,7 +91,6 @@
     private final boolean translateNce;
     private MessageBodyWorkers workers;
     private final Configuration configuration;
-    private final RuntimeDelegate runtimeDelegateDecorator;
     private LazyValue<MediaType> contentTypeCache;
     private LazyValue<List<AcceptableMediaType>> acceptTypeCache;
 
@@ -166,11 +156,11 @@
      *                      as required by JAX-RS specification on the server side.
      */
     public InboundMessageContext(Configuration configuration, boolean translateNce) {
+        super(configuration);
         this.headers = new GuardianStringKeyMultivaluedMap<>(HeaderUtils.createInbound());
         this.entityContent = new EntityContent();
         this.translateNce = translateNce;
         this.configuration = configuration;
-        runtimeDelegateDecorator = RuntimeDelegateDecorator.configured(configuration);
 
         contentTypeCache = contentTypeCache();
         acceptTypeCache = acceptTypeCache();
@@ -319,42 +309,9 @@
         return buffer.toString();
     }
 
-    /**
-     * Get a single typed header value.
-     *
-     * @param name        header name.
-     * @param converter   from string conversion function. Is expected to throw {@link ProcessingException}
-     *                    if conversion fails.
-     * @param convertNull if {@code true} this method calls the provided converter even for {@code null}. Otherwise this
-     *                    method returns the {@code null} without calling the converter.
-     * @return value of the header, or (possibly converted) {@code null} if not present.
-     */
-    private <T> T singleHeader(String name, Function<String, T> converter, boolean convertNull) {
-        final List<String> values = this.headers.get(name);
-
-        if (values == null || values.isEmpty()) {
-            return convertNull ? converter.apply(null) : null;
-        }
-        if (values.size() > 1) {
-            throw new HeaderValueException(LocalizationMessages.TOO_MANY_HEADER_VALUES(name, values.toString()),
-                    HeaderValueException.Context.INBOUND);
-        }
-
-        Object value = values.get(0);
-        if (value == null) {
-            return convertNull ? converter.apply(null) : null;
-        }
-
-        try {
-            return converter.apply(HeaderUtils.asString(value, runtimeDelegateDecorator));
-        } catch (ProcessingException ex) {
-            throw exception(name, value, ex);
-        }
-    }
-
-    private static HeaderValueException exception(final String headerName, Object headerValue, Exception e) {
-        return new HeaderValueException(LocalizationMessages.UNABLE_TO_PARSE_HEADER_VALUE(headerName, headerValue), e,
-                HeaderValueException.Context.INBOUND);
+    @Override
+    public HeaderValueException.Context getHeaderValueExceptionContext() {
+        return HeaderValueException.Context.INBOUND;
     }
 
     /**
@@ -367,24 +324,6 @@
     }
 
     /**
-     * Get message date.
-     *
-     * @return the message date, otherwise {@code null} if not present.
-     */
-    public Date getDate() {
-        return singleHeader(HttpHeaders.DATE, new Function<String, Date>() {
-            @Override
-            public Date apply(String input) {
-                try {
-                    return HttpHeaderReader.readDate(input);
-                } catch (ParseException ex) {
-                    throw new ProcessingException(ex);
-                }
-            }
-        }, false);
-    }
-
-    /**
      * Get If-Match header.
      *
      * @return the If-Match header value, otherwise {@code null} if not present.
@@ -419,42 +358,6 @@
     }
 
     /**
-     * Get the language of the entity.
-     *
-     * @return the language of the entity or {@code null} if not specified.
-     */
-    public Locale getLanguage() {
-        return singleHeader(HttpHeaders.CONTENT_LANGUAGE, new Function<String, Locale>() {
-            @Override
-            public Locale apply(String input) {
-                try {
-                    return new LanguageTag(input).getAsLocale();
-                } catch (ParseException e) {
-                    throw new ProcessingException(e);
-                }
-            }
-        }, false);
-    }
-
-    /**
-     * Get Content-Length value.
-     *
-     * @return Content-Length as integer if present and valid number. In other cases returns -1.
-     */
-    public int getLength() {
-        return singleHeader(HttpHeaders.CONTENT_LENGTH, new Function<String, Integer>() {
-            @Override
-            public Integer apply(String input) {
-                try {
-                    return (input != null && !input.isEmpty()) ? Integer.parseInt(input) : -1;
-                } catch (NumberFormatException ex) {
-                    throw new ProcessingException(ex);
-                }
-            }
-        }, true);
-    }
-
-    /**
      * Get the media type of the entity.
      *
      * @return the media type or {@code null} if not specified (e.g. there's no
@@ -569,120 +472,6 @@
     }
 
     /**
-     * Get any cookies that accompanied the request.
-     *
-     * @return a read-only map of cookie name (String) to {@link jakarta.ws.rs.core.Cookie}.
-     */
-    public Map<String, Cookie> getRequestCookies() {
-        List<String> cookies = this.headers.get(HttpHeaders.COOKIE);
-        if (cookies == null || cookies.isEmpty()) {
-            return Collections.emptyMap();
-        }
-
-        Map<String, Cookie> result = new HashMap<String, Cookie>();
-        for (String cookie : cookies) {
-            if (cookie != null) {
-                result.putAll(HttpHeaderReader.readCookies(cookie));
-            }
-        }
-        return result;
-    }
-
-    /**
-     * Get the allowed HTTP methods from the Allow HTTP header.
-     *
-     * @return the allowed HTTP methods, all methods will returned as upper case
-     * strings.
-     */
-    public Set<String> getAllowedMethods() {
-        final String allowed = getHeaderString(HttpHeaders.ALLOW);
-        if (allowed == null || allowed.isEmpty()) {
-            return Collections.emptySet();
-        }
-        try {
-            return new HashSet<String>(HttpHeaderReader.readStringList(allowed.toUpperCase(Locale.ROOT)));
-        } catch (java.text.ParseException e) {
-            throw exception(HttpHeaders.ALLOW, allowed, e);
-        }
-    }
-
-    /**
-     * Get any new cookies set on the response message.
-     *
-     * @return a read-only map of cookie name (String) to a {@link jakarta.ws.rs.core.NewCookie new cookie}.
-     */
-    public Map<String, NewCookie> getResponseCookies() {
-        List<String> cookies = this.headers.get(HttpHeaders.SET_COOKIE);
-        if (cookies == null || cookies.isEmpty()) {
-            return Collections.emptyMap();
-        }
-
-        Map<String, NewCookie> result = new HashMap<String, NewCookie>();
-        for (String cookie : cookies) {
-            if (cookie != null) {
-                NewCookie newCookie = HttpHeaderReader.readNewCookie(cookie);
-                String cookieName = newCookie.getName();
-                if (result.containsKey(cookieName)) {
-                    result.put(cookieName, HeaderUtils.getPreferredCookie(result.get(cookieName), newCookie));
-                } else {
-                    result.put(cookieName, newCookie);
-                }
-            }
-        }
-        return result;
-    }
-
-    /**
-     * Get the entity tag.
-     *
-     * @return the entity tag, otherwise {@code null} if not present.
-     */
-    public EntityTag getEntityTag() {
-        return singleHeader(HttpHeaders.ETAG, new Function<String, EntityTag>() {
-            @Override
-            public EntityTag apply(String value) {
-                return EntityTag.valueOf(value);
-            }
-        }, false);
-    }
-
-    /**
-     * Get the last modified date.
-     *
-     * @return the last modified date, otherwise {@code null} if not present.
-     */
-    public Date getLastModified() {
-        return singleHeader(HttpHeaders.LAST_MODIFIED, new Function<String, Date>() {
-            @Override
-            public Date apply(String input) {
-                try {
-                    return HttpHeaderReader.readDate(input);
-                } catch (ParseException e) {
-                    throw new ProcessingException(e);
-                }
-            }
-        }, false);
-    }
-
-    /**
-     * Get the location.
-     *
-     * @return the location URI, otherwise {@code null} if not present.
-     */
-    public URI getLocation() {
-        return singleHeader(HttpHeaders.LOCATION, new Function<String, URI>() {
-            @Override
-            public URI apply(String value) {
-                try {
-                    return URI.create(value);
-                } catch (IllegalArgumentException ex) {
-                    throw new ProcessingException(ex);
-                }
-            }
-        }, false);
-    }
-
-    /**
      * Get the links attached to the message as header.
      *
      * @return links, may return empty {@link java.util.Set} if no links are present. Never
@@ -726,57 +515,6 @@
         }
     }
 
-    /**
-     * Check if link for relation exists.
-     *
-     * @param relation link relation.
-     * @return {@code true} if the for the relation link exists, {@code false}
-     * otherwise.
-     */
-    public boolean hasLink(String relation) {
-        for (Link link : getLinks()) {
-            List<String> relations = LinkProvider.getLinkRelations(link.getRel());
-
-            if (relations != null && relations.contains(relation)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Get the link for the relation.
-     *
-     * @param relation link relation.
-     * @return the link for the relation, otherwise {@code null} if not present.
-     */
-    public Link getLink(String relation) {
-        for (Link link : getLinks()) {
-            List<String> relations = LinkProvider.getLinkRelations(link.getRel());
-            if (relations != null && relations.contains(relation)) {
-                return link;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Convenience method that returns a {@link jakarta.ws.rs.core.Link.Builder Link.Builder}
-     * for the relation.
-     *
-     * @param relation link relation.
-     * @return the link builder for the relation, otherwise {@code null} if not
-     * present.
-     */
-    public Link.Builder getLinkBuilder(String relation) {
-        Link link = getLink(relation);
-        if (link == null) {
-            return null;
-        }
-
-        return Link.fromLink(link);
-    }
-
     // Message entity
 
     /**
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/InputStreamProvider.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/InputStreamProvider.java
index c07dcbf..6bf66cb 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/InputStreamProvider.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/InputStreamProvider.java
@@ -78,10 +78,8 @@
             MediaType mediaType,
             MultivaluedMap<String, Object> httpHeaders,
             OutputStream entityStream) throws IOException {
-        try {
+        try (t) {
             ReaderWriter.writeTo(t, entityStream);
-        } finally {
-            t.close();
         }
     }
 }
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/MessageHeaderMethods.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/MessageHeaderMethods.java
new file mode 100644
index 0000000..cd23505
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/MessageHeaderMethods.java
@@ -0,0 +1,461 @@
+/*
+ * Copyright (c) 2024 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 org.glassfish.jersey.internal.LocalizationMessages;
+import org.glassfish.jersey.internal.RuntimeDelegateDecorator;
+
+import jakarta.ws.rs.ProcessingException;
+import jakarta.ws.rs.core.Configuration;
+import jakarta.ws.rs.core.Cookie;
+import jakarta.ws.rs.core.EntityTag;
+import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.Link;
+import jakarta.ws.rs.core.MultivaluedMap;
+import jakarta.ws.rs.core.NewCookie;
+import jakarta.ws.rs.ext.RuntimeDelegate;
+import java.net.URI;
+import java.text.ParseException;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+/**
+ * Common header methods for outbound and inbound messages.
+ */
+public abstract class MessageHeaderMethods {
+    protected RuntimeDelegate runtimeDelegateDecorator;
+
+    protected MessageHeaderMethods(Configuration configuration) {
+        this.runtimeDelegateDecorator = RuntimeDelegateDecorator.configured(configuration);
+    }
+
+    protected MessageHeaderMethods(MessageHeaderMethods other) {
+        this.runtimeDelegateDecorator = other.runtimeDelegateDecorator;
+    }
+
+    /**
+     * Get a message header as a single string value.
+     *
+     * Each single non-string header value is converted to String using a {@code RuntimeDelegate.HeaderDelegate} if one
+     * is available via {@code RuntimeDelegate#createHeaderDelegate(java.lang.Class)} for the header value
+     * class or using its {@code toString} method if a header delegate is not available.
+     *
+     * @param name the message header.
+     * @return the message header value. If the message header is not present then {@code null} is returned. If the message
+     * header is present but has no value then the empty string is returned. If the message header is present more than once
+     * then the values of joined together and separated by a ',' character.
+     */
+    public abstract String getHeaderString(String name);
+
+    /**
+     * Get the mutable message headers multivalued map.
+     *
+     * @return mutable multivalued map of message headers.
+     */
+    public abstract MultivaluedMap<String, ?> getHeaders();
+
+    /**
+     * Return {@link HeaderValueException.Context} type of the message context.
+     * @return {@link HeaderValueException.Context} type of the message context.
+     */
+    protected abstract HeaderValueException.Context getHeaderValueExceptionContext();
+
+    /**
+     * Get the links attached to the message as header.
+     *
+     * @return links, may return empty {@link java.util.Set} if no links are present. Never
+     * returns {@code null}.
+     */
+    public abstract Set<Link> getLinks();
+
+    /**
+     * Checks whether a header with a specific name and value (or item of the token-separated value list) exists.
+     *
+     * Each single non-string header value is converted to String using a {@code RuntimeDelegate.HeaderDelegate} if one
+     * is available via {@code RuntimeDelegate#createHeaderDelegate(java.lang.Class)} for the header value
+     * class or using its {@code toString} method if a header delegate is not available.
+     *
+     * <p>
+     * For example: {@code containsHeaderString("cache-control", ",", "no-store"::equalsIgnoreCase)} will return {@code true} if
+     * a {@code Cache-Control} header exists that has the value {@code no-store}, the value {@code No-Store} or the value
+     * {@code Max-Age, NO-STORE, no-transform}, but {@code false} when it has the value {@code no-store;no-transform}
+     * (missing comma), or the value {@code no - store} (whitespace within value).
+     *
+     * @param name the message header.
+     * @param valueSeparatorRegex Separates the header value into single values. {@code null} does not split.
+     * @param valuePredicate value must fulfil this predicate.
+     * @return {@code true} if and only if a header with the given name exists, having either a whitespace-trimmed value
+     * matching the predicate, or having at least one whitespace-trimmed single value in a token-separated list of single values.
+     */
+    public boolean containsHeaderString(String name, String valueSeparatorRegex, Predicate<String> valuePredicate) {
+        final String header = getHeaderString(name);
+        if (header == null) {
+            return false;
+        }
+        final String[] split = header.split(valueSeparatorRegex);
+        for (String s : split) {
+            if (valuePredicate.test(s.trim())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Checks whether a header with a specific name and value (or item of the comma-separated value list) exists.
+     *
+     * Each single non-string header value is converted to String using a {@code RuntimeDelegate.HeaderDelegate} if one
+     * is available via {@code RuntimeDelegate#createHeaderDelegate(java.lang.Class)} for the header value
+     * class or using its {@code toString} method if a header delegate is not available.
+     *
+     * <p>
+     * For example: {@code containsHeaderString("cache-control", "no-store"::equalsIgnoreCase)} will return {@code true} if
+     * a {@code Cache-Control} header exists that has the value {@code no-store}, the value {@code No-Store} or the value
+     * {@code Max-Age, NO-STORE, no-transform}, but {@code false} when it has the value {@code no-store;no-transform}
+     * (missing comma), or the value {@code no - store} (whitespace within value).
+     *
+     * @param name the message header.
+     * @param valuePredicate value must fulfil this predicate.
+     * @return {@code true} if and only if a header with the given name exists, having either a whitespace-trimmed value
+     * matching the predicate, or having at least one whitespace-trimmed single value in a comma-separated list of single values.
+     */
+    public boolean containsHeaderString(String name, Predicate<String> valuePredicate) {
+        return containsHeaderString(name, ",", valuePredicate);
+    }
+
+    /**
+     * Get the allowed HTTP methods from the Allow HTTP header.
+     *
+     * @return the allowed HTTP methods, all methods will returned as upper case
+     * strings.
+     */
+    public Set<String> getAllowedMethods() {
+        final String allowed = getHeaderString(HttpHeaders.ALLOW);
+        if (allowed == null || allowed.isEmpty()) {
+            return Collections.emptySet();
+        }
+        try {
+            return new HashSet<String>(HttpHeaderReader.readStringList(allowed.toUpperCase(Locale.ROOT)));
+        } catch (java.text.ParseException e) {
+            throw exception(HttpHeaders.ALLOW, allowed, e);
+        }
+    }
+
+    /**
+     * Get message date.
+     *
+     * @return the message date, otherwise {@code null} if not present.
+     */
+    public Date getDate() {
+        return singleHeader(HttpHeaders.DATE, Date.class, input -> {
+            try {
+                return HttpHeaderReader.readDate(input);
+            } catch (ParseException e) {
+                throw new ProcessingException(e);
+            }
+        }, false);
+    }
+
+    /**
+     * Get the entity tag.
+     *
+     * @return the entity tag, otherwise {@code null} if not present.
+     */
+    public EntityTag getEntityTag() {
+        return singleHeader(HttpHeaders.ETAG, EntityTag.class, new Function<String, EntityTag>() {
+            @Override
+            public EntityTag apply(String value) {
+                try {
+                    return value == null ? null : EntityTag.valueOf(value);
+                } catch (IllegalArgumentException ex) {
+                    throw new ProcessingException(ex);
+                }
+            }
+        }, false);
+    }
+
+    /**
+     * Get the language of the entity.
+     *
+     * @return the language of the entity or {@code null} if not specified
+     */
+    public Locale getLanguage() {
+        return singleHeader(HttpHeaders.CONTENT_LANGUAGE, Locale.class, input -> {
+            try {
+                return new LanguageTag(input).getAsLocale();
+            } catch (ParseException e) {
+                throw new ProcessingException(e);
+            }
+        }, false);
+    }
+
+    /**
+     * Get the last modified date.
+     *
+     * @return the last modified date, otherwise {@code null} if not present.
+     */
+    public Date getLastModified() {
+        return singleHeader(HttpHeaders.LAST_MODIFIED, Date.class, new Function<String, Date>() {
+            @Override
+            public Date apply(String input) {
+                try {
+                    return HttpHeaderReader.readDate(input);
+                } catch (ParseException e) {
+                    throw new ProcessingException(e);
+                }
+            }
+        }, false);
+    }
+
+    /**
+     * Get Content-Length value.
+     * <p>
+     * <B>Note</B>: {@link #getLengthLong() getLengthLong()}
+     * should be preferred over this method, since it returns a {@code long}
+     * instead and is therefore more portable.</P>
+     *
+     * @return Content-Length as a postive integer if present and valid number, {@code -1} if negative number.
+     * @throws ProcessingException when {@link Integer#parseInt(String)} (String)} throws {@link NumberFormatException}.
+     */
+    public int getLength() {
+        return singleHeader(HttpHeaders.CONTENT_LENGTH, Integer.class, input -> {
+            try {
+                if (input != null && !input.isEmpty()) {
+                    int i = Integer.parseInt(input);
+                    if (i >= 0) {
+                        return i;
+                    }
+                }
+                return -1;
+
+            } catch (NumberFormatException ex) {
+                throw new ProcessingException(ex);
+            }
+        }, true);
+    }
+
+    /**
+     * Get Content-Length value.
+     *
+     * @return Content-Length as a positive long if present and valid number, {@code -1} if negative number.
+     * @throws ProcessingException when {@link Long#parseLong(String)} throws {@link NumberFormatException}.
+     */
+    public long getLengthLong() {
+        return singleHeader(HttpHeaders.CONTENT_LENGTH, Long.class, input -> {
+            try {
+                if (input != null && !input.isEmpty()) {
+                    long l = Long.parseLong(input);
+                    if (l >= 0) {
+                        return l;
+                    }
+                }
+                return -1L;
+            } catch (NumberFormatException ex) {
+                throw new ProcessingException(ex);
+            }
+        }, true);
+    }
+
+    /**
+     * Get the link for the relation.
+     *
+     * @param relation link relation.
+     * @return the link for the relation, otherwise {@code null} if not present.
+     */
+    public Link getLink(String relation) {
+        for (Link link : getLinks()) {
+            List<String> relations = LinkProvider.getLinkRelations(link.getRel());
+            if (relations != null && relations.contains(relation)) {
+                return link;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Convenience method that returns a {@link jakarta.ws.rs.core.Link.Builder Link.Builder}
+     * for the relation.
+     *
+     * @param relation link relation.
+     * @return the link builder for the relation, otherwise {@code null} if not
+     * present.
+     */
+    public Link.Builder getLinkBuilder(String relation) {
+        Link link = getLink(relation);
+        if (link == null) {
+            return null;
+        }
+
+        return Link.fromLink(link);
+    }
+
+    /**
+     * Get the location.
+     *
+     * @return the location URI, otherwise {@code null} if not present.
+     */
+    public URI getLocation() {
+        return singleHeader(HttpHeaders.LOCATION, URI.class, value -> {
+            try {
+                return value == null ? null : URI.create(value);
+            } catch (IllegalArgumentException ex) {
+                throw new ProcessingException(ex);
+            }
+        }, false);
+    }
+
+    /**
+     * Get any cookies that accompanied the message.
+     *
+     * @return a read-only map of cookie name (String) to {@link jakarta.ws.rs.core.Cookie}.
+     */
+    public Map<String, Cookie> getRequestCookies() {
+        @SuppressWarnings("unchecked")
+        final List<Object> cookies = (List<Object>) getHeaders().get(HttpHeaders.COOKIE);
+        if (cookies == null || cookies.isEmpty()) {
+            return Collections.emptyMap();
+        }
+
+        Map<String, Cookie> result = new HashMap<String, Cookie>();
+        for (String cookie : toStringList(cookies)) {
+            if (cookie != null) {
+                result.putAll(HttpHeaderReader.readCookies(cookie));
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Get any new cookies set on the message.
+     *
+     * @return a read-only map of cookie name (String) to a {@link jakarta.ws.rs.core.NewCookie new cookie}.
+     */
+    public Map<String, NewCookie> getResponseCookies() {
+        @SuppressWarnings("unchecked")
+        List<Object> cookies = (List<Object>) getHeaders().get(HttpHeaders.SET_COOKIE);
+        if (cookies == null || cookies.isEmpty()) {
+            return Collections.emptyMap();
+        }
+
+        Map<String, NewCookie> result = new HashMap<String, NewCookie>();
+        for (String cookie : toStringList(cookies)) {
+            if (cookie != null) {
+                NewCookie newCookie = HttpHeaderReader.readNewCookie(cookie);
+                String cookieName = newCookie.getName();
+                if (result.containsKey(cookieName)) {
+                    result.put(cookieName, HeaderUtils.getPreferredCookie(result.get(cookieName), newCookie));
+                } else {
+                    result.put(cookieName, newCookie);
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Check if link for relation exists.
+     *
+     * @param relation link relation.
+     * @return {@code true} if the for the relation link exists, {@code false}
+     * otherwise.
+     */
+    public boolean hasLink(String relation) {
+        for (Link link : getLinks()) {
+            List<String> relations = LinkProvider.getLinkRelations(link.getRel());
+
+            if (relations != null && relations.contains(relation)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Get a single typed header value.
+     *
+     * @param <T>         header value type.
+     * @param name        header name.
+     * @param valueType   header value class.
+     * @param converter   from string conversion function. Is expected to throw {@link ProcessingException}
+     *                    if conversion fails.
+     * @param convertNull if {@code true} this method calls the provided converter even for {@code null}. Otherwise this
+     *                    method returns the {@code null} without calling the converter.
+     * @return value of the header, or (possibly converted) {@code null} if not present.
+     */
+    protected <T> T singleHeader(String name, Class<T> valueType, Function<String, T> converter, boolean convertNull) {
+        @SuppressWarnings("unchecked")
+        final List<Object> values = (List<Object>) getHeaders().get(name);
+
+        if (values == null || values.isEmpty()) {
+            return convertNull ? converter.apply(null) : null;
+        }
+        if (values.size() > 1) {
+            throw new HeaderValueException(
+                    LocalizationMessages.TOO_MANY_HEADER_VALUES(name, values.toString()),
+                    getHeaderValueExceptionContext());
+        }
+
+        Object value = values.get(0);
+        if (value == null) {
+            return convertNull ? converter.apply(null) : null;
+        }
+
+        if (HeaderValueException.Context.OUTBOUND == getHeaderValueExceptionContext() && valueType.isInstance(value)) {
+            return valueType.cast(value);
+        } else {
+            try {
+                return converter.apply(HeaderUtils.asString(value, runtimeDelegateDecorator));
+            } catch (ProcessingException ex) {
+                throw exception(name, value, ex);
+            }
+        }
+    }
+
+    /**
+     * Get a single typed header value for Inbound messages
+     *
+     * @param <T>         header value type.
+     * @param name        header name.
+     * @param converter   from string conversion function. Is expected to throw {@link ProcessingException}
+     *                    if conversion fails.
+     * @param convertNull if {@code true} this method calls the provided converter even for {@code null}. Otherwise this
+     *                    method returns the {@code null} without calling the converter.
+     * @return value of the header, or (possibly converted) {@code null} if not present.
+     */
+    protected <T> T singleHeader(String name, Function<String, T> converter, boolean convertNull) {
+        return singleHeader(name, null, converter, convertNull);
+    }
+
+    protected HeaderValueException exception(final String headerName, Object headerValue, Exception e) {
+        return new HeaderValueException(LocalizationMessages.UNABLE_TO_PARSE_HEADER_VALUE(headerName, headerValue), e,
+                getHeaderValueExceptionContext());
+    }
+
+    private List<String> toStringList(List list) {
+        return getHeaderValueExceptionContext() == HeaderValueException.Context.OUTBOUND
+                ? HeaderUtils.asStringList(list, runtimeDelegateDecorator)
+                : (List<String>) list;
+    }
+}
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/MessagingBinders.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/MessagingBinders.java
index fa1117a..307f54f 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/MessagingBinders.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/MessagingBinders.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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,7 @@
             bindSingletonWorker(ByteArrayProvider.class);
             // bindSingletonWorker(DataSourceProvider.class);
             bindSingletonWorker(FileProvider.class);
+            bindSingletonWorker(PathProvider.class);
             bindSingletonWorker(FormMultivaluedMapProvider.class);
             bindSingletonWorker(FormProvider.class);
             bindSingletonWorker(InputStreamProvider.class);
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 8615aeb..08fbf5a 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, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 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
@@ -81,7 +81,7 @@
         }
         if (cookie.getExpiry() != null) {
             b.append(";Expires=");
-            b.append(HttpDateFormat.getPreferredDateFormat().format(cookie.getExpiry()));
+            b.append(HttpDateFormat.getPreferredDateFormatter().format(cookie.getExpiry()));
         }
 
         return b.toString();
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/NullOutputStream.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/NullOutputStream.java
index 26d3f14..ae41378 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/NullOutputStream.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/NullOutputStream.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024 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,22 +18,17 @@
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Objects;
 
 import org.glassfish.jersey.internal.LocalizationMessages;
 
 /**
- * A {@code "dev/null"} output stream - an output stream implementation that discards all the
- * data written to it. This implementation is not thread-safe.
- *
- * Note that once a null output stream instance is {@link #close() closed}, any subsequent attempts
- * to write the data to the closed stream result in an {@link java.io.IOException} being thrown.
- *
- * @author Miroslav Fuksa
- * @author Marek Potociar
+ * Since JDK 11 is replaced by {@link OutputStream#nullOutputStream()}
  */
+@Deprecated(since = "3.1.7", forRemoval = true)
 public class NullOutputStream extends OutputStream {
 
-    private boolean isClosed;
+    private volatile boolean isClosed;
 
     @Override
     public void write(int b) throws IOException {
@@ -43,11 +38,7 @@
     @Override
     public void write(byte[] b, int off, int len) throws IOException {
         checkClosed();
-        if (b == null) {
-            throw new NullPointerException();
-        } else if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) {
-            throw new IndexOutOfBoundsException();
-        }
+        Objects.checkFromIndexSize(off, len, b.length);
     }
 
     @Override
@@ -65,4 +56,4 @@
     public void close() throws IOException {
         isClosed = true;
     }
-}
+}
\ No newline at end of file
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java
index d33acc3..b1b7745 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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,50 +20,40 @@
 import java.io.OutputStream;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
-import java.net.URI;
-import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
-import java.util.Map;
 import java.util.Set;
 import java.util.function.Function;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.stream.Collectors;
 
-import jakarta.ws.rs.ProcessingException;
 import jakarta.ws.rs.core.Configuration;
-import jakarta.ws.rs.core.Cookie;
-import jakarta.ws.rs.core.EntityTag;
 import jakarta.ws.rs.core.GenericEntity;
 import jakarta.ws.rs.core.GenericType;
 import jakarta.ws.rs.core.HttpHeaders;
 import jakarta.ws.rs.core.Link;
 import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.MultivaluedMap;
-import jakarta.ws.rs.core.NewCookie;
-import jakarta.ws.rs.ext.RuntimeDelegate;
 
 import org.glassfish.jersey.CommonProperties;
 import org.glassfish.jersey.internal.RuntimeDelegateDecorator;
-import org.glassfish.jersey.internal.LocalizationMessages;
 import org.glassfish.jersey.internal.util.ReflectionHelper;
 import org.glassfish.jersey.internal.util.collection.GuardianStringKeyMultivaluedMap;
 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.io.spi.FlushedCloseable;
 
 /**
  * Base outbound message context implementation.
  *
  * @author Marek Potociar
  */
-public class OutboundMessageContext {
+public class OutboundMessageContext extends MessageHeaderMethods {
     private static final Annotation[] EMPTY_ANNOTATIONS = new Annotation[0];
     private static final List<MediaType> WILDCARD_ACCEPTABLE_TYPE_SINGLETON_LIST =
             Collections.<MediaType>singletonList(MediaTypes.WILDCARD_ACCEPTABLE_TYPE);
@@ -71,7 +61,6 @@
     private final GuardianStringKeyMultivaluedMap<Object> headers;
     private final CommittingOutputStream committingOutputStream;
     private Configuration configuration;
-    private RuntimeDelegate runtimeDelegateDecorator;
     private LazyValue<MediaType> mediaTypeCache;
 
     private Object entity;
@@ -107,11 +96,11 @@
      * @param configuration the client/server {@link Configuration}. If {@code null}, the default behaviour is expected.
      */
     public OutboundMessageContext(Configuration configuration) {
+        super(configuration);
         this.configuration = configuration;
         this.headers = new GuardianStringKeyMultivaluedMap<>(HeaderUtils.createOutbound());
         this.committingOutputStream = new CommittingOutputStream();
         this.entityStream = committingOutputStream;
-        this.runtimeDelegateDecorator = RuntimeDelegateDecorator.configured(configuration);
         this.mediaTypeCache = mediaTypeCache();
 
         headers.setGuard(HttpHeaders.CONTENT_TYPE);
@@ -124,6 +113,7 @@
      * @param original the original outbound message context.
      */
     public OutboundMessageContext(OutboundMessageContext original) {
+        super(original);
         this.headers = new GuardianStringKeyMultivaluedMap<>(HeaderUtils.createOutbound());
         this.headers.setGuard(HttpHeaders.CONTENT_TYPE);
         this.headers.putAll(original.headers);
@@ -134,7 +124,6 @@
         this.entityType = original.entityType;
         this.entityAnnotations = original.entityAnnotations;
         this.configuration = original.configuration;
-        this.runtimeDelegateDecorator = original.runtimeDelegateDecorator;
         this.mediaTypeCache = mediaTypeCache();
     }
 
@@ -190,49 +179,9 @@
         return HeaderUtils.asHeaderString(headers.get(name), runtimeDelegateDecorator);
     }
 
-    /**
-     * Get a single typed header value.
-     *
-     * @param <T>         header value type.
-     * @param name        header name.
-     * @param valueType   header value class.
-     * @param converter   from string conversion function. Is expected to throw {@link ProcessingException}
-     *                    if conversion fails.
-     * @param convertNull if {@code true} this method calls the provided converter even for {@code null}. Otherwise this
-     *                    method returns the {@code null} without calling the converter.
-     * @return value of the header, or (possibly converted) {@code null} if not present.
-     */
-    private <T> T singleHeader(String name, Class<T> valueType, Function<String, T> converter, boolean convertNull) {
-        final List<Object> values = headers.get(name);
-
-        if (values == null || values.isEmpty()) {
-            return convertNull ? converter.apply(null) : null;
-        }
-        if (values.size() > 1) {
-            throw new HeaderValueException(
-                    LocalizationMessages.TOO_MANY_HEADER_VALUES(name, values.toString()),
-                    HeaderValueException.Context.OUTBOUND);
-        }
-
-        Object value = values.get(0);
-        if (value == null) {
-            return convertNull ? converter.apply(null) : null;
-        }
-
-        if (valueType.isInstance(value)) {
-            return valueType.cast(value);
-        } else {
-            try {
-                return converter.apply(HeaderUtils.asString(value, runtimeDelegateDecorator));
-            } catch (ProcessingException ex) {
-                throw exception(name, value, ex);
-            }
-        }
-    }
-
-    private static HeaderValueException exception(final String headerName, Object headerValue, Exception e) {
-        return new HeaderValueException(LocalizationMessages.UNABLE_TO_PARSE_HEADER_VALUE(headerName, headerValue), e,
-                HeaderValueException.Context.OUTBOUND);
+    @Override
+    public HeaderValueException.Context getHeaderValueExceptionContext() {
+        return HeaderValueException.Context.OUTBOUND;
     }
 
     /**
@@ -245,36 +194,6 @@
     }
 
     /**
-     * Get message date.
-     *
-     * @return the message date, otherwise {@code null} if not present.
-     */
-    public Date getDate() {
-        return singleHeader(HttpHeaders.DATE, Date.class, input -> {
-            try {
-                return HttpHeaderReader.readDate(input);
-            } catch (ParseException e) {
-                throw new ProcessingException(e);
-            }
-        }, false);
-    }
-
-    /**
-     * Get the language of the entity.
-     *
-     * @return the language of the entity or {@code null} if not specified
-     */
-    public Locale getLanguage() {
-        return singleHeader(HttpHeaders.CONTENT_LANGUAGE, Locale.class, input -> {
-            try {
-                return new LanguageTag(input).getAsLocale();
-            } catch (ParseException e) {
-                throw new ProcessingException(e);
-            }
-        }, false);
-    }
-
-    /**
      * Get the media type of the entity.
      *
      * @return the media type or {@code null} if not specified (e.g. there's no
@@ -378,174 +297,6 @@
     }
 
     /**
-     * Get any cookies that accompanied the message.
-     *
-     * @return a read-only map of cookie name (String) to {@link jakarta.ws.rs.core.Cookie}.
-     */
-    public Map<String, Cookie> getRequestCookies() {
-        final List<Object> cookies = headers.get(HttpHeaders.COOKIE);
-        if (cookies == null || cookies.isEmpty()) {
-            return Collections.emptyMap();
-        }
-
-        Map<String, Cookie> result = new HashMap<String, Cookie>();
-        for (String cookie : HeaderUtils.asStringList(cookies, runtimeDelegateDecorator)) {
-            if (cookie != null) {
-                result.putAll(HttpHeaderReader.readCookies(cookie));
-            }
-        }
-        return result;
-    }
-
-    /**
-     * Get the allowed HTTP methods from the Allow HTTP header.
-     *
-     * @return the allowed HTTP methods, all methods will returned as upper case
-     * strings.
-     */
-    public Set<String> getAllowedMethods() {
-        final String allowed = getHeaderString(HttpHeaders.ALLOW);
-        if (allowed == null || allowed.isEmpty()) {
-            return Collections.emptySet();
-        }
-        try {
-            return new HashSet<String>(HttpHeaderReader.readStringList(allowed));
-        } catch (java.text.ParseException e) {
-            throw exception(HttpHeaders.ALLOW, allowed, e);
-        }
-    }
-
-    /**
-     * Get Content-Length value.
-     * <p>
-     * <B>Note</B>: {@link #getLengthLong() getLengthLong()}
-     * should be preferred over this method, since it returns a {@code long}
-     * instead and is therefore more portable.</P>
-     *
-     * @return Content-Length as a postive integer if present and valid number, {@code -1} if negative number.
-     * @throws ProcessingException when {@link Integer#parseInt(String)} (String)} throws {@link NumberFormatException}.
-     */
-    public int getLength() {
-
-        return singleHeader(HttpHeaders.CONTENT_LENGTH, Integer.class, input -> {
-            try {
-                if (input != null && !input.isEmpty()) {
-                    int i = Integer.parseInt(input);
-                    if (i >= 0) {
-                        return i;
-                    }
-                }
-                return -1;
-
-            } catch (NumberFormatException ex) {
-                throw new ProcessingException(ex);
-            }
-        }, true);
-    }
-
-    /**
-     * Get Content-Length value.
-     *
-     * @return Content-Length as a positive long if present and valid number, {@code -1} if negative number.
-     * @throws ProcessingException when {@link Long#parseLong(String)} throws {@link NumberFormatException}.
-     */
-    public long getLengthLong() {
-        return singleHeader(HttpHeaders.CONTENT_LENGTH, Long.class, input -> {
-            try {
-                if (input != null && !input.isEmpty()) {
-                    long l = Long.parseLong(input);
-                    if (l >= 0) {
-                        return l;
-                    }
-                }
-                return -1L;
-            } catch (NumberFormatException ex) {
-                throw new ProcessingException(ex);
-            }
-        }, true);
-    }
-
-    /**
-     * Get any new cookies set on the message message.
-     *
-     * @return a read-only map of cookie name (String) to a {@link jakarta.ws.rs.core.NewCookie new cookie}.
-     */
-    public Map<String, NewCookie> getResponseCookies() {
-        List<Object> cookies = headers.get(HttpHeaders.SET_COOKIE);
-        if (cookies == null || cookies.isEmpty()) {
-            return Collections.emptyMap();
-        }
-
-        Map<String, NewCookie> result = new HashMap<String, NewCookie>();
-        for (String cookie : HeaderUtils.asStringList(cookies, runtimeDelegateDecorator)) {
-            if (cookie != null) {
-                NewCookie newCookie = HttpHeaderReader.readNewCookie(cookie);
-                String cookieName = newCookie.getName();
-                if (result.containsKey(cookieName)) {
-                    result.put(cookieName, HeaderUtils.getPreferredCookie(result.get(cookieName), newCookie));
-                } else {
-                    result.put(cookieName, newCookie);
-                }
-            }
-        }
-        return result;
-    }
-
-    /**
-     * Get the entity tag.
-     *
-     * @return the entity tag, otherwise {@code null} if not present.
-     */
-    public EntityTag getEntityTag() {
-        return singleHeader(HttpHeaders.ETAG, EntityTag.class, new Function<String, EntityTag>() {
-            @Override
-            public EntityTag apply(String value) {
-                try {
-                    return value == null ? null : EntityTag.valueOf(value);
-                } catch (IllegalArgumentException ex) {
-                    throw new ProcessingException(ex);
-                }
-            }
-        }, false);
-    }
-
-    /**
-     * Get the last modified date.
-     *
-     * @return the last modified date, otherwise {@code null} if not present.
-     */
-    public Date getLastModified() {
-        return singleHeader(HttpHeaders.LAST_MODIFIED, Date.class, new Function<String, Date>() {
-            @Override
-            public Date apply(String input) {
-                try {
-                    return HttpHeaderReader.readDate(input);
-                } catch (ParseException e) {
-                    throw new ProcessingException(e);
-                }
-            }
-        }, false);
-    }
-
-    /**
-     * Get the location.
-     *
-     * @return the location URI, otherwise {@code null} if not present.
-     */
-    public URI getLocation() {
-        return singleHeader(HttpHeaders.LOCATION, URI.class, new Function<String, URI>() {
-            @Override
-            public URI apply(String value) {
-                try {
-                    return value == null ? null : URI.create(value);
-                } catch (IllegalArgumentException ex) {
-                    throw new ProcessingException(ex);
-                }
-            }
-        }, false);
-    }
-
-    /**
      * Get the links attached to the message as header.
      *
      * @return links, may return empty {@link java.util.Set} if no links are present. Never
@@ -583,56 +334,6 @@
         return Collections.unmodifiableSet(result);
     }
 
-    /**
-     * Check if link for relation exists.
-     *
-     * @param relation link relation.
-     * @return {@code true} if the for the relation link exists, {@code false}
-     * otherwise.
-     */
-    public boolean hasLink(String relation) {
-        for (Link link : getLinks()) {
-            List<String> relations = LinkProvider.getLinkRelations(link.getRel());
-            if (relations != null && relations.contains(relation)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Get the link for the relation.
-     *
-     * @param relation link relation.
-     * @return the link for the relation, otherwise {@code null} if not present.
-     */
-    public Link getLink(String relation) {
-        for (Link link : getLinks()) {
-            List<String> relations = LinkProvider.getLinkRelations(link.getRel());
-            if (relations != null && relations.contains(relation)) {
-                return link;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Convenience method that returns a {@link jakarta.ws.rs.core.Link.Builder Link.Builder}
-     * for the relation.
-     *
-     * @param relation link relation.
-     * @return the link builder for the relation, otherwise {@code null} if not
-     * present.
-     */
-    public Link.Builder getLinkBuilder(String relation) {
-        Link link = getLink(relation);
-        if (link == null) {
-            return null;
-        }
-
-        return Link.fromLink(link);
-    }
-
     // Message entity
 
     /**
@@ -861,11 +562,13 @@
         if (hasEntity()) {
             try {
                 final OutputStream es = getEntityStream();
-                es.flush();
+                if (!FlushedCloseable.class.isInstance(es)) {
+                    es.flush();
+                }
                 es.close();
             } catch (IOException e) {
                 // Happens when the client closed connection before receiving the full response.
-                // This is OK and not interesting in vast majority of the cases
+                // This is OK and not interesting in the vast majority of the cases
                 // hence the log level set to FINE to make sure it does not flood the log unnecessarily
                 // (especially for clients disconnecting from SSE listening, which is very common).
                 Logger.getLogger(OutboundMessageContext.class.getName()).log(Level.FINE, e.getMessage(), e);
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/PathProvider.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/PathProvider.java
new file mode 100644
index 0000000..461a4f8
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/PathProvider.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2024 Markus KARG and others.
+ *
+ * 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 static jakarta.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM;
+import static jakarta.ws.rs.core.MediaType.WILDCARD;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.MultivaluedMap;
+
+import jakarta.inject.Singleton;
+
+/**
+ * Provider for marshalling/un-marshalling of {@code application/octet-stream}
+ * entity type to/from a {@link Path} instance.
+ *
+ * @author Markus KARG
+ */
+@Produces({APPLICATION_OCTET_STREAM, WILDCARD})
+@Consumes({APPLICATION_OCTET_STREAM, WILDCARD})
+@Singleton
+public final class PathProvider extends AbstractMessageReaderWriterProvider<Path> {
+
+    @Override
+    public final boolean isReadable(final Class<?> type,
+            final Type genericType,
+            final Annotation[] annotations,
+            final MediaType mediaType) {
+        return Path.class == type;
+    }
+
+    @Override
+    public final Path readFrom(final Class<Path> type,
+            final Type genericType,
+            final Annotation[] annotations,
+            final MediaType mediaType,
+            final MultivaluedMap<String, String> httpHeaders,
+            final InputStream entityStream) throws IOException {
+        final var path = Utils.createTempFile().toPath();
+        Files.copy(entityStream, path, StandardCopyOption.REPLACE_EXISTING);
+        return path;
+    }
+
+    @Override
+    public final boolean isWriteable(final Class<?> type,
+            final Type genericType,
+            final Annotation[] annotations,
+            final MediaType mediaType) {
+        return Path.class.isAssignableFrom(type);
+    }
+
+    @Override
+    public final void writeTo(final Path t,
+            final Class<?> type,
+            final Type genericType,
+            final Annotation[] annotations,
+            final MediaType mediaType,
+            final MultivaluedMap<String, Object> httpHeaders,
+            final OutputStream entityStream) throws IOException {
+        Files.copy(t, entityStream);
+    }
+}
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderInterceptorExecutor.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderInterceptorExecutor.java
index 91738c1..20a01d6 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderInterceptorExecutor.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderInterceptorExecutor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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
@@ -22,6 +22,8 @@
 import java.lang.reflect.Type;
 import java.util.Iterator;
 import java.util.List;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.stream.Collectors;
@@ -38,6 +40,7 @@
 import jakarta.ws.rs.ext.ReaderInterceptor;
 import jakarta.ws.rs.ext.ReaderInterceptorContext;
 
+import org.glassfish.jersey.innate.io.InputStreamWrapper;
 import org.glassfish.jersey.internal.LocalizationMessages;
 import org.glassfish.jersey.internal.PropertiesDelegate;
 import org.glassfish.jersey.internal.inject.InjectionManager;
@@ -248,10 +251,11 @@
      * {@link jakarta.ws.rs.ext.MessageBodyReader}s should not close the given {@link java.io.InputStream stream}. This input
      * stream makes sure that the stream is not closed even if MBR tries to do it.
      */
-    private static class UnCloseableInputStream extends InputStream {
+    private static class UnCloseableInputStream extends InputStreamWrapper {
 
         private final InputStream original;
         private final MessageBodyReader reader;
+        private final Lock markLock = new ReentrantLock();
 
         private UnCloseableInputStream(final InputStream original, final MessageBodyReader reader) {
             this.original = original;
@@ -259,43 +263,8 @@
         }
 
         @Override
-        public int read() throws IOException {
-            return original.read();
-        }
-
-        @Override
-        public int read(final byte[] b) throws IOException {
-            return original.read(b);
-        }
-
-        @Override
-        public int read(final byte[] b, final int off, final int len) throws IOException {
-            return original.read(b, off, len);
-        }
-
-        @Override
-        public long skip(final long l) throws IOException {
-            return original.skip(l);
-        }
-
-        @Override
-        public int available() throws IOException {
-            return original.available();
-        }
-
-        @Override
-        public synchronized void mark(final int i) {
-            original.mark(i);
-        }
-
-        @Override
-        public synchronized void reset() throws IOException {
-            original.reset();
-        }
-
-        @Override
-        public boolean markSupported() {
-            return original.markSupported();
+        protected InputStream getWrapped() {
+            return original;
         }
 
         @Override
@@ -304,10 +273,6 @@
                 LOGGER.log(Level.FINE, LocalizationMessages.MBR_TRYING_TO_CLOSE_STREAM(reader.getClass()));
             }
         }
-
-        private InputStream unwrap() {
-            return original;
-        }
     }
 
     /**
@@ -320,7 +285,7 @@
      */
     public static InputStream closeableInputStream(InputStream inputStream) {
         if (inputStream instanceof UnCloseableInputStream) {
-            return ((UnCloseableInputStream) inputStream).unwrap();
+            return ((UnCloseableInputStream) inputStream).getWrapped();
         } else {
             return inputStream;
         }
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderWriter.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderWriter.java
index 018c9f0..ae8e7d8 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderWriter.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderWriter.java
@@ -43,7 +43,7 @@
  * A utility class for reading and writing using byte and character streams.
  * <p>
  * If a byte or character array is utilized then the size of the array
- * is by default the value of {@value org.glassfish.jersey.message.MessageProperties#IO_DEFAULT_BUFFER_SIZE}.
+ * is by default decided by the JRE.
  * This value can be set using the system property
  * {@value org.glassfish.jersey.message.MessageProperties#IO_BUFFER_SIZE}.
  *
@@ -57,14 +57,19 @@
      *
      * @deprecated use {@code StandardCharsets.UTF_8} instead
      */
-    @Deprecated
+    @Deprecated(forRemoval = true)
     public static final Charset UTF8 = StandardCharsets.UTF_8;
     /**
      * The buffer size for arrays of byte and character.
      */
     public static final int BUFFER_SIZE = getBufferSize();
 
-    private static int getBufferSize() {
+    /**
+     * Whether {@linkplain BUFFER_SIZE} is to be ignored in favor of JRE's own decision.
+     */
+    public static final boolean AUTOSIZE_BUFFER = getAutosizeBuffer();
+
+    private static int getIOBufferSize() {
         // TODO should we unify this buffer size and CommittingOutputStream buffer size (controlled by CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER)?
         final String value = AccessController.doPrivileged(PropertiesHelper.getSystemProperty(MessageProperties.IO_BUFFER_SIZE));
         if (value != null) {
@@ -78,11 +83,20 @@
                 LOGGER.log(Level.CONFIG,
                         "Value of " + MessageProperties.IO_BUFFER_SIZE
                                 + " property is not a valid positive integer [" + value + "]."
-                                + " Reverting to default [" + MessageProperties.IO_DEFAULT_BUFFER_SIZE + "].",
+                                + " Reverting to default [at JRE's discretion].",
                         e);
             }
         }
-        return MessageProperties.IO_DEFAULT_BUFFER_SIZE;
+        return -1;
+    }
+
+    private static int getBufferSize() {
+        final int ioBufferSize = getIOBufferSize();
+        return ioBufferSize == -1 ? MessageProperties.IO_DEFAULT_BUFFER_SIZE : ioBufferSize;
+    }
+
+    private static boolean getAutosizeBuffer() {
+        return getIOBufferSize() == -1;
     }
 
     /**
@@ -93,6 +107,11 @@
      * @throws IOException if there is an error reading or writing bytes.
      */
     public static void writeTo(InputStream in, OutputStream out) throws IOException {
+        if (AUTOSIZE_BUFFER) {
+            in.transferTo(out);
+            return;
+        }
+
         int read;
         final byte[] data = new byte[BUFFER_SIZE];
         while ((read = in.read(data)) != -1) {
@@ -108,6 +127,11 @@
      * @throws IOException if there is an error reading or writing characters.
      */
     public static void writeTo(Reader in, Writer out) throws IOException {
+        if (AUTOSIZE_BUFFER) {
+            in.transferTo(out);
+            return;
+        }
+
         int read;
         final char[] data = new char[BUFFER_SIZE];
         while ((read = in.read(data)) != -1) {
diff --git a/core-common/src/main/java/org/glassfish/jersey/process/internal/RequestScope.java b/core-common/src/main/java/org/glassfish/jersey/process/internal/RequestScope.java
index ced41cd..4d92447 100644
--- a/core-common/src/main/java/org/glassfish/jersey/process/internal/RequestScope.java
+++ b/core-common/src/main/java/org/glassfish/jersey/process/internal/RequestScope.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 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
@@ -244,6 +244,7 @@
      */
     protected void release(RequestContext context) {
         context.release();
+        currentRequestContext.remove();
     }
 
     /**
diff --git a/core-common/src/main/java/org/glassfish/jersey/spi/AbstractThreadPoolProvider.java b/core-common/src/main/java/org/glassfish/jersey/spi/AbstractThreadPoolProvider.java
index 27cf449..79ac262 100644
--- a/core-common/src/main/java/org/glassfish/jersey/spi/AbstractThreadPoolProvider.java
+++ b/core-common/src/main/java/org/glassfish/jersey/spi/AbstractThreadPoolProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2024 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
@@ -29,6 +29,7 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import org.glassfish.jersey.innate.VirtualThreadUtil;
 import org.glassfish.jersey.internal.LocalizationMessages;
 import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;
 import org.glassfish.jersey.internal.util.ExtendedLogger;
@@ -37,6 +38,8 @@
 import org.glassfish.jersey.internal.util.collection.Values;
 import org.glassfish.jersey.process.JerseyProcessingUncaughtExceptionHandler;
 
+import jakarta.ws.rs.core.Configuration;
+
 /**
  * Abstract thread pool executor provider.
  * <p>
@@ -69,9 +72,7 @@
 
     private final String name;
     private final AtomicBoolean closed = new AtomicBoolean(false);
-    private final LazyValue<E> lazyExecutorServiceProvider =
-            Values.lazy((Value<E>) () -> createExecutor(getCorePoolSize(), createThreadFactory(), getRejectedExecutionHandler()));
-
+    private final LazyValue<E> lazyExecutorServiceProvider;
     /**
      * Inheritance constructor.
      *
@@ -79,7 +80,20 @@
      *             provided thread pool executor.
      */
     protected AbstractThreadPoolProvider(final String name) {
+        this(name, null);
+    }
+
+    /**
+     * Inheritance constructor.
+     *
+     * @param name name of the provided thread pool executor. Will be used in the names of threads created & used by the
+     *             provided thread pool executor.
+     * @param configuration {@link Configuration} properties.
+     */
+    protected AbstractThreadPoolProvider(final String name, Configuration configuration) {
         this.name = name;
+        lazyExecutorServiceProvider = Values.lazy((Value<E>) () ->
+                createExecutor(getCorePoolSize(), createThreadFactory(configuration), getRejectedExecutionHandler()));
     }
 
     /**
@@ -208,9 +222,10 @@
         return null;
     }
 
-    private ThreadFactory createThreadFactory() {
+    private ThreadFactory createThreadFactory(Configuration configuration) {
         final ThreadFactoryBuilder factoryBuilder = new ThreadFactoryBuilder()
                 .setNameFormat(name + "-%d")
+                .setThreadFactory(VirtualThreadUtil.withConfig(configuration).getThreadFactory())
                 .setUncaughtExceptionHandler(new JerseyProcessingUncaughtExceptionHandler());
 
         final ThreadFactory backingThreadFactory = getBackingThreadFactory();
diff --git a/core-common/src/main/java/org/glassfish/jersey/spi/ScheduledThreadPoolExecutorProvider.java b/core-common/src/main/java/org/glassfish/jersey/spi/ScheduledThreadPoolExecutorProvider.java
index e805b23..348db2b 100644
--- a/core-common/src/main/java/org/glassfish/jersey/spi/ScheduledThreadPoolExecutorProvider.java
+++ b/core-common/src/main/java/org/glassfish/jersey/spi/ScheduledThreadPoolExecutorProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2024 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.concurrent.ThreadFactory;
 
 import jakarta.annotation.PreDestroy;
+import jakarta.ws.rs.core.Configuration;
 
 /**
  * Default implementation of the Jersey {@link org.glassfish.jersey.spi.ScheduledExecutorServiceProvider
@@ -66,6 +67,17 @@
         super(name);
     }
 
+    /**
+     * Create a new instance of the scheduled thread pool executor provider.
+     *
+     * @param name provider name. The name will be used to name the threads created & used by the
+     *             provisioned scheduled thread pool executor.
+     * @@param configuration {@link Configuration} properties.
+     */
+    public ScheduledThreadPoolExecutorProvider(final String name, Configuration configuration) {
+        super(name, configuration);
+    }
+
     @Override
     public ScheduledExecutorService getExecutorService() {
         return super.getExecutor();
diff --git a/core-common/src/main/java/org/glassfish/jersey/spi/ThreadPoolExecutorProvider.java b/core-common/src/main/java/org/glassfish/jersey/spi/ThreadPoolExecutorProvider.java
index 09e0770..df3dd09 100644
--- a/core-common/src/main/java/org/glassfish/jersey/spi/ThreadPoolExecutorProvider.java
+++ b/core-common/src/main/java/org/glassfish/jersey/spi/ThreadPoolExecutorProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2024 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
@@ -26,6 +26,7 @@
 import java.util.concurrent.TimeUnit;
 
 import jakarta.annotation.PreDestroy;
+import jakarta.ws.rs.core.Configuration;
 
 /**
  * Default implementation of the Jersey {@link org.glassfish.jersey.spi.ExecutorServiceProvider executor service provider SPI}.
@@ -61,6 +62,17 @@
         super(name);
     }
 
+    /**
+     * Create a new instance of the thread pool executor provider.
+     *
+     * @param name provider name. The name will be used to name the threads created & used by the
+     *             provisioned thread pool executor.
+     * @param configuration {@link Configuration} properties.
+     */
+    public ThreadPoolExecutorProvider(final String name, Configuration configuration) {
+        super(name, configuration);
+    }
+
     @Override
     public ExecutorService getExecutorService() {
         return super.getExecutor();
diff --git a/core-common/src/main/java20-/org/glassfish/jersey/innate/VirtualThreadSupport.java b/core-common/src/main/java20-/org/glassfish/jersey/innate/VirtualThreadSupport.java
new file mode 100644
index 0000000..867a65b
--- /dev/null
+++ b/core-common/src/main/java20-/org/glassfish/jersey/innate/VirtualThreadSupport.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2024 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;
+
+import org.glassfish.jersey.innate.virtual.LoomishExecutors;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+
+/**
+ * Utility class for the virtual thread support.
+ */
+public final class VirtualThreadSupport {
+
+    private static final LoomishExecutors NON_VIRTUAL = new NonLoomishExecutors(Executors.defaultThreadFactory());
+
+    /**
+     * Do not instantiate.
+     */
+    private VirtualThreadSupport() {
+        throw new IllegalStateException();
+    }
+
+    /**
+     * Informs whether the given {@link Thread} is virtual.
+     * @return true when the current thread is virtual.
+     */
+    public static boolean isVirtualThread() {
+        return false;
+    }
+
+    /**
+     * Return an instance of {@link LoomishExecutors} based on a permission to use virtual threads.
+     * @param allow whether to allow virtual threads.
+     * @return the {@link LoomishExecutors} instance.
+     */
+    public static LoomishExecutors allowVirtual(boolean allow) {
+        return NON_VIRTUAL;
+    }
+
+    /**
+     * Return an instance of {@link LoomishExecutors} based on a permission to use virtual threads.
+     * @param allow whether to allow virtual threads.
+     * @param threadFactory the thread factory to be used by a the {@link ExecutorService}.
+     * @return the {@link LoomishExecutors} instance.
+     */
+    public static LoomishExecutors allowVirtual(boolean allow, ThreadFactory threadFactory) {
+        return new NonLoomishExecutors(threadFactory);
+    }
+
+    private static final class NonLoomishExecutors implements LoomishExecutors {
+        private final ThreadFactory threadFactory;
+
+        private NonLoomishExecutors(ThreadFactory threadFactory) {
+            this.threadFactory = threadFactory;
+        }
+
+        @Override
+        public ExecutorService newCachedThreadPool() {
+            return Executors.newCachedThreadPool();
+        }
+
+        @Override
+        public ExecutorService newFixedThreadPool(int nThreads) {
+            return Executors.newFixedThreadPool(nThreads);
+        }
+
+        @Override
+        public ThreadFactory getThreadFactory() {
+            return threadFactory;
+        }
+
+        @Override
+        public boolean isVirtual() {
+            return false;
+        }
+    }
+}
diff --git a/core-common/src/main/java21/org/glassfish/jersey/innate/VirtualThreadSupport.java b/core-common/src/main/java21/org/glassfish/jersey/innate/VirtualThreadSupport.java
new file mode 100644
index 0000000..0e7d695
--- /dev/null
+++ b/core-common/src/main/java21/org/glassfish/jersey/innate/VirtualThreadSupport.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2024 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;
+
+import org.glassfish.jersey.innate.virtual.LoomishExecutors;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+
+/**
+ * Utility class for the virtual thread support.
+ */
+public final class VirtualThreadSupport {
+
+    private static final LoomishExecutors VIRTUAL_THREADS = new Java21LoomishExecutors(Thread.ofVirtual().factory());
+    private static final LoomishExecutors NON_VIRTUAL_THREADS = new NonLoomishExecutors(Executors.defaultThreadFactory());
+
+    /**
+     * Do not instantiate.
+     */
+    private VirtualThreadSupport() {
+        throw new IllegalStateException();
+    }
+
+    /**
+     * Informs whether the given {@link Thread} is virtual.
+     * @return true when the current thread is virtual.
+     */
+    public static boolean isVirtualThread() {
+        return Thread.currentThread().isVirtual();
+    }
+
+    /**
+     * Return an instance of {@link LoomishExecutors} based on a permission to use virtual threads.
+     * @param allow whether to allow virtual threads.
+     * @return the {@link LoomishExecutors} instance.
+     */
+    public static LoomishExecutors allowVirtual(boolean allow) {
+        return allow ? VIRTUAL_THREADS : NON_VIRTUAL_THREADS;
+    }
+
+    /**
+     * Return an instance of {@link LoomishExecutors} based on a permission to use virtual threads.
+     * @param allow whether to allow virtual threads.
+     * @param threadFactory the thread factory to be used by a the {@link ExecutorService}.
+     * @return the {@link LoomishExecutors} instance.
+     */
+    public static LoomishExecutors allowVirtual(boolean allow, ThreadFactory threadFactory) {
+        return allow ? new Java21LoomishExecutors(threadFactory) : new NonLoomishExecutors(threadFactory);
+    }
+
+    private static class NonLoomishExecutors implements LoomishExecutors {
+        private final ThreadFactory threadFactory;
+
+        private NonLoomishExecutors(ThreadFactory threadFactory) {
+            this.threadFactory = threadFactory;
+        }
+
+        @Override
+        public ExecutorService newCachedThreadPool() {
+            return Executors.newCachedThreadPool(getThreadFactory());
+        }
+
+        @Override
+        public ExecutorService newFixedThreadPool(int nThreads) {
+            return Executors.newFixedThreadPool(nThreads, getThreadFactory());
+        }
+
+        @Override
+        public ThreadFactory getThreadFactory() {
+            return threadFactory;
+        }
+
+        @Override
+        public boolean isVirtual() {
+            return false;
+        }
+    }
+
+    private static class Java21LoomishExecutors implements LoomishExecutors {
+        private final ThreadFactory threadFactory;
+
+        private Java21LoomishExecutors(ThreadFactory threadFactory) {
+            this.threadFactory = threadFactory;
+        }
+
+        @Override
+        public ExecutorService newCachedThreadPool() {
+            return Executors.newThreadPerTaskExecutor(getThreadFactory());
+        }
+
+        @Override
+        public ExecutorService newFixedThreadPool(int nThreads) {
+            ThreadFactory threadFactory = this == VIRTUAL_THREADS ? Executors.defaultThreadFactory() : getThreadFactory();
+            return Executors.newFixedThreadPool(nThreads, threadFactory);
+        }
+
+        @Override
+        public ThreadFactory getThreadFactory() {
+            return threadFactory;
+        }
+
+        @Override
+        public boolean isVirtual() {
+            return true;
+        }
+    }
+}
diff --git a/core-common/src/main/resources/META-INF/native-image/org.glassfish.jersey.core/jersey-common/reflect-config.json b/core-common/src/main/resources/META-INF/native-image/org.glassfish.jersey.core/jersey-common/reflect-config.json
index ef2ae4d..184402f 100644
--- a/core-common/src/main/resources/META-INF/native-image/org.glassfish.jersey.core/jersey-common/reflect-config.json
+++ b/core-common/src/main/resources/META-INF/native-image/org.glassfish.jersey.core/jersey-common/reflect-config.json
@@ -58,6 +58,12 @@
     "allDeclaredConstructors":true
   },
   {
+    "name":"org.glassfish.jersey.message.internal.PathProvider",
+    "allDeclaredFields":true,
+    "allDeclaredMethods":true,
+    "allDeclaredConstructors":true
+  },
+  {
     "name":"org.glassfish.jersey.message.internal.FormMultivaluedMapProvider",
     "allDeclaredFields":true,
     "allDeclaredMethods":true,
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 b60bf25..e9cd448 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, 2021 Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2012, 2024 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
@@ -39,6 +39,7 @@
 error.entity.provider.basictypes.character.morechars=A single character expected in the entity input stream.
 error.entity.provider.basictypes.constructor=Error converting entity to {0} type by single String constructor.
 error.entity.provider.basictypes.unkwnown=Unsupported basic type {0}.
+error.external.registerables.ignored=Error reading external registrable contracts: {0}.
 error.finding.exception.mapper.type=Could not find exception type for given ExceptionMapper class: {0}.
 error.interceptor.reader.proceed=Last reader interceptor in the chain called the method proceed.
 error.interceptor.writer.proceed=Last writer interceptor in the chain called the method proceed.
diff --git a/core-common/src/test/java/org/glassfish/jersey/internal/util/PropertiesHelperTest.java b/core-common/src/test/java/org/glassfish/jersey/internal/util/PropertiesHelperTest.java
index 67df369..237e749 100644
--- a/core-common/src/test/java/org/glassfish/jersey/internal/util/PropertiesHelperTest.java
+++ b/core-common/src/test/java/org/glassfish/jersey/internal/util/PropertiesHelperTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2024 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
@@ -199,4 +199,15 @@
 
     }
 
+    @Test
+    public void isPropertyOrNotSetTest() {
+        assertEquals(false, PropertiesHelper.isPropertyOrNotSet((Boolean) null));
+        assertEquals(true, PropertiesHelper.isPropertyOrNotSet(Boolean.TRUE));
+        assertEquals(false, PropertiesHelper.isPropertyOrNotSet(Boolean.FALSE));
+        assertEquals(false, PropertiesHelper.isPropertyOrNotSet((String) null));
+        assertEquals(true, PropertiesHelper.isPropertyOrNotSet(""));
+        assertEquals(false, PropertiesHelper.isPropertyOrNotSet("treu")); // false for non-boolean values
+        assertEquals(true, PropertiesHelper.isPropertyOrNotSet("TRUE"));
+        assertEquals(false, PropertiesHelper.isPropertyOrNotSet("false"));
+    }
 }
diff --git a/core-common/src/test/java/org/glassfish/jersey/logging/LoggingInterceptorTest.java b/core-common/src/test/java/org/glassfish/jersey/logging/LoggingInterceptorTest.java
index ed4d5dc..a138afb 100644
--- a/core-common/src/test/java/org/glassfish/jersey/logging/LoggingInterceptorTest.java
+++ b/core-common/src/test/java/org/glassfish/jersey/logging/LoggingInterceptorTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2024 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
@@ -32,8 +32,8 @@
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
diff --git a/core-server/pom.xml b/core-server/pom.xml
index 15f63b7..f87b600 100644
--- a/core-server/pom.xml
+++ b/core-server/pom.xml
@@ -237,7 +237,13 @@
         <dependency>
             <groupId>org.jboss</groupId>
             <artifactId>jboss-vfs</artifactId>
-            <version>3.2.6.Final</version>
+            <version>${jboss.vfs.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.logging</groupId>
+            <artifactId>jboss-logging</artifactId>
+            <version>${jboss.logging.version}</version>
             <scope>test</scope>
         </dependency>
 
diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/AnnotationWriter.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/AnnotationWriter.java
index d1f86a6..23ea980 100644
--- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/AnnotationWriter.java
+++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/AnnotationWriter.java
@@ -144,7 +144,7 @@
     // Write type_index and reserve space for num_element_value_pairs.
     annotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0);
     return new AnnotationWriter(
-        symbolTable, /* useNamedValues = */ true, annotation, previousAnnotation);
+        symbolTable, /* useNamedValues= */ true, annotation, previousAnnotation);
   }
 
   /**
@@ -179,7 +179,7 @@
     // Write type_index and reserve space for num_element_value_pairs.
     typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0);
     return new AnnotationWriter(
-        symbolTable, /* useNamedValues = */ true, typeAnnotation, previousAnnotation);
+        symbolTable, /* useNamedValues= */ true, typeAnnotation, previousAnnotation);
   }
 
   // -----------------------------------------------------------------------------------------------
@@ -284,7 +284,7 @@
     }
     // Write tag and type_index, and reserve 2 bytes for num_element_value_pairs.
     annotation.put12('@', symbolTable.addConstantUtf8(descriptor)).putShort(0);
-    return new AnnotationWriter(symbolTable, /* useNamedValues = */ true, annotation, null);
+    return new AnnotationWriter(symbolTable, /* useNamedValues= */ true, annotation, null);
   }
 
   @Override
@@ -303,7 +303,7 @@
     // visit the array elements. Its num_element_value_pairs will correspond to the number of array
     // elements and will be stored in what is in fact num_values.
     annotation.put12('[', 0);
-    return new AnnotationWriter(symbolTable, /* useNamedValues = */ false, annotation, null);
+    return new AnnotationWriter(symbolTable, /* useNamedValues= */ false, annotation, null);
   }
 
   @Override
diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassReader.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassReader.java
index 820a3b8..a2bdae3 100644
--- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassReader.java
+++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassReader.java
@@ -177,7 +177,7 @@
       final byte[] classFileBuffer,
       final int classFileOffset,
       final int classFileLength) { // NOPMD(UnusedFormalParameter) used for backward compatibility.
-    this(classFileBuffer, classFileOffset, /* checkClassVersion = */ true);
+    this(classFileBuffer, classFileOffset, /* checkClassVersion= */ true);
   }
 
   /**
@@ -195,7 +195,7 @@
     this.b = classFileBuffer;
     // Check the class' major_version. This field is after the magic and minor_version fields, which
     // use 4 and 2 bytes respectively.
-    if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V22) {
+    if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V23) {
       throw new IllegalArgumentException(
           "Unsupported class file major version " + readShort(classFileOffset + 6));
     }
@@ -607,9 +607,9 @@
         // Parse num_element_value_pairs and element_value_pairs and visit these values.
         currentAnnotationOffset =
             readElementValues(
-                classVisitor.visitAnnotation(annotationDescriptor, /* visible = */ true),
+                classVisitor.visitAnnotation(annotationDescriptor, /* visible= */ true),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -625,9 +625,9 @@
         // Parse num_element_value_pairs and element_value_pairs and visit these values.
         currentAnnotationOffset =
             readElementValues(
-                classVisitor.visitAnnotation(annotationDescriptor, /* visible = */ false),
+                classVisitor.visitAnnotation(annotationDescriptor, /* visible= */ false),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -649,9 +649,9 @@
                     context.currentTypeAnnotationTarget,
                     context.currentTypeAnnotationTargetPath,
                     annotationDescriptor,
-                    /* visible = */ true),
+                    /* visible= */ true),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -673,9 +673,9 @@
                     context.currentTypeAnnotationTarget,
                     context.currentTypeAnnotationTargetPath,
                     annotationDescriptor,
-                    /* visible = */ false),
+                    /* visible= */ false),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -967,9 +967,9 @@
         // Parse num_element_value_pairs and element_value_pairs and visit these values.
         currentAnnotationOffset =
             readElementValues(
-                recordComponentVisitor.visitAnnotation(annotationDescriptor, /* visible = */ true),
+                recordComponentVisitor.visitAnnotation(annotationDescriptor, /* visible= */ true),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -985,9 +985,9 @@
         // Parse num_element_value_pairs and element_value_pairs and visit these values.
         currentAnnotationOffset =
             readElementValues(
-                recordComponentVisitor.visitAnnotation(annotationDescriptor, /* visible = */ false),
+                recordComponentVisitor.visitAnnotation(annotationDescriptor, /* visible= */ false),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -1009,9 +1009,9 @@
                     context.currentTypeAnnotationTarget,
                     context.currentTypeAnnotationTargetPath,
                     annotationDescriptor,
-                    /* visible = */ true),
+                    /* visible= */ true),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -1033,9 +1033,9 @@
                     context.currentTypeAnnotationTarget,
                     context.currentTypeAnnotationTargetPath,
                     annotationDescriptor,
-                    /* visible = */ false),
+                    /* visible= */ false),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -1151,9 +1151,9 @@
         // Parse num_element_value_pairs and element_value_pairs and visit these values.
         currentAnnotationOffset =
             readElementValues(
-                fieldVisitor.visitAnnotation(annotationDescriptor, /* visible = */ true),
+                fieldVisitor.visitAnnotation(annotationDescriptor, /* visible= */ true),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -1169,9 +1169,9 @@
         // Parse num_element_value_pairs and element_value_pairs and visit these values.
         currentAnnotationOffset =
             readElementValues(
-                fieldVisitor.visitAnnotation(annotationDescriptor, /* visible = */ false),
+                fieldVisitor.visitAnnotation(annotationDescriptor, /* visible= */ false),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -1193,9 +1193,9 @@
                     context.currentTypeAnnotationTarget,
                     context.currentTypeAnnotationTargetPath,
                     annotationDescriptor,
-                    /* visible = */ true),
+                    /* visible= */ true),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -1217,9 +1217,9 @@
                     context.currentTypeAnnotationTarget,
                     context.currentTypeAnnotationTargetPath,
                     annotationDescriptor,
-                    /* visible = */ false),
+                    /* visible= */ false),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -1412,9 +1412,9 @@
         // Parse num_element_value_pairs and element_value_pairs and visit these values.
         currentAnnotationOffset =
             readElementValues(
-                methodVisitor.visitAnnotation(annotationDescriptor, /* visible = */ true),
+                methodVisitor.visitAnnotation(annotationDescriptor, /* visible= */ true),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -1430,9 +1430,9 @@
         // Parse num_element_value_pairs and element_value_pairs and visit these values.
         currentAnnotationOffset =
             readElementValues(
-                methodVisitor.visitAnnotation(annotationDescriptor, /* visible = */ false),
+                methodVisitor.visitAnnotation(annotationDescriptor, /* visible= */ false),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -1454,9 +1454,9 @@
                     context.currentTypeAnnotationTarget,
                     context.currentTypeAnnotationTargetPath,
                     annotationDescriptor,
-                    /* visible = */ true),
+                    /* visible= */ true),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -1478,9 +1478,9 @@
                     context.currentTypeAnnotationTarget,
                     context.currentTypeAnnotationTargetPath,
                     annotationDescriptor,
-                    /* visible = */ false),
+                    /* visible= */ false),
                 currentAnnotationOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -1488,16 +1488,13 @@
     // Visit the RuntimeVisibleParameterAnnotations attribute.
     if (runtimeVisibleParameterAnnotationsOffset != 0) {
       readParameterAnnotations(
-          methodVisitor, context, runtimeVisibleParameterAnnotationsOffset, /* visible = */ true);
+          methodVisitor, context, runtimeVisibleParameterAnnotationsOffset, /* visible= */ true);
     }
 
     // Visit the RuntimeInvisibleParameterAnnotations attribute.
     if (runtimeInvisibleParameterAnnotationsOffset != 0) {
       readParameterAnnotations(
-          methodVisitor,
-          context,
-          runtimeInvisibleParameterAnnotationsOffset,
-          /* visible = */ false);
+          methodVisitor, context, runtimeInvisibleParameterAnnotationsOffset, /* visible= */ false);
     }
 
     // Visit the non standard attributes.
@@ -1926,7 +1923,7 @@
         }
       } else if (Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
         visibleTypeAnnotationOffsets =
-            readTypeAnnotations(methodVisitor, context, currentOffset, /* visible = */ true);
+            readTypeAnnotations(methodVisitor, context, currentOffset, /* visible= */ true);
         // Here we do not extract the labels corresponding to the attribute content. This would
         // require a full parsing of the attribute, which would need to be repeated when parsing
         // the bytecode instructions (see below). Instead, the content of the attribute is read one
@@ -1935,7 +1932,7 @@
         // time. This assumes that type annotations are ordered by increasing bytecode offset.
       } else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
         invisibleTypeAnnotationOffsets =
-            readTypeAnnotations(methodVisitor, context, currentOffset, /* visible = */ false);
+            readTypeAnnotations(methodVisitor, context, currentOffset, /* visible= */ false);
         // Same comment as above for the RuntimeVisibleTypeAnnotations attribute.
       } else if (Constants.STACK_MAP_TABLE.equals(attributeName)) {
         if ((context.parsingOptions & SKIP_FRAMES) == 0) {
@@ -2517,9 +2514,9 @@
                   context.currentTypeAnnotationTarget,
                   context.currentTypeAnnotationTargetPath,
                   annotationDescriptor,
-                  /* visible = */ true),
+                  /* visible= */ true),
               currentAnnotationOffset,
-              /* named = */ true,
+              /* named= */ true,
               charBuffer);
         }
         currentVisibleTypeAnnotationBytecodeOffset =
@@ -2545,9 +2542,9 @@
                   context.currentTypeAnnotationTarget,
                   context.currentTypeAnnotationTargetPath,
                   annotationDescriptor,
-                  /* visible = */ false),
+                  /* visible= */ false),
               currentAnnotationOffset,
-              /* named = */ true,
+              /* named= */ true,
               charBuffer);
         }
         currentInvisibleTypeAnnotationBytecodeOffset =
@@ -2618,9 +2615,9 @@
                   context.currentLocalVariableAnnotationRangeEnds,
                   context.currentLocalVariableAnnotationRangeIndices,
                   annotationDescriptor,
-                  /* visible = */ true),
+                  /* visible= */ true),
               currentOffset,
-              /* named = */ true,
+              /* named= */ true,
               charBuffer);
         }
       }
@@ -2646,9 +2643,9 @@
                   context.currentLocalVariableAnnotationRangeEnds,
                   context.currentLocalVariableAnnotationRangeIndices,
                   annotationDescriptor,
-                  /* visible = */ false),
+                  /* visible= */ false),
               currentOffset,
-              /* named = */ true,
+              /* named= */ true,
               charBuffer);
         }
       }
@@ -2821,7 +2818,7 @@
                 methodVisitor.visitTryCatchAnnotation(
                     targetType & 0xFFFFFF00, path, annotationDescriptor, visible),
                 currentOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       } else {
         // We don't want to visit the other target_type annotations, so we just skip them (which
@@ -2832,7 +2829,7 @@
         // with a null AnnotationVisitor).
         currentOffset =
             readElementValues(
-                /* annotationVisitor = */ null, currentOffset, /* named = */ true, charBuffer);
+                /* annotationVisitor= */ null, currentOffset, /* named= */ true, charBuffer);
       }
     }
     return typeAnnotationsOffsets;
@@ -2972,7 +2969,7 @@
             readElementValues(
                 methodVisitor.visitParameterAnnotation(i, annotationDescriptor, visible),
                 currentOffset,
-                /* named = */ true,
+                /* named= */ true,
                 charBuffer);
       }
     }
@@ -3042,9 +3039,9 @@
         case 'e': // enum_const_value
           return currentOffset + 5;
         case '@': // annotation_value
-          return readElementValues(null, currentOffset + 3, /* named = */ true, charBuffer);
+          return readElementValues(null, currentOffset + 3, /* named= */ true, charBuffer);
         case '[': // array_value
-          return readElementValues(null, currentOffset + 1, /* named = */ false, charBuffer);
+          return readElementValues(null, currentOffset + 1, /* named= */ false, charBuffer);
         default:
           return currentOffset + 3;
       }
@@ -3112,7 +3109,7 @@
           return readElementValues(
               annotationVisitor.visitArray(elementName),
               currentOffset - 2,
-              /* named = */ false,
+              /* named= */ false,
               charBuffer);
         }
         switch (classFileBuffer[currentOffset] & 0xFF) {
@@ -3189,7 +3186,7 @@
                 readElementValues(
                     annotationVisitor.visitArray(elementName),
                     currentOffset - 2,
-                    /* named = */ false,
+                    /* named= */ false,
                     charBuffer);
             break;
         }
diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassWriter.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassWriter.java
index 7588188..0af80ce 100644
--- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassWriter.java
+++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassWriter.java
@@ -774,7 +774,7 @@
     lastRecordComponent = null;
     firstAttribute = null;
     compute = hasFrames ? MethodWriter.COMPUTE_INSERTED_FRAMES : MethodWriter.COMPUTE_NOTHING;
-    new ClassReader(classFile, 0, /* checkClassVersion = */ false)
+    new ClassReader(classFile, 0, /* checkClassVersion= */ false)
         .accept(
             this,
             attributes,
diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/MethodWriter.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/MethodWriter.java
index 918bd71..bccc997 100644
--- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/MethodWriter.java
+++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/MethodWriter.java
@@ -651,7 +651,7 @@
   @Override
   public AnnotationVisitor visitAnnotationDefault() {
     defaultValue = new ByteVector();
-    return new AnnotationWriter(symbolTable, /* useNamedValues = */ false, defaultValue, null);
+    return new AnnotationWriter(symbolTable, /* useNamedValues= */ false, defaultValue, null);
   }
 
   @Override
diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/Opcodes.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/Opcodes.java
index 4256afa..f796c94 100644
--- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/Opcodes.java
+++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/Opcodes.java
@@ -288,6 +288,7 @@
   int V20 = 0 << 16 | 64;
   int V21 = 0 << 16 | 65;
   int V22 = 0 << 16 | 66;
+  int V23 = 0 << 16 | 67;
 
   /**
    * Version flag indicating that the class is using 'preview' features.
diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/SymbolTable.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/SymbolTable.java
index dc601a1..9ceffb1 100644
--- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/SymbolTable.java
+++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/SymbolTable.java
@@ -1414,23 +1414,23 @@
     }
 
     Entry(final int index, final int tag, final String value, final int hashCode) {
-      super(index, tag, /* owner = */ null, /* name = */ null, value, /* data = */ 0);
+      super(index, tag, /* owner= */ null, /* name= */ null, value, /* data= */ 0);
       this.hashCode = hashCode;
     }
 
     Entry(final int index, final int tag, final String value, final long data, final int hashCode) {
-      super(index, tag, /* owner = */ null, /* name = */ null, value, data);
+      super(index, tag, /* owner= */ null, /* name= */ null, value, data);
       this.hashCode = hashCode;
     }
 
     Entry(
         final int index, final int tag, final String name, final String value, final int hashCode) {
-      super(index, tag, /* owner = */ null, name, value, /* data = */ 0);
+      super(index, tag, /* owner= */ null, name, value, /* data= */ 0);
       this.hashCode = hashCode;
     }
 
     Entry(final int index, final int tag, final long data, final int hashCode) {
-      super(index, tag, /* owner = */ null, /* name = */ null, /* value = */ null, data);
+      super(index, tag, /* owner= */ null, /* name= */ null, /* value= */ null, data);
       this.hashCode = hashCode;
     }
   }
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 7d75df1..730d1ce 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
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2023 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
@@ -66,7 +66,6 @@
 import org.glassfish.jersey.message.MessageBodyWorkers;
 import org.glassfish.jersey.message.internal.MessageBodyFactory;
 import org.glassfish.jersey.message.internal.MessagingBinders;
-import org.glassfish.jersey.message.internal.NullOutputStream;
 import org.glassfish.jersey.model.internal.ComponentBag;
 import org.glassfish.jersey.model.internal.ManagedObjectsFinalizer;
 import org.glassfish.jersey.model.internal.RankedComparator;
@@ -586,7 +585,7 @@
      * @return response future.
      */
     public Future<ContainerResponse> apply(final ContainerRequest requestContext) {
-        return apply(requestContext, new NullOutputStream());
+        return apply(requestContext, OutputStream.nullOutputStream());
     }
 
     /**
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ChunkedOutput.java b/core-server/src/main/java/org/glassfish/jersey/server/ChunkedOutput.java
index 2ce0542..a0fedf4 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/ChunkedOutput.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/ChunkedOutput.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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
@@ -26,6 +26,8 @@
 import java.util.concurrent.Callable;
 import java.util.concurrent.LinkedBlockingDeque;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 
 import jakarta.ws.rs.container.ConnectionCallback;
 import jakarta.ws.rs.core.GenericType;
@@ -51,10 +53,10 @@
 public class ChunkedOutput<T> extends GenericType<T> implements Closeable {
     private static final byte[] ZERO_LENGTH_DELIMITER = new byte[0];
 
-    private final BlockingDeque<T> queue = new LinkedBlockingDeque<>();
+    private final BlockingDeque<T> queue;
     private final byte[] chunkDelimiter;
     private final AtomicBoolean resumed = new AtomicBoolean(false);
-    private final Object lock = new Object();
+    private final Lock lock = new ReentrantLock();
 
     // the following flushing and touchingEntityStream variables are used in a synchronized block exclusively
     private boolean flushing = false;
@@ -70,12 +72,59 @@
     private volatile ContainerResponse responseContext;
     private volatile ConnectionCallback connectionCallback;
 
-
     /**
      * Create new {@code ChunkedOutput}.
      */
     protected ChunkedOutput() {
         this.chunkDelimiter = ZERO_LENGTH_DELIMITER;
+        queue = new LinkedBlockingDeque<>();
+    }
+
+    /**
+     * Create new {@code ChunkedOutput} based on builder.
+     *
+     * @param builder the builder to use
+     */
+    protected ChunkedOutput(Builder<T> builder) {
+        super();
+        if (builder.queueCapacity > 0) {
+            queue = new LinkedBlockingDeque<>(builder.queueCapacity);
+        } else {
+            queue = new LinkedBlockingDeque<>();
+        }
+        if (builder.chunkDelimiter != null) {
+            this.chunkDelimiter = new byte[builder.chunkDelimiter.length];
+            System.arraycopy(builder.chunkDelimiter, 0, this.chunkDelimiter, 0, builder.chunkDelimiter.length);
+        } else {
+            this.chunkDelimiter = ZERO_LENGTH_DELIMITER;
+        }
+        if (builder.asyncContextProvider != null) {
+            this.asyncContext = builder.asyncContextProvider.get();
+        }
+    }
+
+    /**
+     * Create new {@code ChunkedOutput} based on builder.
+     *
+     * @param builder the builder to use
+     */
+    private ChunkedOutput(TypedBuilder<T> builder) {
+        super(builder.chunkType);
+
+        if (builder.queueCapacity > 0) {
+            queue = new LinkedBlockingDeque<>(builder.queueCapacity);
+        } else {
+            queue = new LinkedBlockingDeque<>();
+        }
+        if (builder.chunkDelimiter != null) {
+            this.chunkDelimiter = new byte[builder.chunkDelimiter.length];
+            System.arraycopy(builder.chunkDelimiter, 0, this.chunkDelimiter, 0, builder.chunkDelimiter.length);
+        } else {
+            this.chunkDelimiter = ZERO_LENGTH_DELIMITER;
+        }
+        if (builder.asyncContextProvider != null) {
+            this.asyncContext = builder.asyncContextProvider.get();
+        }
     }
 
     /**
@@ -86,6 +135,7 @@
     public ChunkedOutput(final Type chunkType) {
         super(chunkType);
         this.chunkDelimiter = ZERO_LENGTH_DELIMITER;
+        queue = new LinkedBlockingDeque<>();
     }
 
     /**
@@ -101,6 +151,7 @@
         } else {
             this.chunkDelimiter = ZERO_LENGTH_DELIMITER;
         }
+        queue = new LinkedBlockingDeque<>();
     }
 
     /**
@@ -118,6 +169,7 @@
         }
 
         this.asyncContext = asyncContextProvider == null ? null : asyncContextProvider.get();
+        queue = new LinkedBlockingDeque<>();
     }
 
     /**
@@ -135,6 +187,7 @@
         } else {
             this.chunkDelimiter = ZERO_LENGTH_DELIMITER;
         }
+        queue = new LinkedBlockingDeque<>();
     }
 
     /**
@@ -149,6 +202,7 @@
         } else {
             this.chunkDelimiter = chunkDelimiter.getBytes();
         }
+        queue = new LinkedBlockingDeque<>();
     }
 
     /**
@@ -165,6 +219,26 @@
         } else {
             this.chunkDelimiter = chunkDelimiter.getBytes();
         }
+        queue = new LinkedBlockingDeque<>();
+    }
+
+    /**
+     * Returns a builder to create a ChunkedOutput with custom configuration.
+     *
+     * @return builder
+     */
+    public static <T> Builder<T> builder() {
+        return new Builder<>();
+    }
+
+    /**
+     * Returns a builder to create a ChunkedOutput with custom configuration.
+     *
+     * @param chunkType      chunk type. Must not be {code null}.
+     * @return builder
+     */
+    public static <T> TypedBuilder<T> builder(Type chunkType) {
+        return new TypedBuilder<>(chunkType);
     }
 
     /**
@@ -179,7 +253,12 @@
         }
 
         if (chunk != null) {
-            queue.add(chunk);
+            try {
+                queue.put(chunk);
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                throw new IOException(e);
+            }
         }
 
         flushQueue();
@@ -202,7 +281,8 @@
                     boolean shouldClose;
                     T t;
 
-                    synchronized (lock) {
+                    lock.lock();
+                    try {
                         if (flushing) {
                             // if another thread is already flushing the queue, we don't have to do anything
                             return null;
@@ -220,13 +300,15 @@
                             // and they don't have to bother
                             flushing = true;
                         }
+                    } finally {
+                        lock.unlock();
                     }
 
                     while (t != null) {
                         try {
-                            synchronized (lock) {
-                                touchingEntityStream = true;
-                            }
+                            lock.lock();
+                            touchingEntityStream = true;
+                            lock.unlock();
 
                             final OutputStream origStream = responseContext.getEntityStream();
                             final OutputStream writtenStream = requestContext.getWorkers().writeTo(
@@ -265,14 +347,15 @@
                             }
                             throw mpe;
                         } finally {
-                           synchronized (lock) {
-                               touchingEntityStream = false;
-                           }
+                           lock.lock();
+                           touchingEntityStream = false;
+                           lock.unlock();
                         }
 
                         t = queue.poll();
                         if (t == null) {
-                            synchronized (lock) {
+                            lock.lock();
+                            try {
                                 // queue seems empty
                                 // check again in the synchronized block before clearing the flushing flag
                                 // first remember the closed flag (this has to be before polling the queue,
@@ -290,6 +373,8 @@
                                     flushing = shouldClose;
                                     break;
                                 }
+                            } finally {
+                                lock.unlock();
                             }
                         }
                     }
@@ -303,18 +388,20 @@
             onClose(e);
         } finally {
             if (closed) {
+                lock.lock();
                 try {
-                    synchronized (lock) {
-                        if (!touchingEntityStream) {
-                            responseContext.close();
-                        } // else the next thread will close responseContext
-                    }
+                    if (!touchingEntityStream) {
+                        responseContext.close();
+                    } // else the next thread will close responseContext
                 } catch (final Exception e) {
                     // if no exception remembered before, remember this one
                     // otherwise the previously remembered exception (from catch clause) takes precedence
                     ex = ex == null ? e : ex;
+                } finally {
+                    lock.unlock();
                 }
 
+
                 requestScopeContext.release();
 
                 // rethrow remembered exception (if any)
@@ -341,7 +428,6 @@
 
     /**
      * Get state information.
-     *
      * Please note that {@code ChunkedOutput} can be closed by the client side - client can close connection
      * from its side.
      *
@@ -353,10 +439,12 @@
 
     /**
      * Executed only in case of close being triggered by client.
+     *
      * @param e Exception causing the close
      */
-    protected void onClose(Exception e){
-
+    protected void onClose(Exception e) {
+        // drain queue when an exception occurs to prevent deadlocks
+        queue.clear();
     }
 
     @SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
@@ -399,4 +487,78 @@
         this.connectionCallback = connectionCallbackRunner;
         flushQueue();
     }
+
+    /**
+     * Builder that allows to create a new ChunkedOutput based on the given configuration options.
+     *
+     * @param <Y>
+     */
+    public static class Builder<Y> {
+        byte[] chunkDelimiter;
+        int queueCapacity = -1;
+        Provider<AsyncContext> asyncContextProvider;
+
+        private Builder() {
+            // hide constructor
+        }
+
+        /**
+         * Set the chunk delimiter, in bytes.
+         * @param chunkDelimiter the chunk delimiter in bytes
+         * @return builder
+         */
+        public Builder<Y> chunkDelimiter(byte[] chunkDelimiter) {
+            this.chunkDelimiter = chunkDelimiter;
+            return this;
+        }
+
+        /**
+         * Set the queue capacity. If greater than 0, the queue is bounded and will block when full.
+         * @param queueCapacity the queue capacity
+         * @return builder
+         */
+        public Builder<Y> queueCapacity(int queueCapacity) {
+            this.queueCapacity = queueCapacity;
+            return this;
+        }
+
+        /**
+         * Set the async context provider.
+         * @param asyncContextProvider the async context provider
+         * @return builder
+         */
+        public Builder<Y> asyncContextProvider(Provider<AsyncContext> asyncContextProvider) {
+            this.asyncContextProvider = asyncContextProvider;
+            return this;
+        }
+
+        /**
+         * Build the ChunkedOutput based on the given configuration.
+         * @return the ChunkedOutput
+         */
+        public ChunkedOutput<Y> build() {
+            return new ChunkedOutput<>(this);
+        }
+    }
+
+    /**
+     * Builder that allows to create a new ChunkedOutput based on the given configuration options.
+     *
+     * @param <Y>
+     */
+    public static class TypedBuilder<Y> extends Builder<Y> {
+        private Type chunkType;
+
+        private TypedBuilder(Type chunkType) {
+            this.chunkType = chunkType;
+        }
+
+        /**
+         * Build the ChunkedOutput based on the given configuration.
+         * @return the ChunkedOutput
+         */
+        public ChunkedOutput<Y> build() {
+            return new ChunkedOutput<>(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 ae39a68..6fc7a2a 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, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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,6 +39,7 @@
 import jakarta.ws.rs.core.Configuration;
 import jakarta.ws.rs.core.Feature;
 
+import org.glassfish.jersey.ApplicationSupplier;
 import org.glassfish.jersey.internal.Errors;
 import org.glassfish.jersey.internal.config.ExternalPropertiesConfigurationFactory;
 import org.glassfish.jersey.internal.inject.Binder;
@@ -70,7 +71,7 @@
  * @author Michal Gajdos
  * @author Marek Potociar
  */
-public class ResourceConfig extends Application implements Configurable<ResourceConfig>, ServerConfig {
+public class ResourceConfig extends Application implements Configurable<ResourceConfig>, ServerConfig, ApplicationSupplier {
 
     private static final Logger LOGGER = Logger.getLogger(ResourceConfig.class.getName());
 
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ServerExecutorProvidersConfigurator.java b/core-server/src/main/java/org/glassfish/jersey/server/ServerExecutorProvidersConfigurator.java
index 77974f1..d620958 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/ServerExecutorProvidersConfigurator.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/ServerExecutorProvidersConfigurator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2024 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,13 +21,14 @@
 import org.glassfish.jersey.internal.inject.InjectionManager;
 import org.glassfish.jersey.internal.inject.InstanceBinding;
 import org.glassfish.jersey.model.internal.ComponentBag;
-import org.glassfish.jersey.model.internal.ManagedObjectsFinalizer;
 import org.glassfish.jersey.process.internal.AbstractExecutorProvidersConfigurator;
 import org.glassfish.jersey.spi.ExecutorServiceProvider;
 import org.glassfish.jersey.spi.ScheduledExecutorServiceProvider;
 import org.glassfish.jersey.spi.ScheduledThreadPoolExecutorProvider;
 import org.glassfish.jersey.spi.ThreadPoolExecutorProvider;
 
+import jakarta.ws.rs.core.Configuration;
+
 /**
  * Configurator which initializes and register {@link org.glassfish.jersey.spi.ExecutorServiceProvider} and
  * {@link org.glassfish.jersey.spi.ScheduledExecutorServiceProvider}.
@@ -43,7 +44,7 @@
         ComponentBag componentBag = runtimeConfig.getComponentBag();
 
         // TODO: Do we need to register DEFAULT Executor and ScheduledExecutor to InjectionManager?
-        ScheduledExecutorServiceProvider defaultScheduledExecutorProvider = new DefaultBackgroundSchedulerProvider();
+        ScheduledExecutorServiceProvider defaultScheduledExecutorProvider = new DefaultBackgroundSchedulerProvider(runtimeConfig);
         InstanceBinding<ScheduledExecutorServiceProvider> schedulerBinding = Bindings
                 .service(defaultScheduledExecutorProvider)
                 .to(ScheduledExecutorServiceProvider.class)
@@ -67,8 +68,8 @@
     @BackgroundScheduler
     private static class DefaultBackgroundSchedulerProvider extends ScheduledThreadPoolExecutorProvider {
 
-        public DefaultBackgroundSchedulerProvider() {
-            super("jersey-background-task-scheduler");
+        public DefaultBackgroundSchedulerProvider(Configuration configuration) {
+            super("jersey-background-task-scheduler", configuration);
         }
 
         @Override
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 5d8a3ad..3ef8565 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
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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
@@ -304,7 +304,7 @@
     /**
      * If {@code true} then disable auto discovery on server.
      *
-     * By default auto discovery is automatically enabled if global property
+     * By default, auto discovery is automatically enabled if global property
      * {@value org.glassfish.jersey.CommonProperties#FEATURE_AUTO_DISCOVERY_DISABLE} is not disabled. If set then the server
      * property value overrides the global property value.
      * <p>
@@ -345,9 +345,28 @@
     public static final String OUTBOUND_CONTENT_LENGTH_BUFFER = CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER_SERVER;
 
     /**
+     * If {@code true} then disable configuration of Json Binding (JSR-367) feature on server.
+     *
+     * By default, Json Binding is automatically enabled if global property
+     * {@value org.glassfish.jersey.CommonProperties#JSON_BINDING_FEATURE_DISABLE} is not disabled. If set then the server
+     * property value overrides the global property value.
+     * <p>
+     * The default value is {@code false}.
+     * </p>
+     * <p>
+     * The name of the configuration property is <tt>{@value}</tt>.
+     * </p>
+     * <p>This constant is an alias for {@link CommonProperties#JSON_BINDING_FEATURE_DISABLE_SERVER}</p>
+     *
+     * @see org.glassfish.jersey.CommonProperties#JSON_BINDING_FEATURE_DISABLE
+     */
+    @PropertyAlias
+    public static final String JSON_BINDING_FEATURE_DISABLE = CommonProperties.JSON_BINDING_FEATURE_DISABLE_SERVER;
+
+    /**
      * If {@code true} then disable configuration of Json Processing (JSR-353) feature on server.
      *
-     * By default Json Processing is automatically enabled if global property
+     * By default, Json Processing is automatically enabled if global property
      * {@value org.glassfish.jersey.CommonProperties#JSON_PROCESSING_FEATURE_DISABLE} is not disabled. If set then the server
      * property value overrides the global property value.
      * <p>
@@ -366,7 +385,7 @@
     /**
      * If {@code true} then disable META-INF/services lookup on server.
      *
-     * By default Jersey looks up SPI implementations described by META-INF/services/* files.
+     * By default, Jersey looks up SPI implementations described by META-INF/services/* files.
      * Then you can register appropriate provider classes by {@link jakarta.ws.rs.core.Application}.
      * <p>
      * The default value is {@code false}.
@@ -385,7 +404,7 @@
     /**
      * If {@code true} then disable configuration of MOXy Json feature on server.
      *
-     * By default MOXy Json is automatically enabled if global property
+     * By default, MOXy Json is automatically enabled if global property
      * {@value org.glassfish.jersey.CommonProperties#MOXY_JSON_FEATURE_DISABLE} is not disabled. If set then the server
      * property value overrides the global property value.
      * <p>
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 7078db8..531e927 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
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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,8 @@
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -129,6 +131,14 @@
     private final boolean rfc7231LocationHeaderRelativeUriResolution;
 
     /**
+     * Cached value of configuration property
+     * {@link org.glassfish.jersey.server.ServerProperties#RESPONSE_SET_STATUS_OVER_SEND_ERROR}.
+     * If {@code true} method {@link ServerRuntime.CompletionCallbackRunner#onComplete(Throwable)}
+     * is used over {@link DefaultExceptionMapper#toResponse(Throwable)}.
+     */
+    private final boolean configSetStatusOverSendError;
+
+    /**
      * Default exception mapper (@since 3.1.0 according to JAX-RS 3.1 spec)
      */
     private static final ExceptionMapper<Throwable> DEFAULT_EXCEPTION_MAPPER = new DefaultExceptionMapper();
@@ -195,6 +205,9 @@
         this.rfc7231LocationHeaderRelativeUriResolution = ServerProperties.getValue(configuration.getProperties(),
                 ServerProperties.LOCATION_HEADER_RELATIVE_URI_RESOLUTION_RFC7231,
                 Boolean.FALSE, Boolean.class);
+
+        this.configSetStatusOverSendError = ServerProperties.getValue(configuration.getProperties(),
+                ServerProperties.RESPONSE_SET_STATUS_OVER_SEND_ERROR, false, Boolean.class);
     }
 
     /**
@@ -450,15 +463,17 @@
 
                 if (!processResponseError(responseError)) {
                     // Pass the exception to the container.
-                    LOGGER.log(Level.WARNING, LocalizationMessages.ERROR_EXCEPTION_MAPPING_THROWN_TO_CONTAINER(), responseError);
-
                     try {
                         request.getResponseWriter().failure(responseError);
                     } finally {
-                        defaultMapperResponse = processResponseWithDefaultExceptionMapper(responseError, request);
-
-                        // completionCallbackRunner.onComplete(responseError); is called from
-                        // processResponseWithDefaultExceptionMapper
+                        if (runtime.configSetStatusOverSendError) {
+                            completionCallbackRunner.onComplete(responseError);
+                        } else {
+                            LOGGER.log(Level.WARNING,
+                                    LocalizationMessages.ERROR_EXCEPTION_MAPPING_THROWN_TO_CONTAINER(),
+                                    responseError);
+                            defaultMapperResponse = processResponseWithDefaultExceptionMapper(responseError, request);
+                        }
                     }
 
                 }
@@ -544,9 +559,8 @@
 
                         waeResponse = webApplicationException.getResponse();
                         if (waeResponse != null && waeResponse.hasEntity()) {
-                            LOGGER.log(Level.FINE,
-                                    LocalizationMessages.EXCEPTION_MAPPING_WAE_ENTITY(waeResponse.getStatus()),
-                                    throwable);
+                            LOGGER.log(Level.FINE, LocalizationMessages
+                                    .EXCEPTION_MAPPING_WAE_ENTITY(waeResponse.getStatus()), throwable);
                             return waeResponse;
                         }
                     }
@@ -801,7 +815,7 @@
             }
         };
 
-        private final Object stateLock = new Object();
+        private final ReadWriteLock stateLock = new ReentrantReadWriteLock();
         private State state = RUNNING;
         private boolean cancelled = false;
 
@@ -833,11 +847,13 @@
         @Override
         public void onTimeout(final ContainerResponseWriter responseWriter) {
             final TimeoutHandler handler = timeoutHandler;
+            stateLock.readLock().lock();
             try {
-                synchronized (stateLock) {
-                    if (state == SUSPENDED) {
-                        handler.handleTimeout(this);
-                    }
+                if (state == SUSPENDED) {
+                    stateLock.readLock().unlock(); // unlock before handleTimeout to prevent write lock in handleTimeout
+                    handler.handleTimeout(this);
+                } else {
+                    stateLock.readLock().unlock();
                 }
             } catch (final Throwable throwable) {
                 resume(throwable);
@@ -846,9 +862,9 @@
 
         @Override
         public void onComplete(final Throwable throwable) {
-            synchronized (stateLock) {
-                state = COMPLETED;
-            }
+            stateLock.writeLock().lock();
+            state = COMPLETED;
+            stateLock.writeLock().unlock();
         }
 
         @Override
@@ -876,14 +892,19 @@
 
         @Override
         public boolean suspend() {
-            synchronized (stateLock) {
-                if (state == RUNNING) {
-                    if (responder.processingContext.request().getResponseWriter().suspend(
-                            AsyncResponse.NO_TIMEOUT, TimeUnit.SECONDS, this)) {
-                        state = SUSPENDED;
-                        return true;
-                    }
+            stateLock.readLock().lock();
+            if (state == RUNNING) {
+                stateLock.readLock().unlock();
+                if (responder.processingContext.request().getResponseWriter().suspend(
+                        AsyncResponse.NO_TIMEOUT, TimeUnit.SECONDS, this)) {
+                    // Must release read lock before acquiring write lock
+                    stateLock.writeLock().lock();
+                    state = SUSPENDED;
+                    stateLock.writeLock().unlock();
+                    return true;
                 }
+            } else {
+                stateLock.readLock().unlock();
             }
             return false;
         }
@@ -926,12 +947,17 @@
         }
 
         private boolean resume(final Runnable handler) {
-            synchronized (stateLock) {
+            stateLock.readLock().lock();
+            try {
                 if (state != SUSPENDED) {
                     return false;
                 }
-                state = RESUMED;
+            } finally {
+                stateLock.readLock().unlock();
             }
+            stateLock.writeLock().lock();
+            state = RESUMED;
+            stateLock.writeLock().unlock();
 
             try {
                 responder.runtime.requestScope.runInScope(requestContext, handler);
@@ -979,7 +1005,8 @@
         }
 
         private boolean cancel(final Value<Response> responseValue) {
-            synchronized (stateLock) {
+            stateLock.readLock().lock();
+            try {
                 if (cancelled) {
                     return true;
                 }
@@ -987,10 +1014,15 @@
                 if (state != SUSPENDED) {
                     return false;
                 }
-                state = RESUMED;
-                cancelled = true;
+            } finally {
+                stateLock.readLock().unlock();
             }
 
+            stateLock.writeLock().lock();
+            state = RESUMED;
+            cancelled = true;
+            stateLock.writeLock().unlock();
+
             responder.runtime.requestScope.runInScope(requestContext, new Runnable() {
                 @Override
                 public void run() {
@@ -1007,29 +1039,41 @@
         }
 
         public boolean isRunning() {
-            synchronized (stateLock) {
+            stateLock.readLock().lock();
+            try {
                 return state == RUNNING;
+            } finally {
+                stateLock.readLock().unlock();
             }
         }
 
         @Override
         public boolean isSuspended() {
-            synchronized (stateLock) {
+            stateLock.readLock().lock();
+            try {
                 return state == SUSPENDED;
+            } finally {
+                stateLock.readLock().unlock();
             }
         }
 
         @Override
         public boolean isCancelled() {
-            synchronized (stateLock) {
+            stateLock.readLock().lock();
+            try {
                 return cancelled;
+            } finally {
+                stateLock.readLock().unlock();
             }
         }
 
         @Override
         public boolean isDone() {
-            synchronized (stateLock) {
+            stateLock.readLock().lock();
+            try {
                 return state == COMPLETED;
+            } finally {
+                stateLock.readLock().unlock();
             }
         }
 
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyRequestTimeoutHandler.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyRequestTimeoutHandler.java
index 761065e..91ec565 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyRequestTimeoutHandler.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyRequestTimeoutHandler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2024 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,6 +19,8 @@
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -42,7 +44,7 @@
     private ScheduledFuture<?> timeoutTask = null; // guarded by runtimeLock
     private ContainerResponseWriter.TimeoutHandler timeoutHandler = null; // guarded by runtimeLock
     private boolean suspended = false; // guarded by runtimeLock
-    private final Object runtimeLock = new Object();
+    private final Lock runtimeLock = new ReentrantLock();
 
     private final ContainerResponseWriter containerResponseWriter;
     private final ScheduledExecutorService executor;
@@ -71,7 +73,8 @@
      * @see ContainerResponseWriter#suspend(long, TimeUnit, ContainerResponseWriter.TimeoutHandler)
      */
     public boolean suspend(final long timeOut, final TimeUnit unit, final TimeoutHandler handler) {
-        synchronized (runtimeLock) {
+        runtimeLock.lock();
+        try {
             if (suspended) {
                 return false;
             }
@@ -81,6 +84,8 @@
 
             containerResponseWriter.setSuspendTimeout(timeOut, unit);
             return true;
+        } finally {
+            runtimeLock.unlock();
         }
     }
 
@@ -94,7 +99,8 @@
      * @see ContainerResponseWriter#setSuspendTimeout(long, TimeUnit)
      */
     public void setSuspendTimeout(final long timeOut, final TimeUnit unit) throws IllegalStateException {
-        synchronized (runtimeLock) {
+        runtimeLock.lock();
+        try {
             if (!suspended) {
                 throw new IllegalStateException(LocalizationMessages.SUSPEND_NOT_SUSPENDED());
             }
@@ -110,18 +116,21 @@
 
                     @Override
                     public void run() {
+                        runtimeLock.lock();
                         try {
-                            synchronized (runtimeLock) {
-                                timeoutHandler.onTimeout(containerResponseWriter);
-                            }
+                            timeoutHandler.onTimeout(containerResponseWriter);
                         } catch (final Throwable throwable) {
                             LOGGER.log(Level.WARNING, LocalizationMessages.SUSPEND_HANDLER_EXECUTION_FAILED(), throwable);
+                        } finally {
+                            runtimeLock.unlock();
                         }
                     }
                 }, timeOut, unit);
             } catch (final IllegalStateException ex) {
                 LOGGER.log(Level.WARNING, LocalizationMessages.SUSPEND_SCHEDULING_ERROR(), ex);
             }
+        } finally {
+            runtimeLock.unlock();
         }
     }
 
@@ -132,10 +141,15 @@
         close(false);
     }
 
-    private synchronized void close(final boolean interruptIfRunning) {
-        if (timeoutTask != null) {
-            timeoutTask.cancel(interruptIfRunning);
-            timeoutTask = null;
+    private void close(final boolean interruptIfRunning) {
+        runtimeLock.lock();
+        try {
+            if (timeoutTask != null) {
+                timeoutTask.cancel(interruptIfRunning);
+                timeoutTask = null;
+            }
+        } finally {
+            runtimeLock.unlock();
         }
     }
 }
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyResourceContext.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyResourceContext.java
index f8abbed..4e95a47 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyResourceContext.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyResourceContext.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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,8 @@
 import java.util.Collections;
 import java.util.IdentityHashMap;
 import java.util.Set;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.logging.Level;
@@ -54,7 +56,7 @@
     private final Consumer<Binding> registerBinding;
 
     private final Set<Class<?>> bindingCache;
-    private final Object bindingCacheLock;
+    private final Lock bindingCacheLock;
 
     private volatile ResourceModel resourceModel;
 
@@ -73,7 +75,7 @@
         this.injectInstance = injectInstance;
         this.registerBinding = registerBinding;
         this.bindingCache = Collections.newSetFromMap(new IdentityHashMap<>());
-        this.bindingCacheLock = new Object();
+        this.bindingCacheLock = new ReentrantLock();
     }
 
     @Override
@@ -110,11 +112,14 @@
             return;
         }
 
-        synchronized (bindingCacheLock) {
+        bindingCacheLock.lock();
+        try {
             if (bindingCache.contains(resourceClass)) {
                 return;
             }
             unsafeBindResource(resourceClass, null);
+        } finally {
+            bindingCacheLock.unlock();
         }
     }
 
@@ -135,7 +140,8 @@
             return;
         }
 
-        synchronized (bindingCacheLock) {
+        bindingCacheLock.lock();
+        try {
             if (bindingCache.contains(resourceClass)) {
                 return;
             }
@@ -144,6 +150,8 @@
             }
 
             bindingCache.add(resourceClass);
+        } finally {
+            bindingCacheLock.unlock();
         }
     }
 
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/MBeanExposer.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/MBeanExposer.java
index a7d5829..dc99a5d 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/MBeanExposer.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/MBeanExposer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2024 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,8 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -60,7 +62,7 @@
     private volatile ResourcesMBeanGroup resourceClassStatsGroup;
     private volatile ExceptionMapperMXBeanImpl exceptionMapperMXBean;
     private final AtomicBoolean destroyed = new AtomicBoolean(false);
-    private final Object LOCK = new Object();
+    private final Lock LOCK = new ReentrantLock();
 
     /**
      * Name of domain that will prefix mbeans {@link ObjectName}. The code uses this
@@ -110,45 +112,47 @@
     void registerMBean(Object mbean, String namePostfix) {
         final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
         final String name = domain + namePostfix;
+        LOCK.lock();
         try {
-            synchronized (LOCK) {
-                if (destroyed.get()) {
-                    // already destroyed
-                    return;
-                }
-                final ObjectName objectName = new ObjectName(name);
-                if (mBeanServer.isRegistered(objectName)) {
-
-                    LOGGER.log(Level.WARNING,
-                            LocalizationMessages.WARNING_MONITORING_MBEANS_BEAN_ALREADY_REGISTERED(objectName));
-                    mBeanServer.unregisterMBean(objectName);
-                }
-                mBeanServer.registerMBean(mbean, objectName);
+            if (destroyed.get()) {
+                // already destroyed
+                return;
             }
+            final ObjectName objectName = new ObjectName(name);
+            if (mBeanServer.isRegistered(objectName)) {
+
+                LOGGER.log(Level.WARNING,
+                        LocalizationMessages.WARNING_MONITORING_MBEANS_BEAN_ALREADY_REGISTERED(objectName));
+                mBeanServer.unregisterMBean(objectName);
+            }
+            mBeanServer.registerMBean(mbean, objectName);
         } catch (JMException e) {
             throw new ProcessingException(LocalizationMessages.ERROR_MONITORING_MBEANS_REGISTRATION(name), e);
+        } finally {
+            LOCK.unlock();
         }
     }
 
     private void unregisterJerseyMBeans(boolean destroy) {
         final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
+        LOCK.lock();
         try {
-            synchronized (LOCK) {
-                if (destroy) {
-                    destroyed.set(true); // do not register new beans since now
-                }
+            if (destroy) {
+                destroyed.set(true); // do not register new beans since now
+            }
 
-                if (domain == null) {
-                    // No bean has been registered yet.
-                    return;
-                }
-                final Set<ObjectName> names = mBeanServer.queryNames(new ObjectName(domain + ",*"), null);
-                for (ObjectName name : names) {
-                    mBeanServer.unregisterMBean(name);
-                }
+            if (domain == null) {
+                // No bean has been registered yet.
+                return;
+            }
+            final Set<ObjectName> names = mBeanServer.queryNames(new ObjectName(domain + ",*"), null);
+            for (ObjectName name : names) {
+                mBeanServer.unregisterMBean(name);
             }
         } catch (Exception e) {
             throw new ProcessingException(LocalizationMessages.ERROR_MONITORING_MBEANS_UNREGISTRATION_DESTROY(), e);
+        } finally {
+            LOCK.unlock();
         }
     }
 
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/UriRoutingContext.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/UriRoutingContext.java
index 40faecc..d936c51 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/UriRoutingContext.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/UriRoutingContext.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2024 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,9 @@
 import jakarta.ws.rs.core.UriBuilder;
 
 import org.glassfish.jersey.internal.util.collection.ImmutableMultivaluedMap;
+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.TracingLogger;
 import org.glassfish.jersey.server.ContainerRequest;
 import org.glassfish.jersey.server.internal.ServerTraceEvent;
@@ -66,7 +69,7 @@
     private final LinkedList<ResourceMethod> matchedLocators = new LinkedList<>();
     private final LinkedList<Resource> locatorSubResources = new LinkedList<>();
 
-    private final TracingLogger tracingLogger;
+    private final LazyValue<TracingLogger> tracingLogger;
 
     private volatile ResourceMethod matchedResourceMethod = null;
     private volatile Throwable mappedThrowable = null;
@@ -86,7 +89,8 @@
      */
     public UriRoutingContext(final ContainerRequest requestContext) {
         this.requestContext = requestContext;
-        this.tracingLogger = TracingLogger.getInstance(requestContext);
+        // Tracing Logger is initialized after UriContext before pushMatchedResource
+        this.tracingLogger = Values.lazy((Value<TracingLogger>) () -> TracingLogger.getInstance(requestContext));
     }
 
     // RoutingContext
@@ -97,7 +101,7 @@
 
     @Override
     public void pushMatchedResource(final Object resource) {
-        tracingLogger.log(ServerTraceEvent.MATCH_RESOURCE, resource);
+        tracingLogger.get().log(ServerTraceEvent.MATCH_RESOURCE, resource);
         matchedResources.push(resource);
     }
 
@@ -108,7 +112,7 @@
 
     @Override
     public void pushMatchedLocator(final ResourceMethod resourceLocator) {
-        tracingLogger.log(ServerTraceEvent.MATCH_LOCATOR, resourceLocator.getInvocable().getHandlingMethod());
+        tracingLogger.get().log(ServerTraceEvent.MATCH_LOCATOR, resourceLocator.getInvocable().getHandlingMethod());
         matchedLocators.push(resourceLocator);
     }
 
@@ -189,14 +193,14 @@
 
     @Override
     public void setMatchedResourceMethod(final ResourceMethod resourceMethod) {
-        tracingLogger.log(ServerTraceEvent.MATCH_RESOURCE_METHOD, resourceMethod.getInvocable().getHandlingMethod());
+        tracingLogger.get().log(ServerTraceEvent.MATCH_RESOURCE_METHOD, resourceMethod.getInvocable().getHandlingMethod());
         this.matchedResourceMethod = resourceMethod;
     }
 
     @Override
     public void pushMatchedRuntimeResource(final RuntimeResource runtimeResource) {
-        if (tracingLogger.isLogEnabled(ServerTraceEvent.MATCH_RUNTIME_RESOURCE)) {
-            tracingLogger.log(ServerTraceEvent.MATCH_RUNTIME_RESOURCE,
+        if (tracingLogger.get().isLogEnabled(ServerTraceEvent.MATCH_RUNTIME_RESOURCE)) {
+            tracingLogger.get().log(ServerTraceEvent.MATCH_RUNTIME_RESOURCE,
                     runtimeResource.getResources().get(0).getPath(),
                     runtimeResource.getResources().get(0).getPathPattern().getRegex(),
                     matchResults.peek().group()
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/AnnotationAcceptingListener.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/AnnotationAcceptingListener.java
index 7e9db27..f5384f8 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/AnnotationAcceptingListener.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/AnnotationAcceptingListener.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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
@@ -309,7 +309,7 @@
 
     private static class ClassReaderWrapper {
         private static final Logger LOGGER = Logger.getLogger(ClassReader.class.getName());
-        private static final int WARN_VERSION = Opcodes.V22;
+        private static final int WARN_VERSION = Opcodes.V23;
         private static final int INPUT_STREAM_DATA_CHUNK_SIZE = 4096;
 
         private final byte[] b;
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/JarFileScanner.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/JarFileScanner.java
index 14e86e3..7b97eb9 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/JarFileScanner.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/JarFileScanner.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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,11 +19,14 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.NoSuchElementException;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 import java.util.jar.JarEntry;
 import java.util.jar.JarInputStream;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import org.glassfish.jersey.innate.io.InputStreamWrapper;
 import org.glassfish.jersey.server.internal.AbstractResourceFinderAdapter;
 import org.glassfish.jersey.server.internal.LocalizationMessages;
 
@@ -109,52 +112,17 @@
     @Override
     public InputStream open() {
         //noinspection NullableProblems
-        return new InputStream() {
+        return new InputStreamWrapper() {
 
             @Override
-            public int read() throws IOException {
-                return jarInputStream.read();
-            }
-
-            @Override
-            public int read(final byte[] bytes) throws IOException {
-                return jarInputStream.read(bytes);
-            }
-
-            @Override
-            public int read(final byte[] bytes, final int i, final int i2) throws IOException {
-                return jarInputStream.read(bytes, i, i2);
-            }
-
-            @Override
-            public long skip(final long l) throws IOException {
-                return jarInputStream.skip(l);
-            }
-
-            @Override
-            public int available() throws IOException {
-                return jarInputStream.available();
+            protected InputStream getWrapped() {
+                return jarInputStream;
             }
 
             @Override
             public void close() throws IOException {
                 jarInputStream.closeEntry();
             }
-
-            @Override
-            public synchronized void mark(final int i) {
-                jarInputStream.mark(i);
-            }
-
-            @Override
-            public synchronized void reset() throws IOException {
-                jarInputStream.reset();
-            }
-
-            @Override
-            public boolean markSupported() {
-                return jarInputStream.markSupported();
-            }
         };
     }
 
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/JarZipSchemeResourceFinderFactory.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/JarZipSchemeResourceFinderFactory.java
index aa2874e..501372a 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/JarZipSchemeResourceFinderFactory.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/JarZipSchemeResourceFinderFactory.java
@@ -16,13 +16,13 @@
 
 package org.glassfish.jersey.server.internal.scanning;
 
-import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.MalformedURLException;
 import java.net.URI;
 import java.net.URL;
 import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
@@ -155,7 +155,7 @@
             return new URL(jarUrlString).openStream();
         } catch (final MalformedURLException e) {
             return Files.newInputStream(
-                    new File(UriComponent.decode(jarUrlString, UriComponent.Type.PATH)).toPath());
+                    Path.of(UriComponent.decode(jarUrlString, UriComponent.Type.PATH)));
         }
     }
 }
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/PackageNamesScanner.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/PackageNamesScanner.java
index 06a9432..d482dde 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/PackageNamesScanner.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/PackageNamesScanner.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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
@@ -27,6 +27,8 @@
 import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 
 import org.glassfish.jersey.internal.OsgiRegistry;
 import org.glassfish.jersey.internal.util.ReflectionHelper;
@@ -54,7 +56,7 @@
  * URI schemes.
  * <p>
  * Further schemes may be registered by registering an implementation of
- * {@link UriSchemeResourceFinderFactory} in the META-INF/services file whose name is the
+ * {@link UriSchemeResourceFinderFactory} in the META-INF/services file whose name is
  * the fully qualified class name of {@link UriSchemeResourceFinderFactory}.
  * <p>
  * If a URI scheme is not supported a {@link ResourceFinderException} will be thrown
@@ -195,13 +197,15 @@
     public abstract static class ResourcesProvider {
 
         private static volatile ResourcesProvider provider;
+        private static final Lock RESOURCE_PROVIDER_LOCK = new ReentrantLock();
 
         private static ResourcesProvider getInstance() {
             // Double-check idiom for lazy initialization
             ResourcesProvider result = provider;
 
             if (result == null) { // first check without locking
-                synchronized (ResourcesProvider.class) {
+                RESOURCE_PROVIDER_LOCK.lock();
+                try {
                     result = provider;
                     if (result == null) { // second check with locking
                         provider = result = new ResourcesProvider() {
@@ -214,6 +218,8 @@
                         };
 
                     }
+                } finally {
+                    RESOURCE_PROVIDER_LOCK.unlock();
                 }
 
             }
@@ -226,9 +232,9 @@
                 final ReflectPermission rp = new ReflectPermission("suppressAccessChecks");
                 security.checkPermission(rp);
             }
-            synchronized (ResourcesProvider.class) {
-                ResourcesProvider.provider = provider;
-            }
+            RESOURCE_PROVIDER_LOCK.lock();
+            ResourcesProvider.provider = provider;
+            RESOURCE_PROVIDER_LOCK.unlock();
         }
 
         /**
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/wadl/internal/WadlResource.java b/core-server/src/main/java/org/glassfish/jersey/server/wadl/internal/WadlResource.java
index 5a7188a..62ce38d 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/wadl/internal/WadlResource.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/wadl/internal/WadlResource.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 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,9 @@
 import java.net.URI;
 import java.text.SimpleDateFormat;
 import java.util.Date;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.Locale;
 
 import jakarta.ws.rs.GET;
 import jakarta.ws.rs.Path;
@@ -56,13 +59,14 @@
 
     private byte[] wadlXmlRepresentation;
     private String lastModified;
+    private final Lock lock = new ReentrantLock();
 
     @Context
     private WadlApplicationContext wadlContext;
 
 
     public WadlResource() {
-        this.lastModified = new SimpleDateFormat(HTTPDATEFORMAT).format(new Date());
+        this.lastModified = new SimpleDateFormat(HTTPDATEFORMAT, Locale.US).format(new Date());
     }
 
     private boolean isCached(UriInfo uriInfo, boolean detailedWadl) {
@@ -71,7 +75,8 @@
 
     @Produces({"application/vnd.sun.wadl+xml", "application/xml"})
     @GET
-    public synchronized Response getWadl(@Context UriInfo uriInfo) {
+    public Response getWadl(@Context UriInfo uriInfo) {
+        lock.lock();
         try {
             if (!wadlContext.isWadlGenerationEnabled()) {
                 return Response.status(Response.Status.NOT_FOUND).build();
@@ -81,7 +86,7 @@
             if ((wadlXmlRepresentation == null) || (!isCached(uriInfo, detailedWadl))) {
                 this.lastBaseUri = uriInfo.getBaseUri();
                 lastDetailedWadl = detailedWadl;
-                this.lastModified = new SimpleDateFormat(HTTPDATEFORMAT).format(new Date());
+                this.lastModified = new SimpleDateFormat(HTTPDATEFORMAT, Locale.US).format(new Date());
 
                 ApplicationDescription applicationDescription = wadlContext.getApplication(uriInfo,
                         detailedWadl);
@@ -103,6 +108,8 @@
             return Response.ok(new ByteArrayInputStream(wadlXmlRepresentation)).header("Last-modified", lastModified).build();
         } catch (Exception e) {
             throw new ProcessingException("Error generating /application.wadl.", e);
+        } finally {
+            lock.unlock();
         }
     }
 
@@ -110,9 +117,10 @@
     @Produces({"application/xml"})
     @GET
     @Path("{path}")
-    public synchronized Response getExternalGrammar(
+    public Response getExternalGrammar(
             @Context UriInfo uriInfo,
             @PathParam("path") String path) {
+        lock.lock();
         try {
             // Fail if wadl generation is disabled
             if (!wadlContext.isWadlGenerationEnabled()) {
@@ -135,6 +143,8 @@
                     .build();
         } catch (Exception e) {
             throw new ProcessingException(LocalizationMessages.ERROR_WADL_RESOURCE_EXTERNAL_GRAMMAR(), e);
+        } finally {
+            lock.unlock();
         }
     }
 
diff --git a/core-server/src/main/resources/META-INF/NOTICE.markdown b/core-server/src/main/resources/META-INF/NOTICE.markdown
index 92d64be..27c798d 100644
--- a/core-server/src/main/resources/META-INF/NOTICE.markdown
+++ b/core-server/src/main/resources/META-INF/NOTICE.markdown
@@ -36,7 +36,7 @@
 * Copyright (c) 2015-2018 Oracle and/or its affiliates. All rights reserved.

 * Copyright 2010-2013 Coda Hale and Yammer, Inc.

 

-org.objectweb.asm Version 9.6

+org.objectweb.asm Version 9.7

 * License: Modified BSD (https://asm.ow2.io/license.html)

 * Copyright: (c) 2000-2011 INRIA, France Telecom. All rights reserved.

 

diff --git a/core-server/src/test/java/org/glassfish/jersey/server/JarUtils.java b/core-server/src/test/java/org/glassfish/jersey/server/JarUtils.java
index 50a5402..003a735 100644
--- a/core-server/src/test/java/org/glassfish/jersey/server/JarUtils.java
+++ b/core-server/src/test/java/org/glassfish/jersey/server/JarUtils.java
@@ -90,11 +90,7 @@
 
             final InputStream f = new BufferedInputStream(
                     Files.newInputStream(new File(base, entry.getKey()).toPath()));
-            final byte[] buf = new byte[1024];
-            int read = 1024;
-            while ((read = f.read(buf, 0, read)) != -1) {
-                jos.write(buf, 0, read);
-            }
+            f.transferTo(jos);
             jos.closeEntry();
         }
 
diff --git a/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/FormParamTest.java b/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/FormParamTest.java
index 34f29d6..07b368e 100644
--- a/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/FormParamTest.java
+++ b/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/FormParamTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 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
@@ -402,8 +402,8 @@
         initiateWebApplication(FormResourceDate.class);
 
         final String date_RFC1123 = "Sun, 06 Nov 1994 08:49:37 GMT";
-        final String date_RFC1036 = "Sunday, 06-Nov-94 08:49:37 GMT";
-        final String date_ANSI_C = "Sun Nov  6 08:49:37 1994";
+        final String date_RFC1036 = "Sunday, 07-Nov-04 08:49:37 GMT";
+        final String date_ANSI_C = "Sun Nov 6 08:49:37 1994";
 
         final Form form = new Form();
         form.param("a", date_RFC1123);
diff --git a/core-server/src/test/java/org/glassfish/jersey/server/internal/scanning/JarFileScannerTest.java b/core-server/src/test/java/org/glassfish/jersey/server/internal/scanning/JarFileScannerTest.java
index 904c2e1..c85d1b2 100644
--- a/core-server/src/test/java/org/glassfish/jersey/server/internal/scanning/JarFileScannerTest.java
+++ b/core-server/src/test/java/org/glassfish/jersey/server/internal/scanning/JarFileScannerTest.java
@@ -19,7 +19,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.file.Files;
-import java.nio.file.Paths;
+import java.nio.file.Path;
 import java.util.Enumeration;
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
@@ -106,7 +106,7 @@
     private int countJarEntriesUsingScanner(final String parent, final boolean recursive) throws IOException {
         int scannedEntryCount = 0;
 
-        try (final InputStream jaxRsApi = Files.newInputStream(Paths.get(this.jaxRsApiPath))) {
+        try (final InputStream jaxRsApi = Files.newInputStream(Path.of(this.jaxRsApiPath))) {
             final JarFileScanner jarFileScanner = new JarFileScanner(jaxRsApi, parent, recursive);
             while (jarFileScanner.hasNext()) {
                 // Fetch next entry.
@@ -128,7 +128,7 @@
     @ParameterizedTest
     @ValueSource(booleans = {true, false})
     public void testClassEnumerationWithNonexistentPackage(final boolean recursive) throws IOException {
-        try (final InputStream jaxRsApi = Files.newInputStream(Paths.get(this.jaxRsApiPath))) {
+        try (final InputStream jaxRsApi = Files.newInputStream(Path.of(this.jaxRsApiPath))) {
             final JarFileScanner jarFileScanner = new JarFileScanner(jaxRsApi, "jakarta/ws/r", recursive);
             assertFalse(jarFileScanner.hasNext(), "Unexpectedly found package 'jakarta.ws.r' in jakarta.ws.rs-api");
         }
@@ -137,7 +137,7 @@
     @ParameterizedTest
     @ValueSource(booleans = {true, false})
     public void testClassEnumerationWithClassPrefix(final boolean recursive) throws IOException {
-        try (final InputStream jaxRsApi = Files.newInputStream(Paths.get(this.jaxRsApiPath))) {
+        try (final InputStream jaxRsApi = Files.newInputStream(Path.of(this.jaxRsApiPath))) {
             final JarFileScanner jarFileScanner = new JarFileScanner(jaxRsApi, "jakarta/ws/rs/GE", recursive);
             assertFalse(jarFileScanner.hasNext(), "Unexpectedly found package 'jakarta.ws.rs.GE' in jakarta.ws.rs-api");
         }
diff --git a/docs/src/main/docbook/appendix-properties.xml b/docs/src/main/docbook/appendix-properties.xml
index aa4aee9..4294bae 100644
--- a/docs/src/main/docbook/appendix-properties.xml
+++ b/docs/src/main/docbook/appendix-properties.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <!--
 
-    Copyright (c) 2013, 2023 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2013, 2024 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
@@ -72,6 +72,40 @@
                         </entry>
                     </row>
                     <row>
+                        <entry>&jersey.common.CommonProperties.JSON_BINDING_FEATURE_DISABLE; /
+                            &jersey.common.CommonProperties.JSON_BINDING_FEATURE_DISABLE_CLIENT; /
+                            &jersey.common.CommonProperties.JSON_BINDING_FEATURE_DISABLE_SERVER;</entry>
+                        <entry><literal>jersey.config.disableJsonBinding</literal> /
+                            <literal>jersey.config.client.disableJsonBinding</literal> /
+                            <literal>jersey.config.server.disableJsonBinding</literal></entry>
+                        <entry>
+                            <para>
+                                Disables configuration of Json Binding (JSR-367) feature. Default value is <literal>false</literal>.
+                                Can also be set as a system property.
+                            </para>
+                            <para>
+                                Since 2.45.
+                            </para>
+                        </entry>
+                    </row>
+                    <row>
+                        <entry>&jersey.common.CommonProperties.JSON_BINDING_FEATURE_DISABLE_APPLICATION; </entry>
+                        <entry><literal>jersey.config.application.disableJsonBinding</literal></entry>
+                        <entry>
+                            <para>
+                                Disables configuration of Json Binding (JSR-367) feature for <literal>jakarta.ws.rs.core.Application</literal>
+                                subclasses whose package names are specified as a value. The value is comma-separated string
+                                defining prefixes of the application package names. Can also be set as a system property.
+                            </para>
+                            <para>
+                                By default, Json Binding is automatically enabled.
+                            </para>
+                            <para>
+                                Since 2.45.
+                            </para>
+                        </entry>
+                    </row>
+                    <row>
                         <entry>&jersey.common.CommonProperties.JSON_PROCESSING_FEATURE_DISABLE; /
                             &jersey.common.CommonProperties.JSON_PROCESSING_FEATURE_DISABLE_CLIENT; /
                             &jersey.common.CommonProperties.JSON_PROCESSING_FEATURE_DISABLE_SERVER;</entry>
@@ -203,6 +237,39 @@
                         </entry>
                     </row>
                     <row>
+                        <entry>&jersey.common.CommonProperties.THREAD_FACTORY;(Jersey 3.1.7 or later)
+                        </entry>
+                        <entry>
+                            <literal>jersey.config.threads.factory</literal>
+                        </entry>
+                        <entry>
+                            <para>
+                                Defines the <literal>java.util.concurrent.ThreadFactory</literal> to be used by internal default
+                                <literal>ExecutorServices</literal>.
+                            </para>
+                            <para>
+                                The default is <literal>java.util.concurrent.Executors#defaultThreadFactory()</literal> on
+                                platform threads and<literal>Thread.ofVirtual().factory()</literal> on virtual threads.
+                            </para>
+                        </entry>
+                    </row>
+                    <row>
+                        <entry>&jersey.common.CommonProperties.USE_VIRTUAL_THREADS;(Jersey 3.1.7 or later)
+                        </entry>
+                        <entry>
+                            <literal>jersey.config.threads.use.virtual</literal>
+                        </entry>
+                        <entry>
+                            <para>
+                                Defines whether the virtual threads should be used by Jersey on JDK 21+ when not using an exact number
+                                of threads by <literal>FixedThreadPool</literal>.
+                            </para>
+                            <para>
+                                The default is &lit.false; for this version of Jersey.
+                            </para>
+                        </entry>
+                    </row>
+                    <row>
                         <entry>&jersey.logging.LoggingFeature.LOGGING_FEATURE_LOGGER_NAME;
                         </entry>
                         <entry>
@@ -383,6 +450,15 @@
                         </entry>
                     </row>
                     <row>
+                        <entry>&jersey.server.ServerProperties.JSON_BINDING_FEATURE_DISABLE;</entry>
+                        <entry><literal>jersey.config.server.disableJsonBinding</literal></entry>
+                        <entry>
+                            <para>
+                                Disables configuration of Json Processing (JSR-353) feature. Default value is <literal>false</literal>.
+                            </para>
+                        </entry>
+                    </row>
+                    <row>
                         <entry>&jersey.server.ServerProperties.JSON_PROCESSING_FEATURE_DISABLE;</entry>
                         <entry><literal>jersey.config.server.disableJsonProcessing</literal></entry>
                         <entry>
@@ -1073,6 +1149,15 @@
                         </entry>
                     </row>
                     <row>
+                        <entry>&jersey.client.ClientProperties.JSON_BINDING_FEATURE_DISABLE;</entry>
+                        <entry><literal>jersey.config.client.disableJsonBinding</literal></entry>
+                        <entry>
+                            <para>
+                                Disables configuration of Json Binding (JSR-367) feature. Default value is <literal>false</literal>.
+                            </para>
+                        </entry>
+                    </row>
+                    <row>
                         <entry>&jersey.client.ClientProperties.JSON_PROCESSING_FEATURE_DISABLE;</entry>
                         <entry><literal>jersey.config.client.disableJsonProcessing</literal></entry>
                         <entry>
@@ -1170,7 +1255,22 @@
                             </para>
                         </entry>
                     </row>
-
+                    <row>
+                        <entry>&jersey.client.ClientProperties.SNI_HOST_NAME; (Jersey 2.43 or later)</entry>
+                        <entry><literal>jersey.config.client.sniHostName</literal></entry>
+                        <entry>
+                            <para>
+                                Sets the host name to be used for calculating the <literal>javax.net.ssl.SNIHostName</literal>
+                                during the HTTPS request. Takes precedence over the HTTP HOST header, if set.
+                            </para>
+                            <para>
+                                By default, the <literal>SNIHostName</literal> is set when the HOST HTTP header differs from
+                                the HTTP request host. When the property value host name matches the HTTPS request host,
+                                the <literal>SNIHostName</literal> is not set, and the HTTP HOST header is not used for
+                                setting the <literal>SNIHostName</literal>. This allows for Domain Fronting.
+                            </para>
+                        </entry>
+                    </row>
                     <row>
                         <entry>&jersey.client.ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION;
                             (Jersey 2.2 or later)</entry>
@@ -1352,7 +1452,7 @@
                         <entry>
                             <para>
                                 If set to &lit.true; then XML root element tag name for collections will
-                                be derived from <literal>javax.xml.bind.annotation.XmlRootElement</literal>
+                                be derived from <literal>jakarta.xml.bind.annotation.XmlRootElement</literal>
                                 annotation value and won't be de-capitalized.
                             </para>
                             <para>
@@ -1412,6 +1512,88 @@
 
 
     </section>
+    <section xml:id="appendix-properties-client-default">
+        <title>The default HttpUrlConnector properties</title>
+        <para>
+            List of properties defined in &jersey.client.HttpUrlConnectorProvider; class.
+        </para>
+
+        <table>
+            <title>List of the default &jersey.client.HttpUrlConnectorProvider; properites</title>
+            <tgroup cols="3">
+                <thead>
+                    <row>
+                        <entry>Constant</entry>
+                        <entry>Value</entry>
+                        <entry>Description</entry>
+                    </row>
+                </thead>
+                <tbody>
+                    <row>
+                        <entry>&jersey.client.HttpUrlConnectorProvider.SET_METHOD_WORKAROUND;</entry>
+                        <entry><literal>jersey.config.client.httpUrlConnection.setMethodWorkaround</literal></entry>
+                        <entry>
+                            <para>
+                                A value of &lit.true; declares that the client will try to set
+                                unsupported HTTP method to <literal>java.net.HttpURLConnection</literal> via
+                                reflection as a workaround for a missing HTTP method.
+                            </para>
+                            <para>
+                                NOTE: Enabling this property may cause security related warnings/errors
+                                and it may break when other JDK implementation is used. <emphasis>Use only
+                                when you know what you are doing.</emphasis>
+                            </para>
+                            <para>
+                                The value MUST be an instance of &lit.jdk6.Boolean;. The default value is &lit.false;.
+                            </para>
+                            <para>Since JDK 16 the JDK internal classes are not opened for reflection and the workaround method
+                                does not work, unless <literal>--add-opens java.base/java.net=ALL-UNNAMED</literal> for HTTP
+                                requests and additional <literal>--add-opens java.base/sun.net.www.protocol.https=ALL-UNNAMED</literal>
+                                for HTTPS (<literal>javax.net.ssl.HttpsUrlConnection</literal>) options are set.
+                            </para>
+                        </entry>
+                    </row>
+                    <row>
+                        <entry>&jersey.client.HttpUrlConnectorProvider.USE_FIXED_LENGTH_STREAMING;</entry>
+                        <entry><literal>jersey.config.client.httpUrlConnector.useFixedLengthStreaming</literal></entry>
+                        <entry>
+                            <para>
+                                If &lit.true;, the &lit.jersey.client.HttpUrlConnector; (if used) will assume the content length
+                                from the value of <literal>jakarta.ws.rs.core.HttpHeaders#CONTENT_LENGTH</literal> request
+                                header (if present).
+                            </para>
+                            <para>
+                                When this property is enabled and the request has a valid non-zero content length
+                                value specified in its <literal>jakarta.ws.rs.core.HttpHeaders#CONTENT_LENGTH</literal> request
+                                header, that this value will be used as an input to the
+                                <literal>java.net.HttpURLConnection#setFixedLengthStreamingMode(int)</literal> method call
+                                invoked on the underlying <literal>java.net.HttpURLConnection</literal>.
+                                This will also suppress the entity buffering in the <literal>HttpURLConnection</literal>,
+                                which is undesirable in certain scenarios, e.g. when streaming large entities.
+                            </para>
+                            <para>
+                                Note that the content length value defined in the request header must exactly match
+                                the real size of the entity. If the <literal>jakarta.ws.rs.core.HttpHeaders#CONTENT_LENGTH</literal>
+                                header is explicitly specified in a request, this property will be ignored and the
+                                request entity will be still buffered by the underlying <literal>HttpURLConnection</literal> infrastructure.
+                            </para>
+                            <para>
+                                This property also overrides the behaviour enabled by the &jersey.client.ClientProperties.CHUNKED_ENCODING_SIZE;
+                                property. Chunked encoding will only be used, if the size is not specified in the header of the request.
+                            </para>
+                            <para>
+                                Note that this property only applies to client run-times that are configured to use the default
+                                &lit.jersey.client.HttpUrlConnector; as the client connector. The property is ignored by other connectors.
+                            </para>
+                            <para>
+                                The default value is &lit.false;.
+                            </para>
+                        </entry>
+                    </row>
+                </tbody>
+            </tgroup>
+        </table>
+    </section>
     <section xml:id="appendix-properties-client-apache">
         <title>Apache HTTP client configuration properties</title>
 
@@ -2199,6 +2381,42 @@
                             </para>
                         </entry>
                     </row>
+                    <row>
+                        <entry>&jersey.netty.NettyClientProperties.MAX_HEADER_SIZE;</entry>
+                        <entry><literal>jersey.config.client.netty.maxHeaderSize</literal></entry>
+                        <entry>
+                            <para>
+                                Parameter which allows extending of the header size for the Netty connector
+
+                                Default header size is 8192b.
+                                <literal>Since 2.44</literal>
+                            </para>
+                        </entry>
+                    </row>
+                    <row>
+                        <entry>&jersey.netty.NettyClientProperties.MAX_CHUNK_SIZE;</entry>
+                        <entry><literal>jersey.config.client.netty.maxChunkSize</literal></entry>
+                        <entry>
+                            <para>
+                                Parameter which allows extending of the chunk size for the Netty connector
+
+                                Default chunk size is 8192b.
+                                <literal>Since 2.44</literal>
+                            </para>
+                        </entry>
+                    </row>
+                    <row>
+                        <entry>&jersey.netty.NettyClientProperties.MAX_INITIAL_LINE_LENGTH;</entry>
+                        <entry><literal>jersey.config.client.netty.maxInitialLineLength</literal></entry>
+                        <entry>
+                            <para>
+                                Parameter which allows extending of the initial line length for the Netty connector
+
+                                Default initial line length is 4096b.
+                                <literal>Since 2.44</literal>
+                            </para>
+                        </entry>
+                    </row>
                 </tbody>
             </tgroup>
         </table>
@@ -2307,4 +2525,80 @@
             </tgroup>
         </table>
     </section>
+    <section xml:id="appendix-properties-multipart">
+        <title>Multipart configuration properties</title>
+
+        <para>
+            List of multipart configuration properties that can be found in &jersey.media.multipart.MultiPartProperties; class.
+        </para>
+
+        <table>
+            <title>
+                List of multipart configuration properties settable in the
+                &jersey.media.multipart.MultiPartProperties.MULTI_PART_CONFIG_RESOURCE; configuration file.
+            </title>
+            <tgroup cols="3">
+                <thead>
+                    <row>
+                        <entry>Constant</entry>
+                        <entry>Value</entry>
+                        <entry>Description</entry>
+                    </row>
+                </thead>
+                <tbody>
+                    <row>
+                        <entry>&jersey.media.multipart.MultiPartProperties.BUFFER_THRESHOLD;</entry>
+                        <entry><literal>jersey.config.multipart.bufferThreshold</literal></entry>
+                        <entry>
+                            <para>
+                                Name of the resource property for the threshold size (in bytes) above which a
+                                body part entity will be buffered to disk instead of being held in memory.
+                            </para>
+                            <para>
+                                The default value is &jersey.message.MessageProperties.IO_DEFAULT_BUFFER_SIZE;
+                            </para>
+                        </entry>
+                    </row>
+                    <row>
+                        <entry>&jersey.media.multipart.MultiPartProperties.MAX_PARTS;</entry>
+                        <entry><literal>jersey.config.multipart.maxParts</literal></entry>
+                        <entry>
+                            <para>
+                                Limit the maximum number of parts the multipart entity can have. If the limit is over,
+                                the error response status <literal>413 - REQUEST_ENTITY_TOO_LARGE</literal>
+                                is returned.
+                            </para>
+                            <para>
+                                By default, the number is unlimited.
+                            </para>
+                        </entry>
+                    </row>
+                    <row>
+                        <entry>&jersey.media.multipart.MultiPartProperties.MULTI_PART_CONFIG_RESOURCE;</entry>
+                        <entry><literal>jersey-multipart-config.properties</literal></entry>
+                        <entry>
+                            <para>
+                                Name of a properties resource that (if found in the classpath
+                                for this application) will be used to configure the settings returned
+                                by our getter methods.
+                            </para>
+                        </entry>
+                    </row>
+                    <row>
+                        <entry>&jersey.media.multipart.MultiPartProperties.TEMP_DIRECTORY;</entry>
+                        <entry><literal>jersey.config.multipart.tempDir</literal></entry>
+                        <entry>
+                            <para>
+                                Name of the resource property for the directory to store temporary files containing body parts
+                                of multipart message that extends allowed memory threshold.
+                            </para>
+                            <para>
+                                The default value is not set (will be taken from <literal>java.io.tmpdir</literal> system property).
+                            </para>
+                        </entry>
+                    </row>
+                </tbody>
+            </tgroup>
+        </table>
+    </section>
 </appendix>
\ No newline at end of file
diff --git a/docs/src/main/docbook/async.xml b/docs/src/main/docbook/async.xml
index f496bab..f39b76b 100644
--- a/docs/src/main/docbook/async.xml
+++ b/docs/src/main/docbook/async.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <!--
 
-    Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2012, 2024 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
@@ -270,6 +270,9 @@
     public ChunkedOutput<String> getChunkedResponse() {
         final ChunkedOutput<String> output = new ChunkedOutput<String>(String.class);
 
+        // Or use the builder pattern instead, which also allows to configure the queue capacity
+        // final ChunkedOutput<String> output = ChunkedOutput.<String>builder(String.class).queueCapacity(10).build();
+
         new Thread() {
             public void run() {
                 try {
diff --git a/docs/src/main/docbook/client.xml b/docs/src/main/docbook/client.xml
index 998e32b..bfda0f5 100644
--- a/docs/src/main/docbook/client.xml
+++ b/docs/src/main/docbook/client.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <!--
 
-    Copyright (c) 2010, 2023 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2010, 2024 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
@@ -688,71 +688,6 @@
                     </tbody>
                 </tgroup>
             </table>
-
-            <warning xml:id="connectors.warning" xreflabel="Header modification issue">
-                <para>
-                    Be aware of using other than default &lit.jersey.client.Connector; implementation.
-                    There is an issue handling HTTP headers in
-                    &lit.jaxrs.WriterInterceptor; or &lit.jaxrs.ext.MessageBodyWriter;.
-                    If you need to change header fields do not use nor
-                    &lit.jersey.apache.ApacheConnectorProvider; nor &lit.jersey.grizzly.GrizzlyConnectorProvider;
-                    nor &lit.jersey.jetty.JettyConnectorProvider; neither &lit.jersey.netty.NettyConnectorProvider;.
-                    The issue for example applies to Jersey <xref linkend="multipart" endterm="multipart.short"/>
-                    feature that also modifies HTTP headers.
-                </para>
-                <para>
-                    On the other hand, in the default transport connector, there are some restrictions on the headers, that
-                    can be sent in the default configuration.
-                    <literal>HttpUrlConnectorProvider</literal> uses &lit.jdk6.HttpUrlConnection; as an underlying connection
-                    implementation. This JDK class by default restricts the use of following headers:
-                    <itemizedlist>
-                        <listitem>&lit.http.header.AccessControlRequestHeaders;</listitem>
-                        <listitem>&lit.http.header.AccessControlRequestMethod;</listitem>
-                        <listitem>&lit.http.header.Connection; (with one exception - &lit.http.header.Connection; header with
-                            value <literal>Closed</literal> is allowed by default)</listitem>
-                        <listitem>&lit.http.header.ContentLength;</listitem>
-                        <listitem>&lit.http.header.ContentTransferEncoding;</listitem>
-                        <listitem>&lit.http.header.Host;</listitem>
-                        <listitem>&lit.http.header.Keep-Alive;</listitem>
-                        <listitem>&lit.http.header.Origin;</listitem>
-                        <listitem>&lit.http.header.Trailer;</listitem>
-                        <listitem>&lit.http.header.Transfer-Encoding;</listitem>
-                        <listitem>&lit.http.header.Upgrade;</listitem>
-                        <listitem>&lit.http.header.Via;</listitem>
-                        <listitem>all the headers starting with &lit.http.header.Sec.prefix;</listitem>
-                    </itemizedlist>
-                    The underlying connection can be configured to permit all headers to be sent,
-                    however this behaviour can be changed only by setting the system property
-                    <literal>sun.net.http.allowRestrictedHeaders</literal>.
-                    <example>
-                        <title>Sending restricted headers with <literal>HttpUrlConnector</literal></title>
-                        <programlisting language="java" linenumbering="numbered">
-                            Client client = ClientBuilder.newClient();
-                            System.setProperty("sun.net.http.allowRestrictedHeaders", "true");
-
-                            Response response = client.target(yourUri).path(yourPath).request().
-                            header("Origin", "http://example.com").
-                            header("Access-Control-Request-Method", "POST").
-                            get();
-                        </programlisting>
-                    </example>
-                </para>
-                <para>
-                    Note, that internally the &lit.jdk6.HttpUrlConnection; instances are pooled, so (un)setting the
-                    property after already creating a target typically does not have any effect.
-                    The property influences all the connections <emphasis>created</emphasis> after the property has been
-                    (un)set, but there is no guarantee, that your request will use a connection
-                    created after the property change.
-                </para>
-                <para>
-                    In a simple environment, setting the property before creating the first target is sufficient, but in complex
-                    environments (such as application servers), where some poolable connections might exist before your
-                    application even bootstraps, this approach is not 100% reliable and we recommend using a different client
-                    transport connector, such as Apache Connector.
-                    These limitations have to be considered especially when invoking <emphasis>CORS</emphasis> (Cross Origin
-                    Resource Sharing) requests.
-                </para>
-            </warning>
         </para>
         <para>
             As indicated earlier, &jersey.client.Connector; and &jersey.client.ConnectorProvider; contracts are Jersey-specific
@@ -761,8 +696,8 @@
             instance:
 
             <programlisting language="java" linenumbering="numbered">ClientConfig clientConfig = new ClientConfig();
-clientConfig.connectorProvider(new GrizzlyConnectorProvider());
-Client client = ClientBuilder.newClient(clientConfig);</programlisting>
+                clientConfig.connectorProvider(new GrizzlyConnectorProvider());
+                Client client = ClientBuilder.newClient(clientConfig);</programlisting>
 
             &lit.jaxrs.client.Client; accepts as a constructor argument a &lit.jaxrs.core.Configurable; instance. Jersey
             implementation of the &lit.jaxrs.core.Configurable; provider for the client is &lit.jersey.client.ClientConfig;.
@@ -781,11 +716,96 @@
             Jersey 2.40. The following example shows how to setup the custom Grizzly Asynchronous HTTP Client based
             &lit.jersey.client.ConnectorProvider; in a Jersey client instance:
             <programlisting language="java" linenumbering="numbered">Client client = ClientBuilder.newBuilder()
-    .property(ClientProperties.CONNECTOR_PROVIDER, "org.glassfish.jersey.grizzly.connector.GrizzlyConnectorProvider")
-    .build();</programlisting>
+                .property(ClientProperties.CONNECTOR_PROVIDER, "org.glassfish.jersey.grizzly.connector.GrizzlyConnectorProvider")
+                .build();</programlisting>
 
             For more information about the property see <xref linkend="appendix-properties"/>.
         </para>
+        <para>
+            <warning xml:id="connectors.warning" xreflabel="Header modification issue">
+                <para>
+                    Be aware of using other than default &lit.jersey.client.Connector; implementation.
+                    There is an issue handling HTTP headers in
+                    &lit.jaxrs.WriterInterceptor; or &lit.jaxrs.ext.MessageBodyWriter;.
+                    If you need to change header fields do not use neither
+                    &lit.jersey.apache.ApacheConnectorProvider; nor &lit.jersey.grizzly.GrizzlyConnectorProvider;
+                    nor &lit.jersey.jetty.JettyConnectorProvider; (for asynchronous requests).
+                    Other older version connectors can be affected by this issue as well.
+                    The issue for example applies to Jersey <xref linkend="multipart" endterm="multipart.short"/>
+                    feature that also modifies HTTP headers.
+                </para>
+            </warning>
+        </para>
+        <section>
+            <title>The default &lit.jersey.client.HttpUrlConnector;</title>
+
+            <para>The default connector is the most advanced connector, and it supports the most features Jersey has to offer.
+                However, there are a few limitations coming from the &lit.jdk6.HttpUrlConnection;</para>.
+            <para>
+                One limitation is in the variety of HTTP methods supported by the &lit.jdk6.HttpUrlConnection;, since only the
+                original HTTP/1.1 methods are supported. For instance, HTTP Patch method is not supported. See
+                property &jersey.client.HttpUrlConnectorProvider.SET_METHOD_WORKAROUND; in the Appendix
+                <xref linkend="appendix-properties-client-default"/> for a possible workaround.
+            </para>
+            <para>
+                Also, in the default transport connector, there are some restrictions on the headers, that
+                can be sent in the default configuration.
+                <literal>HttpUrlConnectorProvider</literal> uses &lit.jdk6.HttpUrlConnection; as an underlying connection
+                implementation. This JDK class by default restricts the use of following headers:
+                <itemizedlist>
+                    <listitem>&lit.http.header.AccessControlRequestHeaders;</listitem>
+                    <listitem>&lit.http.header.AccessControlRequestMethod;</listitem>
+                    <listitem>&lit.http.header.Connection; (with one exception - &lit.http.header.Connection; header with
+                        value <literal>Closed</literal> is allowed by default)</listitem>
+                    <listitem>&lit.http.header.ContentLength;</listitem>
+                    <listitem>&lit.http.header.ContentTransferEncoding;</listitem>
+                    <listitem>&lit.http.header.Host;</listitem>
+                    <listitem>&lit.http.header.Keep-Alive;</listitem>
+                    <listitem>&lit.http.header.Origin;</listitem>
+                    <listitem>&lit.http.header.Trailer;</listitem>
+                    <listitem>&lit.http.header.Transfer-Encoding;</listitem>
+                    <listitem>&lit.http.header.Upgrade;</listitem>
+                    <listitem>&lit.http.header.Via;</listitem>
+                    <listitem>all the headers starting with &lit.http.header.Sec.prefix;</listitem>
+                </itemizedlist>
+                The underlying connection can be configured to permit all headers to be sent,
+                however this behaviour can be changed only by setting the system property
+                <literal>sun.net.http.allowRestrictedHeaders</literal>.
+                <example>
+                    <title>Sending restricted headers with <literal>HttpUrlConnector</literal></title>
+                    <programlisting language="java" linenumbering="numbered">
+                        Client client = ClientBuilder.newClient();
+                        System.setProperty("sun.net.http.allowRestrictedHeaders", "true");
+
+                        Response response = client.target(yourUri).path(yourPath).request().
+                        header("Origin", "http://example.com").
+                        header("Access-Control-Request-Method", "POST").
+                        get();
+                    </programlisting>
+                </example>
+                <warning>
+                    <para>
+                        Internally, the &lit.jdk6.HttpUrlConnection; instances are pooled, so (un)setting the
+                        property after already creating a target typically does not have any effect.
+                        The property influences all the connections <emphasis>created</emphasis> after the property has been
+                        (un)set, but there is no guarantee, that your request will use a connection
+                        created after the property change.
+                    </para>
+                    <para>
+                        In a simple environment, setting the property before creating the first target is sufficient, but in complex
+                        environments (such as application servers), where some poolable connections might exist before your
+                        application even bootstraps, this approach is not 100% reliable and we recommend using a different client
+                        transport connector, such as Apache Connector.
+                        These limitations have to be considered especially when invoking <emphasis>CORS</emphasis> (Cross Origin
+                        Resource Sharing) requests.
+                    </para>
+                </warning>
+            </para>
+            <para>
+                The limited configurability of the &lit.jdk6.HttpUrlConnection; is another aspect to consider. For details, see
+                <literal>Java Networking Properties</literal>, for instance property <literal>http.maxConnections</literal>.
+            </para>
+        </section>
         <section>
             <title>Client Connectors Properties</title>
             <para>
@@ -956,7 +976,7 @@
         <para>
             To solve injection of a custom type into a client provider instance
             use &jersey.client.InjectionManagerClientProvider; to
-            extract &hk2.ServiceLocator; which can return the required injection. The following example shows how to utilize
+            extract &lit.jersey.common.internal.inject.InjectionManager; which can return the required injection. The following example shows how to utilize
             &lit.jersey.client.InjectionManagerClientProvider;:
         </para>
         <example>
@@ -981,7 +1001,7 @@
         </example>
         <para>
             For more information see javadoc of &jersey.client.InjectionManagerClientProvider;
-            (and javadoc of &lit.jersey.common.InjectionManagerProvider; which supports common JAX-RS components).
+            (and javadoc of &lit.jersey.common.InjectionManagerProvider; which supports common Jakarta REST components).
         </para>
     </section>
 
@@ -1142,6 +1162,19 @@
                 Note that only <literal>Apache Connector, JDK Connector, Netty connector</literal>, and the default
                 <literal>HttpUrlConnector</literal> do support this feature.
             </para>
+            <para>
+                Sometimes, it may be required the <literal>SNIHostName</literal> is not set, or it differs from the
+                HTTP <literal>Host</literal> header. In that case, the &jersey.client.ClientProperties.SNI_HOST_NAME;
+                property can be utilized. The property sets the host name to be used for calculating the
+                <literal>javax.net.ssl.SNIHostName</literal>. The property takes precedence over the HTTP
+                <literal>Host</literal> header.
+            </para>
+            <para>
+                When the host name in the the &jersey.client.ClientProperties.SNI_HOST_NAME; property matches the HTTP request
+                host, the <literal>SNIHostName</literal> is not set, and the HTTP <literal>Host</literal> header is not used
+                for setting the <literal>SNIHostName</literal>.
+                Turning the SNI off allows for Domain Fronting.
+            </para>
         </section>
 
     </section>
diff --git a/docs/src/main/docbook/custom-di.xml b/docs/src/main/docbook/custom-di.xml
index 6e83b52..407cc2e 100644
--- a/docs/src/main/docbook/custom-di.xml
+++ b/docs/src/main/docbook/custom-di.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
 
-    Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2013, 2024 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
@@ -60,6 +60,35 @@
     </para>
 
     <para>
+        Since Jersey 2.26, the injection has been abstracted, so that &hk2.link; can be eventually replaced by the CDI or
+        any other injection framework. In the next chapters, we document possibilities provided directly by &hk2.link; and
+        by Jersey abstraction components.
+    </para>
+
+    <section xml:id="injection.manager">
+        <title>InjectionManager</title>
+
+        <para>
+            Since Jersey 2.26, Jersey comes with the main abstraction interface to communicate with the DI container, the
+            &jersey.common.internal.inject.InjectionManager;. What is <literal>ServiceLocator</literal> for &hk2.link;, or
+            <literal>BeanManager</literal> for CDI, that's &jersey.common.internal.inject.InjectionManager; for Jersey.
+        </para>
+        <para>
+            &jersey.common.internal.inject.InjectionManager; can be injected into the user provided classes instantiated
+            by Jersey. It can also be obtained programmatically by &jersey.client.InjectionManagerClientProvider;
+            and &lit.jersey.common.InjectionManagerProvider; from Jakarta REST components, such as
+            &jaxrs.core.FeatureContext;, or &jaxrs.ext.MessageBodyReader; and &jaxrs.ext.MessageBodyReader;.
+        </para>
+        <para>
+            Customers used to the &hk2.ServiceLocator; can still use it directly; the &hk2.ServiceLocator; can be obtained
+            either directly by injection, or programmatically as <literal>InjectionManager.getInstance(ServiceLocator.class)</literal>.
+        </para>
+    </section>
+
+    <section>
+        <title>Implementing Custom Injection Provider</title>
+
+      <para>
         Relying on Servlet HTTP session concept is not very RESTful. It turns the originally state-less HTTP
         communication schema into a state-full manner. However, it could serve
         as a good example that will help me demonstrate implementation of the use cases described above.
@@ -68,10 +97,7 @@
         Below we will show how to make actual Servlet &jee9.servlet.HttpSession; injectable into JAX-RS components
         and how to make this injection work with a custom inject annotation type. Finally, we will demonstrate
         how you can write &lit.jee9.servlet.HttpSession;-scoped JAX-RS resources.
-    </para>
-
-    <section>
-        <title>Implementing Custom Injection Provider</title>
+      </para>
 
         <para>
             Jersey implementation allows you to directly inject &jee9.servlet.HttpServletRequest; instance into
@@ -89,7 +115,11 @@
     ...
 
 }</programlisting>
+        </para>
+        <section>
+            <title>Using HK2 classes</title>
 
+            <para>
             To make the above injection work, you will need to define an additional HK2 binding in your
             application &jersey.server.ResourceConfig;.
             Let's start with a custom HK2 &hk2.Factory; implementation that knows how to extract
@@ -145,48 +175,122 @@
             @Override
             protected void configure() {
                 bindFactory(HttpSessionFactory.class).to(HttpSession.class)
-                .proxy(true).proxyForSameScope(false).in(RequestScoped.class);
+                   .proxy(true).proxyForSameScope(false).in(RequestScoped.class);
             }
         });
     }
 }</programlisting>
 
-            Note that we did not define any explicit injection scope for the new injection binding.
-            By default, HK2 factories are bound in a HK2 &hk2.PerLookup; scope, which is in most
-            cases a good choice and it is suitable also in our example.
-        </para>
+                Note that if we did not define any explicit injection scope for the new injection binding,
+                By default, HK2 factories are bound in a HK2 &hk2.PerLookup; scope, which is in most
+                cases a good choice, and it is suitable also in our example.
+            </para>
 
-        <para>
-            To summarize the approach described above, here is a list of steps to follow
-            when implementing custom injection provider in your Jersey application :
+            <para>
+                To summarize the approach described above, here is a list of steps to follow
+                when implementing custom injection provider in your Jersey application :
 
-            <itemizedlist>
-                <listitem><simpara>Implement your own HK2 &lit.hk2.Factory; to provide the
-                    injectable instances.</simpara></listitem>
-                <listitem><simpara>Use the HK2 &lit.hk2.Factory; to define an injection
-                    binding for the injected instance via custom HK2 &lit.hk2.Binder;.</simpara></listitem>
-                <listitem><simpara>Register the custom HK2 &lit.hk2.Binder; in your application
-                    &lit.jersey.server.ResourceConfig;.</simpara></listitem>
-            </itemizedlist>
-        </para>
+                <itemizedlist>
+                    <listitem><simpara>Implement your own HK2 &lit.hk2.Factory; to provide the
+                        injectable instances.</simpara></listitem>
+                    <listitem><simpara>Use the HK2 &lit.hk2.Factory; to define an injection
+                        binding for the injected instance via custom HK2 &lit.hk2.Binder;.</simpara></listitem>
+                    <listitem><simpara>Register the custom HK2 &lit.hk2.Binder; in your application
+                        &lit.jersey.server.ResourceConfig;.</simpara></listitem>
+                </itemizedlist>
+            </para>
 
-        <para>
-            While the &lit.hk2.Factory;-based approach is quite straight-forward and should help you to
-            quickly prototype or even implement final solutions, you should bear in mind, that your
-            implementation does not need to be based on factories. You can for instance bind your own
-            types directly, while still taking advantage of HK2 provided dependency injection.
-            Also, in your implementation you may want to pay more attention to defining or managing
-            injection binding scopes for the sake of performance or correctness of your custom injection
-            extension.
+            <para>
+                While the &lit.hk2.Factory;-based approach is quite straight-forward and should help you to
+                quickly prototype or even implement final solutions, you should bear in mind, that your
+                implementation does not need to be based on factories. You can for instance bind your own
+                types directly, while still taking advantage of HK2 provided dependency injection.
+                Also, in your implementation you may want to pay more attention to defining or managing
+                injection binding scopes for the sake of performance or correctness of your custom injection
+                extension.
 
-            <important>
-                <para>
-                    While the individual injection binding implementations vary and depend on your use case,
-                    to enable your custom injection extension in Jersey, you must register your custom HK2 &hk2.Binder;
-                    implementation in your application &jersey.server.ResourceConfig;!
-                </para>
-            </important>
-        </para>
+                <important>
+                    <para>
+                        While the individual injection binding implementations vary and depend on your use case,
+                        to enable your custom injection extension in Jersey, you must register your custom HK2 &hk2.Binder;
+                        implementation in your application &jersey.server.ResourceConfig;!
+                    </para>
+                </important>
+            </para>
+        </section>
+        <section>
+            <title>Injection Provider Using Jersey API</title>
+            <para>
+                To make the <literal>HttpSession</literal> injection work without using HK2 API,
+                we will need to create a custom supplier that knows how to extract
+                &lit.jee9.servlet.HttpSession; out of given &lit.jee9.servlet.HttpServletRequest;.
+
+                <programlisting language="java">import java.util.function.Supplier
+    ...
+
+public class HttpSessionSupplier implements Supplier&lt;HttpSession&gt; {
+
+    private final HttpServletRequest request;
+
+    @Inject
+    public HttpSessionSupplier(HttpServletRequest request) {
+        this.request = request;
+    }
+
+    @Override
+    public HttpSession get() {
+        return request.getSession();
+    }
+
+}</programlisting>
+
+                Once implemented, the supplier can be used in a custom Jersey &jersey.common.internal.inject.AbstractBinder;
+                to define the new injection binding for &lit.jee9.servlet.HttpSession;. Finally, the implemented binder
+                can be registered in your &jersey.server.ResourceConfig;:
+
+                <programlisting language="java">import org.glassfish.jersey.internal.inject.AbstractBinder;
+    ...
+public class MyApplication extends ResourceConfig {
+
+    public MyApplication() {
+
+        ...
+
+        register(new AbstractBinder() {
+            @Override
+            protected void configure() {
+                bindFactory(HttpSessionSupplier.class).to(HttpSession.class)
+                    .proxy(true).proxyForSameScope(false).in(RequestScoped.class);
+            }
+        });
+    }
+}</programlisting>
+                   The default scope for Jersey binder is similarly as for the HK2, the
+                &jersey.common.internal.inject.PerLookup;.
+            </para>
+            <para>
+                To summarize the approach described above, here is a list of steps to follow
+                when implementing custom injection provider in your Jersey application :
+
+                <itemizedlist>
+                    <listitem><simpara>Implement your own Supplier to provide the
+                        injectable instances.</simpara></listitem>
+                    <listitem><simpara>Use the Supplier to define an injection
+                        binding for the injected instance via custom &jersey.common.internal.inject.AbstractBinder;.
+                    </simpara></listitem>
+                    <listitem><simpara>Register the custom &jersey.common.internal.inject.AbstractBinder; in your application
+                        &lit.jersey.server.ResourceConfig;.</simpara></listitem>
+                </itemizedlist>
+
+                <important>
+                    <para>
+                        Similarly to the HK2, to enable your custom injection extension in Jersey,
+                        you must register your custom &jersey.common.internal.inject.AbstractBinder;
+                        implementation in your application &jersey.server.ResourceConfig;!
+                    </para>
+                </important>
+            </para>
+        </section>
     </section>
 
     <section>
@@ -231,6 +335,8 @@
             use case will help us to avoid use case specific distractions and allow us better focus on
             the important aspects of the job of defining a custom injection annotation.
         </para>
+        <section>
+            <title>Custom Injection Annotation using HK2</title>
 
         <para>
             If you remember from the previous section, to make the injection in the code snippet above work,
@@ -317,6 +423,104 @@
     }
 }</programlisting>
         </para>
+        </section>
+        <section>
+            <title>Custom Injection Annotation using Jersey InjectionResolver</title>
+
+            <para>
+                Jersey also comes with its &jersey.common.internal.inject.InjectionResolver; used to translate into
+                the HK2 &hk2.InjectionResolver; during runtime. The abstraction is important for allowing to support
+                the custom injection annotation in various DI containers. For instance, the abstraction is used when
+                supporting injection using &jaxrs.core.Context; in the CDI container (<literal>jersey-cdi2-se</literal> module).
+            </para>
+            <para>
+                The SessionInjectResolver then looks as follows:
+<programlisting language="java">import jakarta.inject.Inject;
+
+import jakarta.servlet.http.HttpSession;
+
+import org.glassfish.jersey.internal.inject.InjectionResolver;
+
+...
+
+public class SessionInjectResolver implements InjectionResolver&lt;SessionInject&gt; {
+
+    private final InjectionManger injectionManager;
+
+    public SessionInjectResolver(InjectionManager) {
+        this.injectionManager = injectionManager;
+    }
+
+    @Override
+    public Object resolve(Injectee injectee) {
+        if (HttpSession.class == injectee.getRequiredType()) {
+            return injectionManager.getInstance(HttpServletRequest.class).getSession();
+        }
+        return null;
+    }
+
+    @Override
+    public boolean isConstructorParameterIndicator() {
+        return false;
+    }
+
+    @Override
+    public boolean isMethodParameterIndicator() {
+        return false;
+    }
+
+    @Override
+    public Class&lt;SessionInject&gt; getAnnotation() {
+        return SessionInject.class;
+    }
+}</programlisting>
+
+                The SessionInjectResolver uses &jersey.common.internal.inject.InjectionManager; described in
+                <xref linkend="injection.manager"/>.
+            </para>
+            <para>
+                Unlike with &hk2.link;, Jersey &jersey.common.internal.inject.InjectionResolver; can only be bound
+                as instance in the &jersey.common.internal.inject.AbstractBinder;. That is why the
+                &jersey.common.internal.inject.InjectionManager; is used in the <literal>InjectionResolver</literal>
+                to resolve the <literal>HttpSession</literal> instance.
+            </para>
+            <para>
+                The &jersey.common.internal.inject.InjectionResolver; can be registered in the with Jersey application
+                &lit.jersey.server.ResourceConfig; as follows:
+
+<programlisting language="java">import jakarta.ws.rs.core.Feature;
+
+import org.glassfish.jersey.InjectionManagerProvider;
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+
+
+import jakarta.inject.Singleton;
+
+...
+
+public class MyApplication extends ResourceConfig {
+
+public MyApplication() {
+
+    ...
+
+    register(new Feature() {
+        @Override
+        public boolean configure(FeatureContext context) {
+            final InjectionManager injectionManager = InjectionManagerProvider.getInjectionManager(context);
+            context.register(new AbstractBinder() {
+                @Override
+                protected void configure() {
+                    bind(new SessionInjectResolver(injectionManager)).to(HttpSession.class).in(Singleton.class);
+                }
+            });
+            return true;
+        }
+    });
+}</programlisting>
+            </para>
+        </section>
     </section>
 
     <section>
diff --git a/docs/src/main/docbook/dependencies.xml b/docs/src/main/docbook/dependencies.xml
index 176fe08..8037dc9 100644
--- a/docs/src/main/docbook/dependencies.xml
+++ b/docs/src/main/docbook/dependencies.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
 
-    Copyright (c) 2010, 2023 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2010, 2024 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
@@ -33,18 +33,10 @@
         <title>Java SE Compatibility</title>
 
         <para>
-            <emphasis>3.x branch: </emphasis>
+            <emphasis>3.1 branch: </emphasis>
             <itemizedlist>
                 <listitem>
-                    <para>This user guide refers only to version 3 and above of Jersey, its compatibility is described below</para>
-                </listitem>
-                <listitem>
-                    <para>Since version 3.0.0 all Jersey components are compiled with Java SE 1.8 target.
-                    It means, that you will need at least Java SE 1.8 to be able to compile and run your application
-                        which uses the latest Jersey 3.0.x.
-                    Some modules, however, are fully compatible with JDK 11 and above (Jetty modules based on Jetty 11).
-                    Some modules (Helidon Connector, Spring 6) require JDK 17.
-                    </para>
+                    <para>This user guide refers only to version 3.1.x of Jersey, its compatibility is described below.</para>
                 </listitem>
                 <listitem>
                     <para>Since version 3.1.0 all Jersey components are compiled with Java SE 11 target.
@@ -60,7 +52,47 @@
                     </para>
                 </listitem>
             </itemizedlist>
+            <table pgwide="1" frame='all' xml:id="jersey-jdk-compatibility">
+                <title>Jersey 3.1.x JDK compatibility</title>
+                <tgroup cols='3' align='center' colsep='1' rowsep='1'>
+                    <colspec colname='c1'/>
+                    <colspec colname='c2'/>
+                    <colspec colname='c3'/>
+                    <thead>
+                        <row>
+                            <entry>Jersey version</entry>
+                            <entry>JDK min version</entry>
+                            <entry>JDK max version</entry>
+                        </row>
+                    </thead>
+                    <tbody>
+                        <row><entry>3.1.0</entry><entry>11</entry><entry>19</entry></row>
+                        <row><entry>3.1.1</entry><entry>11</entry><entry>19</entry></row>
+                        <row><entry>3.1.2</entry><entry>11</entry><entry>21</entry></row>
+                        <row><entry>3.1.3</entry><entry>11</entry><entry>21</entry></row>
+                        <row><entry>3.1.4</entry><entry>11</entry><entry>22</entry></row>
+                        <row><entry>3.1.5</entry><entry>11</entry><entry>22</entry></row>
+                        <row><entry>3.1.6</entry><entry>11</entry><entry>23</entry></row>
+                        <row><entry>3.1.7</entry><entry>11</entry><entry>23</entry></row>
+                    </tbody>
+                </tgroup>
+            </table>
         </para>
+        <section>
+            <title>Virtual Threads and Thread Factories</title>
+            <para>
+                With JDK 21 and above, Jersey (since 2.44) has the ability to use virtual threads instead of
+                the <literal>CachedThreadPool</literal> in the internal <literal>ExecutorServices</literal>.
+                Jersey also has the ability to specify the backing <literal>ThreadFactory</literal> for the
+                default <literal>ExecutorServices</literal> (the default <literal>ExecutorServices</literal>
+                can be overridden by the &jersey.common.spi.ExecutorServiceProvider; SPI).
+            </para>
+            <para>
+                To enable virtual threads and/or specify the <literal>ThreadFactory</literal>, use
+                &jersey.common.CommonProperties.USE_VIRTUAL_THREADS; and/or &jersey.common.CommonProperties.THREAD_FACTORY;
+                properties, respectively. See also the <xref linkend="appendix-properties-common"/> in appendix for property details.
+            </para>
+        </section>
     </section>
     <section>
         <title>Introduction to Jersey dependencies</title>
diff --git a/docs/src/main/docbook/jersey.ent b/docs/src/main/docbook/jersey.ent
index be67534..5fe6f8a 100644
--- a/docs/src/main/docbook/jersey.ent
+++ b/docs/src/main/docbook/jersey.ent
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="iso-8859-1" ?>
 <!--
 
-    Copyright (c) 2010, 2023 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2010, 2024 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
@@ -54,7 +54,7 @@
 <!ENTITY heroku.uri "https://www.heroku.com">
 <!ENTITY heroku.link "<link xlink:href='&heroku.uri;'>Heroku</link>">
 <!ENTITY hk2.javadoc.uri "https://javaee.github.io/hk2/apidocs/">
-<!ENTITY hk2.link "<link xlink:href='https://github.com/eclipse-ee4j/glassfish-hk2'>Glassfish-HK2</link>">
+<!ENTITY hk2.link "<link xlink:href='https://eclipse-ee4j.github.io/glassfish-hk2/'>Glassfish-HK2</link>">
 <!ENTITY hk2.spring-bridge.link "<link xlink:href='https://javaee.github.io/hk2/spring-bridge/'>The Spring/HK2 Bridge</link>">
 <!ENTITY jaxb.release.uri "https://eclipse-ee4j.github.io/jaxb-ri">
 <!ENTITY jaxb.javadoc.uri "&jaxb.release.uri;/docs/api/jakarta.xml.bind">
@@ -62,7 +62,7 @@
 <!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 jsonb.javadoc.uri "https://jakarta.ee/specifications/jsonb/2.0/apidocs/jakarta/json/bind">
 <!ENTITY jersey.documentation.uri "https://eclipse-ee4j.github.io/jersey.github.io">
 
 <!ENTITY jersey.ext.bean-validation.deps.link "<link xlink:href='&jersey.project-info.uri.prefix;/jersey-bean-validation/dependencies.html'>jersey-bean-validation</link>" >
@@ -220,6 +220,7 @@
 <!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.FeatureContext "<link xlink:href='&jaxrs.javadoc.uri;/core/FeatureContext.html'>FeatureContext</link>">
 <!ENTITY jaxrs.core.Form "<link xlink:href='&jaxrs.javadoc.uri;/core/Form.html'>Form</link>">
 <!ENTITY jaxrs.core.GenericEntity "<link xlink:href='&jaxrs.javadoc.uri;/core/GenericEntity.html'>GenericEntity&lt;T&gt;</link>">
 <!ENTITY jaxrs.core.GenericType "<link xlink:href='&jaxrs.javadoc.uri;/core/GenericType.html'>GenericType&lt;T&gt;</link>">
@@ -350,6 +351,7 @@
 <!ENTITY jersey.client.ClientProperties.DEFAULT_CHUNK_SIZE "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#DEFAULT_CHUNK_SIZE'>ClientProperties.DEFAULT_CHUNK_SIZE</link>" >
 <!ENTITY jersey.client.ClientProperties.FEATURE_AUTO_DISCOVERY_DISABLE "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#FEATURE_AUTO_DISCOVERY_DISABLE'>ClientProperties.FEATURE_AUTO_DISCOVERY_DISABLE</link>" >
 <!ENTITY jersey.client.ClientProperties.FOLLOW_REDIRECTS "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#FOLLOW_REDIRECTS'>ClientProperties.FOLLOW_REDIRECTS</link>" >
+<!ENTITY jersey.client.ClientProperties.JSON_BINDING_FEATURE_DISABLE "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#JSON_BINDING_FEATURE_DISABLE'>ClientProperties.JSON_BINDING_FEATURE_DISABLE</link>" >
 <!ENTITY jersey.client.ClientProperties.JSON_PROCESSING_FEATURE_DISABLE "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#JSON_PROCESSING_FEATURE_DISABLE'>ClientProperties.JSON_PROCESSING_FEATURE_DISABLE</link>" >
 <!ENTITY jersey.client.ClientProperties.METAINF_SERVICES_LOOKUP_DISABLE "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#METAINF_SERVICES_LOOKUP_DISABLE'>ClientProperties.METAINF_SERVICES_LOOKUP_DISABLE</link>" >
 <!ENTITY jersey.client.ClientProperties.MOXY_JSON_FEATURE_DISABLE "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#MOXY_JSON_FEATURE_DISABLE'>ClientProperties.MOXY_JSON_FEATURE_DISABLE</link>" >
@@ -359,6 +361,7 @@
 <!ENTITY jersey.client.ClientProperties.PROXY_USERNAME "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#PROXY_USERNAME'>ClientProperties.PROXY_USERNAME</link>" >
 <!ENTITY jersey.client.ClientProperties.READ_TIMEOUT "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#READ_TIMEOUT'>ClientProperties.READ_TIMEOUT</link>" >
 <!ENTITY jersey.client.ClientProperties.REQUEST_ENTITY_PROCESSING "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#REQUEST_ENTITY_PROCESSING'>ClientProperties.REQUEST_ENTITY_PROCESSING</link>" >
+<!ENTITY jersey.client.ClientProperties.SNI_HOST_NAME "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#SNI_HOST_NAME'>ClientProperties.SNI_HOST_NAME</link>" >
 <!ENTITY jersey.client.ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#SUPPRESS_HTTP_COMPLIANCE_VALIDATION'>ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION</link>" >
 <!ENTITY jersey.client.ClientProperties.SSL_CONTEXT_SUPPLIER "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#SSL_CONTEXT_SUPPLIER'>ClientProperties.SSL_CONTEXT_SUPPLIER</link>" >
 <!ENTITY jersey.client.ClientProperties.USE_ENCODING "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#USE_ENCODING'>ClientProperties.USE_ENCODING</link>" >
@@ -394,6 +397,10 @@
 <!ENTITY jersey.common.CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#FEATURE_AUTO_DISCOVERY_DISABLE'>CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE</link>" >
 <!ENTITY jersey.common.CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE_CLIENT "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#FEATURE_AUTO_DISCOVERY_DISABLE_CLIENT'>CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE_CLIENT</link>" >
 <!ENTITY jersey.common.CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE_SERVER "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#FEATURE_AUTO_DISCOVERY_DISABLE_SERVER'>CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE_SERVER</link>" >
+<!ENTITY jersey.common.CommonProperties.JSON_BINDING_FEATURE_DISABLE "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#JSON_BINDING_FEATURE_DISABLE'>CommonProperties.JSON_BINDING_FEATURE_DISABLE</link>" >
+<!ENTITY jersey.common.CommonProperties.JSON_BINDING_FEATURE_DISABLE_CLIENT "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#JSON_BINDING_FEATURE_DISABLE_CLIENT'>CommonProperties.JSON_BINDING_FEATURE_DISABLE_CLIENT</link>" >
+<!ENTITY jersey.common.CommonProperties.JSON_BINDING_FEATURE_DISABLE_SERVER "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#JSON_BINDING_FEATURE_DISABLE_SERVER'>CommonProperties.JSON_BINDING_FEATURE_DISABLE_SERVER</link>" >
+<!ENTITY jersey.common.CommonProperties.JSON_BINDING_FEATURE_DISABLE_APPLICATION "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#JSON_BINDING_FEATURE_DISABLE_APPLICATION'>CommonProperties.JSON_BINDING_FEATURE_DISABLE_APPLICATION</link>" >
 <!ENTITY jersey.common.CommonProperties.JSON_PROCESSING_FEATURE_DISABLE "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#JSON_PROCESSING_FEATURE_DISABLE'>CommonProperties.JSON_PROCESSING_FEATURE_DISABLE</link>" >
 <!ENTITY jersey.common.CommonProperties.JSON_PROCESSING_FEATURE_DISABLE_CLIENT "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#JSON_PROCESSING_FEATURE_DISABLE_CLIENT'>CommonProperties.JSON_PROCESSING_FEATURE_DISABLE_CLIENT</link>" >
 <!ENTITY jersey.common.CommonProperties.JSON_PROCESSING_FEATURE_DISABLE_SERVER "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#JSON_PROCESSING_FEATURE_DISABLE_SERVER'>CommonProperties.JSON_PROCESSING_FEATURE_DISABLE_SERVER</link>" >
@@ -414,9 +421,13 @@
 <!ENTITY jersey.common.CommonProperties.JSON_JACKSON_DISABLED_MODULES_CLIENT "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#JSON_JACKSON_DISABLED_MODULES'>CommonProperties.JSON_JACKSON_DISABLED_MODULES_CLIENT</link>" >
 <!ENTITY jersey.common.CommonProperties.JSON_JACKSON_DISABLED_MODULES_SERVER "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#JSON_JACKSON_DISABLED_MODULES'>CommonProperties.JSON_JACKSON_DISABLED_MODULES_SERVER</link>" >
 <!ENTITY jersey.common.CommonProperties.PARAM_CONVERTERS_THROW_IAE "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#PARAM_CONVERTERS_THROW_IAE'>CommonProperties.PARAM_CONVERTERS_THROW_IAE</link>" >
+<!ENTITY jersey.common.CommonProperties.THREAD_FACTORY "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#THREAD_FACTORY'>CommonProperties.THREAD_FACTORY</link>" >
+<!ENTITY jersey.common.CommonProperties.USE_VIRTUAL_THREADS "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#USE_VIRTUAL_THREADS'>CommonProperties.USE_VIRTUAL_THREADS</link>" >
 <!ENTITY jersey.common.internal.inject.DisposableSupplier "<link xlink:href='&jersey.javadoc.uri.prefix;/internal/inject/DisposableSupplier.html'>DisposableSupplier</link>">
 <!ENTITY jersey.common.internal.inject.InjectionManager "<link xlink:href='&jersey.javadoc.uri.prefix;/internal/inject/InjectionManager.html'>InjectionManager</link>">
+<!ENTITY jersey.common.internal.inject.InjectionResolver "<link xlink:href='&jersey.javadoc.uri.prefix;/internal/inject/InjectionResolver.html'>InjectionResolver</link>">
 <!ENTITY jersey.common.internal.inject.AbstractBinder "<link xlink:href='&jersey.javadoc.uri.prefix;/internal/inject/AbstractBinder.html'>AbstractBinder</link>">
+<!ENTITY jersey.common.internal.inject.PerLookup "<link xlink:href='&jersey.javadoc.uri.prefix;/internal/inject/PerLookup.html'>PerLookup</link>">
 <!ENTITY jersey.common.internal.inject.Binder "<link xlink:href='&jersey.javadoc.uri.prefix;/internal/inject/Binder.html'>Binder</link>">
 <!ENTITY jersey.common.InjectionManagerProvider "<link xlink:href='&jersey.javadoc.uri.prefix;/InjectionManagerProvider.html'>InjectionManagerProvider</link>">
 <!ENTITY jersey.common.SslConfigurator "<link xlink:href='&jersey.javadoc.uri.prefix;/SslConfigurator.html'>SslConfigurator</link>">
@@ -553,7 +564,12 @@
 <!ENTITY jersey.media.multipart.FormDataParam "<link xlink:href='&jersey.javadoc.uri.prefix;/media/multipart/FormDataParam.html'>@FormDataParam</link>" >
 <!ENTITY jersey.media.multipart.MultiPart "<link xlink:href='&jersey.javadoc.uri.prefix;/media/multipart/MultiPart.html'>MultiPart</link>" >
 <!ENTITY jersey.media.multipart.MultiPartFeature "<link xlink:href='&jersey.javadoc.uri.prefix;/media/multipart/MultiPartFeature.html'>MultiPartFeature</link>" >
-<!ENTITY jersey.media.multipart.StreamDataBodyPart "<link xlink:href='&jersey.javadoc.uri.prefix;/media/multipart/file/StreamDataBodyPart.html'>StreamDataBodyPart</link>" >
+<!ENTITY jersey.media.multipart.MultiPartProperties "<link xlink:href='&jersey.javadoc.uri.prefix;/media/multipart/MultiPartProperties.html'>MultiPartProperties</link>" >
+<!ENTITY jersey.media.multipart.MultiPartProperties.BUFFER_THRESHOLD "<link xlink:href='&jersey.javadoc.uri.prefix;/media/multipart/MultiPartProperties.html#BUFFER_THRESHOLD'>MultiPartProperties.BUFFER_THRESHOLD</link>">
+<!ENTITY jersey.media.multipart.MultiPartProperties.MAX_PARTS "<link xlink:href='&jersey.javadoc.uri.prefix;/media/multipart/MultiPartProperties.html#MAX_PARTS'>MultiPartProperties.MAX_PARTS</link>">
+<!ENTITY jersey.media.multipart.MultiPartProperties.MULTI_PART_CONFIG_RESOURCE "<link xlink:href='&jersey.javadoc.uri.prefix;/media/multipart/MultiPartProperties.html#MULTI_PART_CONFIG_RESOURCE'>MultiPartProperties.MULTI_PART_CONFIG_RESOURCE</link>">
+<!ENTITY jersey.media.multipart.MultiPartProperties.TEMP_DIRECTORY "<link xlink:href='&jersey.javadoc.uri.prefix;/media/multipart/MultiPartProperties.html#TEMP_DIRECTORY'>MultiPartProperties.TEMP_DIRECTORY</link>">
+<!ENTITY jersey.media.multipart.StreamDataBodyPart "<link xlink:href='&jersey.javadoc.uri.prefix;/media/multipart/file/StreamDataBodyPart.html'>StreamDataBodyPart</link>">
 <!ENTITY jersey.message.MessageBodyWorkers "<link xlink:href='&jersey.javadoc.uri.prefix;/message/MessageBodyWorkers.html'>MessageBodyWorkers</link>">
 <!ENTITY jersey.message.MessageProperties "<link xlink:href='&jersey.javadoc.uri.prefix;/message/MessageProperties.html'>MessageProperties</link>">
 <!ENTITY jersey.message.MessageProperties.DEFLATE_WITHOUT_ZLIB "<link xlink:href='&jersey.javadoc.uri.prefix;/message/MessageProperties.html#DEFLATE_WITHOUT_ZLIB'>MessageProperties.DEFLATE_WITHOUT_ZLIB</link>">
@@ -583,6 +599,9 @@
 <!ENTITY jersey.netty.NettyClientProperties.MAX_REDIRECTS "<link xlink:href='&jersey.javadoc.uri.prefix;/netty/connector/NettyClientProperties.html#MAX_REDIRECTS'>NettyClientProperties.MAX_REDIRECTS</link>" >
 <!ENTITY jersey.netty.NettyClientProperties.PRESERVE_METHOD_ON_REDIRECT "<link xlink:href='&jersey.javadoc.uri.prefix;/netty/connector/NettyClientProperties.html#PRESERVE_METHOD_ON_REDIRECT'>NettyClientProperties.PRESERVE_METHOD_ON_REDIRECT</link>" >
 <!ENTITY jersey.netty.NettyClientProperties.EXPECT_100_CONTINUE_TIMEOUT "<link xlink:href='&jersey.javadoc.uri.prefix;/netty/connector/NettyClientProperties.html#EXPECT_100_CONTINUE_TIMEOUT'>NettyClientProperties.EXPECT_100_CONTINUE_TIMEOUT</link>" >
+<!ENTITY jersey.netty.NettyClientProperties.MAX_HEADER_SIZE "<link xlink:href='&jersey.javadoc.uri.prefix;/netty/connector/NettyClientProperties.html#MAX_HEADER_SIZE'>NettyClientProperties.MAX_HEADER_SIZE</link>" >
+<!ENTITY jersey.netty.NettyClientProperties.MAX_INITIAL_LINE_LENGTH "<link xlink:href='&jersey.javadoc.uri.prefix;/netty/connector/NettyClientProperties.html#MAX_INITIAL_LINE_LENGTH'>NettyClientProperties.MAX_INITIAL_LINE_LENGTH</link>" >
+<!ENTITY jersey.netty.NettyClientProperties.MAX_CHUNK_SIZE "<link xlink:href='&jersey.javadoc.uri.prefix;/netty/connector/NettyClientProperties.html#MAX_CHUNK_SIZE'>NettyClientProperties.MAX_CHUNK_SIZE</link>" >
 <!ENTITY jersey.netty.NettyConnectorProvider "<link xlink:href='&jersey.javadoc.uri.prefix;/netty/connector/NettyConnectorProvider.html'>NettyConnectorProvider</link>">
 <!ENTITY jersey.server.ApplicationHandler "<link xlink:href='&jersey.javadoc.uri.prefix;/server/ApplicationHandler.html'>ApplicationHandler</link>">
 <!ENTITY jersey.server.BackgroundScheduler "<link xlink:href='&jersey.javadoc.uri.prefix;/server/BackgroundScheduler.html'>@BackgroundScheduler</link>">
@@ -612,6 +631,7 @@
 <!ENTITY jersey.server.ServerProperties.BV_SEND_ERROR_IN_RESPONSE "<link xlink:href='&jersey.javadoc.uri.prefix;/server/ServerProperties.html#BV_SEND_ERROR_IN_RESPONSE'>ServerProperties.BV_SEND_ERROR_IN_RESPONSE</link>" >
 <!ENTITY jersey.server.ServerProperties.FEATURE_AUTO_DISCOVERY_DISABLE "<link xlink:href='&jersey.javadoc.uri.prefix;/server/ServerProperties.html#FEATURE_AUTO_DISCOVERY_DISABLE'>ServerProperties.FEATURE_AUTO_DISCOVERY_DISABLE</link>" >
 <!ENTITY jersey.server.ServerProperties.HTTP_METHOD_OVERRIDE "<link xlink:href='&jersey.javadoc.uri.prefix;/server/ServerProperties.html#HTTP_METHOD_OVERRIDE'>ServerProperties.HTTP_METHOD_OVERRIDE</link>" >
+<!ENTITY jersey.server.ServerProperties.JSON_BINDING_FEATURE_DISABLE "<link xlink:href='&jersey.javadoc.uri.prefix;/server/ServerProperties.html#JSON_BINDING_FEATURE_DISABLE'>ServerProperties.JSON_BINDING_FEATURE_DISABLE</link>" >
 <!ENTITY jersey.server.ServerProperties.JSON_PROCESSING_FEATURE_DISABLE "<link xlink:href='&jersey.javadoc.uri.prefix;/server/ServerProperties.html#JSON_PROCESSING_FEATURE_DISABLE'>ServerProperties.JSON_PROCESSING_FEATURE_DISABLE</link>" >
 <!ENTITY jersey.server.ServerProperties.LANGUAGE_MAPPINGS "<link xlink:href='&jersey.javadoc.uri.prefix;/server/ServerProperties.html#LANGUAGE_MAPPINGS'>ServerProperties.LANGUAGE_MAPPINGS</link>" >
 <!ENTITY jersey.server.ServerProperties.MEDIA_TYPE_MAPPINGS "<link xlink:href='&jersey.javadoc.uri.prefix;/server/ServerProperties.html#MEDIA_TYPE_MAPPINGS'>ServerProperties.MEDIA_TYPE_MAPPINGS</link>" >
@@ -971,6 +991,7 @@
 <!ENTITY lit.jersey.client.EncodingFeature "<literal>EncodingFeature</literal>">
 <!ENTITY lit.jersey.client.HttpAuthenticationFeature "<literal>HttpAuthenticationFeature</literal>">
 <!ENTITY lit.jersey.client.HttpUrlConnectorProvider "<literal>HttpUrlConnectorProvider</literal>">
+<!ENTITY lit.jersey.client.HttpUrlConnector "<literal>HttpUrlConnector</literal>">
 <!ENTITY lit.jersey.client.InjectionManagerClientProvider "<literal>InjectionManagerClientProvider</literal>">
 <!ENTITY lit.jersey.client.oauth1.AccessToken "<literal>AccessToken</literal>">
 <!ENTITY lit.jersey.client.oauth1.ConsumerCredentials "<literal>ConsumerCredentials</literal>">
diff --git a/docs/src/main/docbook/media.xml b/docs/src/main/docbook/media.xml
index 288b923..34d2a7d 100644
--- a/docs/src/main/docbook/media.xml
+++ b/docs/src/main/docbook/media.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" standalone="no"?>
 <!--
 
-    Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2012, 2024 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
@@ -1753,8 +1753,8 @@
 
                         <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());
+list.add(EntityPart.withFileName("file001.txt").content(Files.newInputStream(Path.of("file001.txt"))).build());
+list.add(EntityPart.withFileName("mypom.xml").content(Files.newInputStream(Path.of("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);
@@ -1935,6 +1935,25 @@
                 </tip>
             </section>
         </section>
+        <section xml:id="multipart.configuration">
+            <title>Properties for configuring the Multipart</title>
+            <para>
+                There are multiple options that can be used when configuring
+                the multipart. See &jersey.media.multipart.MultiPartProperties; or <xref linkend="appendix-properties-multipart"/>
+                for the possibilities.
+            </para>
+            <para>
+                The options can set in a configuration file specified by the
+                &jersey.media.multipart.MultiPartProperties.MULTI_PART_CONFIG_RESOURCE; property.
+                That is the standard Java properties file.
+            </para>
+            <para>
+                Or the options can be set programmatically,
+                by registering <literal>ContextResolver&lt;MultiPartProperties&gt;</literal>. For instance:
+            </para>
+            <programlisting language="java">ResourceConfig resourceConfig = new ResourceConfig();
+resourceConfig.register(new MultiPartProperties().bufferThreshold(65535).maxParts(2).resolver());</programlisting>
+        </section>
         <section xml:id="multipart.server.rest">
                 <title>Server using Jakarta REST API</title>
                 <para>
diff --git a/docs/src/main/docbook/modules.xml b/docs/src/main/docbook/modules.xml
index fb54949..8ce80ef 100644
--- a/docs/src/main/docbook/modules.xml
+++ b/docs/src/main/docbook/modules.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
 
-    Copyright (c) 2013, 2023 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2013, 2024 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
@@ -479,6 +479,14 @@
 <entry>Jersey extension module providing support for Mustache templates.</entry>
 </row>
 <row>
+    <entry>
+        <link xlink:href="https://eclipse-ee4j.github.io/jersey.github.io/project-info/&version;/jersey/project/jersey-mvc-thymeleaf/dependencies.html">
+            jersey-mvc-thymeleaf
+        </link>
+    </entry>
+    <entry>Jersey extension module providing support for Thymeleaf templates.</entry>
+</row>
+<row>
 <entry>
 <link xlink:href="https://eclipse-ee4j.github.io/jersey.github.io/project-info/&version;/jersey/project/jersey-proxy-client/dependencies.html">
     jersey-proxy-client
diff --git a/etc/config/checkstyle.xml b/etc/config/checkstyle.xml
index 36a7def..52d0455 100644
--- a/etc/config/checkstyle.xml
+++ b/etc/config/checkstyle.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
 
-    Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2010, 2024 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
@@ -88,7 +88,7 @@
         <!-- Checks for Javadoc comments.                     -->
         <!-- See http://checkstyle.sf.net/config_javadoc.html -->
         <module name="JavadocMethod">
-            <property name="scope" value="protected"/>
+            <property name="accessModifiers" value="protected"/>
         <!-- <property name="allowUndeclaredRTE" value="true"/>-->
         </module>
         <module name="JavadocType">
diff --git a/etc/config/copyright-exclude b/etc/config/copyright-exclude
index 62b47fc..de5bfc6 100644
--- a/etc/config/copyright-exclude
+++ b/etc/config/copyright-exclude
@@ -41,6 +41,7 @@
 /etc/config/copyright.txt
 /etc/config/edl-copyright.txt
 /etc/jenkins/
+/etc/scripts/
 /etc/travis/
 /LICENSE.md
 /third-party-license-readme.txt
diff --git a/etc/jenkins/Jenkinsfile_ci_build b/etc/jenkins/Jenkinsfile_ci_build
index 4a622e4..cc3401f 100644
--- a/etc/jenkins/Jenkinsfile_ci_build
+++ b/etc/jenkins/Jenkinsfile_ci_build
@@ -19,6 +19,7 @@
                     steps {
                         sh '''
                                 bash ${WORKSPACE}/etc/jenkins/jenkins_build.sh
+                                bash ${WORKSPACE}/etc/scripts/validation/dependency-convergence.sh
                             '''
                     }
                 }
@@ -33,6 +34,8 @@
                     steps {
                         sh '''
                                 bash ${WORKSPACE}/etc/jenkins/jenkins_build.sh
+                                export EXCLUDE_ARGS=' -pl !:jersey-container-jetty11-http,!:jersey-jetty11-connector '
+                                bash ${WORKSPACE}/etc/scripts/validation/dependency-convergence.sh
                             '''
                     }
                 }
diff --git a/etc/jenkins/jenkins_build.sh b/etc/jenkins/jenkins_build.sh
index 9983a05..3543e90 100644
--- a/etc/jenkins/jenkins_build.sh
+++ b/etc/jenkins/jenkins_build.sh
@@ -2,4 +2,4 @@
 
 export DEBUG=true
 
-mvn -V -U -B -e -Pstaging clean install glassfish-copyright:check -Dcopyright.quiet=false
\ No newline at end of file
+mvn -V -U -B -e -Pstaging clean install glassfish-copyright:check -Dcopyright.quiet=false  -DskipSBOM
diff --git a/etc/scripts/apidocs.sh b/etc/scripts/apidocs.sh
new file mode 100755
index 0000000..12b2364
--- /dev/null
+++ b/etc/scripts/apidocs.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+mvn -U -V -B clean install -Ppre-release -pl :apidocs -am -DskipTests
\ No newline at end of file
diff --git a/etc/scripts/checkstyle.sh b/etc/scripts/checkstyle.sh
new file mode 100755
index 0000000..ad36942
--- /dev/null
+++ b/etc/scripts/checkstyle.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+mvn -V -U -B  checkstyle:checkstyle-aggregate -Dcheckstyle.output.format="plain" -Dcheckstyle.output.file=checkstyle.log
\ No newline at end of file
diff --git a/etc/scripts/copyright.sh b/etc/scripts/copyright.sh
new file mode 100755
index 0000000..a874923
--- /dev/null
+++ b/etc/scripts/copyright.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+readonly CP_PATTERN='Copyright year is wrong'
+
+
+[[ -n ${1} ]] && readonly LOG_FILE=${1} || readonly LOG_FILE='copyright.log'
+
+
+echo ${LOG_FILE}
+
+mvn -U -B  glassfish-copyright:copyright -Dcopyright.quiet=false | grep "${CP_PATTERN}" | tee ${LOG_FILE}
+
+grep "${CP_PATTERN}"  ${LOG_FILE} || exit 0 && exit 1
\ No newline at end of file
diff --git a/etc/scripts/test-archetypes.sh b/etc/scripts/test-archetypes.sh
new file mode 100755
index 0000000..8ac27e3
--- /dev/null
+++ b/etc/scripts/test-archetypes.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+mvn -U -V -B clean install -pl org.glassfish.jersey.archetypes:project
\ No newline at end of file
diff --git a/etc/scripts/validation/dependency-convergence.sh b/etc/scripts/validation/dependency-convergence.sh
new file mode 100755
index 0000000..9bcf4fe
--- /dev/null
+++ b/etc/scripts/validation/dependency-convergence.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+
+readonly VALIDATION_DEPENDENCIES_MATCH='<!-- Insert dependencies here -->'
+readonly VALIDATION_POM=validation.pom.xml
+readonly POM_TEMPLATE_NAME=pom.template.xml
+readonly TEMP_FILE=modules.xml
+
+readonly MVN_CLI='org.apache.maven.plugins:maven-enforcer-plugin:enforce -Denforcer.rules=dependencyConvergence'
+
+readonly DEPENDENCY_GENERATION_PATTERN='<dependency><groupId>${project.groupId}</groupId><artifactId>${project.artifactId}</artifactId><version>${jersey.version}</version></dependency>'
+
+# Path to this script
+[ -h "${0}" ] && readonly SCRIPT_PATH="$(readlink "${0}")" || readonly SCRIPT_PATH="${0}"
+
+readonly CURRENT_LOCATION=$(dirname -- "${SCRIPT_PATH}")
+readonly WS_DIR=$(cd ${CURRENT_LOCATION}; cd '../../..' ; pwd -P)
+
+#Prepare pom.xml from template
+
+cp -a ${CURRENT_LOCATION}/${POM_TEMPLATE_NAME} ${CURRENT_LOCATION}/${VALIDATION_POM}
+
+export JERSEY_VERSION=$(mvn exec:exec -Dexec.executable='echo' -Dexec.args='${project.version}' -f ${WS_DIR}/pom.xml -q -pl org.glassfish.jersey:project)
+
+echo "Validating convergences for Jersey "$JERSEY_VERSION
+echo '****************************************************'
+echo `mvn -v`
+echo '****************************************************'
+
+#get list of modules to b validated
+mvn -f ${WS_DIR}/pom.xml -Dexec.executable='echo' \
+ -Dtests.excluded \
+ -Dexec.args=${DEPENDENCY_GENERATION_PATTERN} \
+ ${MVN_ARGS} \
+ ${EXCLUDE_ARGS} \
+ -pl '!:helloworld-benchmark' exec:exec -q > ${CURRENT_LOCATION}/${TEMP_FILE}
+
+#add the list of modules to the prepared pom.xml
+sed -e "/${VALIDATION_DEPENDENCIES_MATCH}/ {" -e "r ${CURRENT_LOCATION}/${TEMP_FILE}" -e 'd' -e '}'  -i ${CURRENT_LOCATION}/${VALIDATION_POM}
+
+#run validation
+mvn ${MVN_ARGS} ${MVN_CLI} -f ${CURRENT_LOCATION}/${VALIDATION_POM} -Djersey.version=${JERSEY_VERSION}
+
+#save exit status
+export MAVEN_BUILD_RESULT=$?
+
+#clean the mess
+rm ${CURRENT_LOCATION}/${TEMP_FILE}
+
+#exit with saved exit stateus
+exit $MAVEN_BUILD_RESULT
diff --git a/etc/scripts/validation/pom.template.xml b/etc/scripts/validation/pom.template.xml
new file mode 100644
index 0000000..1e58300
--- /dev/null
+++ b/etc/scripts/validation/pom.template.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2024 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">
+    <groupId>org.glassfish.jersey.validation</groupId>
+    <artifactId>dependency-convergences</artifactId>
+    <version>0.0.1</version>
+    <modelVersion>4.0.0</modelVersion>
+
+    <packaging>war</packaging>
+    <name>jersey-dependency-convergence-validation</name>
+
+    <description>dependencyConvergence rule check</description>
+
+
+    <dependencies>
+    <!-- Insert dependencies here -->
+    </dependencies>
+
+    <properties>
+        <jersey.version>3.0.99-SNAPSHOT</jersey.version>
+    </properties>
+</project>
\ No newline at end of file
diff --git a/examples/NOTICE.md b/examples/NOTICE.md
index 0dcc83c..6f522b3 100644
--- a/examples/NOTICE.md
+++ b/examples/NOTICE.md
@@ -1,4 +1,4 @@
-# Notice for Jersey 
+# Notice for Jersey
 This content is produced and maintained by the Eclipse Jersey project.
 
 *  Project home: https://projects.eclipse.org/projects/ee4j.jersey
@@ -39,8 +39,8 @@
 
 Bean Validation API 3.0.2
 * License: Apache License, 2.0
-* Project: http://beanvalidation.org/1.1/
-* Copyright: 2009, Red Hat, Inc. and/or its affiliates, and individual contributors
+* Project: http://beanvalidation.org/3.0/
+* Copyright: 2009, 2020 Red Hat, Inc. and/or its affiliates, and individual contributors
 * by the @authors tag.
 
 Hibernate Validator CDI, 8.0.1.Final
@@ -58,25 +58,25 @@
 * Project: http://www.seamframework.org/Weld
 * Copyright 2010, Red Hat, Inc., and individual contributors by the @authors tag.
 
-Google Guava Version 18.0
+Google Guava Version 33.3.0-jre
 * License: Apache License, 2.0
-* Copyright (C) 2009 The Guava Authors
+* Copyright (C) 2009, 2024 The Guava Authors
 
-jakarta.inject Version: 1
+jakarta.inject Version: 2.0.1
 * License: Apache License, 2.0
-* Copyright (C) 2009 The JSR-330 Expert Group
+* Copyright (C) 2009, 2021 The JSR-330 Expert Group
 
-Javassist Version 3.29.2-GA
+Javassist Version 3.30.2-GA
 * License: Apache License, 2.0
 * Project: http://www.javassist.org/
 * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
 
-Jackson JAX-RS Providers Version 2.15.3
+Jackson JAX-RS Providers Version 2.17.2
 * License: Apache License, 2.0
 * Project: https://github.com/FasterXML/jackson-jaxrs-providers
 * Copyright: (c) 2009-2023 FasterXML, LLC. All rights reserved unless otherwise indicated.
 
-jQuery v1.12.4
+jQuery v3.7.1
 * License: jquery.org/license
 * Project: jquery.org
 * Copyright: (c) jQuery Foundation
@@ -96,7 +96,7 @@
 * Project: http://www.kineticjs.com, https://github.com/ericdrowell/KineticJS
 * Copyright: Eric Rowell
 
-org.objectweb.asm Version 9.6
+org.objectweb.asm Version 9.7
 * License: Modified BSD (https://asm.ow2.io/license.html)
 * Copyright (c) 2000-2011 INRIA, France Telecom. All rights reserved.
 
diff --git a/examples/configured-client/pom.xml b/examples/configured-client/pom.xml
index 99df9fe..5e341e5 100644
--- a/examples/configured-client/pom.xml
+++ b/examples/configured-client/pom.xml
@@ -56,8 +56,32 @@
                     <groupId>org.eclipse.microprofile.config</groupId>
                     <artifactId>microprofile-config-api</artifactId>
                 </exclusion>
+                <exclusion>
+                    <groupId>org.yaml</groupId>
+                    <artifactId>snakeyaml</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>jakarta.annotation</groupId>
+                    <artifactId>jakarta.annotation-api</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.reactivestreams</groupId>
+                    <artifactId>reactive-streams</artifactId>
+                </exclusion>
             </exclusions>
         </dependency>
+        <dependency>
+            <groupId>org.reactivestreams</groupId>
+            <artifactId>reactive-streams</artifactId>
+            <version>1.0.4</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.yaml</groupId>
+            <artifactId>snakeyaml</artifactId>
+            <version>2.2</version>
+            <scope>test</scope>
+        </dependency>
 
 <!--    Instead of helidon-microprofile-config, smallrye-config can be used -->
 <!--        <dependency>-->
diff --git a/examples/declarative-linking/pom.xml b/examples/declarative-linking/pom.xml
index d9d1329..f74187e 100644
--- a/examples/declarative-linking/pom.xml
+++ b/examples/declarative-linking/pom.xml
@@ -50,8 +50,8 @@
         </dependency>
 
         <dependency>
-            <groupId>org.glassfish</groupId>
-            <artifactId>jakarta.el</artifactId>
+            <groupId>org.glassfish.expressly</groupId>
+            <artifactId>expressly</artifactId>
         </dependency>
 
         <dependency>
diff --git a/examples/extended-wadl-webapp/README.MD b/examples/extended-wadl-webapp/README.MD
index d7a59d1..6c4f676 100644
--- a/examples/extended-wadl-webapp/README.MD
+++ b/examples/extended-wadl-webapp/README.MD
@@ -15,9 +15,6 @@
 Contents
 --------
 
-The description of what's done here you'll find in the [jersey 1
-wiki](https://wikis.oracle.com/display/Jersey/HowToConfigureExtendedWADL).
-
 The difference in configuration against jersey 1.x is in property
 configuring the custom WadlGeneratorConfig. Instead of property key
 'com.sun.jersey.config.property.WadlGeneratorConfig' use the property
@@ -48,6 +45,8 @@
 instructions at [the module README file](../../README.html) in order to
 deploy the example.
 
+NOTE: the example must be run with a JDK prior to JDK 13 otherwise javadoc plugin won't work properly.
+
 Otherwise, you can run the example using embedded GlassFish as follows:
 
 You can run the example using Grizzly as follows:
diff --git a/examples/extended-wadl-webapp/pom.xml b/examples/extended-wadl-webapp/pom.xml
index 44e2161..424808a 100644
--- a/examples/extended-wadl-webapp/pom.xml
+++ b/examples/extended-wadl-webapp/pom.xml
@@ -108,7 +108,7 @@
         <!-- logging -->
         <dependency>
             <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-log4j12</artifactId>
+            <artifactId>slf4j-reload4j</artifactId>
             <version>${slf4j.version}</version>
             <scope>test</scope>
         </dependency>
@@ -149,7 +149,7 @@
             <plugin>
                 <groupId>org.codehaus.mojo</groupId>
                 <artifactId>jaxb2-maven-plugin</artifactId>
-                <version>2.5.0</version>
+                <version>3.2.0</version>
                 <executions>
                     <execution>
                         <id>xjc</id>
@@ -289,6 +289,57 @@
 
     <profiles>
         <profile>
+            <id>javadocAndTestsSkipJDK13</id>
+            <activation>
+                <jdk>[13,)</jdk>
+            </activation>
+            <build>
+            <plugins>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-javadoc-plugin</artifactId>
+                    <configuration>
+                        <skip>true</skip>
+                    </configuration>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-surefire-plugin</artifactId>
+                    <configuration>
+                        <skip>true</skip>
+                    </configuration>
+                </plugin>
+                <plugin>
+                    <artifactId>maven-antrun-plugin</artifactId>
+                    <executions>
+                        <execution>
+                            <phase>generate-resources</phase>
+                            <goals>
+                                <goal>run</goal>
+                            </goals>
+                            <configuration>
+                                <target>
+                                    <echo>****************************************************</echo>
+                                    <echo>****THIS EXAMPLE WORKS ONLY ON JDKs PRIOR to 13!****</echo>
+                                    <echo>********CURRENT JDK IS NOT SUPPORTED!***************</echo>
+                                    <echo>****************************************************</echo>
+                                </target>
+                            </configuration>
+                        </execution>
+                    </executions>
+                </plugin>
+                <plugin>
+                    <groupId>org.jvnet.jaxb2.maven2</groupId>
+                    <artifactId>maven-jaxb2-plugin</artifactId>
+                    <version>0.14.0</version>
+                    <configuration>
+                        <verbose>false</verbose>
+                    </configuration>
+                </plugin>
+            </plugins>
+            </build>
+        </profile>
+        <profile>
             <id>pre-release</id>
             <build>
                 <plugins>
diff --git a/examples/groovy/pom.xml b/examples/groovy/pom.xml
index fcb5f2d..1d353ef 100644
--- a/examples/groovy/pom.xml
+++ b/examples/groovy/pom.xml
@@ -42,6 +42,18 @@
                     <groupId>org.ow2.asm</groupId>
                     <artifactId>asm-tree</artifactId>
                 </exclusion>
+                <exclusion>
+                    <groupId>com.fasterxml.jackson.core</groupId>
+                    <artifactId>jackson-core</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>com.fasterxml.jackson.core</groupId>
+                    <artifactId>jackson-databind</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.junit.jupiter</groupId>
+                    <artifactId>junit-jupiter-api</artifactId>
+                </exclusion>
             </exclusions>
         </dependency>
         <dependency>
@@ -62,7 +74,7 @@
         <dependency>
             <groupId>org.junit.jupiter</groupId>
             <artifactId>junit-jupiter-engine</artifactId>
-            <version>5.8.2</version>
+            <version>${junit5.version}</version>
             <scope>test</scope>
         </dependency>
     </dependencies>
@@ -140,7 +152,7 @@
             <plugin>
                 <groupId>org.codehaus.mojo</groupId>
                 <artifactId>build-helper-maven-plugin</artifactId>
-                <version>3.0.0</version>
+                <version>${buildhelper.mvn.plugin.version}</version>
                 <executions>
                     <execution>
                         <id>3</id>
diff --git a/examples/helloworld-spring-annotations/pom.xml b/examples/helloworld-spring-annotations/pom.xml
index a860c3b..6f6a02a 100644
--- a/examples/helloworld-spring-annotations/pom.xml
+++ b/examples/helloworld-spring-annotations/pom.xml
@@ -73,7 +73,7 @@
         <dependency>
             <groupId>commons-logging</groupId>
             <artifactId>commons-logging</artifactId>
-            <version>1.2</version>
+            <version>${commons.logging.version}</version>
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.test-framework.providers</groupId>
diff --git a/examples/helloworld-spring-webapp/pom.xml b/examples/helloworld-spring-webapp/pom.xml
index 9c5b918..3c944a9 100644
--- a/examples/helloworld-spring-webapp/pom.xml
+++ b/examples/helloworld-spring-webapp/pom.xml
@@ -55,6 +55,12 @@
         <dependency>
             <groupId>org.springframework</groupId>
             <artifactId>spring-web</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>io.micrometer</groupId>
+                    <artifactId>micrometer-observation</artifactId>
+                </exclusion>
+            </exclusions>
             <version>${spring6.version}</version>
         </dependency>
         <dependency>
@@ -87,7 +93,7 @@
         <dependency>
             <groupId>commons-logging</groupId>
             <artifactId>commons-logging</artifactId>
-            <version>1.2</version>
+            <version>${commons.logging.version}</version>
             <exclusions>
                 <exclusion>
                     <groupId>jakarta.servlet</groupId>
diff --git a/examples/http-patch/pom.xml b/examples/http-patch/pom.xml
index 5a33c71..adc28d7 100644
--- a/examples/http-patch/pom.xml
+++ b/examples/http-patch/pom.xml
@@ -49,9 +49,22 @@
                     <groupId>com.google.guava</groupId>
                     <artifactId>guava</artifactId>
                 </exclusion>
+                <exclusion>
+                    <groupId>com.google.code.findbugs</groupId>
+                    <artifactId>jsr305</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>com.fasterxml.jackson.core</groupId>
+                    <artifactId>jackson-databind</artifactId>
+                </exclusion>
             </exclusions> 
         </dependency>
         <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <version>${jackson.version}</version>
+        </dependency>
+        <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
             <version>${guava.version}</version>
diff --git a/examples/jaxb/pom.xml b/examples/jaxb/pom.xml
index 71a0217..e231629 100644
--- a/examples/jaxb/pom.xml
+++ b/examples/jaxb/pom.xml
@@ -41,7 +41,7 @@
         <dependency>
             <groupId>org.codehaus.woodstox</groupId>
             <artifactId>woodstox-core-asl</artifactId>
-            <version>4.1.2</version>
+            <version>4.4.1</version>
 	    </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.media</groupId>
diff --git a/examples/oauth-client-twitter/src/main/java/org/glassfish/jersey/examples/oauth/twitterclient/App.java b/examples/oauth-client-twitter/src/main/java/org/glassfish/jersey/examples/oauth/twitterclient/App.java
index 5d39bde..e0b6f77 100644
--- a/examples/oauth-client-twitter/src/main/java/org/glassfish/jersey/examples/oauth/twitterclient/App.java
+++ b/examples/oauth-client-twitter/src/main/java/org/glassfish/jersey/examples/oauth/twitterclient/App.java
@@ -17,7 +17,7 @@
 import java.io.OutputStream;
 import java.nio.charset.Charset;
 import java.nio.file.Files;
-import java.nio.file.Paths;
+import java.nio.file.Path;
 import java.util.List;
 import java.util.Properties;
 
@@ -145,7 +145,7 @@
     private static void loadSettings() {
         InputStream st = null;
         try {
-            st = Files.newInputStream(Paths.get(PROPERTIES_FILE_NAME));
+            st = Files.newInputStream(Path.of(PROPERTIES_FILE_NAME));
             PROPERTIES.load(st);
         } catch (final IOException e) {
             // ignore
@@ -178,7 +178,7 @@
     private static void storeSettings() {
         OutputStream st = null;
         try {
-            st = Files.newOutputStream(Paths.get(PROPERTIES_FILE_NAME));
+            st = Files.newOutputStream(Path.of(PROPERTIES_FILE_NAME));
             PROPERTIES.store(st, null);
         } catch (final IOException e) {
             // ignore
diff --git a/examples/open-tracing/pom.xml b/examples/open-tracing/pom.xml
index a780791..6ee5120 100644
--- a/examples/open-tracing/pom.xml
+++ b/examples/open-tracing/pom.xml
@@ -62,6 +62,22 @@
                     <groupId>org.apache.httpcomponents</groupId>
                     <artifactId>httpcore</artifactId>
                 </exclusion>
+                <exclusion>
+                    <groupId>io.opentracing</groupId>
+                    <artifactId>opentracing-api</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>io.opentracing</groupId>
+                    <artifactId>opentracing-util</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>com.google.code.gson</groupId>
+                    <artifactId>gson</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.httpcomponents</groupId>
+                    <artifactId>httpclient</artifactId>
+                </exclusion>
             </exclusions>
         </dependency>
 
diff --git a/examples/osgi-helloworld-webapp/functional-test/src/test/java/org/glassfish/jersey/examples/helloworld/test/AbstractWebAppTest.java b/examples/osgi-helloworld-webapp/functional-test/src/test/java/org/glassfish/jersey/examples/helloworld/test/AbstractWebAppTest.java
index 404c63d..cfd299d 100644
--- a/examples/osgi-helloworld-webapp/functional-test/src/test/java/org/glassfish/jersey/examples/helloworld/test/AbstractWebAppTest.java
+++ b/examples/osgi-helloworld-webapp/functional-test/src/test/java/org/glassfish/jersey/examples/helloworld/test/AbstractWebAppTest.java
@@ -14,7 +14,7 @@
 import java.io.IOException;
 import java.net.URI;
 import java.nio.file.Files;
-import java.nio.file.Paths;
+import java.nio.file.Path;
 import java.security.AccessController;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -228,7 +228,7 @@
 
         try {
 
-            final BufferedReader reader = Files.newBufferedReader(Paths.get(felixPolicy));
+            final BufferedReader reader = Files.newBufferedReader(Path.of(felixPolicy));
             String line;
             final Set<String> cpiNames = new HashSet<String>();
 
diff --git a/examples/osgi-http-service/bundle/pom.xml b/examples/osgi-http-service/bundle/pom.xml
index eed47ac..3e0b865 100644
--- a/examples/osgi-http-service/bundle/pom.xml
+++ b/examples/osgi-http-service/bundle/pom.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
 
-    Copyright (c) 2010, 2023 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
 
     This program and the accompanying materials are made available under the
     terms of the Eclipse Distribution License v. 1.0, which is available at
diff --git a/examples/osgi-http-service/functional-test/pom.xml b/examples/osgi-http-service/functional-test/pom.xml
index ccc2999..6e74fc8 100644
--- a/examples/osgi-http-service/functional-test/pom.xml
+++ b/examples/osgi-http-service/functional-test/pom.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
 
-    Copyright (c) 2010, 2023 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
 
     This program and the accompanying materials are made available under the
     terms of the Eclipse Distribution License v. 1.0, which is available at
@@ -144,8 +144,8 @@
         <!-- Logging dependencies-->
         <dependency>
             <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-log4j12</artifactId>
-            <version>1.6.4</version>
+            <artifactId>slf4j-reload4j</artifactId>
+            <version>${slf4j.version}</version>
             <scope>test</scope>
         </dependency>
 
diff --git a/examples/osgi-http-service/pom.xml b/examples/osgi-http-service/pom.xml
index e560c3b..fa1bd88 100644
--- a/examples/osgi-http-service/pom.xml
+++ b/examples/osgi-http-service/pom.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
 
-    Copyright (c) 2010, 2023 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
 
     This program and the accompanying materials are made available under the
     terms of the Eclipse Distribution License v. 1.0, which is available at
diff --git a/examples/pom.xml b/examples/pom.xml
index 738a2c2..eeaa80a 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -183,7 +183,7 @@
                 <plugin>
                     <groupId>org.commonjava.maven.plugins</groupId>
                     <artifactId>directory-maven-plugin</artifactId>
-                    <version>0.3.1</version>
+                    <version>1.0</version>
                     <executions>
                         <execution>
                             <id>directories</id>
diff --git a/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/App.java b/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/App.java
index 8e922b7..e28fe2d 100644
--- a/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/App.java
+++ b/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/App.java
@@ -18,7 +18,7 @@
 import java.nio.file.Files;
 import java.nio.file.FileSystems;
 import java.nio.file.Path;
-import java.nio.file.Paths;
+import java.nio.file.Path;
 import java.nio.file.StandardWatchEventKinds;
 import java.nio.file.WatchEvent;
 import java.nio.file.WatchKey;
@@ -72,10 +72,10 @@
             try {
                 watcher = FileSystems.getDefault().newWatchService();
 
-                Path srcDir = Paths.get("src/main/java/org/glassfish/jersey/examples/reload");
+                Path srcDir = Path.of("src/main/java/org/glassfish/jersey/examples/reload");
                 registerWatcher(watcher, srcDir);
 
-                Path configFilePath = Paths.get(".");
+                Path configFilePath = Path.of(".");
                 registerWatcher(watcher, configFilePath);
 
             } catch (IOException e) {
diff --git a/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/compiler/JavaFile.java b/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/compiler/JavaFile.java
index 2a789ff..5c2b57c 100644
--- a/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/compiler/JavaFile.java
+++ b/examples/reload/src/main/java/org/glassfish/jersey/examples/reload/compiler/JavaFile.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Distribution License v. 1.0, which is available at
@@ -14,7 +14,7 @@
 import java.io.IOException;
 import java.net.URI;
 import java.nio.file.Files;
-import java.nio.file.Paths;
+import java.nio.file.Path;
 
 import javax.tools.SimpleJavaFileObject;
 
@@ -47,7 +47,7 @@
     public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
 
         String filePath = path + File.separator + className.replace('.', '/') + Kind.SOURCE.extension;
-        final byte[] bytes = Files.readAllBytes(Paths.get(filePath));
+        final byte[] bytes = Files.readAllBytes(Path.of(filePath));
 
         return new String(bytes);
     }
diff --git a/examples/servlet3-webapp/pom.xml b/examples/servlet3-webapp/pom.xml
index 4fc1811..23fa2a9 100644
--- a/examples/servlet3-webapp/pom.xml
+++ b/examples/servlet3-webapp/pom.xml
@@ -112,6 +112,15 @@
 
     <profiles>
         <profile>
+            <id>jdk8_tests</id>
+            <activation>
+                <jdk>1.8</jdk>
+            </activation>
+            <properties>
+                <junit5.version>${junit5.jdk8.version}</junit5.version>
+            </properties>
+        </profile>
+        <profile>
             <id>pre-release</id>
             <build>
                 <plugins>
diff --git a/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/App.java b/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/App.java
index a3e6788..ebde366 100644
--- a/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/App.java
+++ b/examples/sse-twitter-aggregator/src/main/java/org/glassfish/jersey/examples/aggregator/App.java
@@ -13,7 +13,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.file.Files;
-import java.nio.file.Paths;
+import java.nio.file.Path;
 import java.util.HashMap;
 import java.util.Properties;
 import java.util.logging.Level;
@@ -144,7 +144,7 @@
         InputStream st = null;
         try {
             String homeDir = System.getProperty("user.home");
-            st = Files.newInputStream(Paths.get(homeDir, TWITTER_PROPERTIES_FILE_NAME));
+            st = Files.newInputStream(Path.of(homeDir, TWITTER_PROPERTIES_FILE_NAME));
             properties.load(st);
         } catch (IOException e) {
             // ignore
@@ -230,7 +230,7 @@
             try {
                 fileStream = webRootPath == null
                         ? App.class.getResourceAsStream(WEB_ROOT + uri)
-                        : Files.newInputStream(Paths.get(webRootPath, uri));
+                        : Files.newInputStream(Path.of(webRootPath, uri));
             } catch (IOException e) {
                 fileStream = null;
             }
diff --git a/ext/bean-validation/pom.xml b/ext/bean-validation/pom.xml
index ad8dca3..5270668 100644
--- a/ext/bean-validation/pom.xml
+++ b/ext/bean-validation/pom.xml
@@ -141,10 +141,15 @@
             <groupId>jakarta.el</groupId>
             <artifactId>jakarta.el-api</artifactId>
         </dependency>
-
         <dependency>
-            <groupId>org.glassfish</groupId>
-            <artifactId>jakarta.el</artifactId>
+            <groupId>org.glassfish.expressly</groupId>
+            <artifactId>expressly</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>jakarta.el</groupId>
+                    <artifactId>jakarta.el-api</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
 
         <dependency>
diff --git a/ext/bean-validation/src/main/java/module-info.java b/ext/bean-validation/src/main/java/module-info.java
index 9113ddc..a0d312e 100644
--- a/ext/bean-validation/src/main/java/module-info.java
+++ b/ext/bean-validation/src/main/java/module-info.java
@@ -32,6 +32,8 @@
 
     exports org.glassfish.jersey.server.validation;
     exports org.glassfish.jersey.server.validation.internal;
+    exports org.glassfish.jersey.server.validation.internal.hibernate;
+    opens org.glassfish.jersey.server.validation;
     opens org.glassfish.jersey.server.validation.internal;
     opens org.glassfish.jersey.server.validation.internal.hibernate;
 }
\ No newline at end of file
diff --git a/ext/cdi/jersey-cdi1x/src/main/java/org/glassfish/jersey/ext/cdi1x/internal/CdiComponentProvider.java b/ext/cdi/jersey-cdi1x/src/main/java/org/glassfish/jersey/ext/cdi1x/internal/CdiComponentProvider.java
index e41d4c9..a47aced 100644
--- a/ext/cdi/jersey-cdi1x/src/main/java/org/glassfish/jersey/ext/cdi1x/internal/CdiComponentProvider.java
+++ b/ext/cdi/jersey-cdi1x/src/main/java/org/glassfish/jersey/ext/cdi1x/internal/CdiComponentProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2024 Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2018, 2022 Payara Foundation and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
@@ -45,7 +45,6 @@
 import jakarta.inject.Singleton;
 import jakarta.ws.rs.core.Application;
 
-
 import jakarta.annotation.ManagedBean;
 import jakarta.enterprise.context.Dependent;
 import jakarta.enterprise.context.RequestScoped;
@@ -143,6 +142,8 @@
     private volatile Map<Class<?>, Set<Method>> methodsToSkip = new HashMap<>();
     private volatile Map<Class<?>, Set<Field>> fieldsToSkip = new HashMap<>();
 
+    private boolean initialized = false;
+
     public CdiComponentProvider() {
         customHk2TypesProvider = CdiUtil.lookupService(Hk2CustomBoundTypesProvider.class);
         injectionManagerStore = CdiUtil.createHk2InjectionManagerStore();
@@ -154,7 +155,7 @@
         this.injectionManager = injectionManager;
         this.beanManager = CdiUtil.getBeanManager();
 
-        if (beanManager != null) {
+        if (beanManager != null && !injectionManager.getClass().getSimpleName().equals("NonInjectionManager")) {
             // Try to get CdiComponentProvider created by CDI.
             final CdiComponentProvider extension = beanManager.getExtension(CdiComponentProvider.class);
 
@@ -167,18 +168,19 @@
                 bindHk2ClassAnalyzer();
 
                 LOGGER.config(LocalizationMessages.CDI_PROVIDER_INITIALIZED());
+                initialized = true;
             }
         }
     }
 
     @Override
     public boolean bind(final Class<?> clazz, final Set<Class<?>> providerContracts) {
-        return bind(clazz, providerContracts, ContractProvider.NO_PRIORITY);
+        return initialized && bind(clazz, providerContracts, ContractProvider.NO_PRIORITY);
     }
 
     @Override
     public boolean bind(Class<?> component, ContractProvider contractProvider) {
-        return contractProvider != null
+        return initialized && contractProvider != null
                 ? bind(component, contractProvider.getContracts(), contractProvider.getPriority(component))
                 : bind(component, Collections.EMPTY_SET);
     }
@@ -628,11 +630,8 @@
         ClassAnalyzer defaultClassAnalyzer =
                 injectionManager.getInstance(ClassAnalyzer.class, ClassAnalyzer.DEFAULT_IMPLEMENTATION_NAME);
 
-        int skippedElements = methodsToSkip.size() + fieldsToSkip.size();
-
-        ClassAnalyzer customizedClassAnalyzer = skippedElements > 0
-                ? new InjecteeSkippingAnalyzer(defaultClassAnalyzer, methodsToSkip, fieldsToSkip, beanManager)
-                : defaultClassAnalyzer;
+        ClassAnalyzer customizedClassAnalyzer =
+                new InjecteeSkippingAnalyzer(defaultClassAnalyzer, methodsToSkip, fieldsToSkip, beanManager);
 
         Binder binder = new AbstractBinder() {
             @Override
diff --git a/ext/cdi/jersey-weld2-se/pom.xml b/ext/cdi/jersey-weld2-se/pom.xml
index ba3f6b3..72ecba9 100644
--- a/ext/cdi/jersey-weld2-se/pom.xml
+++ b/ext/cdi/jersey-weld2-se/pom.xml
@@ -67,6 +67,18 @@
                     <groupId>jakarta.interceptor</groupId>
                     <artifactId>jakarta.interceptor-api</artifactId>
                 </exclusion>
+                <exclusion>
+                    <groupId>jakarta.inject</groupId>
+                    <artifactId>jakarta.inject-api</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>jakarta.el</groupId>
+                    <artifactId>jakarta.el-api</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>jakarta.annotation</groupId>
+                    <artifactId>jakarta.annotation-api</artifactId>
+                </exclusion>
             </exclusions>
         </dependency>
         <dependency>
diff --git a/ext/micrometer/src/main/java/org/glassfish/jersey/micrometer/server/JerseyKeyValues.java b/ext/micrometer/src/main/java/org/glassfish/jersey/micrometer/server/JerseyKeyValues.java
index 66cdaf7..8b6d9b3 100644
--- a/ext/micrometer/src/main/java/org/glassfish/jersey/micrometer/server/JerseyKeyValues.java
+++ b/ext/micrometer/src/main/java/org/glassfish/jersey/micrometer/server/JerseyKeyValues.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2024 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,9 @@
 import org.glassfish.jersey.server.ExtendedUriInfo;
 import org.glassfish.jersey.server.monitoring.RequestEvent;
 
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.core.Response;
+
 /**
  * Factory methods for {@link KeyValue KeyValues} associated with a request-response
  * exchange that is handled by Jersey server.
@@ -38,6 +41,9 @@
     private static final KeyValue URI_ROOT = JerseyObservationDocumentation.JerseyLegacyLowCardinalityTags.URI
         .withValue("root");
 
+    private static final KeyValue URI_UNKNOWN = JerseyObservationDocumentation.JerseyLegacyLowCardinalityTags.URI
+            .withValue("UNKNOWN");
+
     private static final KeyValue EXCEPTION_NONE = JerseyObservationDocumentation.JerseyLegacyLowCardinalityTags.EXCEPTION
         .withValue("None");
 
@@ -82,17 +88,30 @@
      * @return the uri KeyValue derived from the request event
      */
     static KeyValue uri(RequestEvent event) {
-        ContainerResponse response = event.getContainerResponse();
-        if (response != null) {
-            int status = response.getStatus();
+        int status = 0;
+        if (event.getContainerResponse() != null) {
+            status = event.getContainerResponse().getStatus();
+        } else if (WebApplicationException.class.isInstance(event.getException())) {
+            Response webAppResponse = ((WebApplicationException) event.getException()).getResponse();
+            if (webAppResponse != null) {
+                status = webAppResponse.getStatus();
+            }
+        }
+        if (status != 0) {
             if (JerseyTags.isRedirection(status) && event.getUriInfo().getMatchedResourceMethod() == null) {
                 return URI_REDIRECTION;
             }
             if (status == 404 && event.getUriInfo().getMatchedResourceMethod() == null) {
                 return URI_NOT_FOUND;
             }
+            if (status >= 500 && status <= 599) {
+                return STATUS_SERVER_ERROR;
+            }
         }
         String matchingPattern = JerseyTags.getMatchingPattern(event);
+        if (matchingPattern == null) {
+            return URI_UNKNOWN;
+        }
         if (matchingPattern.equals("/")) {
             return URI_ROOT;
         }
diff --git a/ext/micrometer/src/main/java/org/glassfish/jersey/micrometer/server/JerseyTags.java b/ext/micrometer/src/main/java/org/glassfish/jersey/micrometer/server/JerseyTags.java
index d723c7c..29494f4 100644
--- a/ext/micrometer/src/main/java/org/glassfish/jersey/micrometer/server/JerseyTags.java
+++ b/ext/micrometer/src/main/java/org/glassfish/jersey/micrometer/server/JerseyTags.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024 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,8 @@
 
     private static final Tag URI_ROOT = Tag.of("uri", "root");
 
+    private static final Tag URI_UNKNOWN = Tag.of("uri", "UNKNOWN");
+
     private static final Tag EXCEPTION_NONE = Tag.of("exception", "None");
 
     private static final Tag STATUS_SERVER_ERROR = Tag.of("status", "500");
@@ -95,7 +97,10 @@
             }
         }
         String matchingPattern = getMatchingPattern(event);
-        if (matchingPattern.equals("/")) {
+        if (matchingPattern == null) {
+            return URI_UNKNOWN;
+        }
+        else if (matchingPattern.equals("/")) {
             return URI_ROOT;
         }
         return Tag.of("uri", matchingPattern);
@@ -108,7 +113,9 @@
     static String getMatchingPattern(RequestEvent event) {
         ExtendedUriInfo uriInfo = event.getUriInfo();
         List<UriTemplate> templates = uriInfo.getMatchedTemplates();
-
+        if (templates.isEmpty()) {
+            return null;
+        }
         StringBuilder sb = new StringBuilder();
         sb.append(uriInfo.getBaseUri().getPath());
         for (int i = templates.size() - 1; i >= 0; i--) {
diff --git a/ext/micrometer/src/main/java/org/glassfish/jersey/micrometer/server/MetricsRequestEventListener.java b/ext/micrometer/src/main/java/org/glassfish/jersey/micrometer/server/MetricsRequestEventListener.java
index cca1d13..0b05442 100644
--- a/ext/micrometer/src/main/java/org/glassfish/jersey/micrometer/server/MetricsRequestEventListener.java
+++ b/ext/micrometer/src/main/java/org/glassfish/jersey/micrometer/server/MetricsRequestEventListener.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024 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
@@ -79,7 +79,7 @@
 
         switch (event.getType()) {
             case ON_EXCEPTION:
-                if (!isNotFoundException(event)) {
+                if (!isClientError(event)) {
                     break;
                 }
                 time(event, containerRequest);
@@ -122,13 +122,14 @@
         }
     }
 
-    private boolean isNotFoundException(RequestEvent event) {
+    private boolean isClientError(RequestEvent event) {
         Throwable t = event.getException();
         if (t == null) {
             return false;
         }
-        String className = t.getClass().getCanonicalName();
-        return className.equals("jakarta.ws.rs.NotFoundException") || className.equals("jakarta.ws.rs.NotFoundException");
+        String className = t.getClass().getSuperclass().getCanonicalName();
+        return className.equals("jakarta.ws.rs.ClientErrorException")
+                || className.equals("javax.ws.rs.ClientErrorException");
     }
 
     private Set<Timer> shortTimers(Set<Timed> timed, RequestEvent event) {
diff --git a/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/DefaultJerseyTagsProviderTest.java b/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/DefaultJerseyTagsProviderTest.java
index d0445f9..93a9e4d 100644
--- a/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/DefaultJerseyTagsProviderTest.java
+++ b/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/DefaultJerseyTagsProviderTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024 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
@@ -51,7 +51,7 @@
 
     @Test
     void testRootPath() {
-        assertThat(tagsProvider.httpRequestTags(event(200, null, "/", (String[]) null)))
+        assertThat(tagsProvider.httpRequestTags(event(200, null, "/", "/")))
             .containsExactlyInAnyOrder(tagsFrom("root", 200, null, "SUCCESS"));
     }
 
@@ -86,21 +86,21 @@
     @Test
     @SuppressWarnings("serial")
     void exceptionsAreMappedCorrectly() {
-        assertThat(tagsProvider.httpRequestTags(event(500, new IllegalArgumentException(), "/app", (String[]) null)))
+        assertThat(tagsProvider.httpRequestTags(event(500, new IllegalArgumentException(), "/app", "/")))
             .containsExactlyInAnyOrder(tagsFrom("/app", 500, "IllegalArgumentException", "SERVER_ERROR"));
         assertThat(tagsProvider.httpRequestTags(
-                event(500, new IllegalArgumentException(new NullPointerException()), "/app", (String[]) null)))
+                event(500, new IllegalArgumentException(new NullPointerException()), "/app", "/")))
             .containsExactlyInAnyOrder(tagsFrom("/app", 500, "NullPointerException", "SERVER_ERROR"));
-        assertThat(tagsProvider.httpRequestTags(event(406, new NotAcceptableException(), "/app", (String[]) null)))
+        assertThat(tagsProvider.httpRequestTags(event(406, new NotAcceptableException(), "/app", "/")))
             .containsExactlyInAnyOrder(tagsFrom("/app", 406, "NotAcceptableException", "CLIENT_ERROR"));
         assertThat(tagsProvider.httpRequestTags(event(500, new Exception("anonymous") {
-        }, "/app", (String[]) null))).containsExactlyInAnyOrder(tagsFrom("/app", 500,
+        }, "/app", "/"))).containsExactlyInAnyOrder(tagsFrom("/app", 500,
                 "org.glassfish.jersey.micrometer.server.DefaultJerseyTagsProviderTest$1", "SERVER_ERROR"));
     }
 
     @Test
     void longRequestTags() {
-        assertThat(tagsProvider.httpLongRequestTags(event(0, null, "/app", (String[]) null)))
+        assertThat(tagsProvider.httpLongRequestTags(event(0, null, "/app", "/")))
             .containsExactlyInAnyOrder(Tag.of("method", "GET"), Tag.of("uri", "/app"));
     }
 
diff --git a/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/MetricsRequestEventListenerTest.java b/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/MetricsRequestEventListenerTest.java
index 96324b2..ce2bc4a 100644
--- a/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/MetricsRequestEventListenerTest.java
+++ b/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/MetricsRequestEventListenerTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024 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,7 @@
 
 import jakarta.ws.rs.NotFoundException;
 import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.MediaType;
 
 import io.micrometer.core.instrument.MeterRegistry;
 import io.micrometer.core.instrument.Tag;
@@ -149,6 +150,11 @@
         }
         catch (Exception ignored) {
         }
+        try {
+            target("produces-text-plain").request(MediaType.APPLICATION_JSON).get();
+        }
+        catch (Exception ignored) {
+        }
 
         assertThat(registry.get(METRIC_NAME)
             .tags(tagsFrom("/throws-exception", "500", "SERVER_ERROR", "IllegalArgumentException"))
@@ -164,6 +170,11 @@
             .tags(tagsFrom("/throws-mappable-exception", "410", "CLIENT_ERROR", "ResourceGoneException"))
             .timer()
             .count()).isEqualTo(1);
+
+        assertThat(registry.get(METRIC_NAME)
+                       .tags(tagsFrom("UNKNOWN", "406", "CLIENT_ERROR", "NotAcceptableException"))
+                       .timer()
+                       .count()).isEqualTo(1);
     }
 
     private static Iterable<Tag> tagsFrom(String uri, String status, String outcome, String exception) {
diff --git a/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/exception/JerseyKeyValuesTest.java b/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/exception/JerseyKeyValuesTest.java
new file mode 100644
index 0000000..99bad31
--- /dev/null
+++ b/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/exception/JerseyKeyValuesTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2024 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.micrometer.server.exception;
+
+import io.micrometer.common.KeyValue;
+import io.micrometer.common.KeyValues;
+import org.glassfish.jersey.micrometer.server.DefaultJerseyObservationConvention;
+import org.glassfish.jersey.micrometer.server.JerseyContext;
+import org.glassfish.jersey.server.ExtendedUriInfo;
+import org.glassfish.jersey.server.internal.monitoring.RequestEventImpl;
+import org.glassfish.jersey.server.monitoring.RequestEvent;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.NotFoundException;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Collections;
+import java.util.Optional;
+
+public class JerseyKeyValuesTest {
+    @Test
+    public void testOnException() {
+        ExtendedUriInfo uriInfo = (ExtendedUriInfo) Proxy.newProxyInstance(getClass().getClassLoader(),
+                new Class[]{ExtendedUriInfo.class},
+                new InvocationHandler() {
+            @Override
+            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                switch (method.getName()) {
+                    case "getMatchedTemplates":
+                        return Collections.emptyList();
+                }
+                return null;
+            }
+        });
+        RequestEventImpl event = new RequestEventImpl.Builder()
+                .setExtendedUriInfo(uriInfo)
+                .setException(new NotFoundException(), RequestEvent.ExceptionCause.ORIGINAL)
+                .build(RequestEvent.Type.ON_EXCEPTION);
+        JerseyContext context = new JerseyContext(event);
+        DefaultJerseyObservationConvention convention = new DefaultJerseyObservationConvention("Test-Metric");
+        KeyValues values = convention.getLowCardinalityKeyValues(context);
+        Optional<KeyValue> kv = values.stream().filter(p -> p.getValue().equals("NOT_FOUND")).findFirst();
+        MatcherAssert.assertThat(kv.isPresent(), Matchers.equalTo(true));
+    }
+}
diff --git a/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/resources/TestResource.java b/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/resources/TestResource.java
index 41e529d..36abeb2 100644
--- a/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/resources/TestResource.java
+++ b/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/resources/TestResource.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024 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
@@ -90,6 +90,13 @@
     }
 
     @GET
+    @Path("produces-text-plain")
+    @Produces(MediaType.TEXT_PLAIN)
+    public String producesTextPlain() {
+        return "hello";
+    }
+
+    @GET
     @Path("redirect/{status}")
     public Response redirect(@PathParam("status") int status) {
         if (status == 307) {
diff --git a/ext/microprofile/mp-config/pom.xml b/ext/microprofile/mp-config/pom.xml
index 7461b17..f954c8e 100644
--- a/ext/microprofile/mp-config/pom.xml
+++ b/ext/microprofile/mp-config/pom.xml
@@ -30,7 +30,11 @@
     <artifactId>jersey-mp-config</artifactId>
 
     <properties>
-        <surefire.coverage.argline>--add-modules=ALL-MODULE-PATH</surefire.coverage.argline>
+        <surefire.coverage.argline>
+            --add-opens org.glassfish.jersey.core.common/org.glassfish.jersey.innate=ALL-UNNAMED
+            --add-opens org.glassfish.jersey.core.common/org.glassfish.jersey.innate.virtual=ALL-UNNAMED
+            --add-modules=ALL-MODULE-PATH
+        </surefire.coverage.argline>
     </properties>
 
     <dependencies>
diff --git a/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/RestClientBuilderImpl.java b/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/RestClientBuilderImpl.java
index 1e81f52..2b7425f 100644
--- a/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/RestClientBuilderImpl.java
+++ b/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/RestClientBuilderImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2024 Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2019, 2021 Payara Foundation and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
@@ -33,7 +33,6 @@
 import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Supplier;
 import java.util.logging.Level;
@@ -66,6 +65,7 @@
 import org.glassfish.jersey.client.Initializable;
 import org.glassfish.jersey.client.spi.ConnectorProvider;
 import org.glassfish.jersey.ext.cdi1x.internal.CdiUtil;
+import org.glassfish.jersey.innate.VirtualThreadUtil;
 import org.glassfish.jersey.internal.ServiceFinder;
 import org.glassfish.jersey.internal.inject.InjectionManager;
 import org.glassfish.jersey.internal.inject.InjectionManagerSupplier;
@@ -111,7 +111,7 @@
         asyncInterceptorFactories = new ArrayList<>();
         config = ConfigProvider.getConfig();
         configWrapper = new ConfigWrapper(clientBuilder.getConfiguration());
-        executorService = Executors::newCachedThreadPool;
+        executorService = () -> VirtualThreadUtil.withConfig(configWrapper).newCachedThreadPool();
     }
 
     @Override
diff --git a/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/RestClientProducer.java b/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/RestClientProducer.java
index d583419..1fbf420 100644
--- a/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/RestClientProducer.java
+++ b/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/RestClientProducer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2023 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -24,7 +24,7 @@
 import java.net.URI;
 import java.net.URL;
 import java.nio.file.Files;
-import java.nio.file.Paths;
+import java.nio.file.Path;
 import java.security.AccessController;
 import java.security.KeyStore;
 import java.security.KeyStoreException;
@@ -350,7 +350,7 @@
             }
             return result;
         } else if (location.startsWith(FILE_LOCATION)) {
-            return Files.newInputStream(Paths.get(URI.create(location)));
+            return Files.newInputStream(Path.of(URI.create(location)));
         } else {
             throw new IllegalStateException("Location of keystore must start with either classpath: or file:, but is: "
                                                     + location
diff --git a/ext/mvc-thymeleaf/pom.xml b/ext/mvc-thymeleaf/pom.xml
new file mode 100644
index 0000000..124bfa5
--- /dev/null
+++ b/ext/mvc-thymeleaf/pom.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2024 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">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <artifactId>project</artifactId>
+        <groupId>org.glassfish.jersey.ext</groupId>
+        <version>3.5.99-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-mvc-thymeleaf</artifactId>
+    <name>jersey-ext-mvc-thymeleaf</name>
+
+    <description>
+        Jersey extension module providing support for Thymeleaf templates.
+    </description>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <inherited>true</inherited>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Export-Package>org.glassfish.jersey.server.mvc.thymeleaf.*;version=${project.version}</Export-Package>
+                    </instructions>
+                    <unpackBundle>true</unpackBundle>
+                </configuration>
+            </plugin>
+        </plugins>
+        <resources>
+            <resource>
+                <directory>${project.build.directory}/legal</directory>
+            </resource>
+        </resources>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>jakarta.servlet</groupId>
+            <artifactId>jakarta.servlet-api</artifactId>
+            <version>${servlet6.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-mvc</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.thymeleaf</groupId>
+            <artifactId>thymeleaf</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-api</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.javassist</groupId>
+                    <artifactId>javassist</artifactId>
+                </exclusion>
+            </exclusions>
+            <version>${thymeleaf.version}</version>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/ThymeleafConfigurationFactory.java b/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/ThymeleafConfigurationFactory.java
new file mode 100644
index 0000000..aba4aac
--- /dev/null
+++ b/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/ThymeleafConfigurationFactory.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2024 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.mvc.thymeleaf;
+
+import org.thymeleaf.TemplateEngine;
+
+public interface ThymeleafConfigurationFactory {
+    TemplateEngine getTemplateEngine();
+}
diff --git a/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/ThymeleafDefaultConfigurationFactory.java b/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/ThymeleafDefaultConfigurationFactory.java
new file mode 100644
index 0000000..588c475
--- /dev/null
+++ b/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/ThymeleafDefaultConfigurationFactory.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2024 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.mvc.thymeleaf;
+
+import jakarta.ws.rs.core.Configuration;
+import org.glassfish.jersey.internal.util.PropertiesHelper;
+import org.thymeleaf.TemplateEngine;
+import org.thymeleaf.messageresolver.IMessageResolver;
+import org.thymeleaf.messageresolver.StandardMessageResolver;
+import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
+import org.thymeleaf.templateresolver.ITemplateResolver;
+
+import java.util.Map;
+
+/**
+ * Handy {@link ThymeleafConfigurationFactory} that supplies a minimally
+ * configured {@link org.thymeleaf.TemplateEngine } able to
+ * render Thymeleaf templates.
+ * The recommended method to provide custom Thymeleaf engine settings is to
+ * sub-class this class, further customize the
+ * {@link org.thymeleaf.TemplateEngine settings} as desired in that
+ * class, and then register the sub-class with the {@link ThymeleafMvcFeature}
+ * TEMPLATE_OBJECT_FACTORY property.
+ *
+ * @author Dmytro Dovnar (dimonmc@gmail.com)
+ */
+public class ThymeleafDefaultConfigurationFactory implements ThymeleafConfigurationFactory {
+    private final Configuration config;
+    private final TemplateEngine templateEngine;
+
+    public ThymeleafDefaultConfigurationFactory(Configuration config) {
+        this.config = config;
+        this.templateEngine = initTemplateEngine();
+    }
+
+    @Override
+    public TemplateEngine getTemplateEngine() {
+        return templateEngine;
+    }
+
+    private ITemplateResolver getTemplateResolver() {
+        Map<String, Object> properties = config.getProperties();
+        String basePath = (String) PropertiesHelper.getValue(properties,
+                "jersey.config.server.mvc.templateBasePath" + ThymeleafMvcFeature.SUFFIX,
+                String.class, (Map) null);
+        if (basePath == null) {
+            basePath = (String) PropertiesHelper.getValue(properties,
+                    "jersey.config.server.mvc.templateBasePath", "", (Map) null);
+        }
+
+        if (basePath != null && !basePath.startsWith("/")) {
+            basePath = "/" + basePath;
+        }
+
+        String templateFileSuffix = (String) PropertiesHelper.getValue(properties,
+                "jersey.config.server.mvc.templateFileSuffix" + ThymeleafMvcFeature.SUFFIX,
+                ".html", (Map) null);
+
+        String templateFileMode = (String) PropertiesHelper.getValue(properties,
+                "jersey.config.server.mvc.templateMode" + ThymeleafMvcFeature.SUFFIX,
+                "HTML5", (Map) null);
+
+        Boolean cacheEnabled = (Boolean) PropertiesHelper.getValue(properties,
+                "jersey.config.server.mvc.caching" + ThymeleafMvcFeature.SUFFIX, Boolean.class, (Map) null);
+        if (cacheEnabled == null) {
+            cacheEnabled = (Boolean) PropertiesHelper.getValue(properties,
+                    "jersey.config.server.mvc.caching", false, (Map) null);
+        }
+
+        Long cacheLiveMs = (Long) PropertiesHelper.getValue(properties,
+                "jersey.config.server.mvc.cacheTTLMs" + ThymeleafMvcFeature.SUFFIX, 3600000L, (Map) null);
+
+        ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
+        templateResolver.setPrefix(basePath);
+        templateResolver.setSuffix(templateFileSuffix);
+        templateResolver.setTemplateMode(templateFileMode);
+        templateResolver.setCacheTTLMs(cacheLiveMs);
+        templateResolver.setCacheable(cacheEnabled);
+        return templateResolver;
+    }
+
+    private TemplateEngine initTemplateEngine() {
+        TemplateEngine templateEngine = new TemplateEngine();
+        templateEngine.setTemplateResolver(getTemplateResolver());
+        return templateEngine;
+    }
+
+    private IMessageResolver getMessageResolver() {
+        StandardMessageResolver messageResolver = new StandardMessageResolver();
+        return messageResolver;
+    }
+}
diff --git a/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/ThymeleafMvcFeature.java b/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/ThymeleafMvcFeature.java
new file mode 100644
index 0000000..b43b9a6
--- /dev/null
+++ b/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/ThymeleafMvcFeature.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2024 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.mvc.thymeleaf;
+
+import jakarta.ws.rs.ConstrainedTo;
+import jakarta.ws.rs.RuntimeType;
+import jakarta.ws.rs.core.Configuration;
+import jakarta.ws.rs.core.Feature;
+import jakarta.ws.rs.core.FeatureContext;
+import org.glassfish.jersey.server.mvc.MvcFeature;
+
+@ConstrainedTo(RuntimeType.SERVER)
+public final class ThymeleafMvcFeature implements Feature {
+    public static final String SUFFIX = ".thymeleaf";
+    public static final String TEMPLATE_BASE_PATH = MvcFeature.TEMPLATE_BASE_PATH + SUFFIX;
+    public static final String CACHE_TEMPLATES = MvcFeature.CACHE_TEMPLATES + SUFFIX;
+    public static final String TEMPLATE_OBJECT_FACTORY = MvcFeature.TEMPLATE_OBJECT_FACTORY + SUFFIX;
+    public static final String ENCODING = MvcFeature.ENCODING + SUFFIX;
+
+    public static final String TEMPLATE_FILE_SUFFIX = "jersey.config.server.mvc.templateFileSuffix" + SUFFIX;
+    public static final String TEMPLATE_MODE = "jersey.config.server.mvc.templateMode" + SUFFIX;
+    public static final String CACHE_TTLMS = "jersey.config.server.mvc.cacheTTLMs" + SUFFIX;
+
+    @Override
+    public boolean configure(FeatureContext context) {
+        final Configuration config = context.getConfiguration();
+
+        if (!config.isRegistered(ThymeleafViewProcessor.class)) {
+            context.register(ThymeleafViewProcessor.class);
+
+            // MvcFeature.
+            if (!config.isRegistered(MvcFeature.class)) {
+                context.register(MvcFeature.class);
+            }
+
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/ThymeleafSuppliedConfigurationFactory.java b/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/ThymeleafSuppliedConfigurationFactory.java
new file mode 100644
index 0000000..4f21ce1
--- /dev/null
+++ b/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/ThymeleafSuppliedConfigurationFactory.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2024 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.mvc.thymeleaf;
+
+import org.thymeleaf.TemplateEngine;
+
+/**
+ * {@link ThymeleafConfigurationFactory} that supplies an unchanged
+ * {@link ThymeleafConfigurationFactory Configuration} as passed-in to
+ * the constructor.
+ * <p/>
+ * Used to support backwards-compatibility in {@link ThymeleafViewProcessor}
+ * to wrap directly-configured {@link org.thymeleaf.TemplateEngine}
+ * objects instead of the recommended {@link ThymeleafDefaultConfigurationFactory}
+ * or a sub-class thereof.
+ *
+ * @author Dmytro Dovnar (dimonmc@gmail.com)
+ */
+public class ThymeleafSuppliedConfigurationFactory implements ThymeleafConfigurationFactory {
+    private final ThymeleafConfigurationFactory configurationFactory;
+
+    public ThymeleafSuppliedConfigurationFactory(ThymeleafConfigurationFactory configurationFactory) {
+        this.configurationFactory = configurationFactory;
+    }
+
+    @Override
+    public TemplateEngine getTemplateEngine() {
+        return configurationFactory.getTemplateEngine();
+    }
+
+}
diff --git a/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/ThymeleafViewProcessor.java b/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/ThymeleafViewProcessor.java
new file mode 100644
index 0000000..fc8139c
--- /dev/null
+++ b/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/ThymeleafViewProcessor.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2024 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.mvc.thymeleaf;
+
+import jakarta.inject.Inject;
+import jakarta.servlet.ServletContext;
+import jakarta.ws.rs.core.Configuration;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.MultivaluedMap;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.util.collection.Values;
+import org.glassfish.jersey.server.mvc.Viewable;
+import org.glassfish.jersey.server.mvc.spi.AbstractTemplateProcessor;
+import org.thymeleaf.TemplateEngine;
+import org.thymeleaf.context.Context;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * {@link org.glassfish.jersey.server.mvc.spi.TemplateProcessor Template processor} providing support for Thymeleaf templates.
+ *
+ * @author Dmytro Dovnar (dimonmc@gmail.com)
+ */
+public final class ThymeleafViewProcessor extends AbstractTemplateProcessor<TemplateEngine> {
+    private final ThymeleafConfigurationFactory factory;
+
+    /**
+     * Create an instance of this processor with injected {@link jakarta.ws.rs.core.Configuration config}.
+     *
+     * @param config           config to configure this processor from.
+     * @param injectionManager injection manager.
+     */
+    @Inject
+    public ThymeleafViewProcessor(Configuration config, InjectionManager injectionManager) {
+        super(config, injectionManager.getInstance(ServletContext.class), "thymeleaf", "html");
+        this.factory = getTemplateObjectFactory(injectionManager::createAndInitialize, ThymeleafConfigurationFactory.class,
+                () -> {
+                    ThymeleafConfigurationFactory configuration =
+                            getTemplateObjectFactory(
+                                    injectionManager::createAndInitialize,
+                                    ThymeleafConfigurationFactory.class, Values.empty());
+                    if (configuration == null) {
+                        return new ThymeleafDefaultConfigurationFactory(config);
+                    } else {
+                        return new ThymeleafSuppliedConfigurationFactory(configuration);
+                    }
+                });
+    }
+
+    @Override
+    protected TemplateEngine resolve(final String templatePath, final Reader reader) throws Exception {
+        return factory.getTemplateEngine();
+    }
+
+    @Override
+    public void writeTo(final TemplateEngine templateEngine, final Viewable viewable, final MediaType mediaType,
+                        final MultivaluedMap<String, Object> httpHeaders, final OutputStream out) throws IOException {
+        Context context = new Context();
+
+        Object model = viewable.getModel();
+        if (!(model instanceof Map)) {
+            context.setVariable("model", viewable.getModel());
+        } else {
+            context.setVariables((Map) viewable.getModel());
+        }
+
+        if (context.containsVariable("lang")) {
+            Object langValue = context.getVariable("lang");
+            if (langValue instanceof Locale) {
+                context.setLocale((Locale) langValue);
+            } else if (langValue instanceof String) {
+                Locale locale = Locale.forLanguageTag((String) langValue);
+                context.setLocale(locale);
+            }
+        }
+
+        Charset encoding = setContentType(mediaType, httpHeaders);
+
+        final Writer writer = new BufferedWriter(new OutputStreamWriter(out, encoding));
+        templateEngine.process(viewable.getTemplateName(), context, writer);
+    }
+}
diff --git a/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/package-info.java b/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/package-info.java
new file mode 100644
index 0000000..d8cd7ae
--- /dev/null
+++ b/ext/mvc-thymeleaf/src/main/java/org/glassfish/jersey/server/mvc/thymeleaf/package-info.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2024 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.mvc.thymeleaf;
diff --git a/ext/mvc/src/main/java/org/glassfish/jersey/server/mvc/spi/AbstractTemplateProcessor.java b/ext/mvc/src/main/java/org/glassfish/jersey/server/mvc/spi/AbstractTemplateProcessor.java
index ed0c590..b375afe 100644
--- a/ext/mvc/src/main/java/org/glassfish/jersey/server/mvc/spi/AbstractTemplateProcessor.java
+++ b/ext/mvc/src/main/java/org/glassfish/jersey/server/mvc/spi/AbstractTemplateProcessor.java
@@ -16,13 +16,13 @@
 
 package org.glassfish.jersey.server.mvc.spi;
 
-import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
 import java.nio.charset.Charset;
 import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -182,7 +182,7 @@
             // File-system path.
             if (reader == null) {
                 try {
-                    reader = new InputStreamReader(Files.newInputStream(new File(template).toPath()), encoding);
+                    reader = new InputStreamReader(Files.newInputStream(Path.of(template)), encoding);
                 } catch (final IOException ioe) {
                     // NOOP.
                 }
diff --git a/ext/pom.xml b/ext/pom.xml
index 3a78913..fd4f43c 100644
--- a/ext/pom.xml
+++ b/ext/pom.xml
@@ -50,6 +50,7 @@
         <module>mvc-freemarker</module>
         <module>mvc-jsp</module>
         <module>mvc-mustache</module>
+        <module>mvc-thymeleaf</module>
         <module>proxy-client</module>
         <module>rx</module>
         <module>spring6</module>
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 6bca315..aeffb03 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
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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
@@ -35,6 +35,7 @@
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -43,6 +44,7 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
  * Collector to retrieve parameters for setting up the HTTP request sent in the invoke method of WebResourceFactory
@@ -145,7 +147,7 @@
             throws IllegalAccessException, IntrospectionException, InvocationTargetException {
         Class<?> beanClass = beanParam.getClass();
         List<Field> fields = new ArrayList<>();
-        getAllFields(fields, beanClass);
+        getAllNonStaticFields(fields, beanClass);
 
         for (final Field field : fields) {
             Object value = null;
@@ -156,7 +158,7 @@
                 anns.put(ann.annotationType(), ann);
             }
 
-            if (hasAnyParamAnnotation(anns)) {
+            if (field.canAccess(beanParam) && hasAnyParamAnnotation(anns)) {
                 value = field.get(beanParam);
             } else {
                 // get getter annotations if there are no field annotations
@@ -178,11 +180,15 @@
         }
     }
 
-    private List<Field> getAllFields(List<Field> fields, Class<?> type) {
-        fields.addAll(Arrays.asList(type.getDeclaredFields()));
+    private List<Field> getAllNonStaticFields(List<Field> fields, Class<?> type) {
+
+        List<Field> nonStaticFields = Arrays.stream(type.getDeclaredFields())
+                .filter(field -> !Modifier.isStatic(field.getModifiers()))
+                .collect(Collectors.toList());
+        fields.addAll(nonStaticFields);
 
         if (type.getSuperclass() != null) {
-            getAllFields(fields, type.getSuperclass());
+            getAllNonStaticFields(fields, type.getSuperclass());
         }
 
         return fields;
diff --git a/ext/proxy-client/src/test/java/org/glassfish/jersey/client/proxy/MyBeanParamWithPrivateField.java b/ext/proxy-client/src/test/java/org/glassfish/jersey/client/proxy/MyBeanParamWithPrivateField.java
new file mode 100644
index 0000000..06250c7
--- /dev/null
+++ b/ext/proxy-client/src/test/java/org/glassfish/jersey/client/proxy/MyBeanParamWithPrivateField.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2024 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.client.proxy;
+
+
+import jakarta.ws.rs.QueryParam;
+
+/**
+ * @author Divyansh Shekhar Gaur
+ */
+public class MyBeanParamWithPrivateField {
+
+    @QueryParam("privateFieldParam")
+    private String privateFieldParam;
+
+    private static String privateStaticField;
+
+    static String staticField;
+
+    public MyBeanParamWithPrivateField() {}
+
+    public String getPrivateFieldParam() {
+        return privateFieldParam;
+    }
+
+    public void setPrivateFieldParam(String privateFieldParam) {
+        this.privateFieldParam = privateFieldParam;
+    }
+
+    public static String getPrivateStaticField() {
+        return privateStaticField;
+    }
+
+    public static void setPrivateStaticField(String privateStaticField) {
+        MyBeanParamWithPrivateField.privateStaticField = privateStaticField;
+    }
+
+    public static String getStaticField() {
+        return staticField;
+    }
+
+    public static void setStaticField(String staticField) {
+        MyBeanParamWithPrivateField.staticField = staticField;
+    }
+}
diff --git a/ext/proxy-client/src/test/java/org/glassfish/jersey/client/proxy/MyResourceWithBeanParam.java b/ext/proxy-client/src/test/java/org/glassfish/jersey/client/proxy/MyResourceWithBeanParam.java
index 1b49f50..81fd9b4 100644
--- a/ext/proxy-client/src/test/java/org/glassfish/jersey/client/proxy/MyResourceWithBeanParam.java
+++ b/ext/proxy-client/src/test/java/org/glassfish/jersey/client/proxy/MyResourceWithBeanParam.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024 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
@@ -54,6 +54,11 @@
     }
 
     @Override
+    public String echoPrivateField(@BeanParam MyBeanParamWithPrivateField bean) {
+        return bean.getPrivateFieldParam();
+    }
+
+    @Override
     public String echo(MyBeanParam bean) {
         return ("HEADER=" + bean.getHeaderParam() + ",PATH=" + bean.getPathParam() + ",FORM="
                 + bean.getFormParam1() + "," + bean.getFormParam2() + ",QUERY=" + bean.getQueryParam()
diff --git a/ext/proxy-client/src/test/java/org/glassfish/jersey/client/proxy/MyResourceWithBeanParamIfc.java b/ext/proxy-client/src/test/java/org/glassfish/jersey/client/proxy/MyResourceWithBeanParamIfc.java
index 5e73615..f4b7f5c 100644
--- a/ext/proxy-client/src/test/java/org/glassfish/jersey/client/proxy/MyResourceWithBeanParamIfc.java
+++ b/ext/proxy-client/src/test/java/org/glassfish/jersey/client/proxy/MyResourceWithBeanParamIfc.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024 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,12 @@
 
     @POST
     @Consumes("application/x-www-form-urlencoded")
+    @Path("getPrivateField")
+    @Produces("text/plain")
+    public String echoPrivateField(@BeanParam MyBeanParamWithPrivateField bean);
+
+    @POST
+    @Consumes("application/x-www-form-urlencoded")
     @Path("all/{pathParam}")
     @Produces("text/plain")
     public String echo(@BeanParam MyBeanParam bean);
diff --git a/ext/proxy-client/src/test/java/org/glassfish/jersey/client/proxy/WebResourceFactoryBeanParamTest.java b/ext/proxy-client/src/test/java/org/glassfish/jersey/client/proxy/WebResourceFactoryBeanParamTest.java
index d9b361a..6819e0c 100644
--- a/ext/proxy-client/src/test/java/org/glassfish/jersey/client/proxy/WebResourceFactoryBeanParamTest.java
+++ b/ext/proxy-client/src/test/java/org/glassfish/jersey/client/proxy/WebResourceFactoryBeanParamTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2024 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
@@ -138,4 +138,14 @@
 
         assertEquals("query", response);
     }
+
+    @Test
+    public void testBeanParamPrivateFieldQuery() {
+        MyBeanParamWithPrivateField myGetBeanParam = new MyBeanParamWithPrivateField();
+        myGetBeanParam.setPrivateFieldParam("query");
+
+        String response = resourceWithBeanParam.echoPrivateField(myGetBeanParam);
+
+        assertEquals("query", response);
+    }
 }
\ No newline at end of file
diff --git a/ext/spring6/pom.xml b/ext/spring6/pom.xml
index ea337d7..c80cf53 100644
--- a/ext/spring6/pom.xml
+++ b/ext/spring6/pom.xml
@@ -81,7 +81,6 @@
         <dependency>
             <groupId>commons-logging</groupId>
             <artifactId>commons-logging</artifactId>
-            <version>1.2</version>
             <scope>test</scope>
         </dependency>
 
@@ -94,6 +93,22 @@
                     <groupId>org.ow2.asm</groupId>
                     <artifactId>asm</artifactId>
                 </exclusion>
+                <exclusion>
+                    <groupId>org.ow2.asm</groupId>
+                    <artifactId>asm-util</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.ow2.asm</groupId>
+                    <artifactId>asm-tree</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.ow2.asm</groupId>
+                    <artifactId>asm-analysis</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>jakarta.annotation</groupId>
+                    <artifactId>jakarta.annotation-api</artifactId>
+                </exclusion>
             </exclusions>
         </dependency>
 
@@ -114,6 +129,10 @@
                     <groupId>org.springframework</groupId>
                     <artifactId>spring-context</artifactId>
                 </exclusion>
+                <exclusion>
+                    <groupId>jakarta.inject</groupId>
+                    <artifactId>jakarta.inject-api</artifactId>
+                </exclusion>
             </exclusions>
         </dependency>
 
@@ -210,6 +229,23 @@
 
     <profiles>
         <profile>
+            <id>HK2_JDK8_dependencyConvergence_skip</id>
+            <activation>
+                <jdk>1.8</jdk>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-enforcer-plugin</artifactId>
+                        <configuration>
+                            <rulesToSkip>dependencyConvergence</rulesToSkip>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
             <id>SpringExclude</id>
             <activation>
                 <jdk>[1.8,17)</jdk>
diff --git a/ext/spring6/src/main/java17/org/glassfish/jersey/server/spring/scope/RequestContextFilter.java b/ext/spring6/src/main/java17/org/glassfish/jersey/server/spring/scope/RequestContextFilter.java
index a489695..11f7984 100644
--- a/ext/spring6/src/main/java17/org/glassfish/jersey/server/spring/scope/RequestContextFilter.java
+++ b/ext/spring6/src/main/java17/org/glassfish/jersey/server/spring/scope/RequestContextFilter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2024 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
@@ -98,7 +98,9 @@
                 final AbstractRequestAttributes attributes =
                         (AbstractRequestAttributes) requestContext.getProperty(REQUEST_ATTRIBUTES_PROPERTY);
                 RequestContextHolder.resetRequestAttributes();
-                attributes.requestCompleted();
+                if (attributes != null) {
+                    attributes.requestCompleted();
+                }
             }
         } : EMPTY_ATTRIBUTE_CONTROLLER;
     }
diff --git a/ext/spring6/src/test/java/org/glassfish/jersey/server/spring/filter/SpringRequestContextFilterTest.java b/ext/spring6/src/test/java/org/glassfish/jersey/server/spring/filter/SpringRequestContextFilterTest.java
new file mode 100644
index 0000000..3e08125
--- /dev/null
+++ b/ext/spring6/src/test/java/org/glassfish/jersey/server/spring/filter/SpringRequestContextFilterTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2024 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.spring.filter;
+
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.inject.Injections;
+import org.glassfish.jersey.server.spring.scope.RequestContextFilter;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.ApplicationContext;
+import org.springframework.web.context.WebApplicationContext;
+
+import jakarta.ws.rs.container.ContainerRequestContext;
+import jakarta.ws.rs.container.ContainerResponseContext;
+import java.io.IOException;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+public class SpringRequestContextFilterTest {
+    @Test
+    public void testMissingAttributes() throws IOException {
+        WebApplicationContext webAppCtx = (WebApplicationContext) Proxy.newProxyInstance(
+                WebApplicationContext.class.getClassLoader(),
+                new Class[]{WebApplicationContext.class},
+                new InvocationHandler() {
+                    @Override
+                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                        return null;
+                    }
+                });
+
+        InjectionManager injectionManager = Injections.createInjectionManager();
+        injectionManager.register(new AbstractBinder() {
+            @Override
+            protected void configure() {
+                bind(webAppCtx).to(ApplicationContext.class);
+            }
+        });
+        injectionManager.completeRegistration();
+
+        ContainerRequestContext requestContext = (ContainerRequestContext) Proxy.newProxyInstance(
+                ContainerRequestContext.class.getClassLoader(),
+                new Class[]{ContainerRequestContext.class},
+                new InvocationHandler() {
+                    @Override
+                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                        return null;
+                    }
+                });
+
+        RequestContextFilter filter = new RequestContextFilter(injectionManager);
+        filter.filter(requestContext, (ContainerResponseContext) null);
+    }
+}
diff --git a/ext/wadl-doclet/pom.xml b/ext/wadl-doclet/pom.xml
index 1d64afe..96c5cc6 100644
--- a/ext/wadl-doclet/pom.xml
+++ b/ext/wadl-doclet/pom.xml
@@ -35,8 +35,8 @@
     
     <properties>
         <java.sourceDirectory>${project.basedir}/src/main/java</java.sourceDirectory>
-        <java8_11.build.outputDirectory>${project.basedir}/target</java8_11.build.outputDirectory>
-        <java8_11.sourceDirectory>${project.basedir}/src/main/java8_11</java8_11.sourceDirectory>
+        <java11.build.outputDirectory>${project.basedir}/target</java11.build.outputDirectory>
+        <java11.sourceDirectory>${project.basedir}/src/main/java8_11</java11.sourceDirectory>
         <java12.build.outputDirectory>${project.basedir}/target-java12</java12.build.outputDirectory>
         <java12.sourceDirectory>${project.basedir}/src/main/java12</java12.sourceDirectory>
     </properties>
@@ -44,12 +44,12 @@
 
     <profiles>
         <profile>
-            <id>jdk1.8_11</id>
+            <id>jdk11</id>
             <activation>
-                <jdk>[1.8,12)</jdk>
+                <jdk>[11,12)</jdk>
             </activation>
             <build>
-            	<directory>${java8_11.build.outputDirectory}</directory>
+            	<directory>${java11.build.outputDirectory}</directory>
                 <plugins>
                     <plugin>
                         <groupId>org.codehaus.mojo</groupId>
@@ -63,7 +63,7 @@
                                 <configuration>
                                     <sources>
                                         <source>${java.sourceDirectory}</source>
-                                        <source>${java8_11.sourceDirectory}</source>
+                                        <source>${java11.sourceDirectory}</source>
                                     </sources>
                                 </configuration>
                             </execution>
@@ -109,7 +109,7 @@
                     <!-- ${java12.build.outputDirectory} does not work here -->
                     <exists>target-java12/classes/org/glassfish/jersey/wadl/doclet/ResourceDoclet.class</exists>
                 </file>
-                <jdk>[1.8,12)</jdk>
+                <jdk>[11,12)</jdk>
             </activation>
             <build>
                 <plugins>
@@ -136,7 +136,7 @@
                                     <goal>copy-resources</goal>
                                 </goals>
                                 <configuration>
-                                    <outputDirectory>${java8_11.build.outputDirectory}/classes/META-INF/versions/12</outputDirectory>
+                                    <outputDirectory>${java11.build.outputDirectory}/classes/META-INF/versions/12</outputDirectory>
                                     <resources>
                                         <resource>
                                             <directory>${java12.build.outputDirectory}/classes</directory>
@@ -155,7 +155,7 @@
                                 <phase>package</phase>
                                 <configuration>
                                     <target>
-                                    	<property name="sources-jar" value="${java8_11.build.outputDirectory}/${project.artifactId}-${project.version}-sources.jar"/>
+                                    	<property name="sources-jar" value="${java11.build.outputDirectory}/${project.artifactId}-${project.version}-sources.jar"/>
                                     	<echo>sources-jar: ${sources-jar}</echo>
                                     	<zip destfile="${sources-jar}" update="true">
                                     		<zipfileset dir="${java12.sourceDirectory}" prefix="META-INF/versions/12"/>
diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/BeanHelper.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/BeanHelper.java
index b653b54..a1c5032 100644
--- a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/BeanHelper.java
+++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/BeanHelper.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2024 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
@@ -129,13 +129,7 @@
          * CDI does not provide sufficient support for ThreadScoped Supplier
          */
         if (binding.getScope() == PerThread.class) {
-            BeanManagerImpl manager;
-            if (beanManager instanceof BeanManagerProxy) {
-                manager = ((BeanManagerProxy) beanManager).unwrap();
-            } else {
-                manager = (BeanManagerImpl) beanManager;
-            }
-            abd.addBean(new InitializableSupplierThreadScopeBean(runtimeType, binding, manager));
+            abd.addBean(new InitializableSupplierThreadScopeBean(runtimeType, binding, beanManagerImpl(beanManager)));
         } else {
             abd.addBean(new InitializableSupplierInstanceBean<>(runtimeType, binding));
             abd.addBean(new InitializableSupplierInstanceBeanBridge<>(runtimeType, binding));
@@ -164,12 +158,18 @@
         InjectionTarget<Supplier<T>> jit = getJerseyInjectionTarget(supplierClass, injectionTarget, supplierBean, resolvers);
         supplierBean.setInjectionTarget(jit);
 
-        final SupplierBeanBridge supplierBeanBridge = new SupplierBeanBridge(runtimeType, binding, beanManager);
-
-        abd.addBean(supplierBean);
-        abd.addBean(supplierBeanBridge);
-
-        return new BindingBeanPair(binding, supplierBean, supplierBeanBridge);
+        /*
+         * CDI does not provide sufficient support for ThreadScoped Supplier
+         */
+        if (binding.getScope() == PerThread.class) {
+            abd.addBean(new SupplierThreadScopeClassBean(runtimeType, binding, supplierBean, beanManagerImpl(beanManager)));
+            return null;
+        } else {
+            final SupplierBeanBridge supplierBeanBridge = new SupplierBeanBridge(runtimeType, binding, beanManager);
+            abd.addBean(supplierBean);
+            abd.addBean(supplierBeanBridge);
+            return new BindingBeanPair(binding, supplierBean, supplierBeanBridge);
+        }
     }
 
     /**
@@ -258,6 +258,14 @@
         return null;
     }
 
+    private static BeanManagerImpl beanManagerImpl(BeanManager beanManager) {
+        if (beanManager instanceof BeanManagerProxy) {
+            return ((BeanManagerProxy) beanManager).unwrap();
+        } else {
+            return (BeanManagerImpl) beanManager;
+        }
+    }
+
     private static <T> InjectionTarget<T> getJerseyInjectionTarget(Class<T> clazz, InjectionTarget<T> injectionTarget,
             Bean<T> bean, Collection<InjectionResolver> resolvers) {
         BasicInjectionTarget<T> it = (BasicInjectionTarget<T>) injectionTarget;
diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/InitializableSupplierThreadScopeBean.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/InitializableSupplierThreadScopeBean.java
index c5f211e..3415b46 100644
--- a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/InitializableSupplierThreadScopeBean.java
+++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/InitializableSupplierThreadScopeBean.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2024 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
@@ -111,31 +111,4 @@
         ProxyFactory<T> factory = new ProxyFactory<>(contextId, getBeanClass(), getTypes(), this);
         return factory.create(beanInstance);
     }
-
-    private static class ThreadScopeBeanInstance<T> extends ContextBeanInstance<T> {
-
-        private final WeakHashMap<Thread, Object> instances = new WeakHashMap<>();
-
-        private final Supplier<T> supplier;
-
-        /**
-         * Creates a new invocation handler with supplier which provides a current injected value in proper scope.
-         *
-         * @param supplier provider of the value.
-         */
-        private ThreadScopeBeanInstance(Supplier<T> supplier, Bean<T> bean, String contextId) {
-            super(bean, new StringBeanIdentifier(((PassivationCapable) bean).getId()), contextId);
-            this.supplier = supplier;
-        }
-
-        @Override
-        public Object invoke(Object obj, Method method, Object... arguments) throws Throwable {
-            Object instance = instances.computeIfAbsent(Thread.currentThread(), thread -> supplier.get());
-            return super.invoke(instance, method, arguments);
-        }
-
-        public void dispose() {
-            this.instances.clear();
-        }
-    }
 }
diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/SupplierThreadScopeClassBean.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/SupplierThreadScopeClassBean.java
new file mode 100644
index 0000000..fe3e8b4
--- /dev/null
+++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/SupplierThreadScopeClassBean.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2024 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.inject.weld.internal.bean;
+
+import org.glassfish.jersey.internal.inject.SupplierClassBinding;
+import org.glassfish.jersey.internal.inject.SupplierInstanceBinding;
+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.jboss.weld.bean.proxy.BeanInstance;
+import org.jboss.weld.bean.proxy.ProxyFactory;
+import org.jboss.weld.manager.BeanManagerImpl;
+
+import jakarta.enterprise.context.Dependent;
+import jakarta.enterprise.context.spi.CreationalContext;
+import jakarta.ws.rs.RuntimeType;
+import java.lang.annotation.Annotation;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+
+
+/**
+ * Creates an implementation of {@link jakarta.enterprise.inject.spi.Bean} interface using Jersey's {@link SupplierInstanceBinding}.
+ * Binding provides the information about the bean also called {@link jakarta.enterprise.inject.spi.BeanAttributes} information.
+ * The {@code Bean} does not use {@link org.glassfish.jersey.inject.weld.internal.injector.JerseyInjectionTarget} because serves already
+ * created proxy, therefore the create operation just return provided instance without any other contextual operation
+ * (produce, inject, destroy).
+ * <p>
+ * This bean is special and is used only for service registered as a {@link org.glassfish.jersey.internal.inject.PerThread} and
+ * works through the proxy which serves the correct instance per the given thread.
+ * <p>
+ * Register example:
+ * <pre>
+ * AbstractBinder {
+ *     &#64;Override
+ *     protected void configure() {
+ *         bindFactory(MyFactoryInjectionSupplier.class)
+ *              .to(MyBean.class)
+ *              .in(PerThread.class);
+ *     }
+ * }
+ * </pre>
+ * Inject example:
+ * <pre>
+ * &#64;Path("/")
+ * public class MyResource {
+ *   &#64;Inject
+ *   private MyBean myBean&#59;
+ * }
+ * </pre>
+ */
+class SupplierThreadScopeClassBean extends JerseyBean<Object> {
+    private final LazyValue<ThreadScopeBeanInstance<Object>> beanInstance;
+    private final SupplierClassBinding binding;
+    private final LazyValue<Object> proxy;
+    private final AtomicReference<CreationalContext> creationalContextAtomicReference = new AtomicReference<>();
+
+    SupplierThreadScopeClassBean(RuntimeType runtimeType,
+                                              SupplierClassBinding binding,
+                                              SupplierClassBean supplierClassBean,
+                                              BeanManagerImpl beanManager) {
+        super(runtimeType, binding);
+        this.binding = binding;
+        this.beanInstance = Values.lazy((Value<ThreadScopeBeanInstance<Object>>) () -> {
+            Supplier supplierInstance = supplierClassBean.create(creationalContextAtomicReference.get());
+            ThreadScopeBeanInstance scopeBeanInstance =
+                    new ThreadScopeBeanInstance(supplierInstance, this, beanManager.getContextId());
+            return scopeBeanInstance;
+        });
+        this.proxy = Values.lazy((Value<Object>) () -> createClientProxy(beanInstance.get(), beanManager.getContextId()));
+    }
+
+    @Override
+    public Class<? extends Annotation> getScope() {
+        return Dependent.class;
+    }
+
+    @Override
+    public Object create(CreationalContext<Object> ctx) {
+        creationalContextAtomicReference.set(ctx);
+        return proxy.get();
+    }
+
+    @Override
+    public void destroy(Object instance, CreationalContext<Object> creationalContext) {
+        if (beanInstance.isInitialized()) {
+            this.beanInstance.get().dispose();
+        }
+    }
+
+    @Override
+    public Class<?> getBeanClass() {
+        return (Class<?>) this.binding.getContracts().iterator().next();
+    }
+
+    private <T> T createClientProxy(BeanInstance beanInstance, String contextId) {
+        ProxyFactory<T> factory = new ProxyFactory<>(contextId, getBeanClass(), getTypes(), this);
+        return factory.create(beanInstance);
+    }
+}
diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/ThreadScopeBeanInstance.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/ThreadScopeBeanInstance.java
new file mode 100644
index 0000000..73ac651
--- /dev/null
+++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/ThreadScopeBeanInstance.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2021, 2024 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.inject.weld.internal.bean;
+
+import org.jboss.weld.bean.StringBeanIdentifier;
+import org.jboss.weld.bean.proxy.ContextBeanInstance;
+
+import jakarta.enterprise.inject.spi.Bean;
+import jakarta.enterprise.inject.spi.PassivationCapable;
+import java.lang.reflect.Method;
+import java.util.WeakHashMap;
+import java.util.function.Supplier;
+
+/**
+ * {@link org.glassfish.jersey.internal.inject.PerThread} scope bean instance used from
+ * {@link InitializableSupplierThreadScopeBean} and {@link SupplierThreadScopeClassBean}.
+ *
+ * @param <T> Typed of the bean supplied by a {@code Supplier}.
+ */
+class ThreadScopeBeanInstance<T> extends ContextBeanInstance<T> {
+
+    private final WeakHashMap<Thread, Object> instances = new WeakHashMap<>();
+
+    private final Supplier<T> supplier;
+
+    /**
+     * Creates a new invocation handler with supplier which provides a current injected value in proper scope.
+     *
+     * @param supplier provider of the value.
+     */
+    ThreadScopeBeanInstance(Supplier<T> supplier, Bean<T> bean, String contextId) {
+        super(bean, new StringBeanIdentifier(((PassivationCapable) bean).getId()), contextId);
+        this.supplier = supplier;
+    }
+
+    @Override
+    public Object invoke(Object obj, Method method, Object... arguments) throws Throwable {
+        Object instance = instances.computeIfAbsent(Thread.currentThread(), thread -> supplier.get());
+        return super.invoke(instance, method, arguments);
+    }
+
+    public void dispose() {
+        this.instances.clear();
+    }
+}
diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/BinderRegisterExtension.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/BinderRegisterExtension.java
index 60d48e4..8917bb0 100644
--- a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/BinderRegisterExtension.java
+++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/BinderRegisterExtension.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2024 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
@@ -347,8 +347,10 @@
                 }
                 BindingBeanPair pair = BeanHelper.registerSupplier(
                         runtimeType, (SupplierClassBinding<?>) binding, abd, injectionResolvers, beanManager);
-                for (Type contract : ((SupplierClassBinding<?>) binding).getContracts()) {
-                    supplierClassBindings.add(contract, pair);
+                if (pair != null) {
+                    for (Type contract : ((SupplierClassBinding<?>) binding).getContracts()) {
+                        supplierClassBindings.add(contract, pair);
+                    }
                 }
             } else if (InitializableInstanceBinding.class.isAssignableFrom(binding.getClass())) {
                 if (RuntimeType.SERVER == runtimeType
diff --git a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/CzechGreeting.java b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/CzechGreeting.java
index 68b57d9..14bd268 100644
--- a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/CzechGreeting.java
+++ b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/CzechGreeting.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2024 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
@@ -26,9 +26,11 @@
 
     static final String GREETING = "Ahoj";
 
+    private String greeting = GREETING + "#" + Thread.currentThread().getName();
+
     @Override
     public String getGreeting() {
-        return GREETING + "#" + Thread.currentThread().getName();
+        return greeting;
     }
 
     @Override
diff --git a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/TestPreinitialization.java b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/TestPreinitialization.java
index b7806c1..97d4a49 100644
--- a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/TestPreinitialization.java
+++ b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/TestPreinitialization.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2024 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
@@ -97,6 +97,13 @@
             binder.bindFactory(new ThreadScopeTest.SupplierGreeting2(ThreadScopeTest.EnglishGreeting2.GREETING))
                     .to(ThreadScopeTest.EnglishGreeting2.class)
                     .in(PerThread.class);
+
+            //testSupplierClassBindingThreadScopedInSingletonScope
+            binder.bindAsContract(ThreadScopeTest.SingletonObject3.class)
+                    .in(Singleton.class);
+            binder.bindFactory(ThreadScopeTest.SupplierGreeting3.class)
+                    .to(ThreadScopeTest.Greeting3.class)
+                    .in(PerThread.class);
         }
 
         //ClientInstanceInjectionTest
diff --git a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/ThreadScopeTest.java b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/ThreadScopeTest.java
index 144c333..7c959fe 100644
--- a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/ThreadScopeTest.java
+++ b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/ThreadScopeTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2024 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.util.concurrent.CountDownLatch;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Supplier;
 
 import jakarta.enterprise.context.RequestScoped;
@@ -181,7 +182,7 @@
     }
 
     @Test
-    public void testThreadScopedInSingletonScope() {
+    public void testThreadScopedInSingletonScope() throws InterruptedException {
 //        InjectionManager injectionManager = BindingTestHelper.createInjectionManager();
 //        BindingTestHelper.bind(injectionManager, binder -> {
 //            binder.bindAsContract(SingletonObject.class)
@@ -202,6 +203,52 @@
         assertNotNull(greeting2);
 
         assertEquals(greeting1, greeting2);
+
+        final AtomicReference<String> greetingAtomicReference = new AtomicReference<>();
+        Runnable runnable = () ->
+                greetingAtomicReference.set(injectionManager.getInstance(SingletonObject.class).getGreeting().getGreeting());
+
+        Thread newThread = new Thread(runnable);
+        newThread.start();
+        newThread.join();
+
+        assertEquals(greeting1.getGreeting(), greeting2.getGreeting());
+        assertNotEquals(greeting1.getGreeting(), greetingAtomicReference.get());
+    }
+
+    @Test
+    public void testSupplierClassBindingThreadScopedInSingletonScope() throws InterruptedException {
+//        InjectionManager injectionManager = BindingTestHelper.createInjectionManager();
+//        BindingTestHelper.bind(injectionManager, binder -> {
+//            binder.bindAsContract(SingletonObject.class)
+//                    .in(Singleton.class);
+//
+//            binder.bindFactory(SupplierGreeting.class)
+//                    .to(Greeting.class)
+//                    .in(PerThread.class);
+//        });
+
+        SingletonObject3 instance1 = injectionManager.getInstance(SingletonObject3.class);
+        Greeting3 greeting1 = instance1.getGreeting();
+        assertNotNull(greeting1);
+
+        // Precisely the same object
+        SingletonObject3 instance2 = injectionManager.getInstance(SingletonObject3.class);
+        Greeting3 greeting2 = instance2.getGreeting();
+        assertNotNull(greeting2);
+
+        assertEquals(greeting1, greeting2);
+
+        final AtomicReference<String> greetingAtomicReference = new AtomicReference<>();
+        Runnable runnable = () ->
+                greetingAtomicReference.set(injectionManager.getInstance(SingletonObject3.class).getGreeting().getGreeting());
+
+        Thread newThread = new Thread(runnable);
+        newThread.start();
+        newThread.join();
+
+        assertEquals(greeting1.getGreeting(), greeting2.getGreeting());
+        assertNotEquals(greeting1.getGreeting(), greetingAtomicReference.get());
     }
 
     @RequestScoped
@@ -227,6 +274,17 @@
     }
 
     @Singleton
+    public static class SingletonObject3 {
+
+        @Inject
+        Greeting3 greeting;
+
+        public Greeting3 getGreeting() {
+            return greeting;
+        }
+    }
+
+    @Singleton
     public static class SingletonObject {
 
         @Inject
@@ -260,6 +318,14 @@
     }
 
     @Vetoed
+    static class SupplierGreeting3 implements Supplier<Greeting3> {
+        @Override
+        public Greeting3 get() {
+            return new CzechGreeting3();
+        }
+    }
+
+    @Vetoed
     static class SupplierGreeting2 implements Supplier<Greeting2> {
 
         private final String greetingType;
@@ -291,13 +357,33 @@
     }
 
     @Vetoed
+    static class CzechGreeting3 implements Greeting3 {
+
+        static final String GREETING = "Ahoj";
+
+        private String greeting = GREETING + "#" + Thread.currentThread().getName();
+
+        @Override
+        public String getGreeting() {
+            return greeting;
+        }
+
+        @Override
+        public String toString() {
+            return "CzechGreeting";
+        }
+    }
+
+    @Vetoed
     static class CzechGreeting2 implements Greeting2 {
 
         static final String GREETING = "Ahoj";
 
+        private String greeting = GREETING + "#" + Thread.currentThread().getName();
+
         @Override
         public String getGreeting() {
-            return GREETING + "#" + Thread.currentThread().getName();
+            return greeting;
         }
 
         @Override
@@ -374,9 +460,11 @@
 
         static final String GREETING = "Ahoj";
 
+        private String greeting = GREETING + "#" + Thread.currentThread().getName();
+
         @Override
         public String getGreeting() {
-            return GREETING + "#" + Thread.currentThread().getName();
+            return greeting;
         }
 
         @Override
@@ -386,6 +474,11 @@
     }
 
     @FunctionalInterface
+    static interface Greeting3 {
+        String getGreeting();
+    }
+
+    @FunctionalInterface
     static interface Greeting2 {
         String getGreeting();
     }
diff --git a/incubator/declarative-linking/pom.xml b/incubator/declarative-linking/pom.xml
index c8d7a37..2a5b425 100644
--- a/incubator/declarative-linking/pom.xml
+++ b/incubator/declarative-linking/pom.xml
@@ -58,9 +58,9 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
-            <groupId>org.glassfish</groupId>
-            <artifactId>jakarta.el</artifactId>
-            <scope>provided</scope>
+            <groupId>org.glassfish.expressly</groupId>
+            <artifactId>expressly</artifactId>
+            <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.junit.jupiter</groupId>
@@ -88,7 +88,7 @@
         <dependency>
             <groupId>org.skyscreamer</groupId>
             <artifactId>jsonassert</artifactId>
-            <version>1.5.0</version>
+            <version>1.5.1</version>
             <scope>test</scope>
         </dependency>
     </dependencies>
diff --git a/incubator/html-json/pom.xml b/incubator/html-json/pom.xml
index cb4f25a..f2359be 100644
--- a/incubator/html-json/pom.xml
+++ b/incubator/html-json/pom.xml
@@ -44,6 +44,8 @@
         <surefire.coverage.argline>
             --add-exports org.junit.platform.commons/org.junit.platform.commons.util=ALL-UNNAMED
             --add-exports org.junit.platform.commons/org.junit.platform.commons.logging=ALL-UNNAMED
+            --add-exports org.glassfish.jersey.core.common/org.glassfish.jersey.innate=ALL-UNNAMED
+            --add-exports org.glassfish.jersey.core.common/org.glassfish.jersey.innate.virtual=ALL-UNNAMED
             --add-reads org.glassfish.jersey.incubator.html.json=org.glassfish.jersey.core.server
             --add-reads org.glassfish.jersey.incubator.html.json=org.glassfish.jersey.tests.framework.core
             --add-reads org.glassfish.jersey.incubator.html.json=org.glassfish.jersey.tests.framework.provider.grizzly
@@ -105,6 +107,16 @@
             <groupId>org.netbeans.html</groupId>
             <artifactId>ko-ws-tyrus</artifactId>
             <version>${net.java.html.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.glassfish.grizzly</groupId>
+                    <artifactId>grizzly-framework</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.glassfish.grizzly</groupId>
+                    <artifactId>grizzly-http-server</artifactId>
+                </exclusion>
+            </exclusions>
             <scope>runtime</scope>
         </dependency>
         <!-- Overwrite Grizzly dependency defined in ko-ws-tyrus -->
diff --git a/incubator/kryo/pom.xml b/incubator/kryo/pom.xml
index 0b8483d..3bd3d43 100644
--- a/incubator/kryo/pom.xml
+++ b/incubator/kryo/pom.xml
@@ -36,6 +36,8 @@
 
     <properties>
         <surefire.coverage.argline>
+            --add-exports org.glassfish.jersey.core.common/org.glassfish.jersey.innate=ALL-UNNAMED
+            --add-exports org.glassfish.jersey.core.common/org.glassfish.jersey.innate.virtual=ALL-UNNAMED
             --add-reads org.glassfish.jersey.incubator.media.kryo=org.glassfish.jersey.core.server
             --add-reads org.glassfish.jersey.incubator.media.kryo=org.glassfish.jersey.core.client
             --add-modules=ALL-MODULE-PATH
diff --git a/inject/cdi2-se/pom.xml b/inject/cdi2-se/pom.xml
index abcc77d..fa8cb3f 100644
--- a/inject/cdi2-se/pom.xml
+++ b/inject/cdi2-se/pom.xml
@@ -51,6 +51,10 @@
                     <groupId>org.jboss.logging</groupId>
                     <artifactId>jboss-logging</artifactId>
                 </exclusion>
+                <exclusion>
+                    <groupId>jakarta.annotation</groupId>
+                    <artifactId>jakarta.annotation-api</artifactId>
+                </exclusion>
             </exclusions>
         </dependency>
 
diff --git a/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/BeanHelper.java b/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/BeanHelper.java
index 584193f..053dbe8 100644
--- a/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/BeanHelper.java
+++ b/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/BeanHelper.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2024 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
@@ -116,18 +116,11 @@
      * @param <T>     type of the instance which is registered.
      */
     public static <T> void registerSupplier(SupplierInstanceBinding<T> binding, AfterBeanDiscovery abd, BeanManager beanManager) {
-        BeanManagerImpl manager;
-        if (beanManager instanceof BeanManagerProxy) {
-            manager = ((BeanManagerProxy) beanManager).unwrap();
-        } else {
-            manager = (BeanManagerImpl) beanManager;
-        }
-
         /*
          * CDI does not provide sufficient support for ThreadScoped Supplier
          */
         if (binding.getScope() == PerThread.class) {
-            abd.addBean(new SupplierThreadScopeBean(binding, manager));
+            abd.addBean(new SupplierThreadScopeBean(binding, beanManagerImpl(beanManager)));
         } else {
             abd.addBean(new SupplierInstanceBean<>(binding));
             abd.addBean(new SupplierInstanceBeanBridge<>(binding));
@@ -156,8 +149,23 @@
         InjectionTarget<Supplier<T>> jit = getJerseyInjectionTarget(supplierClass, injectionTarget, supplierBean, resolvers);
         supplierBean.setInjectionTarget(jit);
 
-        abd.addBean(supplierBean);
-        abd.addBean(new SupplierBeanBridge(binding, beanManager));
+        /*
+         * CDI does not provide sufficient support for ThreadScoped Supplier
+         */
+        if (binding.getScope() == PerThread.class) {
+            abd.addBean(new SupplierThreadScopeClassBean(binding, supplierBean, beanManagerImpl(beanManager)));
+        } else {
+            abd.addBean(supplierBean);
+            abd.addBean(new SupplierBeanBridge(binding, beanManager));
+        }
+    }
+
+    private static BeanManagerImpl beanManagerImpl(BeanManager beanManager) {
+        if (beanManager instanceof BeanManagerProxy) {
+            return ((BeanManagerProxy) beanManager).unwrap();
+        } else {
+            return (BeanManagerImpl) beanManager;
+        }
     }
 
     private static <T> InjectionTarget<T> getJerseyInjectionTarget(Class<T> clazz, InjectionTarget<T> injectionTarget,
diff --git a/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/SupplierThreadScopeBean.java b/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/SupplierThreadScopeBean.java
index a0c0d34..b95ae0a 100644
--- a/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/SupplierThreadScopeBean.java
+++ b/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/SupplierThreadScopeBean.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2024 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,20 +17,13 @@
 package org.glassfish.jersey.inject.cdi.se.bean;
 
 import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
-import java.util.WeakHashMap;
-import java.util.function.Supplier;
 
 import jakarta.enterprise.context.Dependent;
 import jakarta.enterprise.context.spi.CreationalContext;
-import jakarta.enterprise.inject.spi.Bean;
-import jakarta.enterprise.inject.spi.PassivationCapable;
 
 import org.glassfish.jersey.internal.inject.SupplierInstanceBinding;
 
-import org.jboss.weld.bean.StringBeanIdentifier;
 import org.jboss.weld.bean.proxy.BeanInstance;
-import org.jboss.weld.bean.proxy.ContextBeanInstance;
 import org.jboss.weld.bean.proxy.ProxyFactory;
 import org.jboss.weld.manager.BeanManagerImpl;
 
@@ -107,31 +100,4 @@
         ProxyFactory<T> factory = new ProxyFactory<>(contextId, getBeanClass(), getTypes(), this);
         return factory.create(beanInstance);
     }
-
-    private static class ThreadScopeBeanInstance<T> extends ContextBeanInstance<T> {
-
-        private final WeakHashMap<Thread, Object> instances = new WeakHashMap<>();
-
-        private final Supplier<T> supplier;
-
-        /**
-         * Creates a new invocation handler with supplier which provides a current injected value in proper scope.
-         *
-         * @param supplier provider of the value.
-         */
-        private ThreadScopeBeanInstance(Supplier<T> supplier, Bean<T> bean, String contextId) {
-            super(bean, new StringBeanIdentifier(((PassivationCapable) bean).getId()), contextId);
-            this.supplier = supplier;
-        }
-
-        @Override
-        public Object invoke(Object obj, Method method, Object... arguments) throws Throwable {
-            Object instance = instances.computeIfAbsent(Thread.currentThread(), thread -> supplier.get());
-            return super.invoke(instance, method, arguments);
-        }
-
-        public void dispose() {
-            this.instances.clear();
-        }
-    }
 }
diff --git a/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/SupplierThreadScopeClassBean.java b/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/SupplierThreadScopeClassBean.java
new file mode 100644
index 0000000..698bfb0
--- /dev/null
+++ b/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/SupplierThreadScopeClassBean.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2024 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.inject.cdi.se.bean;
+
+import java.lang.annotation.Annotation;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+
+import jakarta.enterprise.context.Dependent;
+import jakarta.enterprise.context.spi.CreationalContext;
+
+import org.glassfish.jersey.internal.inject.SupplierClassBinding;
+import org.glassfish.jersey.internal.inject.SupplierInstanceBinding;
+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.jboss.weld.bean.proxy.BeanInstance;
+import org.jboss.weld.bean.proxy.ProxyFactory;
+import org.jboss.weld.manager.BeanManagerImpl;
+
+
+/**
+ * Creates an implementation of {@link jakarta.enterprise.inject.spi.Bean} interface using Jersey's {@link SupplierInstanceBinding}.
+ * Binding provides the information about the bean also called {@link jakarta.enterprise.inject.spi.BeanAttributes} information.
+ * The {@code Bean} does not use {@link org.glassfish.jersey.inject.cdi.se.injector.JerseyInjectionTarget} because serves already
+ * created proxy, therefore the create operation just return provided instance without any other contextual operation
+ * (produce, inject, destroy).
+ * <p>
+ * This bean is special and is used only for service registered as a {@link org.glassfish.jersey.internal.inject.PerThread} and
+ * works through the proxy which serves the correct instance per the given thread.
+ * <p>
+ * Register example:
+ * <pre>
+ * AbstractBinder {
+ *     &#64;Override
+ *     protected void configure() {
+ *         bindFactory(MyFactoryInjectionSupplier.class)
+ *              .to(MyBean.class)
+ *              .in(PerThread.class);
+ *     }
+ * }
+ * </pre>
+ * Inject example:
+ * <pre>
+ * &#64;Path("/")
+ * public class MyResource {
+ *   &#64;Inject
+ *   private MyBean myBean&#59;
+ * }
+ * </pre>
+ */
+class SupplierThreadScopeClassBean extends JerseyBean<Object> {
+    private final LazyValue<ThreadScopeBeanInstance<Object>> beanInstance;
+    private final SupplierClassBinding binding;
+    private final LazyValue<Object> proxy;
+    private final AtomicReference<CreationalContext> creationalContextAtomicReference = new AtomicReference<>();
+
+    SupplierThreadScopeClassBean(SupplierClassBinding binding, SupplierClassBean supplierClassBean, BeanManagerImpl beanManager) {
+        super(binding);
+        this.binding = binding;
+        this.beanInstance = Values.lazy((Value<ThreadScopeBeanInstance<Object>>) () -> {
+            Supplier supplierInstance = supplierClassBean.create(creationalContextAtomicReference.get());
+            ThreadScopeBeanInstance scopeBeanInstance =
+                    new ThreadScopeBeanInstance(supplierInstance, this, beanManager.getContextId());
+            return scopeBeanInstance;
+        });
+        this.proxy = Values.lazy((Value<Object>) () -> createClientProxy(beanInstance.get(), beanManager.getContextId()));
+    }
+
+    @Override
+    public Class<? extends Annotation> getScope() {
+        return Dependent.class;
+    }
+
+    @Override
+    public Object create(CreationalContext<Object> ctx) {
+        creationalContextAtomicReference.set(ctx);
+        return proxy.get();
+    }
+
+    @Override
+    public void destroy(Object instance, CreationalContext<Object> creationalContext) {
+        if (beanInstance.isInitialized()) {
+            this.beanInstance.get().dispose();
+        }
+    }
+
+    @Override
+    public Class<?> getBeanClass() {
+        return (Class<?>) this.binding.getContracts().iterator().next();
+    }
+
+    private <T> T createClientProxy(BeanInstance beanInstance, String contextId) {
+        ProxyFactory<T> factory = new ProxyFactory<>(contextId, getBeanClass(), getTypes(), this);
+        return factory.create(beanInstance);
+    }
+}
diff --git a/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/ThreadScopeBeanInstance.java b/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/ThreadScopeBeanInstance.java
new file mode 100644
index 0000000..a200960
--- /dev/null
+++ b/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/ThreadScopeBeanInstance.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2017, 2024 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.inject.cdi.se.bean;
+
+import org.jboss.weld.bean.StringBeanIdentifier;
+import org.jboss.weld.bean.proxy.ContextBeanInstance;
+
+import java.lang.reflect.Method;
+import java.util.WeakHashMap;
+import java.util.function.Supplier;
+
+import jakarta.enterprise.inject.spi.Bean;
+import jakarta.enterprise.inject.spi.PassivationCapable;
+
+/**
+ * {@link org.glassfish.jersey.internal.inject.PerThread} scope bean instance used from
+ * {@link SupplierThreadScopeBean} and {@link SupplierThreadScopeClassBean}.
+ *
+ * @param <T> Typed of the bean supplied by a {@code Supplier}.
+ */
+class ThreadScopeBeanInstance<T> extends ContextBeanInstance<T> {
+
+    private final WeakHashMap<Thread, Object> instances = new WeakHashMap<>();
+
+    private final Supplier<T> supplier;
+
+    /**
+     * Creates a new invocation handler with supplier which provides a current injected value in proper scope.
+     *
+     * @param supplier provider of the value.
+     */
+    ThreadScopeBeanInstance(Supplier<T> supplier, Bean<T> bean, String contextId) {
+        super(bean, new StringBeanIdentifier(((PassivationCapable) bean).getId()), contextId);
+        this.supplier = supplier;
+    }
+
+    @Override
+    public Object invoke(Object obj, Method method, Object... arguments) throws Throwable {
+        Object instance = instances.computeIfAbsent(Thread.currentThread(), thread -> supplier.get());
+        return super.invoke(instance, method, arguments);
+    }
+
+    public void dispose() {
+        this.instances.clear();
+    }
+}
diff --git a/inject/cdi2-se/src/test/java/org/glassfish/jersey/inject/cdi/se/CzechGreeting.java b/inject/cdi2-se/src/test/java/org/glassfish/jersey/inject/cdi/se/CzechGreeting.java
index fd4ae86..3f4f924 100644
--- a/inject/cdi2-se/src/test/java/org/glassfish/jersey/inject/cdi/se/CzechGreeting.java
+++ b/inject/cdi2-se/src/test/java/org/glassfish/jersey/inject/cdi/se/CzechGreeting.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2024 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
@@ -26,9 +26,11 @@
 
     static final String GREETING = "Ahoj";
 
+    private String greeting = GREETING + "#" + Thread.currentThread().getName();
+
     @Override
     public String getGreeting() {
-        return GREETING + "#" + Thread.currentThread().getName();
+        return greeting;
     }
 
     @Override
diff --git a/inject/cdi2-se/src/test/java/org/glassfish/jersey/inject/cdi/se/ThreadScopeTest.java b/inject/cdi2-se/src/test/java/org/glassfish/jersey/inject/cdi/se/ThreadScopeTest.java
index cf377c1..1fe627a 100644
--- a/inject/cdi2-se/src/test/java/org/glassfish/jersey/inject/cdi/se/ThreadScopeTest.java
+++ b/inject/cdi2-se/src/test/java/org/glassfish/jersey/inject/cdi/se/ThreadScopeTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2024 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.inject.cdi.se;
 
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicReference;
 
 import jakarta.enterprise.context.RequestScoped;
 import jakarta.inject.Inject;
@@ -167,7 +168,7 @@
     }
 
     @Test
-    public void testThreadScopedInSingletonScope() {
+    public void testThreadScopedInSingletonScope() throws InterruptedException {
         InjectionManager injectionManager = BindingTestHelper.createInjectionManager();
         BindingTestHelper.bind(injectionManager, binder -> {
             binder.bindAsContract(SingletonObject.class)
@@ -188,6 +189,52 @@
         assertNotNull(greeting2);
 
         assertEquals(greeting1, greeting2);
+
+        final AtomicReference<String> greetingAtomicReference = new AtomicReference<>();
+        Runnable runnable = () ->
+                greetingAtomicReference.set(injectionManager.getInstance(SingletonObject.class).getGreeting().getGreeting());
+
+        Thread newThread = new Thread(runnable);
+        newThread.start();
+        newThread.join();
+
+        assertEquals(greeting1.getGreeting(), greeting2.getGreeting());
+        assertNotEquals(greeting1.getGreeting(), greetingAtomicReference.get());
+    }
+
+    @Test
+    public void testSupplierClassBindingThreadScopedInSingletonScope() throws InterruptedException {
+        InjectionManager injectionManager = BindingTestHelper.createInjectionManager();
+        BindingTestHelper.bind(injectionManager, binder -> {
+            binder.bindAsContract(SingletonObject.class)
+                    .in(Singleton.class);
+
+            binder.bindFactory(SupplierGreeting.class)
+                    .to(Greeting.class)
+                    .in(PerThread.class);
+        });
+
+        SingletonObject instance1 = injectionManager.getInstance(SingletonObject.class);
+        Greeting greeting1 = instance1.getGreeting();
+        assertNotNull(greeting1);
+
+        // Precisely the same object
+        SingletonObject instance2 = injectionManager.getInstance(SingletonObject.class);
+        Greeting greeting2 = instance2.getGreeting();
+        assertNotNull(greeting2);
+
+        assertEquals(greeting1, greeting2);
+
+        final AtomicReference<String> greetingAtomicReference = new AtomicReference<>();
+        Runnable runnable = () ->
+                greetingAtomicReference.set(injectionManager.getInstance(SingletonObject.class).getGreeting().getGreeting());
+
+        Thread newThread = new Thread(runnable);
+        newThread.start();
+        newThread.join();
+
+        assertEquals(greeting1.getGreeting(), greeting2.getGreeting());
+        assertNotEquals(greeting1.getGreeting(), greetingAtomicReference.get());
     }
 
     @RequestScoped
diff --git a/inject/hk2/pom.xml b/inject/hk2/pom.xml
index 81c7787..ba2de56 100644
--- a/inject/hk2/pom.xml
+++ b/inject/hk2/pom.xml
@@ -61,7 +61,6 @@
         <dependency>
             <groupId>org.javassist</groupId>
             <artifactId>javassist</artifactId>
-            <version>${javassist.version}</version>
         </dependency>
 
         <dependency>
diff --git a/inject/hk2/src/main/java/org/glassfish/jersey/inject/hk2/Hk2BootstrapBinder.java b/inject/hk2/src/main/java/org/glassfish/jersey/inject/hk2/Hk2BootstrapBinder.java
index 7794aa6..f3fd9d7 100644
--- a/inject/hk2/src/main/java/org/glassfish/jersey/inject/hk2/Hk2BootstrapBinder.java
+++ b/inject/hk2/src/main/java/org/glassfish/jersey/inject/hk2/Hk2BootstrapBinder.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2024 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,10 @@
 
     @Override
     protected void configure() {
+        // Singletons, install once
+        if (serviceLocator.getService(RequestScope.class) != null) {
+            return;
+        }
         install(
                 // Jersey-like class analyzer that is able to choose the right services' constructor.
                 new JerseyClassAnalyzer.Binder(serviceLocator),
diff --git a/inject/hk2/src/main/java/org/glassfish/jersey/inject/hk2/Hk2Helper.java b/inject/hk2/src/main/java/org/glassfish/jersey/inject/hk2/Hk2Helper.java
index 0f731f6..bf3c229 100644
--- a/inject/hk2/src/main/java/org/glassfish/jersey/inject/hk2/Hk2Helper.java
+++ b/inject/hk2/src/main/java/org/glassfish/jersey/inject/hk2/Hk2Helper.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2024 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,8 +20,10 @@
 import java.lang.reflect.Type;
 import java.util.Set;
 import java.util.function.Consumer;
+import java.util.function.Function;
 import java.util.function.Supplier;
 
+import org.glassfish.hk2.api.Factory;
 import org.glassfish.jersey.internal.LocalizationMessages;
 import org.glassfish.jersey.internal.inject.AliasBinding;
 import org.glassfish.jersey.internal.inject.Binding;
@@ -121,14 +123,25 @@
      * @param dc      HK2 Dynamic configuration to bind the object.
      * @param binding Jersey descriptor as a holder of information about an injection point.
      */
-    private static void bindBinding(ServiceLocator locator, DynamicConfiguration dc, Binding<?, ?> binding) {
+    private static <T> void bindBinding(ServiceLocator locator, DynamicConfiguration dc, Binding<T, ?> binding) {
         if (ClassBinding.class.isAssignableFrom(binding.getClass())) {
-            ActiveDescriptor<?> activeDescriptor = translateToActiveDescriptor((ClassBinding<?>) binding);
-            bindBinding(locator, dc, activeDescriptor, binding.getAliases());
+            final Class<?> implClass = binding.getImplementationType();
+            if (Factory.class.isAssignableFrom(implClass)) {
+                final Class<? extends Factory<T>> factoryClass = (Class<? extends Factory<T>>) implClass;
+                bindFactory(locator, binding, (binder) -> binder.bindFactory(factoryClass));
+            } else {
+                ActiveDescriptor<?> activeDescriptor = translateToActiveDescriptor((ClassBinding<?>) binding);
+                bindBinding(locator, dc, activeDescriptor, binding.getAliases());
+            }
 
         } else if (InstanceBinding.class.isAssignableFrom(binding.getClass())) {
-            ActiveDescriptor<?> activeDescriptor = translateToActiveDescriptor((InstanceBinding<?>) binding);
-            bindBinding(locator, dc, activeDescriptor, binding.getAliases());
+            if (Factory.class.isAssignableFrom(binding.getImplementationType())) {
+                final Factory<?> factory = (Factory) ((InstanceBinding<T>) binding).getService();
+                bindFactory(locator, binding, (binder) -> binder.bindFactory(factory));
+            } else {
+                ActiveDescriptor<?> activeDescriptor = translateToActiveDescriptor((InstanceBinding<?>) binding);
+                bindBinding(locator, dc, activeDescriptor, binding.getAliases());
+            }
 
         } else if (InjectionResolverBinding.class.isAssignableFrom(binding.getClass())) {
             InjectionResolverBinding resolverDescriptor = (InjectionResolverBinding) binding;
@@ -234,6 +247,13 @@
         ServiceLocatorUtilities.bind(locator, createBinder(bindConsumer));
     }
 
+    private static <T> void bindFactory(ServiceLocator locator,
+                                        Binding<T, ?> binding,
+                                        Function<AbstractBinder, ServiceBindingBuilder> builder) {
+        ServiceLocatorUtilities
+                .bind(locator, createBinder((binder) -> setupSupplierFactoryBridge(binding, builder.apply(binder))));
+    }
+
     private static void setupSupplierFactoryBridge(Binding<?, ?> binding, ServiceBindingBuilder<?> builder) {
         builder.named(binding.getName());
         binding.getContracts().forEach(builder::to);
diff --git a/inject/hk2/src/main/java/org/glassfish/jersey/inject/hk2/Hk2Registrables.java b/inject/hk2/src/main/java/org/glassfish/jersey/inject/hk2/Hk2Registrables.java
new file mode 100644
index 0000000..6231f74
--- /dev/null
+++ b/inject/hk2/src/main/java/org/glassfish/jersey/inject/hk2/Hk2Registrables.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2024 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.inject.hk2;
+
+import org.glassfish.hk2.utilities.binding.AbstractBinder;
+import org.glassfish.jersey.innate.inject.spi.ExternalRegistrables;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Hk2Registrables implements ExternalRegistrables {
+    @Override
+    public List<ClassRuntimeTypePair> registrableContracts() {
+        List<ClassRuntimeTypePair> list = new ArrayList<>();
+        list.add(new ClassRuntimeTypePair(AbstractBinder.class, null));
+        return list;
+    }
+}
diff --git a/inject/hk2/src/main/resources/META-INF/services/org.glassfish.jersey.innate.inject.spi.ExternalRegistrables b/inject/hk2/src/main/resources/META-INF/services/org.glassfish.jersey.innate.inject.spi.ExternalRegistrables
new file mode 100644
index 0000000..6f7da1a
--- /dev/null
+++ b/inject/hk2/src/main/resources/META-INF/services/org.glassfish.jersey.innate.inject.spi.ExternalRegistrables
@@ -0,0 +1 @@
+org.glassfish.jersey.inject.hk2.Hk2Registrables
\ No newline at end of file
diff --git a/media/jaxb/pom.xml b/media/jaxb/pom.xml
index f93452f..fbe456c 100644
--- a/media/jaxb/pom.xml
+++ b/media/jaxb/pom.xml
@@ -83,21 +83,6 @@
                 <inherited>true</inherited>
             </plugin>
             <plugin>
-                <groupId>de.jflex</groupId>
-                <artifactId>maven-jflex-plugin</artifactId>
-                <version>1.4.3</version>
-                <executions>
-                    <execution>
-                        <goals>
-                            <goal>generate</goal>
-                        </goals>
-                        <configuration>
-                            <outputDirectory>${project.build.directory}/generated-sources/rsrc-gen</outputDirectory>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
-            <plugin>
                 <groupId>org.apache.felix</groupId>
                 <artifactId>maven-bundle-plugin</artifactId>
                 <inherited>true</inherited>
diff --git a/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/AbstractJaxbProvider.java b/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/AbstractJaxbProvider.java
index 1d3c5c5..0b95efd 100644
--- a/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/AbstractJaxbProvider.java
+++ b/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/AbstractJaxbProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 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,8 @@
 import java.lang.ref.WeakReference;
 import java.util.Map;
 import java.util.WeakHashMap;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -57,6 +59,7 @@
 
     private static final Map<Class<?>, WeakReference<JAXBContext>> jaxbContexts =
             new WeakHashMap<Class<?>, WeakReference<JAXBContext>>();
+    private static final Lock jaxbContextsLock = new ReentrantLock();
     private final Providers jaxrsProviders;
     private final boolean fixedResolverMediaType;
     private final Value<ContextResolver<JAXBContext>> mtContext;
@@ -149,7 +152,7 @@
     /**
      * Get the JAXB unmarshaller for the given class and media type.
      * <p>
-     * In case this provider instance has been {@link #AbstractJaxbProvider(Providers, MediaType)
+     * In case this provider instance has been {@link AbstractJaxbProvider(Providers, MediaType)
      * created with a fixed resolver media type}, the supplied media type argument will be ignored.
      * </p>
      *
@@ -192,7 +195,7 @@
     /**
      * Get the JAXB marshaller for the given class and media type.
      * <p>
-     * In case this provider instance has been {@link #AbstractJaxbProvider(Providers, MediaType)
+     * In case this provider instance has been {@link AbstractJaxbProvider(Providers, MediaType)
      * created with a fixed resolver media type}, the supplied media type argument will be ignored.
      * </p>
      *
@@ -280,7 +283,8 @@
      * @throws JAXBException in case the JAXB context retrieval fails.
      */
     protected JAXBContext getStoredJaxbContext(Class type) throws JAXBException {
-        synchronized (jaxbContexts) {
+        jaxbContextsLock.lock();
+        try {
             final WeakReference<JAXBContext> ref = jaxbContexts.get(type);
             JAXBContext c = (ref != null) ? ref.get() : null;
             if (c == null) {
@@ -288,7 +292,10 @@
                 jaxbContexts.put(type, new WeakReference<JAXBContext>(c));
             }
             return c;
+        } finally {
+            jaxbContextsLock.unlock();
         }
+
     }
 
     /**
diff --git a/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/JaxbStringReaderProvider.java b/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/JaxbStringReaderProvider.java
index e7e399d..7065c43 100644
--- a/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/JaxbStringReaderProvider.java
+++ b/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/JaxbStringReaderProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 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 java.lang.reflect.Type;
 import java.util.Map;
 import java.util.WeakHashMap;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 
 import jakarta.ws.rs.ProcessingException;
 import jakarta.ws.rs.core.Context;
@@ -54,6 +56,7 @@
 public class JaxbStringReaderProvider {
 
     private static final Map<Class, JAXBContext> jaxbContexts = new WeakHashMap<Class, JAXBContext>();
+    private static final Lock jaxbContextsLock = new ReentrantLock();
     private final Value<ContextResolver<JAXBContext>> mtContext;
     private final Value<ContextResolver<Unmarshaller>> mtUnmarshaller;
 
@@ -116,13 +119,16 @@
      * @throws JAXBException in case JAXB context retrieval fails.
      */
     protected JAXBContext getStoredJAXBContext(Class type) throws JAXBException {
-        synchronized (jaxbContexts) {
+        jaxbContextsLock.lock();
+        try {
             JAXBContext c = jaxbContexts.get(type);
             if (c == null) {
                 c = JAXBContext.newInstance(type);
                 jaxbContexts.put(type, c);
             }
             return c;
+        } finally {
+            jaxbContextsLock.unlock();
         }
     }
 
diff --git a/media/json-binding/pom.xml b/media/json-binding/pom.xml
index 427b9aa..bfd1eaf 100644
--- a/media/json-binding/pom.xml
+++ b/media/json-binding/pom.xml
@@ -94,6 +94,14 @@
                     <groupId>jakarta.json</groupId>
                     <artifactId>jakarta.json-api</artifactId>
                 </exclusion>
+                <exclusion>
+                    <groupId>org.glassfish</groupId>
+                    <artifactId>jakarta.json</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.eclipse.parsson</groupId>
+                    <artifactId>parsson</artifactId>
+                </exclusion>
             </exclusions>
         </dependency>
 
diff --git a/media/json-binding/src/main/java/module-info.java b/media/json-binding/src/main/java/module-info.java
index 146f6df..357206f 100644
--- a/media/json-binding/src/main/java/module-info.java
+++ b/media/json-binding/src/main/java/module-info.java
@@ -15,6 +15,8 @@
  */
 
 module org.glassfish.jersey.media.jsonb {
+    requires java.logging;
+
     requires jakarta.annotation;
     requires jakarta.inject;
     requires jakarta.json.bind;
diff --git a/media/json-binding/src/main/java/org/glassfish/jersey/jsonb/JsonBindingFeature.java b/media/json-binding/src/main/java/org/glassfish/jersey/jsonb/JsonBindingFeature.java
index b80ac9e..a41afb0 100644
--- a/media/json-binding/src/main/java/org/glassfish/jersey/jsonb/JsonBindingFeature.java
+++ b/media/json-binding/src/main/java/org/glassfish/jersey/jsonb/JsonBindingFeature.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2024 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,25 @@
 
 package org.glassfish.jersey.jsonb;
 
+import jakarta.ws.rs.RuntimeType;
+import jakarta.ws.rs.core.Application;
 import jakarta.ws.rs.core.Configuration;
 import jakarta.ws.rs.core.Feature;
 import jakarta.ws.rs.core.FeatureContext;
 
+import org.glassfish.jersey.ApplicationSupplier;
 import org.glassfish.jersey.CommonProperties;
 import org.glassfish.jersey.internal.InternalProperties;
+import org.glassfish.jersey.internal.inject.InjectionManagerSupplier;
 import org.glassfish.jersey.internal.util.PropertiesHelper;
 import org.glassfish.jersey.jsonb.internal.JsonBindingAutoDiscoverable;
 import org.glassfish.jersey.jsonb.internal.JsonBindingProvider;
 
+import java.security.AccessController;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.logging.Logger;
+
 /**
  * Feature used to register JSON-B providers.
  * <p>
@@ -50,12 +59,85 @@
  */
 public class JsonBindingFeature implements Feature {
 
+    private static final Logger LOGGER = Logger.getLogger(JsonBindingFeature.class.getName());
     private static final String JSON_FEATURE = JsonBindingFeature.class.getSimpleName();
 
     @Override
     public boolean configure(final FeatureContext context) {
         final Configuration config = context.getConfiguration();
 
+        // ---- Allow to disable for compatibility with Pre JAX-RS 2.1 Jersey.
+
+        /* Either system properties */
+        final String bindingDisabledBySystemProperty = AccessController.doPrivileged(
+                PropertiesHelper.getSystemProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE));
+
+        final String bindingDisabledBySystemPropertyClient = AccessController.doPrivileged(
+                PropertiesHelper.getSystemProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE_CLIENT));
+
+        final String bindingDisabledBySystemPropertyServer = AccessController.doPrivileged(
+                PropertiesHelper.getSystemProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE_SERVER));
+
+        final RuntimeType runtimeType = config.getRuntimeType();
+
+        boolean bindingDisabledBySystem = PropertiesHelper.isPropertyOrNotSet(bindingDisabledBySystemProperty)
+                || (runtimeType == RuntimeType.CLIENT
+                    && PropertiesHelper.isPropertyOrNotSet(bindingDisabledBySystemPropertyClient))
+                || (runtimeType == RuntimeType.SERVER
+                    && PropertiesHelper.isPropertyOrNotSet(bindingDisabledBySystemPropertyServer));
+
+        /* Or config property */
+        final Boolean bindingDisabled = CommonProperties.getValue(config.getProperties(), runtimeType,
+                CommonProperties.JSON_BINDING_FEATURE_DISABLE, Boolean.class);
+
+        /* Config property takes precedence */
+        if ((bindingDisabledBySystem && !Boolean.FALSE.equals(bindingDisabled)) || Boolean.TRUE.equals(bindingDisabled)) {
+            return false;
+        }
+
+        final Set<String> disabledPackageNames = new HashSet<>();
+
+        /* Only a certain package names */
+        final String bindingDisabledPackageBySystemProperty = RuntimeType.SERVER == runtimeType
+                ? AccessController.doPrivileged(PropertiesHelper.getSystemProperty(
+                        CommonProperties.JSON_BINDING_FEATURE_DISABLE_APPLICATION))
+                : null;
+
+        final String bindingDisabledPackage = RuntimeType.SERVER == runtimeType
+                ? CommonProperties.getValue(config.getProperties(), runtimeType,
+                        CommonProperties.JSON_BINDING_FEATURE_DISABLE_APPLICATION, String.class)
+                : null;
+
+        separatePackageNames(disabledPackageNames, bindingDisabledPackageBySystemProperty);
+        separatePackageNames(disabledPackageNames, bindingDisabledPackage);
+
+        if (!disabledPackageNames.isEmpty() && !Boolean.FALSE.equals(bindingDisabled)) {
+            try {
+                Application app = null;
+                if (InjectionManagerSupplier.class.isInstance(context)) {
+                    app = ((InjectionManagerSupplier) context).getInjectionManager().getInstance(Application.class);
+                    if (app != null) {
+                        while (ApplicationSupplier.class.isInstance(app) && ((ApplicationSupplier) app).getApplication() != app) {
+                            app = ((ApplicationSupplier) app).getApplication();
+                        }
+                        for (String disabledPackageName : disabledPackageNames) {
+                            if (app.getClass().getName().startsWith(disabledPackageName)) {
+                                return false;
+                            }
+                        }
+                    }
+                }
+                if (app == null) {
+                    LOGGER.warning(LocalizationMessages.ERROR_JSONB_DETECTING_APPLICATION(
+                            LocalizationMessages.ERROR_JSONB_APPLICATION_NOT_FOUND()));
+                }
+            } catch (Throwable throwable) {
+                LOGGER.warning(LocalizationMessages.ERROR_JSONB_DETECTING_APPLICATION(throwable.getMessage()));
+            }
+        }
+
+        // ---- End of disabling for compatibility with Pre JAX-RS 2.1 Jersey.
+
         final String jsonFeature = CommonProperties.getValue(
                 config.getProperties(),
                 config.getRuntimeType(),
@@ -74,4 +156,12 @@
 
         return true;
     }
+
+    private static void separatePackageNames(Set<String> set, String packages) {
+        if (packages != null) {
+            for (String packageName : packages.split(",")) {
+                set.add(packageName.trim());
+            }
+        }
+    }
 }
diff --git a/media/json-binding/src/main/resources/org/glassfish/jersey/jsonb/localization.properties b/media/json-binding/src/main/resources/org/glassfish/jersey/jsonb/localization.properties
index 4eee493..371e641 100644
--- a/media/json-binding/src/main/resources/org/glassfish/jersey/jsonb/localization.properties
+++ b/media/json-binding/src/main/resources/org/glassfish/jersey/jsonb/localization.properties
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2017, 2019 Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2017, 2024 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,6 +14,8 @@
 # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 #
 
+error.jsonb.application.not.found=Application not found.
+error.jsonb.detecting.application=JSON-B could not detect the application name: {0}.
 error.jsonb.serialization=Error writing JSON-B serialized object.
 error.jsonb.deserialization=Error deserializing object from entity stream.
 error.jsonb.emptystream=JSON-B cannot parse empty input stream.
diff --git a/media/json-binding/src/test/java/org/glassfish/jersey/jsonb/internal/JsonbDisabledTest.java b/media/json-binding/src/test/java/org/glassfish/jersey/jsonb/internal/JsonbDisabledTest.java
new file mode 100644
index 0000000..7dc8a3f
--- /dev/null
+++ b/media/json-binding/src/test/java/org/glassfish/jersey/jsonb/internal/JsonbDisabledTest.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2024 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.jsonb.internal;
+
+import org.glassfish.jersey.CommonProperties;
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.inject.InjectionManagerSupplier;
+import org.glassfish.jersey.internal.inject.Injections;
+import org.glassfish.jersey.jsonb.JsonBindingFeature;
+import org.glassfish.jersey.model.internal.CommonConfig;
+import org.glassfish.jersey.model.internal.ComponentBag;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.RuntimeType;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.FeatureContext;
+import java.lang.reflect.Proxy;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class JsonbDisabledTest {
+    @Test
+    public void testDisabled() {
+        AtomicReference<CommonConfig> configReference = new AtomicReference<>();
+        FeatureContext featureContext1 = featureContextForConfig(configReference);
+
+        CommonConfig config1 = new CommonConfig(RuntimeType.SERVER, ComponentBag.INCLUDE_ALL);
+        configReference.set(config1);
+        Assertions.assertTrue(new JsonBindingFeature().configure(featureContext1));
+
+        CommonConfig config2 = new CommonConfig(RuntimeType.SERVER, ComponentBag.INCLUDE_ALL);
+        config2.property(CommonProperties.JSON_BINDING_FEATURE_DISABLE, true);
+        configReference.set(config2);
+        Assertions.assertFalse(new JsonBindingFeature().configure(featureContext1));
+    }
+
+    @Test
+    public void testDisabledBySystemProperty() {
+        AtomicReference<CommonConfig> configReference = new AtomicReference<>();
+        FeatureContext featureContext1 = featureContextForConfig(configReference);
+
+        CommonConfig config1 = new CommonConfig(RuntimeType.SERVER, ComponentBag.INCLUDE_ALL);
+        configReference.set(config1);
+        Assertions.assertTrue(new JsonBindingFeature().configure(featureContext1));
+
+        CommonConfig config2 = new CommonConfig(RuntimeType.SERVER, ComponentBag.INCLUDE_ALL);
+        System.setProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE_SERVER, "true");
+        configReference.set(config2);
+        Assertions.assertFalse(new JsonBindingFeature().configure(featureContext1));
+        System.clearProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE_SERVER);
+    }
+
+    @Test
+    public void testDisabledBySystemPropertyOverridenByConfigProperty() {
+        AtomicReference<CommonConfig> configReference = new AtomicReference<>();
+        FeatureContext featureContext1 = featureContextForConfig(configReference);
+
+        CommonConfig config1 = new CommonConfig(RuntimeType.SERVER, ComponentBag.INCLUDE_ALL);
+        configReference.set(config1);
+        Assertions.assertTrue(new JsonBindingFeature().configure(featureContext1));
+
+        CommonConfig config2 = new CommonConfig(RuntimeType.SERVER, ComponentBag.INCLUDE_ALL);
+        System.setProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE, "true");
+        config2.property(CommonProperties.JSON_BINDING_FEATURE_DISABLE, false);
+        configReference.set(config2);
+        Assertions.assertTrue(new JsonBindingFeature().configure(featureContext1));
+        System.clearProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE);
+    }
+
+    @Test
+    public void testDisabledByPropertyApplicationPackage() {
+        Application application = new Application() {
+        };
+        InjectionManager injectionManager = Injections.createInjectionManager();
+        injectionManager.register(new AbstractBinder() {
+            @Override
+            protected void configure() {
+                bind(application).to(Application.class);
+            }
+        });
+        AtomicReference<InjectionManager> injectionManagerReference = new AtomicReference<>(injectionManager);
+        AtomicReference<CommonConfig> configReference = new AtomicReference<>();
+        FeatureContext featureContext1 = featureContextForConfig(configReference, injectionManagerReference);
+
+        CommonConfig config1 = new CommonConfig(RuntimeType.SERVER, ComponentBag.INCLUDE_ALL);
+        configReference.set(config1);
+        Assertions.assertTrue(new JsonBindingFeature().configure(featureContext1));
+
+        System.setProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE_APPLICATION,
+                "some.does.not.matter, org.glassfish.jersey.jsonb.internal");
+        CommonConfig config2 = new CommonConfig(RuntimeType.SERVER, ComponentBag.INCLUDE_ALL);
+        configReference.set(config2);
+        Assertions.assertFalse(new JsonBindingFeature().configure(featureContext1));
+        System.clearProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE_APPLICATION);
+    }
+
+    @Test
+    public void disableOnClientOnlyTest() {
+        AtomicReference<CommonConfig> configReference = new AtomicReference<>();
+        FeatureContext featureContext1 = featureContextForConfig(configReference);
+
+        CommonConfig config1 = new CommonConfig(RuntimeType.CLIENT, ComponentBag.INCLUDE_ALL);
+        configReference.set(config1);
+        Assertions.assertTrue(new JsonBindingFeature().configure(featureContext1));
+
+        config1.property(CommonProperties.JSON_BINDING_FEATURE_DISABLE_SERVER, true);
+        Assertions.assertTrue(new JsonBindingFeature().configure(featureContext1));
+
+        config1.property(CommonProperties.JSON_BINDING_FEATURE_DISABLE_CLIENT, true);
+        Assertions.assertFalse(new JsonBindingFeature().configure(featureContext1));
+
+        CommonConfig config2 = new CommonConfig(RuntimeType.CLIENT, ComponentBag.INCLUDE_ALL);
+        System.setProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE_SERVER, "true");
+        configReference.set(config2);
+        Assertions.assertTrue(new JsonBindingFeature().configure(featureContext1));
+
+        System.setProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE_CLIENT, "true");
+        Assertions.assertFalse(new JsonBindingFeature().configure(featureContext1));
+
+        System.clearProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE_SERVER);
+        System.clearProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE_CLIENT);
+    }
+
+    @Test
+    public void testDisabledBySystemPropertyApplicationPackage() {
+        Application application = new Application() {
+        };
+        InjectionManager injectionManager = Injections.createInjectionManager();
+        injectionManager.register(new AbstractBinder() {
+            @Override
+            protected void configure() {
+                bind(application).to(Application.class);
+            }
+        });
+        AtomicReference<InjectionManager> injectionManagerReference = new AtomicReference<>(injectionManager);
+        AtomicReference<CommonConfig> configReference = new AtomicReference<>();
+        FeatureContext featureContext1 = featureContextForConfig(configReference, injectionManagerReference);
+
+        System.setProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE_APPLICATION,
+                "some.does.not.matter, org.glassfish.jersey.jsonb.internal");
+
+        CommonConfig config1 = new CommonConfig(RuntimeType.SERVER, ComponentBag.INCLUDE_ALL);
+        configReference.set(config1);
+        Assertions.assertFalse(new JsonBindingFeature().configure(featureContext1));
+
+        CommonConfig config2 = new CommonConfig(RuntimeType.SERVER, ComponentBag.INCLUDE_ALL);
+        configReference.set(config2);
+        Assertions.assertFalse(new JsonBindingFeature().configure(featureContext1));
+        System.clearProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE_APPLICATION);
+    }
+
+    @Test
+    public void testDisabledBySystemPropertyApplicationPackageEnabledByProperty() {
+        Application application = new Application() {
+        };
+        InjectionManager injectionManager = Injections.createInjectionManager();
+        injectionManager.register(new AbstractBinder() {
+            @Override
+            protected void configure() {
+                bind(application).to(Application.class);
+            }
+        });
+        AtomicReference<InjectionManager> injectionManagerReference = new AtomicReference<>(injectionManager);
+        AtomicReference<CommonConfig> configReference = new AtomicReference<>();
+        FeatureContext featureContext1 = featureContextForConfig(configReference, injectionManagerReference);
+
+        CommonConfig config1 = new CommonConfig(RuntimeType.SERVER, ComponentBag.INCLUDE_ALL);
+        configReference.set(config1);
+        Assertions.assertTrue(new JsonBindingFeature().configure(featureContext1));
+
+        System.setProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE_APPLICATION,
+                "some.does.not.matter, org.glassfish.jersey.jsonb.internal");
+
+        CommonConfig config2 = new CommonConfig(RuntimeType.SERVER, ComponentBag.INCLUDE_ALL);
+        config2.property(CommonProperties.JSON_BINDING_FEATURE_DISABLE, Boolean.FALSE);
+        configReference.set(config2);
+        Assertions.assertTrue(new JsonBindingFeature().configure(featureContext1));
+        System.clearProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE_APPLICATION);
+    }
+
+    private static FeatureContext featureContextForConfig(AtomicReference<CommonConfig> configReference) {
+        return featureContextForConfig(configReference, new AtomicReference<>());
+    }
+    private static FeatureContext featureContextForConfig(AtomicReference<CommonConfig> configReference,
+                                                          AtomicReference<InjectionManager> injectionManager) {
+        return (FeatureContext) Proxy.newProxyInstance(
+                JsonbDisabledTest.class.getClassLoader(),
+                new Class[] {FeatureContext.class, InjectionManagerSupplier.class}, (proxy, method, args) -> {
+                    switch (method.getName()) {
+                        case "getConfiguration":
+                            return configReference.get();
+                        case "getInjectionManager":
+                            return injectionManager.get();
+                    }
+                    return null;
+                });
+    }
+}
diff --git a/media/json-gson/pom.xml b/media/json-gson/pom.xml
index 8a78e0a..923d5b6 100644
--- a/media/json-gson/pom.xml
+++ b/media/json-gson/pom.xml
@@ -71,6 +71,12 @@
         <dependency>
             <groupId>com.google.code.gson</groupId>
             <artifactId>gson</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.google.errorprone</groupId>
+                    <artifactId>error_prone_annotations</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
         <dependency>
             <groupId>org.junit.jupiter</groupId>
diff --git a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/DefaultJacksonJaxbJsonProvider.java b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/DefaultJacksonJaxbJsonProvider.java
index f715407..a209c27 100644
--- a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/DefaultJacksonJaxbJsonProvider.java
+++ b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/DefaultJacksonJaxbJsonProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2024 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 java.lang.annotation.Annotation;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.logging.Logger;
 import jakarta.annotation.PostConstruct;
@@ -58,10 +59,6 @@
     //do not register JaxbAnnotationModule because it brakes default annotations processing
     private static final String[] EXCLUDE_MODULE_NAMES = {"JaxbAnnotationModule", "JakartaXmlBindAnnotationModule"};
 
-    public DefaultJacksonJaxbJsonProvider() {
-        super(new JacksonMapperConfigurator(null, DEFAULT_ANNOTATIONS));
-    }
-
     public DefaultJacksonJaxbJsonProvider(Providers providers, Configuration config, Annotations... annotationsToUse) {
         super(new JacksonMapperConfigurator(null, annotationsToUse));
         this.commonConfig = config;
@@ -104,7 +101,13 @@
                         commonConfig.getRuntimeType(),
                         CommonProperties.JSON_JACKSON_ENABLED_MODULES, String.class);
 
-        final List<Module> modules = ObjectMapper.findModules();
+        final List<Module> modules;
+        try {
+            modules = ObjectMapper.findModules();
+        } catch (Throwable e) {
+            LOGGER.warning(LocalizationMessages.ERROR_MODULES_NOT_LOADED(e.getMessage()));
+            return Collections.emptyList();
+        }
         for (String exludeModuleName : EXCLUDE_MODULE_NAMES) {
             modules.removeIf(mod -> mod.getModuleName().contains(exludeModuleName));
         }
@@ -129,11 +132,15 @@
             final StreamReadConstraints constraints = jsonFactory.streamReadConstraints();
             jsonFactory.setStreamReadConstraints(
                     StreamReadConstraints.builder()
+                            // our
                             .maxStringLength(maxStringLength)
+                            // customers
+                            .maxDocumentLength(constraints.getMaxDocumentLength())
+                            .maxNameLength(constraints.getMaxNameLength())
                             .maxNestingDepth(constraints.getMaxNestingDepth())
                             .maxNumberLength(constraints.getMaxNumberLength())
                             .build()
             );
         }
     }
-}
\ No newline at end of file
+}
diff --git a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/JacksonObjectProvider.java b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/JacksonObjectProvider.java
index a12d4d0..74f510c 100644
--- a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/JacksonObjectProvider.java
+++ b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/JacksonObjectProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2024 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
@@ -26,6 +26,7 @@
 import org.glassfish.jersey.message.filtering.spi.EntityGraphProvider;
 import org.glassfish.jersey.message.filtering.spi.EntityInspector;
 import org.glassfish.jersey.message.filtering.spi.ObjectGraph;
+import org.glassfish.jersey.message.filtering.spi.ScopeProvider;
 
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.databind.JsonMappingException;
@@ -37,7 +38,6 @@
 import com.fasterxml.jackson.databind.ser.PropertyFilter;
 import com.fasterxml.jackson.databind.ser.PropertyWriter;
 import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
-import org.glassfish.jersey.message.filtering.spi.ScopeProvider;
 
 import jakarta.inject.Inject;
 import jakarta.ws.rs.core.Context;
diff --git a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/base/ProviderBase.java b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/base/ProviderBase.java
index ecdfbae..bf365bb 100644
--- a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/base/ProviderBase.java
+++ b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/base/ProviderBase.java
@@ -31,7 +31,6 @@
 import org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.ObjectWriterInjector;
 import org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.ObjectWriterModifier;
 import org.glassfish.jersey.jackson.internal.jackson.jaxrs.util.ClassKey;
-import org.glassfish.jersey.jackson.internal.jackson.jaxrs.util.LRUMap;
 
 import com.fasterxml.jackson.core.JsonEncoding;
 import com.fasterxml.jackson.core.JsonGenerator;
@@ -44,6 +43,7 @@
 import com.fasterxml.jackson.databind.ObjectReader;
 import com.fasterxml.jackson.databind.ObjectWriter;
 import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.util.LRUMap;
 import com.fasterxml.jackson.databind.type.TypeFactory;
 
 public abstract class ProviderBase<
@@ -1056,4 +1056,4 @@
     private final THIS _this() {
         return (THIS) this;
     }
-}
\ No newline at end of file
+}
diff --git a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/cfg/JaxRSFeature.java b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/cfg/JaxRSFeature.java
index 6f864c9..c0f0da5 100644
--- a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/cfg/JaxRSFeature.java
+++ b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/cfg/JaxRSFeature.java
@@ -40,6 +40,7 @@
      * @since 2.15
      */
     READ_FULL_STREAM(true),
+
     /*
     /**********************************************************
     /* HTTP headers
diff --git a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/cfg/MapperConfiguratorBase.java b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/cfg/MapperConfiguratorBase.java
index 75bc66e..bc280cc 100644
--- a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/cfg/MapperConfiguratorBase.java
+++ b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/cfg/MapperConfiguratorBase.java
@@ -13,8 +13,8 @@
  * well as accessing it.
  */
 public abstract class MapperConfiguratorBase<IMPL extends MapperConfiguratorBase<IMPL,MAPPER>,
-    MAPPER extends ObjectMapper
->
+        MAPPER extends ObjectMapper
+        >
 {
     /**
      * Mapper provider was constructed with if any, or that was constructed
@@ -22,7 +22,7 @@
      * If defined (explicitly or implicitly) it will be used, instead
      * of using provider-based lookup.
      */
-    protected MAPPER _mapper;
+    protected volatile MAPPER _mapper;
 
     /**
      * If no mapper was specified when constructed, and no configuration
@@ -30,14 +30,14 @@
      * between default mapper and regular one is that default mapper
      * is only used if no mapper is found via provider lookup.
      */
-    protected MAPPER _defaultMapper;
+    protected volatile MAPPER _defaultMapper;
 
     /**
      * Annotations set to use by default; overridden by explicit call
-     * to {@link #setAnnotationsToUse}
+     * to {@link #setAnnotationsToUse}. Marked final in v2.17.1.
      */
-    protected Annotations[] _defaultAnnotationsToUse;
-    
+    protected final Annotations[] _defaultAnnotationsToUse;
+
     /**
      * To support optional dependency to Jackson JAXB annotations module
      * (needed iff JAXB annotations are used for configuration)
@@ -49,7 +49,7 @@
     /* Construction
     /**********************************************************
      */
-    
+
     public MapperConfiguratorBase(MAPPER mapper, Annotations[] defaultAnnotations)
     {
         _mapper = mapper;
@@ -61,7 +61,7 @@
     /* Abstract methods to implement
     /***********************************************************
      */
-    
+
     /**
      * Method that locates, configures and returns {@link ObjectMapper} to use
      */
@@ -84,27 +84,27 @@
     /***********************************************************
      */
 
-    public synchronized final void setMapper(MAPPER m) {
+    public final void setMapper(MAPPER m) {
         _mapper = m;
     }
 
-    public synchronized final void setAnnotationsToUse(Annotations[] annotationsToUse) {
+    public final void setAnnotationsToUse(Annotations[] annotationsToUse) {
         _setAnnotations(mapper(), annotationsToUse);
     }
 
-    public synchronized final void configure(DeserializationFeature f, boolean state) {
+    public final void configure(DeserializationFeature f, boolean state) {
         mapper().configure(f, state);
     }
 
-    public synchronized final void configure(SerializationFeature f, boolean state) {
+    public final void configure(SerializationFeature f, boolean state) {
         mapper().configure(f, state);
     }
 
-    public synchronized final void configure(JsonParser.Feature f, boolean state) {
+    public final void configure(JsonParser.Feature f, boolean state) {
         mapper().configure(f, state);
     }
 
-    public synchronized final void configure(JsonGenerator.Feature f, boolean state) {
+    public final void configure(JsonGenerator.Feature f, boolean state) {
         mapper().configure(f, state);
     }
 
diff --git a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/json/JsonMapperConfigurator.java b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/json/JsonMapperConfigurator.java
index 3e9c770..8e8091a 100644
--- a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/json/JsonMapperConfigurator.java
+++ b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/json/JsonMapperConfigurator.java
@@ -1,6 +1,7 @@
 package org.glassfish.jersey.jackson.internal.jackson.jaxrs.json;
 
 import java.util.ArrayList;
+import java.util.concurrent.locks.ReentrantLock;
 
 import org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.Annotations;
 import org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.MapperConfiguratorBase;
@@ -16,14 +17,17 @@
  * well as accessing it.
  */
 public class JsonMapperConfigurator
-    extends MapperConfiguratorBase<JsonMapperConfigurator, ObjectMapper>
+        extends MapperConfiguratorBase<JsonMapperConfigurator, ObjectMapper>
 {
+    // @since 2.17.1
+    private final ReentrantLock _lock = new ReentrantLock();
+
     /*
     /**********************************************************
     /* Construction
     /**********************************************************
      */
-    
+
     public JsonMapperConfigurator(ObjectMapper mapper, Annotations[] defAnnotations)
     {
         super(mapper, defAnnotations);
@@ -33,18 +37,24 @@
      * Method that locates, configures and returns {@link ObjectMapper} to use
      */
     @Override
-    public synchronized ObjectMapper getConfiguredMapper() {
-        /* important: should NOT call mapper(); needs to return null
-         * if no instance has been passed or constructed
-         */
+    public ObjectMapper getConfiguredMapper() {
+        // important: should NOT call mapper(); needs to return null
+        // if no instance has been passed or constructed
         return _mapper;
     }
 
     @Override
-    public synchronized ObjectMapper getDefaultMapper() {
+    public ObjectMapper getDefaultMapper() {
         if (_defaultMapper == null) {
-            _defaultMapper = new ObjectMapper();
-            _setAnnotations(_defaultMapper, _defaultAnnotationsToUse);
+            _lock.lock();
+            try {
+                if (_defaultMapper == null) {
+                    _defaultMapper = new ObjectMapper();
+                    _setAnnotations(_defaultMapper, _defaultAnnotationsToUse);
+                }
+            } finally {
+                _lock.unlock();
+            }
         }
         return _defaultMapper;
     }
@@ -64,8 +74,15 @@
     protected ObjectMapper mapper()
     {
         if (_mapper == null) {
-            _mapper = new ObjectMapper();
-            _setAnnotations(_mapper, _defaultAnnotationsToUse);
+            _lock.lock();
+            try {
+                if (_mapper == null) {
+                    _mapper = new ObjectMapper();
+                    _setAnnotations(_mapper, _defaultAnnotationsToUse);
+                }
+            } finally {
+                _lock.unlock();
+            }
         }
         return _mapper;
     }
@@ -100,22 +117,22 @@
     protected AnnotationIntrospector _resolveIntrospector(Annotations ann)
     {
         switch (ann) {
-        case JACKSON:
-            return new JacksonAnnotationIntrospector();
-        case JAXB:
-            /* For this, need to use indirection just so that error occurs
-             * when we get here, and not when this class is being loaded
-             */
-            try {
-                if (_jaxbIntrospectorClass == null) {
-                    _jaxbIntrospectorClass = JaxbAnnotationIntrospector.class;
+            case JACKSON:
+                return new JacksonAnnotationIntrospector();
+            case JAXB:
+                /* For this, need to use indirection just so that error occurs
+                 * when we get here, and not when this class is being loaded
+                 */
+                try {
+                    if (_jaxbIntrospectorClass == null) {
+                        _jaxbIntrospectorClass = JaxbAnnotationIntrospector.class;
+                    }
+                    return _jaxbIntrospectorClass.newInstance();
+                } catch (Exception e) {
+                    throw new IllegalStateException("Failed to instantiate JaxbAnnotationIntrospector: "+e.getMessage(), e);
                 }
-                return _jaxbIntrospectorClass.newInstance();
-            } catch (Exception e) {
-                throw new IllegalStateException("Failed to instantiate JaxbAnnotationIntrospector: "+e.getMessage(), e);
-            }
-        default:
-            throw new IllegalStateException(); 
+            default:
+                throw new IllegalStateException();
         }
     }
 }
diff --git a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/json/PackageVersion.java b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/json/PackageVersion.java
index 3a34c28..56b1bf6 100644
--- a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/json/PackageVersion.java
+++ b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/json/PackageVersion.java
@@ -11,7 +11,7 @@
  */
 public final class PackageVersion implements Versioned {
     public final static Version VERSION = VersionUtil.parseVersion(
-        "2.15.3", "com.fasterxml.jackson.jaxrs", "jackson-jaxrs-json-provider");
+        "2.17.2", "com.fasterxml.jackson.jaxrs", "jackson-jaxrs-json-provider");
 
     @Override
     public Version version() {
diff --git a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/util/LRUMap.java b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/util/LRUMap.java
index 7252cd5..c99c987 100644
--- a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/util/LRUMap.java
+++ b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/util/LRUMap.java
@@ -3,11 +3,15 @@
 import java.util.LinkedHashMap;
 import java.util.Map;
 
+// TO BE REMOVED FROM JACKSON 2.18 or later
 /**
  * Helper for simple bounded LRU maps used for reusing lookup values.
  *
  * @since 2.2
+ *
+ * @deprecated Since 2.16.1 Use one from {@code jackson-databind} instead.
  */
+@Deprecated // since 2.16.1
 @SuppressWarnings("serial")
 public class LRUMap<K,V> extends LinkedHashMap<K,V>
 {
@@ -24,5 +28,4 @@
     {
         return size() > _maxEntries;
     }
-
 }
diff --git a/media/json-jackson/src/main/resources/META-INF/NOTICE.markdown b/media/json-jackson/src/main/resources/META-INF/NOTICE.markdown
index 4251b2f..9440229 100644
--- a/media/json-jackson/src/main/resources/META-INF/NOTICE.markdown
+++ b/media/json-jackson/src/main/resources/META-INF/NOTICE.markdown
@@ -31,7 +31,7 @@
 

 ## Third-party Content

 

-Jackson JAX-RS Providers version 2.15.3

+Jackson JAX-RS Providers version 2.17.2

 * License: Apache License, 2.0

 * Project: https://github.com/FasterXML/jackson-jaxrs-providers

 * Copyright: (c) 2009-2023 FasterXML, LLC. All rights reserved unless otherwise indicated.

diff --git a/media/json-jackson/src/main/resources/org/glassfish/jersey/jackson/localization.properties b/media/json-jackson/src/main/resources/org/glassfish/jersey/jackson/localization.properties
index f8b59da..a9144fd 100644
--- a/media/json-jackson/src/main/resources/org/glassfish/jersey/jackson/localization.properties
+++ b/media/json-jackson/src/main/resources/org/glassfish/jersey/jackson/localization.properties
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2023, 2024 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,4 +14,5 @@
 # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 #
 
-error.jackson.streamreadconstraints=Error setting StreamReadConstraints: {0}. Possibly not Jackson 2.15?
\ No newline at end of file
+error.jackson.streamreadconstraints=Error setting StreamReadConstraints: {0}. Possibly not Jackson 2.15?
+error.modules.not.loaded=Jackson modules could not be loaded: {0}
\ No newline at end of file
diff --git a/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/DefaultJsonJacksonProviderForDisabledModulesTest.java b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/DefaultJsonJacksonProviderForDisabledModulesTest.java
index e75400a..3e40866 100644
--- a/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/DefaultJsonJacksonProviderForDisabledModulesTest.java
+++ b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/DefaultJsonJacksonProviderForDisabledModulesTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2024 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.core.Application;
 import jakarta.ws.rs.core.Configuration;
 import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.Response;
 
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -48,9 +49,10 @@
                 .register(JacksonFeature.class)
                 .register(TestJacksonJaxbJsonProvider.class)
                 .property("jersey.config.json.jackson.disabled.modules", "Jdk8Module");
-        final String response = target("JAXBEntity")
-                .request().get(String.class);
-        assertNotEquals("{\"key\":\"key\",\"value\":\"value\"}", response);
+        try (Response response = target("JAXBEntity").request().get()) {
+            assertEquals(400, response.getStatus());
+            assertNotEquals("{\"key\":\"key\",\"value\":\"value\"}", response.readEntity(String.class));
+        }
     }
 
     private static class TestJacksonJaxbJsonProvider extends JacksonJaxbJsonProvider {
diff --git a/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/DefaultJsonJacksonProviderForEnabledModulesTest.java b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/DefaultJsonJacksonProviderForEnabledModulesTest.java
index eaf293d..58a5e5a 100644
--- a/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/DefaultJsonJacksonProviderForEnabledModulesTest.java
+++ b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/DefaultJsonJacksonProviderForEnabledModulesTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2024 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
@@ -22,7 +22,9 @@
 import org.junit.jupiter.api.Test;
 
 import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Response;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
 
 public class DefaultJsonJacksonProviderForEnabledModulesTest extends JerseyTest {
@@ -34,9 +36,10 @@
 
     @Test
     public final void testDisabledModule() {
-        final String response = target("entity/simple")
-                .request().get(String.class);
-        assertNotEquals("{\"name\":\"Hello\",\"value\":\"World\"}", response);
+        try (Response response = target("entity/simple").request().get()) {
+            assertEquals(400, response.getStatus());
+            assertNotEquals("{\"name\":\"Hello\",\"value\":\"World\"}", response.readEntity(String.class));
+        }
     }
 
 }
diff --git a/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/StreamReadConstrainsTest.java b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/StreamReadConstrainsTest.java
index 925c17e..8fa7cb9 100644
--- a/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/StreamReadConstrainsTest.java
+++ b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/StreamReadConstrainsTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2024 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
@@ -50,6 +50,7 @@
 import java.util.List;
 
 public class StreamReadConstrainsTest extends JerseyTest {
+    private static final String ERROR_MSG_PART = "maximum allowed (";
 
     @Override
     protected final Application configure() {
@@ -78,7 +79,7 @@
                 .post(Entity.entity(new MyEntity(8), MediaType.APPLICATION_JSON_TYPE))) {
             Assertions.assertEquals(200, response.getStatus());
             String errorMsg = response.readEntity(String.class);
-            Assertions.assertTrue(errorMsg.contains("maximum length (4)"));
+            Assertions.assertTrue(errorMsg.contains(ERROR_MSG_PART + 4));
         }
     }
 
@@ -125,7 +126,7 @@
                 throw ex;
             }
             String errorMsg = ex.getCause().getMessage();
-            Assertions.assertTrue(errorMsg.contains("maximum length (" + String.valueOf(expectedLength) + ")"));
+            Assertions.assertTrue(errorMsg.contains(ERROR_MSG_PART + String.valueOf(expectedLength)));
         }
     }
 
@@ -137,7 +138,7 @@
                 + " Please update the code in " + DefaultJacksonJaxbJsonProvider.class.getName()
                 + " updateFactoryConstraints method";
         Method[] method = StreamReadConstraints.Builder.class.getDeclaredMethods();
-        Assertions.assertEquals(4, method.length, message); // three max + build methods
+        Assertions.assertEquals(6, method.length, message); // five setMax... + build() methods
     }
 
     @Path("len")
diff --git a/media/moxy/pom.xml b/media/moxy/pom.xml
index 33e2c86..534c4c3 100644
--- a/media/moxy/pom.xml
+++ b/media/moxy/pom.xml
@@ -62,21 +62,6 @@
                 </configuration>
             </plugin>
 
-            <plugin>
-                <groupId>de.jflex</groupId>
-                <artifactId>maven-jflex-plugin</artifactId>
-                <version>1.4.3</version>
-                <executions>
-                    <execution>
-                        <goals>
-                            <goal>generate</goal>
-                        </goals>
-                        <configuration>
-                            <outputDirectory>${project.build.directory}/generated-sources/rsrc-gen</outputDirectory>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
         </plugins>
     </build>
 
@@ -111,6 +96,11 @@
         </dependency>
 
         <dependency>
+            <groupId>jakarta.xml.bind</groupId>
+            <artifactId>jakarta.xml.bind-api</artifactId>
+        </dependency>
+
+        <dependency>
             <groupId>org.eclipse.persistence</groupId>
             <artifactId>org.eclipse.persistence.moxy</artifactId>
             <exclusions>
@@ -122,6 +112,14 @@
                     <groupId>org.eclipse.angus</groupId>
                     <artifactId>angus-activation</artifactId>
                 </exclusion>
+                <exclusion>
+                    <groupId>jakarta.xml.bind</groupId>
+                    <artifactId>jakarta.xml.bind-api</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>jakarta.activation</groupId>
+                    <artifactId>jakarta.activation-api</artifactId>
+                </exclusion>
             </exclusions>
         </dependency>
 
@@ -138,12 +136,4 @@
         </dependency>
     </dependencies>
 
-    <repositories>
-        <repository>
-            <id>eclipselink.repository</id>
-            <name>Eclipse Maven Repository</name>
-            <url>https://download.eclipse.org/rt/eclipselink/maven.repo</url>
-            <layout>default</layout>
-        </repository>
-    </repositories>
-</project>
+</project>
\ No newline at end of file
diff --git a/media/multipart/pom.xml b/media/multipart/pom.xml
index 8021c13..e61999e 100644
--- a/media/multipart/pom.xml
+++ b/media/multipart/pom.xml
@@ -36,6 +36,9 @@
 
     <properties>
         <surefire.coverage.argline>
+            --add-exports org.glassfish.jersey.core.common/org.glassfish.jersey.innate=ALL-UNNAMED
+            --add-exports org.glassfish.jersey.core.common/org.glassfish.jersey.innate.virtual=ALL-UNNAMED
+            --add-exports org.glassfish.jersey.core.common/org.glassfish.jersey.innate.io=ALL-UNNAMED
             --add-exports org.glassfish.jersey.core.client/org.glassfish.jersey.client.innate=ALL-UNNAMED
             --add-modules=ALL-MODULE-PATH
         </surefire.coverage.argline>
@@ -143,6 +146,7 @@
                     <plugin>
                         <groupId>org.apache.maven.plugins</groupId>
                         <artifactId>maven-compiler-plugin</artifactId>
+                        <inherited>true</inherited>
                         <configuration>
                             <testExcludes>
                                 <testExclude>org/glassfish/jersey/media/multipart/internal/MultiPartHeaderModificationTest.java</testExclude>
diff --git a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/ContentDisposition.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/ContentDisposition.java
index f95d521..f6c7165 100644
--- a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/ContentDisposition.java
+++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/ContentDisposition.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 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,8 +28,6 @@
 import org.glassfish.jersey.message.internal.HttpHeaderReader;
 import org.glassfish.jersey.uri.UriComponent;
 
-import jakarta.ws.rs.core.HttpHeaders;
-
 /**
  * A content disposition header.
  *
@@ -60,10 +58,13 @@
     private static final Pattern FILENAME_VALUE_CHARS_PATTERN =
             Pattern.compile("(%[a-f0-9]{2}|[a-z0-9!#$&+.^_`|~-])+", Pattern.CASE_INSENSITIVE);
 
+    private static final char QUOTE = '"';
+    private static final char BACK_SLASH = '\\';
+
     protected ContentDisposition(final String type, final String fileName, final Date creationDate,
                                  final Date modificationDate, final Date readDate, final long size) {
         this.type = type;
-        this.fileName = fileName;
+        this.fileName = encodeAsciiFileName(fileName);
         this.creationDate = creationDate;
         this.modificationDate = modificationDate;
         this.readDate = readDate;
@@ -201,7 +202,9 @@
 
     protected void addDateParameter(final StringBuilder sb, final String name, final Date p) {
         if (p != null) {
-            sb.append("; ").append(name).append("=\"").append(HttpDateFormat.getPreferredDateFormat().format(p)).append("\"");
+            sb.append("; ").append(name).append("=\"")
+                    .append(HttpDateFormat.getPreferredDateFormatter().format(p))
+                    .append("\"");
         }
     }
 
@@ -211,6 +214,23 @@
         }
     }
 
+    protected String encodeAsciiFileName(String fileName) {
+        if (fileName == null
+                || (fileName.indexOf(QUOTE) == -1
+                && fileName.indexOf(BACK_SLASH) == -1)) {
+            return fileName;
+        }
+        final char[] chars = fileName.toCharArray();
+        final StringBuilder encodedBuffer = new StringBuilder();
+        for (char c : chars) {
+            if (c == QUOTE || c == BACK_SLASH) {
+                encodedBuffer.append(BACK_SLASH);
+            }
+            encodedBuffer.append(c);
+        }
+        return encodedBuffer.toString();
+    }
+
     private void createParameters() throws ParseException {
         defineFileName();
 
@@ -229,7 +249,7 @@
         final String fileNameExt = parameters.get("filename*");
 
         if (fileNameExt == null) {
-            this.fileName = fileName;
+            this.fileName = encodeAsciiFileName(fileName);
             return;
         }
 
@@ -284,7 +304,7 @@
         if (value == null) {
             return null;
         }
-        return HttpDateFormat.getPreferredDateFormat().parse(value);
+        return HttpDateFormat.getPreferredDateFormatter().toDate(value);
     }
 
     private long createLong(final String name) throws ParseException {
diff --git a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/MultiPartProperties.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/MultiPartProperties.java
index 68abedb..00d6aba 100644
--- a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/MultiPartProperties.java
+++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/MultiPartProperties.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 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 jakarta.ws.rs.ext.ContextResolver;
 
 import org.glassfish.jersey.internal.util.PropertiesClass;
+import org.glassfish.jersey.message.internal.ReaderWriter;
 
 /**
  * Injectable JavaBean containing the configuration parameters for
@@ -38,12 +39,17 @@
     /**
      * Default threshold size for buffer.
      */
-    public static final int DEFAULT_BUFFER_THRESHOLD = 4096;
+    public static final int DEFAULT_BUFFER_THRESHOLD = ReaderWriter.BUFFER_SIZE;
 
     /**
+     * <p>
      * Name of a properties resource that (if found in the classpath
      * for this application) will be used to configure the settings returned
      * by our getter methods.
+     * </p>
+     * <p>
+     *     The resource name is {@code jersey-multipart-config.properties}.
+     * </p>
      */
     public static final String MULTI_PART_CONFIG_RESOURCE = "jersey-multipart-config.properties";
 
@@ -51,7 +57,7 @@
      * Name of the resource property for the threshold size (in bytes) above which a body part entity will be
      * buffered to disk instead of being held in memory.
      *
-     * The default value is {@value #DEFAULT_BUFFER_THRESHOLD}.
+     * The default value is {@link #DEFAULT_BUFFER_THRESHOLD}.
      */
     public static final String BUFFER_THRESHOLD = "jersey.config.multipart.bufferThreshold";
 
@@ -61,8 +67,20 @@
     public static final int BUFFER_THRESHOLD_MEMORY_ONLY = -1;
 
     /**
+     * <p>
+     *     Limit the maximum number of parts the multipart entity can have. If the limit is over,
+     *     the error response status {@link jakarta.ws.rs.core.Response.Status#REQUEST_ENTITY_TOO_LARGE} is returned.
+     * </p>
+     * <p>
+     *     By default, the number is unlimited.
+     * </p>
+     * @since 2.44
+     */
+    public static final String MAX_PARTS = "jersey.config.multipart.maxParts";
+
+    /**
      * Name of the resource property for the directory to store temporary files containing body parts of multipart message that
-     * extends allowed memory threshold..
+     * extends allowed memory threshold.
      *
      * The default value is not set (will be taken from {@code java.io.tmpdir} system property).
      */
@@ -80,6 +98,11 @@
     private String tempDir = null;
 
     /**
+     * Maximum number of entity parts allowed.
+     */
+    private int maxParts = Integer.MAX_VALUE;
+
+    /**
      * Load and customize (if necessary) the configuration values for the
      * {@code jersey-multipart} injection binder.
      *
@@ -114,6 +137,15 @@
     }
 
     /**
+     * Return maximum number of entity parts allowed.
+     * @return maximum number of parts.
+     * @since 2.44
+     */
+    public int getMaxParts() {
+        return maxParts;
+    }
+
+    /**
      * Set the size (in bytes) of the entity of an incoming {@link BodyPart} before it will be buffered to disk.
      *
      * @param threshold size of body part.
@@ -139,6 +171,17 @@
     }
 
     /**
+     * Set the maximum number of received parts of a multipart entity.
+     * @param maxParts The maximum number of entity parts.
+     * @return {@code MultiPartProperties} instance.
+     * @since 2.44
+     */
+    public MultiPartProperties maxParts(int maxParts) {
+        this.maxParts = maxParts;
+        return this;
+    }
+
+    /**
      * Configure the values returned by this instance's getters based on
      * the contents of a properties resource, if it exists on the classpath
      * for this application.
@@ -169,6 +212,9 @@
             if (props.containsKey(TEMP_DIRECTORY)) {
                 this.tempDir = props.getProperty(TEMP_DIRECTORY);
             }
+            if (props.contains(MAX_PARTS)) {
+                this.maxParts = Integer.parseInt(props.getProperty(MAX_PARTS));
+            }
         } catch (final IOException e) {
             throw new IllegalArgumentException(e);
         } finally {
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 6c834d9..277855b 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, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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 java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
@@ -234,7 +236,13 @@
         @Override
         public Object apply(ContainerRequest request) {
             // Return the field value for the field specified by the sourceName property.
-            final List<FormDataBodyPart> parts = getEntity(request).getFields(parameter.getSourceName());
+            final String sourceName = parameter.getAnnotations().length == 1
+                    ? parameter.getSourceName()
+                    : Arrays.stream(parameter.getAnnotations())
+                        .filter(ann -> FormDataParam.class.isInstance(ann))
+                        .map(ann -> FormDataParam.class.cast(ann))
+                        .findFirst().get().value();
+            final List<FormDataBodyPart> parts = getEntity(request).getFields(sourceName);
 
             final FormDataBodyPart part = parts != null ? parts.get(0) : null;
             final MediaType mediaType = part != null ? part.getMediaType() : MediaType.TEXT_PLAIN_TYPE;
@@ -396,44 +404,48 @@
             } else {
                 return null;
             }
-        } else if (parameter.getSourceAnnotation().annotationType() == FormDataParam.class) {
-            final String paramName = parameter.getSourceName();
-            if (paramName == null || paramName.isEmpty()) {
-                // Invalid query parameter name
-                return null;
-            }
+        } else {
+            for (Annotation sourceAnnotation : parameter.getAnnotations()) {
+                if (sourceAnnotation.annotationType() == FormDataParam.class) {
+                    final String paramName = ((FormDataParam) sourceAnnotation).value(); // sourceName refers to the last anno
+                    if (paramName == null || paramName.isEmpty()) {
+                        // Invalid query parameter name
+                        return null;
+                    }
 
-            if (Collection.class == rawType || List.class == rawType) {
-                final Class clazz = ReflectionHelper.getGenericTypeArgumentClasses(parameter.getType()).get(0);
+                    if (Collection.class == rawType || List.class == rawType) {
+                        final Class clazz = ReflectionHelper.getGenericTypeArgumentClasses(parameter.getType()).get(0);
 
-                if (FormDataBodyPart.class == clazz) {
-                    // Return a collection of form data body part.
-                    return new ListFormDataBodyPartValueProvider(paramName);
-                } else if (FormDataContentDisposition.class == clazz) {
-                    // Return a collection of form data content disposition.
-                    return new ListFormDataContentDispositionProvider(paramName);
-                } else {
-                    // Return a collection of specific type.
-                    return new FormDataParamValueProvider(parameter, get(parameter));
+                        if (FormDataBodyPart.class == clazz) {
+                            // Return a collection of form data body part.
+                            return new ListFormDataBodyPartValueProvider(paramName);
+                        } else if (FormDataContentDisposition.class == clazz) {
+                            // Return a collection of form data content disposition.
+                            return new ListFormDataContentDispositionProvider(paramName);
+                        } else {
+                            // Return a collection of specific type.
+                            return new FormDataParamValueProvider(parameter, get(parameter));
+                        }
+                    } else if (FormDataBodyPart.class == rawType) {
+                        return new FormDataBodyPartProvider(paramName);
+                    } else if (FormDataContentDisposition.class == rawType) {
+                        return new FormDataContentDispositionProvider(paramName);
+                    } else if (File.class == rawType) {
+                        return new FileProvider(paramName);
+                    } else {
+                        return new FormDataParamValueProvider(parameter, get(parameter));
+                    }
+                } else if (FormParam.class.equals(sourceAnnotation.annotationType())) {
+                    final String paramName = ((FormParam) sourceAnnotation).value();
+                    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);
+                    }
                 }
-            } else if (FormDataBodyPart.class == rawType) {
-                return new FormDataBodyPartProvider(paramName);
-            } else if (FormDataContentDisposition.class == rawType) {
-                return new FormDataContentDispositionProvider(paramName);
-            } else if (File.class == rawType) {
-                return new FileProvider(paramName);
-            } 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);
             }
         }
 
diff --git a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/MultiPartReaderClientSide.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/MultiPartReaderClientSide.java
index c867078..edb80bd 100644
--- a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/MultiPartReaderClientSide.java
+++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/MultiPartReaderClientSide.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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 java.util.logging.Logger;
 
 import jakarta.ws.rs.BadRequestException;
+import jakarta.ws.rs.ClientErrorException;
 import jakarta.ws.rs.ConstrainedTo;
 import jakarta.ws.rs.Consumes;
 import jakarta.ws.rs.RuntimeType;
@@ -36,6 +37,7 @@
 import jakarta.ws.rs.core.HttpHeaders;
 import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.MultivaluedMap;
+import jakarta.ws.rs.core.Response;
 import jakarta.ws.rs.ext.ContextResolver;
 import jakarta.ws.rs.ext.MessageBodyReader;
 import jakarta.ws.rs.ext.Providers;
@@ -79,6 +81,7 @@
      */
     private Provider<MessageBodyWorkers> messageBodyWorkers;
     private final MIMEConfig mimeConfig;
+    private final int maxParts;
 
     /**
      * Accepts constructor injection of the configuration parameters for this
@@ -98,6 +101,8 @@
             properties = new MultiPartProperties();
         }
 
+        maxParts = properties.getMaxParts();
+
         this.messageBodyWorkers = messageBodyWorkers;
         mimeConfig = createMimeConfig(properties);
     }
@@ -205,7 +210,12 @@
             fileNameFix = userAgent != null && userAgent.contains(" MSIE ");
         }
 
-        for (final MIMEPart mimePart : getMimeParts(mimeMessage)) {
+        final List<MIMEPart> mimeParts = getMimeParts(mimeMessage);
+        if (mimeParts.size() > maxParts) {
+            throw new ClientErrorException(Response.Status.REQUEST_ENTITY_TOO_LARGE);
+        }
+
+        for (final MIMEPart mimePart : mimeParts) {
             final BodyPart bodyPart = formData ? new FormDataBodyPart(fileNameFix) : new BodyPart();
 
             // Configure providers.
diff --git a/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/internal/OrderParamTest.java b/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/internal/OrderParamTest.java
new file mode 100644
index 0000000..23cc7a8
--- /dev/null
+++ b/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/internal/OrderParamTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2024 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 org.glassfish.jersey.media.multipart.FormDataMultiPart;
+import org.glassfish.jersey.media.multipart.FormDataParam;
+import org.glassfish.jersey.media.multipart.MultiPartFeature;
+import org.glassfish.jersey.message.internal.ReaderWriter;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.model.ParamQualifier;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class OrderParamTest extends JerseyTest {
+    @Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
+    @Retention(RetentionPolicy.RUNTIME)
+    @ParamQualifier
+    public static @interface AnnoWithValue {
+        String value() default "";
+    }
+
+    @Path("/order")
+    public static class OrderTestResource {
+        @POST
+        @Path("/dataAfter")
+        @Consumes(value = MediaType.MULTIPART_FORM_DATA)
+        public String orderBefore(@FormDataParam("file") @AnnoWithValue("xxx") InputStream inputStream) throws IOException {
+            return ReaderWriter.readFromAsString(inputStream, MediaType.TEXT_PLAIN_TYPE);
+        }
+
+        @POST
+        @Path("/dataBefore")
+        @Consumes(value = MediaType.MULTIPART_FORM_DATA)
+        public String orderAfter(@AnnoWithValue("zzz") @FormDataParam("file") InputStream inputStream) throws IOException {
+            return ReaderWriter.readFromAsString(inputStream, MediaType.TEXT_PLAIN_TYPE);
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(OrderTestResource.class).register(MultiPartFeature.class);
+    }
+
+    @Test
+    public void testOrder() {
+        final String MSG = "Hello";
+        FormDataMultiPart multiPart = new FormDataMultiPart();
+        multiPart.field("file", MSG, MediaType.TEXT_PLAIN_TYPE);
+        try (Response response = target("order")
+                .register(MultiPartFeature.class)
+                .path("dataBefore").request()
+                .post(Entity.entity(multiPart, MediaType.MULTIPART_FORM_DATA_TYPE))) {
+            assertEquals(200, response.getStatus());
+            assertEquals(MSG, response.readEntity(String.class));
+        }
+
+        try (Response response = target("order")
+                .register(MultiPartFeature.class)
+                .path("dataAfter").request()
+                .post(Entity.entity(multiPart, MediaType.MULTIPART_FORM_DATA_TYPE))) {
+            assertEquals(200, response.getStatus());
+            assertEquals(MSG, response.readEntity(String.class));
+        }
+    }
+}
diff --git a/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/internal/RestrictionsTest.java b/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/internal/RestrictionsTest.java
new file mode 100644
index 0000000..9906f11
--- /dev/null
+++ b/media/multipart/src/test/java/org/glassfish/jersey/media/multipart/internal/RestrictionsTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2024 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 org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.media.multipart.FormDataMultiPart;
+import org.glassfish.jersey.media.multipart.FormDataParam;
+import org.glassfish.jersey.media.multipart.MultiPartFeature;
+import org.glassfish.jersey.media.multipart.MultiPartProperties;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+
+public class RestrictionsTest extends JerseyTest {
+    @Path("/")
+    public static class RestrictionsTestResource {
+        @POST
+        @Path("max.parts")
+        public String postMaxPart(@FormDataParam("part1") String part1, @FormDataParam("part2") String part2) {
+            return part1 + part2;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(RestrictionsTestResource.class)
+                .register(MultiPartFeature.class)
+                .register(new MultiPartProperties().maxParts(2).resolver());
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(MultiPartFeature.class);
+    }
+
+    @Test
+    public void testPassNumberOfParts() {
+        FormDataMultiPart multiPart = new FormDataMultiPart();
+        multiPart.field("part1", "he", MediaType.TEXT_PLAIN_TYPE);
+        multiPart.field("part2", "llo", MediaType.TEXT_PLAIN_TYPE);
+        try (Response r = target("max.parts").request().post(Entity.entity(multiPart, MediaType.MULTIPART_FORM_DATA_TYPE))) {
+            Assertions.assertEquals(200, r.getStatus());
+            Assertions.assertEquals("hello", r.readEntity(String.class));
+        }
+    }
+
+    @Test
+    public void testFailsNumberOfParts() {
+        FormDataMultiPart multiPart = new FormDataMultiPart();
+        multiPart.field("part1", "he", MediaType.TEXT_PLAIN_TYPE);
+        multiPart.field("part2", "llo", MediaType.TEXT_PLAIN_TYPE);
+        multiPart.field("part3", "!", MediaType.TEXT_PLAIN_TYPE);
+        try (Response r = target("max.parts").request().post(Entity.entity(multiPart, MediaType.MULTIPART_FORM_DATA_TYPE))) {
+            Assertions.assertEquals(Response.Status.REQUEST_ENTITY_TOO_LARGE.getStatusCode(), r.getStatus());
+        }
+    }
+}
diff --git a/media/sse/pom.xml b/media/sse/pom.xml
index 28ce019..f83762f 100644
--- a/media/sse/pom.xml
+++ b/media/sse/pom.xml
@@ -89,6 +89,10 @@
         </plugins>
     </build>
     <properties>
-        <surefire.coverage.argline>--add-modules=ALL-MODULE-PATH</surefire.coverage.argline>
+        <surefire.coverage.argline>
+            --add-opens org.glassfish.jersey.core.common/org.glassfish.jersey.innate=ALL-UNNAMED
+            --add-opens org.glassfish.jersey.core.common/org.glassfish.jersey.innate.virtual=ALL-UNNAMED
+            --add-modules=ALL-MODULE-PATH
+        </surefire.coverage.argline>
     </properties>
 </project>
diff --git a/media/sse/src/main/java/org/glassfish/jersey/media/sse/internal/EventProcessor.java b/media/sse/src/main/java/org/glassfish/jersey/media/sse/internal/EventProcessor.java
index 00e9fe7..ee9f56d 100644
--- a/media/sse/src/main/java/org/glassfish/jersey/media/sse/internal/EventProcessor.java
+++ b/media/sse/src/main/java/org/glassfish/jersey/media/sse/internal/EventProcessor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2024 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
@@ -24,6 +24,7 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -87,6 +88,10 @@
      * A map of listeners bound to receive only events of a particular name.
      */
     private final Map<String, List<EventListener>> boundListeners;
+    /**
+     * A list of Error Consumers.
+     */
+    private final List<Consumer<Throwable>> throwableConsumers;
 
     /**
      * Shutdown handler is invoked when Event processor reaches terminal stage.
@@ -111,6 +116,7 @@
         this.unboundListeners = that.unboundListeners;
         this.eventListener = that.eventListener;
         this.shutdownHandler = that.shutdownHandler;
+        this.throwableConsumers = that.throwableConsumers;
     }
 
     private EventProcessor(Builder builder) {
@@ -128,6 +134,7 @@
         this.unboundListeners = builder.unboundListeners == null ? Collections.EMPTY_LIST : builder.unboundListeners;
         this.eventListener = builder.eventListener;
         this.shutdownHandler = builder.shutdownHandler;
+        this.throwableConsumers = builder.throwableConsumers;
     }
 
     /**
@@ -199,6 +206,16 @@
             }
             // if we're here, an unrecoverable error has occurred - just turn off the lights...
             shutdownHandler.shutdown();
+            // and notify error handlers
+            if (throwableConsumers != null) {
+                for (Consumer<Throwable> consumer : throwableConsumers) {
+                    try {
+                        consumer.accept(ex);
+                    } catch (Throwable throwable) {
+                        LOGGER.fine(String.format("User throwable ignored: %s", throwable.getMessage()));
+                    }
+                }
+            }
         } finally {
             if (eventInput != null && !eventInput.isClosed()) {
                 eventInput.close();
@@ -357,6 +374,7 @@
         private boolean disableKeepAlive;
         private List<EventListener> unboundListeners;
         private Map<String, List<EventListener>> boundListeners;
+        private List<Consumer<Throwable>> throwableConsumers = null;
 
         private Builder(WebTarget target,
                         AtomicReference<State> state,
@@ -421,6 +439,17 @@
         }
 
         /**
+         * Set the consumers of {@link Throwable} occurring during connection.
+         *
+         * @param throwableConsumers a list of consumers of throwable.
+         * @return updated builder instance.
+         */
+        public Builder throwableConsumers(List<Consumer<Throwable>> throwableConsumers) {
+            this.throwableConsumers = throwableConsumers;
+            return this;
+        }
+
+        /**
          * Build the {@link EventProcessor}.
          *
          * @return built Event processor instance.
diff --git a/media/sse/src/main/java/org/glassfish/jersey/media/sse/internal/JerseySseEventSource.java b/media/sse/src/main/java/org/glassfish/jersey/media/sse/internal/JerseySseEventSource.java
index d455465..f675181 100644
--- a/media/sse/src/main/java/org/glassfish/jersey/media/sse/internal/JerseySseEventSource.java
+++ b/media/sse/src/main/java/org/glassfish/jersey/media/sse/internal/JerseySseEventSource.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2024 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,8 @@
 
 package org.glassfish.jersey.media.sse.internal;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
@@ -72,6 +74,10 @@
      * Client provided executor facade.
      */
     private final ClientExecutor clientExecutor;
+    /**
+     * List of Throwable consumers passed to EventProcessor.Builder.
+     */
+    private final List<Consumer<Throwable>> throwableConsumers = new ArrayList<>();
 
     /**
      * Private constructor.
@@ -110,11 +116,13 @@
     public void register(final Consumer<InboundSseEvent> onEvent, final Consumer<Throwable> onError) {
         this.subscribe(DEFAULT_SUBSCRIPTION_HANDLER, onEvent, onError, () -> {
         });
+        throwableConsumers.add(onError);
     }
 
     @Override
     public void register(final Consumer<InboundSseEvent> onEvent, final Consumer<Throwable> onError, final Runnable onComplete) {
         this.subscribe(DEFAULT_SUBSCRIPTION_HANDLER, onEvent, onError, onComplete);
+        throwableConsumers.add(onError);
     }
 
     private void subscribe(final Consumer<Flow.Subscription> onSubscribe,
@@ -173,6 +181,7 @@
         EventProcessor processor = EventProcessor
                 .builder(endpoint, state, clientExecutor, this::onEvent, this::close)
                 .reconnectDelay(reconnectDelay, reconnectTimeUnit)
+                .throwableConsumers(throwableConsumers)
                 .build();
         clientExecutor.submit(processor);
 
diff --git a/media/sse/src/test/java/org/glassfish/jersey/media/sse/SseEventSourceRegisterErrorHandlerTest.java b/media/sse/src/test/java/org/glassfish/jersey/media/sse/SseEventSourceRegisterErrorHandlerTest.java
new file mode 100644
index 0000000..036a568
--- /dev/null
+++ b/media/sse/src/test/java/org/glassfish/jersey/media/sse/SseEventSourceRegisterErrorHandlerTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2024 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.sse;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.BadRequestException;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.InternalServerErrorException;
+import jakarta.ws.rs.NotFoundException;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.sse.InboundSseEvent;
+import jakarta.ws.rs.sse.Sse;
+import jakarta.ws.rs.sse.SseEventSink;
+import jakarta.ws.rs.sse.SseEventSource;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+
+public class SseEventSourceRegisterErrorHandlerTest extends JerseyTest {
+    @Path("sse")
+    public static class SseEventSourceRegisterTestSseEndpoint {
+
+        @Path("hello")
+        @GET
+        @Produces(SseFeature.SERVER_SENT_EVENTS)
+        public void hello(@Context SseEventSink output, @Context Sse sse) throws InterruptedException {
+            output.send(sse.newEvent("HELLO"));
+        }
+
+        @Path("close")
+        @GET
+        @Produces(SseFeature.SERVER_SENT_EVENTS)
+        public void close(@Context SseEventSink output, @Context Sse sse) throws InterruptedException {
+            output.close();
+        }
+
+        @Path("500")
+        @GET
+        @Produces(SseFeature.SERVER_SENT_EVENTS)
+        public void throw500(@Context SseEventSink output, @Context Sse sse) throws InterruptedException {
+            throw new WebApplicationException();
+        }
+
+        @Path("400")
+        @GET
+        @Produces(SseFeature.SERVER_SENT_EVENTS)
+        public void throw400(@Context SseEventSink output, @Context Sse sse) throws InterruptedException {
+            throw new BadRequestException();
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(SseEventSourceRegisterTestSseEndpoint.class);
+    }
+
+    private static final Consumer<InboundSseEvent> EMPTY = event -> {
+    };
+
+    @Test
+    public void testConnection404() throws InterruptedException {
+        WebTarget sseTarget = target("sse");
+        AtomicReference<Throwable> throwable = new AtomicReference<>();
+        CountDownLatch completeLatch = new CountDownLatch(1);
+
+        SseEventSource eventSource = SseEventSource.target(sseTarget).build();
+        eventSource.register(EMPTY, throwable::set, completeLatch::countDown);
+        eventSource.open();
+        completeLatch.await(10_000, TimeUnit.MILLISECONDS);
+        MatcherAssert.assertThat(throwable.get(), Matchers.notNullValue());
+        MatcherAssert.assertThat(throwable.get().getClass(), Matchers.is(NotFoundException.class));
+    }
+
+    @Test
+    public void testError500() throws InterruptedException {
+        WebTarget sseTarget = target("sse/500");
+        AtomicReference<Throwable> throwable = new AtomicReference<>();
+        CountDownLatch completeLatch = new CountDownLatch(1);
+
+        SseEventSource eventSource = SseEventSource.target(sseTarget).build();
+        eventSource.register(EMPTY, throwable::set, completeLatch::countDown);
+        eventSource.open();
+        completeLatch.await(10_000, TimeUnit.MILLISECONDS);
+        MatcherAssert.assertThat(throwable.get(), Matchers.notNullValue());
+        MatcherAssert.assertThat(throwable.get().getClass(), Matchers.is(InternalServerErrorException.class));
+    }
+
+    @Test
+    public void testError400() throws InterruptedException {
+        WebTarget sseTarget = target("sse/400");
+        AtomicReference<Throwable> throwable = new AtomicReference<>();
+        CountDownLatch completeLatch = new CountDownLatch(1);
+
+        SseEventSource eventSource = SseEventSource.target(sseTarget).build();
+        eventSource.register(EMPTY, throwable::set, completeLatch::countDown);
+        eventSource.open();
+        completeLatch.await(10_000, TimeUnit.MILLISECONDS);
+        MatcherAssert.assertThat(throwable.get(), Matchers.notNullValue());
+        MatcherAssert.assertThat(throwable.get().getClass(), Matchers.is(BadRequestException.class));
+    }
+}
diff --git a/pom.xml b/pom.xml
index 742cfbb..557fad5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -142,6 +142,9 @@
         <contributor>
             <name>Stepan Vavra</name>
         </contributor>
+        <contributor>
+            <name>Dmytro Dovnar</name>
+        </contributor>
     </contributors>
 
     <licenses>
@@ -150,14 +153,14 @@
             <url>http://www.eclipse.org/legal/epl-2.0</url>
             <distribution>repo</distribution>
             <comments>Except for 3rd content and examples.
-                      See also https://github.com/eclipse-ee4j/jersey/blob/master/NOTICE.md</comments>
+                See also https://github.com/eclipse-ee4j/jersey/blob/master/NOTICE.md</comments>
         </license>
         <license>
             <name>GPL2 w/ CPE</name>
             <url>https://www.gnu.org/software/classpath/license.html</url>
             <distribution>repo</distribution>
             <comments>Except for 3rd content and examples.
-                      See also https://github.com/eclipse-ee4j/jersey/blob/master/NOTICE.md</comments>
+                See also https://github.com/eclipse-ee4j/jersey/blob/master/NOTICE.md</comments>
         </license>
         <license>
             <name>EDL 1.0</name>
@@ -176,9 +179,9 @@
             <url>http://www.apache.org/licenses/LICENSE-2.0.html</url>
             <distribution>repo</distribution>
             <comments>Google Guava @ org.glassfish.jersey.internal.guava,
-                      Dropwizard Monitoring inspired classes @ org.glassfish.jersey.server.internal.monitoring.core,
-                      Hibernate Validation classes @ org.glassfish.jersey.server.validation.internal.hibernate, and
-                      Jackson JAX-RS Providers @ org.glassfish.jersey.jackson.internal.jackson.jaxrs</comments>
+                Dropwizard Monitoring inspired classes @ org.glassfish.jersey.server.internal.monitoring.core,
+                Hibernate Validation classes @ org.glassfish.jersey.server.validation.internal.hibernate, and
+                Jackson JAX-RS Providers @ org.glassfish.jersey.jackson.internal.jackson.jaxrs</comments>
         </license>
         <license>
             <name>Public Domain</name>
@@ -203,7 +206,7 @@
             <url>http://www.opensource.org/licenses/mit-license.php</url>
             <distribution>repo</distribution>
             <comments>AngularJS, Bootstrap v3.3.7,
-                      jQuery Barcode plugin 0.3, KineticJS v4.7.1</comments>
+                jQuery Barcode plugin 0.3, KineticJS v4.7.1</comments>
         </license>
         <license>
             <name>W3C license</name>
@@ -305,7 +308,7 @@
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-clean-plugin</artifactId>
-                    <version>2.5</version>
+                    <version>${clean.mvn.plugin.version}</version>
                 </plugin>
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
@@ -425,12 +428,10 @@
                                 junit.jupiter.execution.parallel.mode.default=same_thread
                             </configurationParameters>
                         </properties>
-                        <systemProperties>
-                            <property>
-                                <name>jersey.config.test.container.port</name>
-                                <value>${jersey.config.test.container.port}</value>
-                            </property>
-                        </systemProperties>
+                        <systemPropertyVariables>
+                            <jersey.config.test.container.port>${jersey.config.test.container.port}
+                            </jersey.config.test.container.port>
+                        </systemPropertyVariables>
                         <threadCount>124</threadCount>
                     </configuration>
                     <dependencies>
@@ -470,7 +471,7 @@
                         <doctitle>Jersey ${jersey.version} API Documentation</doctitle>
                         <windowtitle>Jersey ${jersey.version} API</windowtitle>
                         <bottom>
-                            <![CDATA[Copyright &#169; 2007-2023,
+                            <![CDATA[Copyright &#169; 2007-2024,
                                 <a href="http://www.oracle.com">Oracle</a>
                                 and/or its affiliates.
                                 All Rights Reserved. Use is subject to license terms.]]>
@@ -481,7 +482,7 @@
                             <link>https://eclipse-ee4j.github.io/jersey.github.io/apidocs/latest/jersey/</link>
                         </links>
                         <excludePackageNames>
-                            *.internal.*:*.innate.*:*.tests.*
+                            *.innate:*.innate.*:*.tests:*.tests.*
                         </excludePackageNames>
                         <includeDependencySources>false</includeDependencySources>
                         <dependencySourceIncludes>
@@ -546,11 +547,6 @@
                     </configuration>
                 </plugin>
                 <plugin>
-                    <groupId>org.apache.maven.plugins</groupId>
-                    <artifactId>maven-site-plugin</artifactId>
-                    <version>${site.mvn.plugin.version}</version>
-                </plugin>
-                <plugin>
                     <groupId>org.codehaus.mojo</groupId>
                     <artifactId>exec-maven-plugin</artifactId>
                     <version>${exec.mvn.plugin.version}</version>
@@ -637,12 +633,10 @@
                         <skipTests>${skip.tests}</skipTests>
                         <skipITs>${skip.tests}</skipITs>
                         <argLine>${failsafe.coverage.argline}</argLine>
-                        <systemProperties>
-                            <property>
-                                <name>jersey.config.test.container.port</name>
-                                <value>${jersey.config.test.container.port}</value>
-                            </property>
-                        </systemProperties>
+                        <systemPropertyVariables>
+                            <jersey.config.test.container.port>${jersey.config.test.container.port}
+                            </jersey.config.test.container.port>
+                        </systemPropertyVariables>
                     </configuration>
                     <executions>
                         <execution>
@@ -795,25 +789,7 @@
                         <dependency>
                             <groupId>org.apache.ant</groupId>
                             <artifactId>ant</artifactId>
-                            <version>1.10.12</version>
-                        </dependency>
-                    </dependencies>
-                </plugin>
-                <plugin>
-                    <groupId>org.fortasoft</groupId>
-                    <artifactId>gradle-maven-plugin</artifactId>
-                    <version>1.0.8</version>
-                </plugin>
-                <plugin>
-                    <groupId>com.github.wvengen</groupId>
-                    <artifactId>proguard-maven-plugin</artifactId>
-                    <version>${proguard.mvn.plugin.version}</version>
-                    <dependencies>
-                        <dependency>
-                            <groupId>net.sf.proguard</groupId>
-                            <artifactId>proguard-base</artifactId>
-                            <version>5.1</version><!-- transitive dependency version increased from 5.0 -->
-                            <scope>runtime</scope>
+                            <version>${mvn.ant.version}</version>
                         </dependency>
                     </dependencies>
                 </plugin>
@@ -865,7 +841,6 @@
             <plugin>
                 <groupId>org.codehaus.mojo</groupId>
                 <artifactId>build-helper-maven-plugin</artifactId>
-                <version>${buildhelper.mvn.plugin.version}</version>
                 <configuration>
                     <portNames>
                         <portName>jersey.config.test.container.port</portName>
@@ -882,6 +857,27 @@
                     </execution>
                 </executions>
             </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-checkstyle-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>verify</id>
+                        <phase>validate</phase>
+                        <goals>
+                            <!-- Fail the build if checkstyle rules for contributions are not met. -->
+                            <goal>check</goal>
+                        </goals>
+                        <configuration>
+                            <configLocation>etc/config/checkstyle-verify.xml</configLocation>
+                            <consoleOutput>true</consoleOutput>
+                            <failOnViolation>true</failOnViolation>
+                            <includeTestSourceDirectory>true</includeTestSourceDirectory>
+                            <excludes>**/module-info.java</excludes>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
         </plugins>
         <extensions>
             <extension>
@@ -943,23 +939,23 @@
                                     <id>default-compile</id>
                                     <configuration>
                                         compile everything to ensure module-info contains right entries
-                                        <release>11</release>
+                                        <release>${java.version}</release>
                                     </configuration>
                                 </execution>
-                                <execution>
+                                <!--<execution>
                                     <id>base-compile</id>
                                     <goals>
                                         <goal>compile</goal>
                                     </goals>
-                                    <!-- recompile everything for target VM except the module-info.java -->
+                                    &lt;!&ndash; recompile everything for target VM except the module-info.java &ndash;&gt;
                                     <configuration>
                                         <excludes>
                                             <exclude>module-info.java</exclude>
                                         </excludes>
-                                        <source>1.8</source>
-                                        <target>1.8</target>
+                                       &lt;!&ndash; <source>1.8</source>
+                                        <target>1.8</target>&ndash;&gt;
                                     </configuration>
-                                </execution>
+                                </execution>-->
                             </executions>
                         </plugin>
                     </plugins>
@@ -979,9 +975,8 @@
                 </property>
             </activation>
             <properties>
-<!--                <release.tests.args>-Dskip.tests=true</release.tests.args>-->
+                <!--                <release.tests.args>-Dskip.tests=true</release.tests.args>-->
                 <skip.tests>true</skip.tests>
-                <skip.e2e>true</skip.e2e>
             </properties>
         </profile>
         <profile>
@@ -1007,7 +1002,7 @@
             <activation>
                 <activeByDefault>false</activeByDefault>
                 <property>
-                  <name>!tests.excluded</name>
+                    <name>!tests.excluded</name>
                 </property>
             </activation>
             <modules>
@@ -1103,10 +1098,10 @@
                         <artifactId>maven-enforcer-plugin</artifactId>
                         <executions>
                             <execution>
-                               <id>enforce-property</id>
-                               <goals>
-                                   <goal>enforce</goal>
-                               </goals>
+                                <id>enforce-property</id>
+                                <goals>
+                                    <goal>enforce</goal>
+                                </goals>
                                 <configuration>
                                     <rules>
                                         <requireProperty>
@@ -1140,14 +1135,6 @@
                     <version>${moxy.version}</version>
                 </dependency>
             </dependencies>
-            <repositories>
-                <repository>
-                    <id>eclipselink.repository</id>
-                    <name>Eclipse Maven Repository</name>
-                    <url>https://www.eclipse.org/downloads/download.php?r=1&amp;nf=1&amp;file=/rt/eclipselink/maven.repo</url>
-                    <layout>default</layout>
-                </repository>
-            </repositories>
         </profile>
         <profile>
             <id>securityOff</id>
@@ -1165,7 +1152,7 @@
                     <plugin>
                         <groupId>org.apache.maven.plugins</groupId>
                         <artifactId>maven-project-info-reports-plugin</artifactId>
-                        <version>3.4.5</version>
+                        <version>${project.info.reports.mvn.plugin.version}</version>
                         <reportSets>
                             <reportSet>
                                 <reports>
@@ -1186,29 +1173,29 @@
             </distributionManagement>
         </profile>
         <profile>
-            <id>jdk1.7+</id>
+            <id>sbom</id>
             <activation>
-                <jdk>[1.7,)</jdk>
+                <property>
+                    <name>!skipSBOM</name>
+                </property>
             </activation>
             <build>
                 <plugins>
                     <plugin>
-                        <groupId>org.apache.maven.plugins</groupId>
-                        <artifactId>maven-checkstyle-plugin</artifactId>
+                        <groupId>org.cyclonedx</groupId>
+                        <artifactId>cyclonedx-maven-plugin</artifactId>
+                        <version>${cyclonedx.mvn.plugin.version}</version>
+                        <inherited>true</inherited>
                         <executions>
                             <execution>
-                                <id>verify</id>
-                                <phase>validate</phase>
+                                <phase>package</phase>
                                 <goals>
-                                    <!-- Fail the build if checkstyle rules for contributions are not met. -->
-                                    <goal>check</goal>
+                                    <goal>makeAggregateBom</goal>
                                 </goals>
                                 <configuration>
-                                    <configLocation>etc/config/checkstyle-verify.xml</configLocation>
-                                    <consoleOutput>true</consoleOutput>
-                                    <failOnViolation>true</failOnViolation>
-                                    <includeTestSourceDirectory>true</includeTestSourceDirectory>
-                                    <excludes>**/module-info.java</excludes>
+                                    <!--                            <schemaVersion>1.4</schemaVersion>-->
+                                    <projectType>framework</projectType>
+                                    <excludeTestProject>true</excludeTestProject>
                                 </configuration>
                             </execution>
                         </executions>
@@ -1357,51 +1344,6 @@
             </build>
         </profile>
         <profile>
-            <!--
-            Profile is aimed to run the build on travis
-            due to travis limitations for output (max 4MB) this profile is used along with grep which reduces
-            the output.
-            However some e2e tests produce output which is not grepped (thus is not visible) and run longer than
-            10 minutes which results in the whole build is being murdered by Travis because of death suspection
-
-            the whole build is run as clean install but excludes several e2e tests because of the not grepped output
-            -->
-            <id>travis_e2e_skip</id>
-            <properties>
-                <skip.e2e>true</skip.e2e>
-            </properties>
-        </profile>
-        <profile>
-            <!--
-             Profile is aimed to run the build on travis
-            due to travis limitations for output (max 4MB) this profile is used to run e2e tests only.
-
-            the only thing which is happen using profile is run of e2e tests (with additional build)
-            everything is already build using travis_e2e_skip profile
-
-            the whole build is run as test -Ptravis_e2e
-            -->
-            <id>travis_e2e</id>
-            <properties>
-                <skip.e2e>false</skip.e2e>
-                <skip.tests>true</skip.tests>
-            </properties>
-        </profile>
-        <profile>
-                <!-- Use -Peclipse_repo to use SNAPSHOTs stored in Eclipse's Nexus instance ("Nightly Builds") -->
-                <id>eclipse_repo</id>
-            <repositories>
-                    <repository>
-                        <snapshots>
-                            <enabled>true</enabled>
-                        </snapshots>
-                        <id>repo.jaxrs-api.eclipse.org</id>
-                        <name>JAX-RS API Repository - Snapshots</name>
-                        <url>https://repo.eclipse.org/content/repositories/jax-rs-api-snapshots</url>
-                    </repository>
-                </repositories>
-        </profile>
-        <profile>
             <id>license_check</id>
             <pluginRepositories>
                 <pluginRepository>
@@ -1424,7 +1366,7 @@
                     <plugin>
                         <groupId>org.eclipse.dash</groupId>
                         <artifactId>license-tool-plugin</artifactId>
-                        <version>1.0.2</version>
+                        <version>1.1.0</version>
                         <executions>
                             <execution>
                                 <id>license-check</id>
@@ -1477,20 +1419,20 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-javadoc-plugin</artifactId>
-                <version>3.4.1</version>
+                <version>${javadoc.mvn.plugin.version}</version>
                 <!-- Run this plugin report sets only in the main Jersey pom -->
                 <inherited>false</inherited>
                 <configuration>
                     <doctitle>Jersey ${jersey.version} API Documentation</doctitle>
                     <windowtitle>Jersey ${jersey.version} API</windowtitle>
                     <bottom>
-                        <![CDATA[Copyright &#169; 2007-2023,
+                        <![CDATA[Copyright &#169; 2007-2024,
                             <a href="http://www.oracle.com">Oracle</a>
                             and/or its affiliates.
                             All Rights Reserved. Use is subject to license terms.]]>
                     </bottom>
                     <excludePackageNames>
-                        com.sun.ws.rs.ext:*.examples.*:*.internal.*:*.tests.*
+                        com.sun.ws.rs.ext:*.examples.*:*.innate:*.innate.*:*.tests:*.tests.*
                     </excludePackageNames>
                     <links>
                         <link>https://jax-rs.github.io/apidocs/2.1</link>
@@ -1530,7 +1472,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-jxr-plugin</artifactId>
-                <version>2.3</version>
+                <version>${jxr.mvn.plugin.version}</version>
                 <reportSets>
                     <reportSet>
                         <reports>
@@ -1781,7 +1723,7 @@
                 <groupId>org.simpleframework</groupId>
                 <artifactId>simple-http</artifactId>
                 <version>${simple.version}</version>
-             </dependency>
+            </dependency>
 
             <dependency>
                 <groupId>org.simpleframework</groupId>
@@ -1936,8 +1878,8 @@
                 <version>${jakarta.el.version}</version>
             </dependency>
             <dependency>
-                <groupId>org.glassfish</groupId>
-                <artifactId>jakarta.el</artifactId>
+                <groupId>org.glassfish.expressly</groupId>
+                <artifactId>expressly</artifactId>
                 <version>${jakarta.el.impl.version}</version>
             </dependency>
 
@@ -2159,9 +2101,9 @@
             </dependency>
 
             <dependency>
-               <groupId>com.google.code.gson</groupId>
-               <artifactId>gson</artifactId>
-               <version>${gson.version}</version>
+                <groupId>com.google.code.gson</groupId>
+                <artifactId>gson</artifactId>
+                <version>${gson.version}</version>
             </dependency>
 
             <dependency>
@@ -2195,9 +2137,8 @@
 <!--        <netbeans.hint.license>gf-cddl-gpl</netbeans.hint.license>-->
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
-<!--        <release.tests.args>-Dmaven.test.skip=false</release.tests.args>-->
-<!--        <release.preparationGoals>clean install</release.preparationGoals>-->
-        <skip.e2e>false</skip.e2e>
+        <!--        <release.tests.args>-Dmaven.test.skip=false</release.tests.args>-->
+        <!--        <release.preparationGoals>clean install</release.preparationGoals>-->
         <skip.tests>false</skip.tests>
         <xdk.absolute.path />
         <surefire.security.argline />
@@ -2208,14 +2149,16 @@
 
         <!-- Versions of Maven plugins -->
         <antrun.mvn.plugin.version>3.1.0</antrun.mvn.plugin.version>
-        <assembly.mvn.plugin.version>3.6.0</assembly.mvn.plugin.version>
-        <enforcer.mvn.plugin.version>3.4.1</enforcer.mvn.plugin.version>
-        <exec.mvn.plugin.version>3.1.0</exec.mvn.plugin.version>
-        <buildhelper.mvn.plugin.version>3.4.0</buildhelper.mvn.plugin.version>
+        <mvn.ant.version>1.10.14</mvn.ant.version>
+        <assembly.mvn.plugin.version>3.7.1</assembly.mvn.plugin.version>
+        <clean.mvn.plugin.version>3.4.0</clean.mvn.plugin.version>
+        <enforcer.mvn.plugin.version>3.5.0</enforcer.mvn.plugin.version>
+        <exec.mvn.plugin.version>3.4.1</exec.mvn.plugin.version>
+        <buildhelper.mvn.plugin.version>3.6.0</buildhelper.mvn.plugin.version>
         <buildnumber.mvn.plugin.version>3.2.0</buildnumber.mvn.plugin.version>
-        <checkstyle.mvn.plugin.version>3.3.1</checkstyle.mvn.plugin.version>
-        <checkstyle.version>10.12.4</checkstyle.version>
-        <compiler.mvn.plugin.version>3.11.0</compiler.mvn.plugin.version>
+        <checkstyle.mvn.plugin.version>3.4.0</checkstyle.mvn.plugin.version>
+        <checkstyle.version>10.17.0</checkstyle.version>
+        <compiler.mvn.plugin.version>3.13.0</compiler.mvn.plugin.version>
         <!--
         Special version of the compiler plugin just for the jersey-common. All versions above
         generate too much for OSGi manifest.mf imports (awt etc). The version 3.11.0 however
@@ -2223,90 +2166,94 @@
         but the jersey-common module which has to have the separate version for OSGi reasons.
          -->
         <compiler.common.mvn.plugin.version>3.9.0</compiler.common.mvn.plugin.version>
-        <dependency.mvn.plugin.version>3.6.1</dependency.mvn.plugin.version>
-        <deploy.mvn.plugin.version>3.1.1</deploy.mvn.plugin.version>
+        <cyclonedx.mvn.plugin.version>2.8.1</cyclonedx.mvn.plugin.version>
+        <dependency.mvn.plugin.version>3.7.1</dependency.mvn.plugin.version>
+        <deploy.mvn.plugin.version>3.1.2</deploy.mvn.plugin.version>
         <ear.mvn.plugin.version>3.3.0</ear.mvn.plugin.version>
-        <failsafe.mvn.plugin.version>3.2.1</failsafe.mvn.plugin.version>
+        <failsafe.mvn.plugin.version>3.3.1</failsafe.mvn.plugin.version>
         <felix.mvn.plugin.version>5.1.9</felix.mvn.plugin.version>
         <findbugs.mvn.plugin.version>3.0.5</findbugs.mvn.plugin.version>
         <gfembedded.mvn.plugin.version>5.1</gfembedded.mvn.plugin.version>
-        <install.mvn.plugin.version>3.1.1</install.mvn.plugin.version>
+        <install.mvn.plugin.version>3.1.2</install.mvn.plugin.version>
         <istack.mvn.plugin.version>4.2.0</istack.mvn.plugin.version>
-        <jar.mvn.plugin.version>3.3.0</jar.mvn.plugin.version>
-        <javadoc.mvn.plugin.version>3.6.0</javadoc.mvn.plugin.version>
-        <jxr.mvn.plugin.version>3.3.1</jxr.mvn.plugin.version>
+        <jar.mvn.plugin.version>3.4.2</jar.mvn.plugin.version>
+        <javadoc.mvn.plugin.version>3.8.0</javadoc.mvn.plugin.version>
+        <jxr.mvn.plugin.version>3.4.0</jxr.mvn.plugin.version>
         <paxexam.mvn.plugin.version>1.2.4</paxexam.mvn.plugin.version>
-        <proguard.mvn.plugin.version>2.6.0</proguard.mvn.plugin.version>
+        <project.info.reports.mvn.plugin.version>3.6.2</project.info.reports.mvn.plugin.version>
         <resources.mvn.plugin.version>3.3.1</resources.mvn.plugin.version>
-        <shade.mvn.plugin.version>3.5.1</shade.mvn.plugin.version>
-        <site.mvn.plugin.version>3.9.1</site.mvn.plugin.version>
-        <source.mvn.plugin.version>3.3.0</source.mvn.plugin.version>
-        <surefire.mvn.plugin.version>3.2.1</surefire.mvn.plugin.version>
+        <shade.mvn.plugin.version>3.6.0</shade.mvn.plugin.version>
+        <source.mvn.plugin.version>3.3.1</source.mvn.plugin.version>
+        <surefire.mvn.plugin.version>3.3.1</surefire.mvn.plugin.version>
         <war.mvn.plugin.version>3.4.0</war.mvn.plugin.version>
         <wiremock.mvn.plugin.version>2.11.0</wiremock.mvn.plugin.version>
         <xml.mvn.plugin.version>1.1.0</xml.mvn.plugin.version>
+        <harness.testing.mvn.plugin.version>3.3.0</harness.testing.mvn.plugin.version>
         <!-- END of Versions of Maven plugins -->
 
         <!-- Dependency versions -->
         <jersey.version>${project.version}</jersey.version>
-        <arquillian.version>1.7.1.Final</arquillian.version>
+        <arquillian.version>1.8.0.Final</arquillian.version>
         <arquillian.weld.version>3.0.1.Final</arquillian.weld.version> <!-- 3.0.2.Final fails microprofile TCK tests -->
         <!-- asm is now source integrated - keeping this property to see the version -->
         <!-- see core-server/src/main/java/jersey/repackaged/asm/.. -->
-        <asm.version>9.6</asm.version>
+        <asm.version>9.7</asm.version>
         <!--required for spring (ext) modules integration -->
-        <aspectj.weaver.version>1.9.20.1</aspectj.weaver.version>
-<!--        <bnd.plugin.version>2.3.6</bnd.plugin.version>-->
+        <aspectj.weaver.version>1.9.22.1</aspectj.weaver.version>
+        <!--        <bnd.plugin.version>2.3.6</bnd.plugin.version>-->
         <bouncycastle.version>1.70</bouncycastle.version>
-        <commons.io.version>2.15.0</commons.io.version>
-<!--        <commons-lang3.version>3.3.2</commons-lang3.version>-->
-        <commons.logging.version>1.2</commons.logging.version>
-        <fasterxml.classmate.version>1.6.0</fasterxml.classmate.version>
+        <commons.io.version>2.16.1</commons.io.version>
+        <commons.codec.version>1.16.1</commons.codec.version>
+        <!--        <commons-lang3.version>3.3.2</commons-lang3.version>-->
+        <commons.logging.version>1.3.3</commons.logging.version>
+        <fasterxml.classmate.version>1.7.0</fasterxml.classmate.version>
         <felix.eventadmin.version>1.6.4</felix.eventadmin.version>
         <felix.framework.security.version>2.8.4</felix.framework.security.version>
         <felix.framework.version>7.0.5</felix.framework.version>
         <findbugs.glassfish.version>1.7</findbugs.glassfish.version>
-        <freemarker.version>2.3.32</freemarker.version>
-        <gae.version>2.0.21</gae.version>
-        <groovy.version>4.0.15</groovy.version>
-        <gson.version>2.10.1</gson.version>
+        <freemarker.version>2.3.33</freemarker.version>
+        <gae.version>2.0.29</gae.version>
+        <groovy.version>4.0.23</groovy.version>
+        <gson.version>2.11.0</gson.version>
 
         <!--versions, extracted here due to maven-enforcer-plugin -->
-<!--        <commons.codec.version>1.15</commons.codec.version>-->
+        <!--        <commons.codec.version>1.15</commons.codec.version>-->
         <com.uber.jaeger.version>0.27.0</com.uber.jaeger.version>
         <org.codehaus.gmavenplus.version>3.0.2</org.codehaus.gmavenplus.version>
         <!-- end of versions extracted here due to maven-enforcer-plugin -->
 
         <!-- micrometer -->
-        <micrometer.version>1.11.5</micrometer.version>
-        <micrometer-tracing.version>1.0.11</micrometer-tracing.version>
+        <micrometer.version>1.12.4</micrometer.version>
+        <micrometer-tracing.version>1.0.12</micrometer-tracing.version>
 
         <!-- microprofile -->
         <microprofile.config.version>3.0.3</microprofile.config.version>
         <microprofile.rest.client.version>3.0.1</microprofile.rest.client.version>
-        <helidon.config.version>3.0.2</helidon.config.version>
-        <helidon.config.11.version>1.4.13</helidon.config.11.version> <!-- JDK 11- support -->
-        <smallrye.config.version>3.0.0</smallrye.config.version>
+        <helidon.config.version>3.2.6</helidon.config.version>
+        <helidon.connector.version>3.2.8</helidon.connector.version>
+        <helidon.config.11.version>1.4.14</helidon.config.11.version> <!-- JDK 11- support -->
+        <smallrye.config.version>3.7.1</smallrye.config.version>
 
-        <guava.version>31.1-jre</guava.version>
-        <hamcrest.version>2.2</hamcrest.version>
-        <helidon.jersey.connector.version>3.0.2</helidon.jersey.connector.version>
-        <xmlunit.version>2.9.0</xmlunit.version>
+        <guava.version>33.3.0-jre</guava.version>
+        <hamcrest.version>3.0</hamcrest.version>
+        <xmlunit.version>2.10.0</xmlunit.version>
         <httpclient.version>4.5.14</httpclient.version>
-        <httpclient5.version>5.2.1</httpclient5.version>
-        <jackson.version>2.15.3</jackson.version>
-        <javassist.version>3.29.2-GA</javassist.version>
-        <jboss.logging.version>3.5.3.Final</jboss.logging.version>
+        <httpclient5.version>5.3.1</httpclient5.version>
+        <jackson.version>2.17.2</jackson.version>
+        <javassist.version>3.30.2-GA</javassist.version>
         <jettison.version>1.3.7</jettison.version> <!-- TODO: 1.3.8 doesn't work; AbstractJsonTest complexBeanWithAttributes -->
+        <jboss.vfs.version>3.3.2.Final</jboss.vfs.version>
+        <jboss.logging.version>3.6.1.Final</jboss.logging.version>
         <jmh.version>1.37</jmh.version>
         <jmockit.version>1.49</jmockit.version>
         <junit4.version>4.13.2</junit4.version>
-        <junit5.version>5.10.0</junit5.version>
-        <junit-platform-suite.version>1.10.0</junit-platform-suite.version>
-        <kryo.version>4.0.2</kryo.version>
-        <mockito.version>3.12.4</mockito.version> <!-- CQ 17673 -->
-        <mustache.version>0.9.10</mustache.version>
-        <netty.version>4.1.100.Final</netty.version>
+        <junit5.version>5.11.0</junit5.version>
+        <junit-platform-suite.version>1.11.0</junit-platform-suite.version>
+        <junit-platform-suite.legacy.version>1.10.0</junit-platform-suite.legacy.version>
+        <kryo.version>4.0.3</kryo.version>
+        <mockito.version>4.11.0</mockito.version> <!-- CQ 17673 -->
+        <mustache.version>0.9.14</mustache.version>
+        <netty.version>4.1.112.Final</netty.version>
         <opentracing.version>0.33.0</opentracing.version>
         <osgi.version>6.0.0</osgi.version>
         <osgi.framework.version>1.10.0</osgi.framework.version>
@@ -2322,13 +2269,14 @@
         <servlet6.version>6.0.0</servlet6.version>
 
         <simple.version>6.0.1</simple.version>
-        <slf4j.version>2.0.9</slf4j.version>
-        <spring6.version>6.0.13</spring6.version>
-        <testng.version>7.8.0</testng.version>
-        <testng6.version>6.9.13.6</testng6.version>
+        <slf4j.version>2.0.13</slf4j.version>
+        <spring6.version>6.0.18</spring6.version>
+        <testng.version>7.10.2</testng.version>
+        <testng6.version>6.14.3</testng6.version>
+        <thymeleaf.version>3.1.2.RELEASE</thymeleaf.version>
         <!-- Jakartified, eligible for CQ -->
         <weld.api.version>5.0.SP3</weld.api.version>
-        <weld.version>5.1.2.Final</weld.version>
+        <weld.version>5.1.3.Final</weld.version>
         <weld3.version>3.1.9.Final</weld3.version>
         <validation.impl.version>8.0.1.Final</validation.impl.version>
         <!-- END of Jakartified, eligible for CQ -->
@@ -2336,7 +2284,7 @@
         <xerces.version>2.12.2</xerces.version>
 
         <!-- Graal VM       -->
-        <graalvm.version>20.3.12</graalvm.version>
+        <graalvm.version>20.3.15</graalvm.version>
 
         <!-- do not need CQs (below this line till the end of version properties)-->
         <gf.impl.version>7.0.6</gf.impl.version>
@@ -2347,43 +2295,43 @@
         <grizzly2.version>4.0.2</grizzly2.version>
         <grizzly.client.version>1.16</grizzly.client.version>
         <grizzly.npn.version>2.0.0</grizzly.npn.version>
-        <hk2.version>3.0.5</hk2.version>
+        <hk2.version>3.0.6</hk2.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>7.0.3</hk2.config.version>
+        <hk2.config.version>7.0.4</hk2.config.version>
         <jsp.version>3.1.1</jsp.version>
-        <jstl.version>3.0.0</jstl.version>
+        <jstl.version>3.0.2</jstl.version>
         <jta.api.version>2.0.1</jta.api.version>
-        <istack.commons.runtime.version>4.1.1</istack.commons.runtime.version>
-        <jakarta.activation-api.version>2.1.1</jakarta.activation-api.version>
-        <jakarta.activation.version>2.0.0</jakarta.activation.version>
+        <istack.commons.runtime.version>4.1.2</istack.commons.runtime.version>
+        <jakarta.activation-api.version>2.1.3</jakarta.activation-api.version>
+        <jakarta.activation.version>2.0.2</jakarta.activation.version>
         <jakarta.el.version>5.0.1</jakarta.el.version>
-        <jakarta.el.impl.version>5.0.0-M1</jakarta.el.impl.version>
+        <jakarta.el.impl.version>5.0.0</jakarta.el.impl.version>
         <jakarta.annotation.osgi.version>jakarta.annotation.*;version="[2.0,3)"</jakarta.annotation.osgi.version>
         <jakarta.annotation.version>2.1.1</jakarta.annotation.version>
         <jakarta.inject.version>2.0.1</jakarta.inject.version>
         <jakarta.interceptor.version>2.1.0</jakarta.interceptor.version>
-        <jakarta.jsonp.version>2.1.2</jakarta.jsonp.version>
+        <jakarta.jsonp.version>2.1.3</jakarta.jsonp.version>
         <jakarta.persistence.version>3.1.0</jakarta.persistence.version>
         <jakarta.validation.api.version>3.0.2</jakarta.validation.api.version>
-        <jakarta.jaxb.api.version>4.0.0</jakarta.jaxb.api.version>
-        <jaxb.ri.version>4.0.2</jaxb.ri.version>
+        <jakarta.jaxb.api.version>4.0.2</jakarta.jaxb.api.version>
+        <jaxb.ri.version>4.0.5</jaxb.ri.version>
         <jaxrs.api.spec.version>3.1</jaxrs.api.spec.version>
         <jaxrs.api.impl.version>3.1.0</jaxrs.api.impl.version>
         <jetty.osgi.version>org.eclipse.jetty.*;version="[11,15)"</jetty.osgi.version>
-        <jetty.version>12.0.3</jetty.version>
-        <jetty9.version>9.4.53.v20231009</jetty9.version>
+        <jetty.version>12.0.14</jetty.version>
+        <jetty9.version>9.4.56.v20240826</jetty9.version>
         <jetty.servlet.api.version>5.0.2</jetty.servlet.api.version>
-        <jetty11.version>11.0.18</jetty11.version>
-        <jetty.plugin.version>12.0.3</jetty.plugin.version>
-        <jsonb.api.version>3.0.0</jsonb.api.version>
-        <jsonp.ri.version>1.1.1</jsonp.ri.version>
-        <jsonp.jaxrs.version>1.1.1</jsonp.jaxrs.version>
-        <moxy.version>4.0.2</moxy.version>
-        <yasson.version>3.0.3</yasson.version>
+        <jetty11.version>11.0.24</jetty11.version>
+        <jetty.plugin.version>12.0.8</jetty.plugin.version>
+        <jsonb.api.version>3.0.1</jsonb.api.version>
+        <jsonp.ri.version>1.1.7</jsonp.ri.version>
+        <jsonp.jaxrs.version>1.1.7</jsonp.jaxrs.version>
+        <moxy.version>4.0.4</moxy.version>
+        <yasson.version>3.0.4</yasson.version>
         <!-- END of Jakartified -->
 
         <javax.annotation.version>1.3.2</javax.annotation.version> <!--Deprecated, used only for @generated annotation in perf tests -->
         <mimepull.version>1.9.15</mimepull.version>
     </properties>
-</project>
+</project>
\ No newline at end of file
diff --git a/test-framework/core/src/main/java/org/glassfish/jersey/test/JerseyTest.java b/test-framework/core/src/main/java/org/glassfish/jersey/test/JerseyTest.java
index 243466c..fe85e9c 100644
--- a/test-framework/core/src/main/java/org/glassfish/jersey/test/JerseyTest.java
+++ b/test-framework/core/src/main/java/org/glassfish/jersey/test/JerseyTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 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
@@ -621,6 +621,7 @@
             if (!isConcurrent() || activeThreadCount.getAndIncrement() == 0) {
                 registerLogHandlerIfEnabled();
                 final TestContainer testContainer = createTestContainer(context);
+                testContainer.configureContainer();
 
                 // Set current instance of test container and start it.
                 setTestContainer(testContainer);
diff --git a/test-framework/core/src/main/java/org/glassfish/jersey/test/spi/TestContainer.java b/test-framework/core/src/main/java/org/glassfish/jersey/test/spi/TestContainer.java
index 68a5bf8..d2a0030 100644
--- a/test-framework/core/src/main/java/org/glassfish/jersey/test/spi/TestContainer.java
+++ b/test-framework/core/src/main/java/org/glassfish/jersey/test/spi/TestContainer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 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
@@ -54,4 +54,13 @@
      * Stop the container.
      */
     public void stop();
+
+    /**
+     * optional method to configure container before it's being started
+     *
+     * @since 2.44
+     */
+    default void configureContainer() {
+
+    }
 }
diff --git a/test-framework/maven/container-runner-maven-plugin/pom.xml b/test-framework/maven/container-runner-maven-plugin/pom.xml
index a012326..4e6de63 100644
--- a/test-framework/maven/container-runner-maven-plugin/pom.xml
+++ b/test-framework/maven/container-runner-maven-plugin/pom.xml
@@ -36,7 +36,7 @@
     </description>
 
     <properties>
-        <groovy.version>3.0.17</groovy.version>
+<!--        <groovy.version>3.0.21</groovy.version>-->
         <groovy-eclipse-compiler.version>3.7.0</groovy-eclipse-compiler.version>
         <groovy-eclipse-batch.version>3.0.8-01</groovy-eclipse-batch.version>
         <maven.version>3.9.2</maven.version>
@@ -116,7 +116,7 @@
         <dependency>
             <groupId>org.apache.maven.plugin-testing</groupId>
             <artifactId>maven-plugin-testing-harness</artifactId>
-            <version>3.3.0</version>
+            <version>${harness.testing.mvn.plugin.version}</version>
             <scope>test</scope>
         </dependency>
 
@@ -126,6 +126,14 @@
             <version>${org.codehaus.gmavenplus.version}</version>
             <exclusions>
                 <exclusion>
+                    <groupId>org.apache.ant</groupId>
+                    <artifactId>ant-launcher</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.ant</groupId>
+                    <artifactId>ant</artifactId>
+                </exclusion>
+                <exclusion>
                     <groupId>org.apache.maven.shared</groupId>
                     <artifactId>file-management</artifactId>
                 </exclusion>
@@ -161,6 +169,14 @@
                     <groupId>org.codehaus.plexus</groupId>
                     <artifactId>plexus-archiver</artifactId>
                 </exclusion>
+                <exclusion>
+                    <groupId>commons-io</groupId>
+                    <artifactId>commons-io</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.junit.jupiter</groupId>
+                    <artifactId>junit-jupiter-api</artifactId>
+                </exclusion>
             </exclusions>
         </dependency>
 
@@ -178,24 +194,10 @@
         </dependency>
 
         <dependency>
-            <groupId>org.codehaus.groovy</groupId>
+            <groupId>org.apache.groovy</groupId>
             <artifactId>groovy-all</artifactId>
             <type>pom</type>
             <version>${groovy.version}</version>
-            <exclusions>
-                <exclusion>
-                    <groupId>org.apache.ant</groupId>
-                    <artifactId>ant-launcher</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>org.apache.ant</groupId>
-                    <artifactId>ant</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>org.slf4j</groupId>
-                    <artifactId>slf4j-api</artifactId>
-                </exclusion>
-            </exclusions>
         </dependency>
 
         <dependency>
diff --git a/test-framework/maven/container-runner-maven-plugin/src/main/groovy/org/glassfish/jersey/test/maven/runner/RunnerMojo.groovy b/test-framework/maven/container-runner-maven-plugin/src/main/groovy/org/glassfish/jersey/test/maven/runner/RunnerMojo.groovy
index b04c36b..865afb7 100644
--- a/test-framework/maven/container-runner-maven-plugin/src/main/groovy/org/glassfish/jersey/test/maven/runner/RunnerMojo.groovy
+++ b/test-framework/maven/container-runner-maven-plugin/src/main/groovy/org/glassfish/jersey/test/maven/runner/RunnerMojo.groovy
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -23,7 +23,7 @@
 import org.apache.maven.project.MavenProject
 
 import java.nio.file.Files
-import java.nio.file.Paths
+import java.nio.file.Path
 import java.util.regex.Pattern
 
 /**
@@ -96,7 +96,7 @@
 
         def command = string.split(" +(?=((.*?(?<!\\\\)'){2})*[^']*\$)")
 
-        return command?.length > 0 && Files.exists(Paths.get(command[0])) ? command : ["sh"]
+        return command?.length > 0 && Files.exists(Path.of(command[0])) ? command : ["sh"]
     }
 
     abstract Map commonEnvironment()
@@ -184,8 +184,8 @@
             sb.append(System.lineSeparator())
             def matcher = Pattern.compile("(#![^\r\n]*)(.*)", Pattern.DOTALL).matcher(new String(shellContent))
             def string = matcher.matches() ? matcher.replaceFirst("\$1${sb.toString()}\$2") : sb.append(shellContent).toString()
-            Paths.get(scriptsDirectory).toFile().mkdirs()
-            def reExecutableShell = Paths.get(scriptsDirectory, Paths.get(shell).fileName.toString())
+            Path.of(scriptsDirectory).toFile().mkdirs()
+            def reExecutableShell = Path.of(scriptsDirectory, Path.of(shell).fileName.toString())
             Files.write(reExecutableShell, string.bytes)
             getLog().info("Re-executable shell written to: $reExecutableShell")
         } catch (Exception e) {
diff --git a/test-framework/maven/container-runner-maven-plugin/src/main/groovy/org/glassfish/jersey/test/maven/runner/gf4/AbstractGlassfishRunnerMojo.groovy b/test-framework/maven/container-runner-maven-plugin/src/main/groovy/org/glassfish/jersey/test/maven/runner/gf4/AbstractGlassfishRunnerMojo.groovy
index c72b020..9d8a6ba 100644
--- a/test-framework/maven/container-runner-maven-plugin/src/main/groovy/org/glassfish/jersey/test/maven/runner/gf4/AbstractGlassfishRunnerMojo.groovy
+++ b/test-framework/maven/container-runner-maven-plugin/src/main/groovy/org/glassfish/jersey/test/maven/runner/gf4/AbstractGlassfishRunnerMojo.groovy
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -22,7 +22,7 @@
 import org.codehaus.gmavenplus.mojo.AbstractGroovyMojo
 import org.glassfish.jersey.test.maven.runner.RunnerMojo
 
-import java.nio.file.Paths
+import java.nio.file.Path
 /**
  * Abstract class for all Glassfish4 related mojos.
  *
@@ -58,8 +58,8 @@
 
     @Override
     void execute() throws MojoExecutionException, MojoFailureException {
-        asHome = Paths.get(asHome).isAbsolute() ? asHome : Paths.get(distDir, distSubdir, asHome)
-        logFile = Paths.get(logFile).isAbsolute()? logFile : Paths.get(asHome, "domains", domain, "logs", logFile)
+        asHome = Path.of(asHome).isAbsolute() ? asHome : Path.of(distDir, distSubdir, asHome)
+        logFile = Path.of(logFile).isAbsolute()? logFile : Path.of(asHome, "domains", domain, "logs", logFile)
         executeRunner()
     }
 
diff --git a/test-framework/maven/container-runner-maven-plugin/src/main/groovy/org/glassfish/jersey/test/maven/runner/tomcat/AbstractTomcatRunnerMojo.groovy b/test-framework/maven/container-runner-maven-plugin/src/main/groovy/org/glassfish/jersey/test/maven/runner/tomcat/AbstractTomcatRunnerMojo.groovy
index d98b59d..2b041e8 100644
--- a/test-framework/maven/container-runner-maven-plugin/src/main/groovy/org/glassfish/jersey/test/maven/runner/tomcat/AbstractTomcatRunnerMojo.groovy
+++ b/test-framework/maven/container-runner-maven-plugin/src/main/groovy/org/glassfish/jersey/test/maven/runner/tomcat/AbstractTomcatRunnerMojo.groovy
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -22,7 +22,7 @@
 import org.codehaus.gmavenplus.mojo.AbstractGroovyMojo
 import org.glassfish.jersey.test.maven.runner.RunnerMojo
 
-import java.nio.file.Paths
+import java.nio.file.Path
 /**
  * Abstract class for all Tomcat related mojos.
  *
@@ -47,8 +47,8 @@
 
     @Override
     void execute() throws MojoExecutionException, MojoFailureException {
-        catalinaHome = Paths.get(catalinaHome).isAbsolute() ? catalinaHome : Paths.get(distDir, distSubdir, catalinaHome)
-        logFile = logFile ?: Paths.get(catalinaHome, "logs", "catalina.out").toString()
+        catalinaHome = Path.of(catalinaHome).isAbsolute() ? catalinaHome : Path.of(distDir, distSubdir, catalinaHome)
+        logFile = logFile ?: Path.of(catalinaHome, "logs", "catalina.out").toString()
         executeRunner()
     }
 
diff --git a/test-framework/maven/container-runner-maven-plugin/src/main/groovy/org/glassfish/jersey/test/maven/runner/wls/AbstractWlsRunnerMojo.groovy b/test-framework/maven/container-runner-maven-plugin/src/main/groovy/org/glassfish/jersey/test/maven/runner/wls/AbstractWlsRunnerMojo.groovy
index 3fa9ffd..6232472 100644
--- a/test-framework/maven/container-runner-maven-plugin/src/main/groovy/org/glassfish/jersey/test/maven/runner/wls/AbstractWlsRunnerMojo.groovy
+++ b/test-framework/maven/container-runner-maven-plugin/src/main/groovy/org/glassfish/jersey/test/maven/runner/wls/AbstractWlsRunnerMojo.groovy
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -22,7 +22,7 @@
 import org.codehaus.gmavenplus.mojo.AbstractGroovyMojo
 import org.glassfish.jersey.test.maven.runner.RunnerMojo
 
-import java.nio.file.Paths
+import java.nio.file.Path
 
 /**
  * Abstract class for all Weblogic related mojos.
@@ -52,8 +52,8 @@
 
     @Override
     void execute() throws MojoExecutionException, MojoFailureException {
-        mwHome = Paths.get(mwHome).isAbsolute() ? mwHome : Paths.get(distDir, distSubdir, mwHome)
-        logFile = logFile ?: Paths.get(mwHome, domain, "wls.log").toString()
+        mwHome = Path.of(mwHome).isAbsolute() ? mwHome : Path.of(distDir, distSubdir, mwHome)
+        logFile = logFile ?: Path.of(mwHome, domain, "wls.log").toString()
         executeRunner()
     }
 
diff --git a/test-framework/maven/custom-enforcer-rules/pom.xml b/test-framework/maven/custom-enforcer-rules/pom.xml
index 9995912..81dcd87 100644
--- a/test-framework/maven/custom-enforcer-rules/pom.xml
+++ b/test-framework/maven/custom-enforcer-rules/pom.xml
@@ -53,24 +53,8 @@
                     <artifactId>commons-io</artifactId>
                 </exclusion>
                 <exclusion>
-                    <groupId>org.apache.maven</groupId>
-                    <artifactId>maven-core</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>org.eclipse.aether</groupId>
-                    <artifactId>aether-util</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>org.codehaus.plexus</groupId>
-                    <artifactId>plexus-classworlds</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>org.codehaus.plexus</groupId>
-                    <artifactId>plexus-utils</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>commons-io</groupId>
-                    <artifactId>commons-io</artifactId>
+                    <groupId>commons-codec</groupId>
+                    <artifactId>commons-codec</artifactId>
                 </exclusion>
                 <exclusion>
                     <groupId>org.apache.maven</groupId>
diff --git a/test-framework/memleak-test-common/src/main/java/org/glassfish/jersey/test/memleak/common/MemoryLeakUtils.java b/test-framework/memleak-test-common/src/main/java/org/glassfish/jersey/test/memleak/common/MemoryLeakUtils.java
index d519d3f..ecfe68e 100644
--- a/test-framework/memleak-test-common/src/main/java/org/glassfish/jersey/test/memleak/common/MemoryLeakUtils.java
+++ b/test-framework/memleak-test-common/src/main/java/org/glassfish/jersey/test/memleak/common/MemoryLeakUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -23,7 +23,7 @@
 import java.lang.reflect.Method;
 import java.nio.charset.Charset;
 import java.nio.file.Files;
-import java.nio.file.Paths;
+import java.nio.file.Path;
 import java.util.Arrays;
 import java.util.List;
 import java.util.regex.Pattern;
@@ -121,7 +121,7 @@
             throws InvocationTargetException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException, IOException {
         conditionallyInitHotSpotDiagnosticMXBean();
         try {
-            java.nio.file.Files.deleteIfExists(Paths.get(fileName));
+            java.nio.file.Files.deleteIfExists(Path.of(fileName));
         } catch (IOException e) {
             // do nothing and try to go further
         }
diff --git a/test-framework/pom.xml b/test-framework/pom.xml
index b9034e9..344dd47 100644
--- a/test-framework/pom.xml
+++ b/test-framework/pom.xml
@@ -44,6 +44,12 @@
         <dependency>
             <groupId>org.junit.jupiter</groupId>
             <artifactId>junit-jupiter</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.junit.platform</groupId>
+                    <artifactId>junit-platform-engine</artifactId>
+                </exclusion>
+            </exclusions>
             <scope>compile</scope>
         </dependency>
         <dependency>
diff --git a/test-framework/providers/grizzly2/pom.xml b/test-framework/providers/grizzly2/pom.xml
index a503c36..687bed7 100644
--- a/test-framework/providers/grizzly2/pom.xml
+++ b/test-framework/providers/grizzly2/pom.xml
@@ -53,5 +53,10 @@
             <artifactId>jersey-container-grizzly2-servlet</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
diff --git a/test-framework/providers/jetty-http2/pom.xml b/test-framework/providers/jetty-http2/pom.xml
index 76a7920..f65e487 100644
--- a/test-framework/providers/jetty-http2/pom.xml
+++ b/test-framework/providers/jetty-http2/pom.xml
@@ -49,6 +49,11 @@
             <artifactId>jersey-container-jetty-http2</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <profiles>
diff --git a/test-framework/providers/jetty/pom.xml b/test-framework/providers/jetty/pom.xml
index cc9f4b5..7684651 100644
--- a/test-framework/providers/jetty/pom.xml
+++ b/test-framework/providers/jetty/pom.xml
@@ -108,6 +108,13 @@
             <activation>
                 <jdk>[17,)</jdk>
             </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>org.junit.platform</groupId>
+                    <artifactId>junit-platform-engine</artifactId>
+                    <scope>test</scope>
+                </dependency>
+            </dependencies>
             <build>
                 <directory>${java17.build.outputDirectory}</directory>
                 <plugins>
diff --git a/test-framework/providers/jetty/src/main/java/org/glassfish/jersey/test/jetty/JettyTestContainerProperties.java b/test-framework/providers/jetty/src/main/java/org/glassfish/jersey/test/jetty/JettyTestContainerProperties.java
new file mode 100644
index 0000000..beabef2
--- /dev/null
+++ b/test-framework/providers/jetty/src/main/java/org/glassfish/jersey/test/jetty/JettyTestContainerProperties.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2024 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 org.glassfish.jersey.internal.util.PropertiesClass;
+
+/**
+ * Properties which relates only to Jetty test container configuration
+ *
+ * @since 2.44
+ */
+@PropertiesClass
+public class JettyTestContainerProperties {
+
+    /**
+     * Parameter which allows settings custom header size for request and response.
+     *
+     * @since 2.44
+     */
+    public static final String HEADER_SIZE = "jersey.test.jetty.container.header.size";
+
+}
diff --git a/test-framework/providers/jetty/src/main/java11/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java b/test-framework/providers/jetty/src/main/java11/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java
index 315c49d..b487361 100644
--- a/test-framework/providers/jetty/src/main/java11/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java
+++ b/test-framework/providers/jetty/src/main/java11/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2024 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.test.jetty;
 
+import java.util.Map;
 import jakarta.ws.rs.ProcessingException;
 import org.glassfish.jersey.test.jetty.internal.LocalizationMessages;
 import org.glassfish.jersey.test.DeploymentContext;
@@ -26,6 +27,13 @@
 
 public class JettyTestContainerFactory implements TestContainerFactory {
 
+    public JettyTestContainerFactory() {
+    }
+
+    public JettyTestContainerFactory(Map<String, Object> properties) {
+    }
+
+
     @Override
     public TestContainer create(final URI baseUri, final DeploymentContext context) throws IllegalArgumentException {
         throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
diff --git a/test-framework/providers/jetty/src/main/java17/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java b/test-framework/providers/jetty/src/main/java17/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java
index de1ba2c..1af49d5 100644
--- a/test-framework/providers/jetty/src/main/java17/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java
+++ b/test-framework/providers/jetty/src/main/java17/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2024 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,11 +17,13 @@
 package org.glassfish.jersey.test.jetty;
 
 import java.net.URI;
+import java.util.Map;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import jakarta.ws.rs.core.UriBuilder;
 
+import org.eclipse.jetty.server.HttpConnectionFactory;
 import org.glassfish.jersey.client.ClientConfig;
 import org.glassfish.jersey.jetty.JettyHttpContainerFactory;
 import org.glassfish.jersey.test.DeploymentContext;
@@ -42,16 +44,21 @@
  */
 public class JettyTestContainerFactory implements TestContainerFactory {
 
+    private final Map<String, Object> propertiesMap;
+
     private static class JettyTestContainer implements TestContainer {
 
         private static final Logger LOGGER = Logger.getLogger(JettyTestContainer.class.getName());
 
+        private final Map<String, Object> propertiesMap;
+
         private URI baseUri;
         private final Server server;
-
-        private JettyTestContainer(final URI baseUri, final DeploymentContext context) {
+        private JettyTestContainer(final URI baseUri, final DeploymentContext context, final Map<String, Object> propertiesMap) {
             final URI base = UriBuilder.fromUri(baseUri).path(context.getContextPath()).build();
 
+            this.propertiesMap = propertiesMap;
+
             if (!"/".equals(base.getRawPath())) {
                 throw new TestContainerException(String.format(
                         "Cannot deploy on %s. Jetty HTTP container only supports deployment on root path.",
@@ -69,6 +76,26 @@
         }
 
         @Override
+        public void configureContainer() {
+
+            if (propertiesMap == null
+                    || !propertiesMap.containsKey(JettyTestContainerProperties.HEADER_SIZE)) {
+                return;
+            }
+
+            for (Connector c : server.getConnectors()) {
+                c.getConnectionFactory(HttpConnectionFactory.class)
+                        .getHttpConfiguration().setRequestHeaderSize(
+                                (Integer) propertiesMap.get(JettyTestContainerProperties.HEADER_SIZE));
+                c.getConnectionFactory(HttpConnectionFactory.class)
+                        .getHttpConfiguration().setResponseHeaderSize(
+                                (Integer) propertiesMap.get(JettyTestContainerProperties.HEADER_SIZE));
+                c.getConnectionFactory(HttpConnectionFactory.class);
+            }
+
+        }
+
+        @Override
         public ClientConfig getClientConfig() {
             return null;
         }
@@ -123,6 +150,14 @@
 
     @Override
     public TestContainer create(final URI baseUri, final DeploymentContext context) throws IllegalArgumentException {
-        return new JettyTestContainer(baseUri, context);
+        return new JettyTestContainer(baseUri, context, propertiesMap);
     }
-}
+
+    public JettyTestContainerFactory() {
+        this(null);
+    }
+
+    public JettyTestContainerFactory(Map<String, Object> properties) {
+        this.propertiesMap = properties;
+    }
+}
\ No newline at end of file
diff --git a/test-framework/util/pom.xml b/test-framework/util/pom.xml
index 263330f..fca4bb6 100644
--- a/test-framework/util/pom.xml
+++ b/test-framework/util/pom.xml
@@ -54,6 +54,11 @@
             <artifactId>hamcrest</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/tests/e2e-client/pom.xml b/tests/e2e-client/pom.xml
index b729861..b1ae1e1 100644
--- a/tests/e2e-client/pom.xml
+++ b/tests/e2e-client/pom.xml
@@ -41,7 +41,6 @@
                     <forkCount>1</forkCount>
                     <reuseForks>false</reuseForks>
                     <enableAssertions>false</enableAssertions>
-                    <skipTests>${skip.e2e}</skipTests>
                     <systemPropertyVariables>
                         <sun.net.http.allowRestrictedHeaders>true</sun.net.http.allowRestrictedHeaders>
                     </systemPropertyVariables>
@@ -114,20 +113,10 @@
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.ext</groupId>
-            <artifactId>jersey-bean-validation</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.glassfish.jersey.ext</groupId>
             <artifactId>jersey-entity-filtering</artifactId>
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>org.glassfish.jersey.ext</groupId>
-            <artifactId>jersey-mvc-bean-validation</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
             <groupId>org.glassfish.jersey.media</groupId>
             <artifactId>jersey-media-sse</artifactId>
             <scope>test</scope>
@@ -313,29 +302,6 @@
                     <version>${slf4j.version}</version>
                 </dependency>
             </dependencies>
-            <build>
-                <plugins>
-                    <plugin>
-                        <groupId>org.apache.maven.plugins</groupId>
-                        <artifactId>maven-compiler-plugin</artifactId>
-                        <executions>
-                            <execution>
-                                <id>default-testCompile</id>
-                                <phase>test-compile</phase>
-                                <configuration>
-                                    <testExcludes>
-                                        <testExclude>org/glassfish/jersey/tests/e2e/client/connector/proxy/Proxy*Test.java</testExclude>
-                                        <testExclude>org/glassfish/jersey/tests/e2e/client/nettyconnector/Expect100ContinueTest.java</testExclude>
-                                    </testExcludes>
-                                </configuration>
-                                <goals>
-                                    <goal>testCompile</goal>
-                                </goals>
-                            </execution>
-                        </executions>
-                    </plugin>
-                </plugins>
-            </build>
         </profile>
 
         <profile>
@@ -355,6 +321,7 @@
                                 <configuration>
                                     <testExcludes>
                                         <testExclude>org/glassfish/jersey/tests/e2e/client/connector/proxy/Proxy*Test.java</testExclude>
+                                        <testExclude>org/glassfish/jersey/tests/e2e/client/connector/NoContentLengthTest.java</testExclude>
                                         <testExclude>org/glassfish/jersey/tests/e2e/client/nettyconnector/Expect100ContinueTest.java</testExclude>
                                     </testExcludes>
                                 </configuration>
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ResponseReadAndBufferEntityTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ResponseReadAndBufferEntityTest.java
index 8b54346..951de51 100644
--- a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ResponseReadAndBufferEntityTest.java
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ResponseReadAndBufferEntityTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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
@@ -35,6 +35,7 @@
 import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.Response;
 
+import org.glassfish.jersey.innate.io.InputStreamWrapper;
 import org.glassfish.jersey.logging.LoggingFeature;
 import org.glassfish.jersey.server.ResourceConfig;
 import org.glassfish.jersey.test.JerseyTest;
@@ -57,7 +58,7 @@
 
     private static final Logger LOGGER = Logger.getLogger(ResponseReadAndBufferEntityTest.class.getName());
 
-    public static class CorruptableInputStream extends InputStream {
+    public static class CorruptableInputStream extends InputStreamWrapper {
 
         private final AtomicInteger closeCounter = new AtomicInteger(0);
 
@@ -71,53 +72,16 @@
         }
 
         @Override
-        public synchronized int read() throws IOException {
+        protected InputStream getWrapped() {
+            return delegate;
+        }
+
+        @Override
+        protected InputStream getWrappedIOE() throws IOException {
             if (corruptRead) {
                 corrupt();
             }
-            return delegate.read();
-        }
-
-        @Override
-        public int read(final byte[] b) throws IOException {
-            if (corruptRead) {
-                corrupt();
-            }
-            return delegate.read(b);
-        }
-
-        @Override
-        public int read(final byte[] b, final int off, final int len) throws IOException {
-            if (corruptRead) {
-                corrupt();
-            }
-            return delegate.read(b, off, len);
-        }
-
-        @Override
-        public long skip(final long n) throws IOException {
-            if (corruptRead) {
-                corrupt();
-            }
-            return delegate.skip(n);
-        }
-
-        @Override
-        public int available() throws IOException {
-            if (corruptRead) {
-                corrupt();
-            }
-            return delegate.available();
-        }
-
-        @Override
-        public boolean markSupported() {
-            return delegate.markSupported();
-        }
-
-        @Override
-        public void mark(final int readAheadLimit) {
-            delegate.mark(readAheadLimit);
+            return delegate;
         }
 
         @Override
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/FutureCancelTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/FutureCancelTest.java
new file mode 100644
index 0000000..72b2d41
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/FutureCancelTest.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2024 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.e2e.client.connector;
+
+import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
+import org.glassfish.jersey.apache5.connector.Apache5ConnectorProvider;
+import org.glassfish.jersey.client.AbstractRxInvoker;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.client.HttpUrlConnectorProvider;
+import org.glassfish.jersey.client.JerseyInvocation;
+import org.glassfish.jersey.client.RequestEntityProcessing;
+import org.glassfish.jersey.client.spi.ConnectorProvider;
+import org.glassfish.jersey.netty.connector.NettyConnectorProvider;
+import org.glassfish.jersey.server.ChunkedOutput;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.CompletionStageRxInvoker;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.client.RxInvokerProvider;
+import jakarta.ws.rs.client.SyncInvoker;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.GenericType;
+import jakarta.ws.rs.core.Response;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+public class FutureCancelTest extends JerseyTest {
+
+    public static final long SLEEP = 100L;
+
+    public static List<ConnectorProvider> testData() {
+        return Arrays.asList(
+                new ApacheConnectorProvider(),
+                new Apache5ConnectorProvider(),
+                new HttpUrlConnectorProvider(),
+                new NettyConnectorProvider()
+        );
+    }
+
+    @Path("/")
+    public static class FutureCancelResource {
+        @GET
+        public ChunkedOutput<String> sendData() {
+            ChunkedOutput<String> chunkedOutput = new ChunkedOutput<>(String.class);
+            Thread newThread = new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    for (int i = 0; i != 100; i++) {
+                        try {
+                            chunkedOutput.write(String.valueOf(i));
+                            Thread.sleep(SLEEP);
+                        } catch (Exception e) {
+                            // consume
+                        }
+                    }
+                }
+            });
+            newThread.start();
+
+            return chunkedOutput;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(FutureCancelResource.class);
+    }
+
+    @ParameterizedTest
+    @MethodSource("testData")
+    public void testFutureCancel(ConnectorProvider connectorProvider) throws InterruptedException, ExecutionException {
+        ClientConfig config = new ClientConfig();
+        config.connectorProvider(connectorProvider);
+
+        Future<List<String>> future = ClientBuilder.newClient(config)
+                .register(new FutureCancelRxInvokerProvider())
+                .property(ClientProperties.REQUEST_ENTITY_PROCESSING, RequestEntityProcessing.CHUNKED)
+                .target(target().getUri()).request().rx(FutureCancelRxInvoker.class).get().toCompletableFuture();
+
+        int expectedSize = 2;
+
+        while (RX_LIST.size() < expectedSize) {
+            Thread.sleep(SLEEP);
+        }
+        future.cancel(true);
+
+        Thread.sleep(2 * SLEEP); // wait to see no new messages arrive
+        int size = RX_LIST.size(); // some might have beween RX_LIST.size() and cancel()
+        while (size > expectedSize) { // be sure no more come
+            Thread.sleep(SLEEP);
+            expectedSize = size;
+            size = RX_LIST.size();
+        }
+
+        Assertions.assertTrue(size < 10, "Received " + size + " messages");
+    }
+
+    private static List<String> RX_LIST = new LinkedList<>();
+
+    public static class FutureCancelRxInvokerProvider implements RxInvokerProvider<FutureCancelRxInvoker> {
+
+        Function<InputStream, Object> function = new Function<InputStream, Object>() {
+            @Override
+            public Object apply(InputStream inputStream) {
+                byte[] number = new byte[8];
+                int len = 0;
+                do {
+                    try {
+                        if ((len = inputStream.read(number)) != -1) {
+                            RX_LIST.add(new String(number).substring(0, len));
+                        } else {
+                            break;
+                        }
+                    } catch (IOException e) {
+                        throw new RuntimeException(e);
+                    }
+                } while (true);
+                return RX_LIST;
+            }
+        };
+
+        @Override
+        public boolean isProviderFor(Class<?> clazz) {
+            return FutureCancelRxInvoker.class.equals(clazz);
+        }
+
+        @Override
+        public FutureCancelRxInvoker getRxInvoker(SyncInvoker syncInvoker, ExecutorService executorService) {
+            return new FutureCancelRxInvoker(syncInvoker, executorService, function);
+        }
+    }
+
+    private static class FutureCancelRxInvoker extends AbstractRxInvoker<CompletionStage> implements CompletionStageRxInvoker {
+        private final Function<InputStream, Object> consumer;
+
+        public FutureCancelRxInvoker(SyncInvoker syncInvoker, ExecutorService executor, Function<InputStream, Object> consumer) {
+            super(syncInvoker, executor);
+            this.consumer = consumer;
+        }
+
+        @Override
+        public <R> CompletionStage method(String name, Entity<?> entity, Class<R> responseType) {
+            CompletableFuture<R> completableFuture = CompletableFuture.supplyAsync(new Supplier<R>() {
+                @Override
+                public R get() {
+                    Response r = getSyncInvoker().get();
+                    InputStream is = r.readEntity(InputStream.class);
+                    Object o = consumer.apply(is);
+                    return (R) o;
+                }
+            }, getExecutorService());
+            ((JerseyInvocation.Builder) getSyncInvoker()).setCancellable(completableFuture);
+            return completableFuture;
+        }
+
+        @Override
+        public <R> CompletionStage method(String name, Entity<?> entity, GenericType<R> responseType) {
+            CompletableFuture<R> completableFuture = CompletableFuture.supplyAsync(new Supplier<R>() {
+                @Override
+                public R get() {
+                    Response r = getSyncInvoker().get();
+                    InputStream is = r.readEntity(InputStream.class);
+                    Object o = consumer.apply(is);
+                    return (R) o;
+                }
+            }, getExecutorService());
+            return completableFuture;
+        }
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/MultiPartTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/MultiPartTest.java
index 83a52dd..f381d5f 100644
--- a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/MultiPartTest.java
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/MultiPartTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2024 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,18 @@
 
 package org.glassfish.jersey.tests.e2e.client.connector;
 
+import io.netty.handler.logging.LogLevel;
+import io.netty.handler.logging.LoggingHandler;
 import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
 import org.glassfish.jersey.client.HttpUrlConnectorProvider;
+import org.glassfish.jersey.client.RequestEntityProcessing;
 import org.glassfish.jersey.client.spi.ConnectorProvider;
 import org.glassfish.jersey.jdk.connector.JdkConnectorProvider;
 import org.glassfish.jersey.jetty.connector.JettyConnectorProvider;
+import org.glassfish.jersey.media.multipart.FormDataBodyPart;
+import org.glassfish.jersey.media.multipart.FormDataMultiPart;
+import org.glassfish.jersey.netty.connector.NettyConnectorProvider;
 import org.glassfish.jersey.logging.LoggingFeature;
 import org.glassfish.jersey.media.multipart.BodyPart;
 import org.glassfish.jersey.media.multipart.BodyPartEntity;
@@ -32,6 +39,8 @@
 import org.glassfish.jersey.test.JerseyTest;
 import org.glassfish.jersey.test.TestProperties;
 import org.glassfish.jersey.test.spi.TestHelper;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.DynamicContainer;
 import org.junit.jupiter.api.Test;
@@ -40,16 +49,25 @@
 import jakarta.ws.rs.Consumes;
 import jakarta.ws.rs.POST;
 import jakarta.ws.rs.Path;
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
 import jakarta.ws.rs.client.Entity;
 import jakarta.ws.rs.core.Application;
 import jakarta.ws.rs.core.Context;
 import jakarta.ws.rs.core.HttpHeaders;
 import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.MultivaluedMap;
 import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.ext.Provider;
+import jakarta.ws.rs.ext.WriterInterceptor;
+import jakarta.ws.rs.ext.WriterInterceptorContext;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.logging.Handler;
 import java.util.logging.Level;
+import java.util.logging.LogManager;
 import java.util.logging.Logger;
 
 public class MultiPartTest {
@@ -129,5 +147,72 @@
                 }
             }
         }
+
+        @Test
+        public void testNettyBufferedMultipart() {
+//            setDebugLevel(Level.FINEST);
+            ClientConfig config = new ClientConfig();
+
+            config.connectorProvider(new NettyConnectorProvider());
+            config.property(ClientProperties.REQUEST_ENTITY_PROCESSING, RequestEntityProcessing.BUFFERED);
+            config.register(org.glassfish.jersey.media.multipart.MultiPartFeature.class);
+            config.register(new LoggingHandler(LogLevel.DEBUG));
+            config.register(new LoggingInterceptor());
+            config.property(ClientProperties.ASYNC_THREADPOOL_SIZE, 10);
+            config.property("jersey.config.client.logging.verbosity", LoggingFeature.Verbosity.PAYLOAD_TEXT);
+            config.property("jersey.config.client.logging.logger.level", Level.FINEST.toString());
+
+            Client client = ClientBuilder.newClient(config);
+
+            FormDataMultiPart formData = new FormDataMultiPart();
+            FormDataBodyPart bodyPart1 = new FormDataBodyPart("hello1", "{\"first\":\"firstLine\",\"second\":\"secondLine\"}",
+                    MediaType.APPLICATION_JSON_TYPE);
+            formData.bodyPart(bodyPart1);
+            formData.bodyPart(new FormDataBodyPart("hello2",
+                    "{\"first\":\"firstLine\",\"second\":\"secondLine\",\"third\":\"thirdLine\"}",
+                    MediaType.APPLICATION_JSON_TYPE));
+            formData.bodyPart(new FormDataBodyPart("hello3",
+                    "{\"first\":\"firstLine\",\"second\":\"secondLine\",\""
+                            + "second\":\"secondLine\",\"second\":\"secondLine\",\"second\":\"secondLine\"}",
+                    MediaType.APPLICATION_JSON_TYPE));
+            formData.bodyPart(new FormDataBodyPart("plaintext", "hello"));
+
+            Response response1 = client.target(target().getUri()).path("upload")
+                    .request()
+                    .post(Entity.entity(formData, formData.getMediaType()));
+
+            MatcherAssert.assertThat(response1.getStatus(), Matchers.is(200));
+            MatcherAssert.assertThat(response1.readEntity(String.class),
+                    Matchers.stringContainsInOrder("first", "firstLine", "second", "secondLine"));
+            response1.close();
+            client.close();
+        }
+
+        public static void setDebugLevel(Level newLvl) {
+            Logger rootLogger = LogManager.getLogManager().getLogger("");
+            Handler[] handlers = rootLogger.getHandlers();
+            rootLogger.setLevel(newLvl);
+            for (Handler h : handlers) {
+                h.setLevel(Level.ALL);
+            }
+            Logger nettyLogger = Logger.getLogger("io.netty");
+            nettyLogger.setLevel(Level.FINEST);
+        }
+
+        @Provider
+        public class LoggingInterceptor implements WriterInterceptor {
+
+            @Override
+            public void aroundWriteTo(WriterInterceptorContext context)
+                    throws IOException, WebApplicationException {
+                try {
+                    MultivaluedMap<String, Object> headers = context.getHeaders();
+                    headers.forEach((key, val) -> System.out.println(key + ":" + val));
+                    context.proceed();
+                } catch (Exception e) {
+                    throw e;
+                }
+            }
+        }
     }
 }
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/NoContentLengthTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/NoContentLengthTest.java
new file mode 100644
index 0000000..e3c3caf
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/NoContentLengthTest.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2024 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.e2e.client.connector;
+
+import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
+import org.glassfish.jersey.apache5.connector.Apache5ConnectorProvider;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.HttpUrlConnectorProvider;
+import org.glassfish.jersey.client.spi.ConnectorProvider;
+import org.glassfish.jersey.grizzly.connector.GrizzlyConnectorProvider;
+import org.glassfish.jersey.jdk.connector.JdkConnectorProvider;
+import org.glassfish.jersey.jetty.connector.JettyConnectorProvider;
+import org.glassfish.jersey.netty.connector.NettyConnectorProvider;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.Response;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.UncheckedIOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class NoContentLengthTest {
+
+    private static final String MSG = "12345678901234567890123456789012345678901234567890";
+
+    private static int port;
+    private static AtomicBoolean running = new AtomicBoolean(false);
+
+    @BeforeEach
+    void beforeEach() {
+        while (!running.compareAndSet(false, true)) {
+            try {
+                Thread.sleep(1000L);
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        }
+        Runnable runnable = new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    String _port = System.getProperty("jersey.config.test.container.port");
+                    port = Integer.parseInt(_port == null || _port.isEmpty() ? "8080" : _port);
+                    ServerSocket serverSocket = new ServerSocket(port);
+                    System.err.println("Starting server on port : " + port);
+
+                    Socket clientSocket = serverSocket.accept();
+
+                    BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
+                    BufferedWriter out = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));
+
+                    String s;
+                    while ((s = in.readLine()) != null) {
+                        // System.out.println(s);
+                        if (s.isEmpty()) {
+                            break;
+                        }
+                    }
+
+                    out.write("HTTP/1.0 200 OK\r\n");
+                    out.write("Content-Type: text/plain\r\n");
+                    out.write("\r\n");
+                    out.write(MSG);
+
+                    out.close();
+                    in.close();
+                    clientSocket.close();
+                    serverSocket.close();
+                } catch (IOException e) {
+                    throw new UncheckedIOException(e);
+                } finally {
+                    running.set(false);
+                }
+            }
+        };
+        Thread newThread = new Thread(runnable);
+        newThread.start();
+    }
+
+    public static List<ConnectorProvider> providers() {
+        return Arrays.asList(
+                new ApacheConnectorProvider(),
+                new Apache5ConnectorProvider(),
+                new HttpUrlConnectorProvider(),
+                new NettyConnectorProvider(),
+                new JettyConnectorProvider(),
+                new GrizzlyConnectorProvider(),
+                new JdkConnectorProvider()
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource("providers")
+    public void testNoContentLength(ConnectorProvider connectorProvider) {
+        try (Response r = target(connectorProvider).request().get()) {
+            MatcherAssert.assertThat(r.getStatus(), Matchers.is(200));
+            MatcherAssert.assertThat(r.getHeaderString(HttpHeaders.CONTENT_LENGTH), Matchers.nullValue());
+            MatcherAssert.assertThat(r.hasEntity(), Matchers.is(true));
+            MatcherAssert.assertThat(r.readEntity(String.class), Matchers.is(MSG));
+        }
+    }
+
+    private WebTarget target(ConnectorProvider connectorProvider) {
+        ClientConfig config = new ClientConfig();
+        config.connectorProvider(connectorProvider);
+        return ClientBuilder.newClient(config).target("http://localhost:" + port);
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/nettyconnector/Expect100ContinueTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/nettyconnector/Expect100ContinueTest.java
index 1f18b80..ffb8b1f 100644
--- a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/nettyconnector/Expect100ContinueTest.java
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/nettyconnector/Expect100ContinueTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2024 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,11 +28,8 @@
 import org.glassfish.jersey.netty.connector.NettyConnectorProvider;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
 import jakarta.ws.rs.ProcessingException;
 import jakarta.ws.rs.client.Client;
 import jakarta.ws.rs.client.ClientBuilder;
@@ -66,9 +63,10 @@
 
     private static Server server;
     @BeforeAll
-    public static void startExpect100ContinueTestServer() {
+    private static void startExpect100ContinueTestServer() {
         server = new Server(portNumber);
-        server.setHandler(new Expect100ContinueTestHandler());
+        server.setDefaultHandler(new Expect100ContinueTestHandler());
+        server.setDynamic(true);
         try {
             server.start();
         } catch (Exception e) {
@@ -77,31 +75,28 @@
     }
 
     @AfterAll
-    public static void stopExpect100ContinueTestServer() {
+    private static void stopExpect100ContinueTestServer() {
         try {
             server.stop();
         } catch (Exception e) {
         }
     }
 
-    private static Client client;
-    @BeforeEach
-    public void beforeEach() {
+    @BeforeAll
+    private static void initClient() {
         final ClientConfig config = new ClientConfig();
-        this.configureClient(config);
+        config.connectorProvider(new NettyConnectorProvider());
         client = ClientBuilder.newClient(config);
     }
 
-    private Client client() {
-        return client;
+    @AfterAll
+    private static void stopClient() {
+        client.close();
     }
 
+    private static Client client;
     public WebTarget target(String path) {
-        return client().target(String.format("http://localhost:%d", portNumber)).path(path);
-    }
-
-    protected void configureClient(ClientConfig config) {
-        config.connectorProvider(new NettyConnectorProvider());
+        return client.target(String.format("http://localhost:%d", portNumber)).path(path);
     }
 
     @Test
@@ -234,6 +229,11 @@
                 callback.succeeded();
                 return true;
             }
+            if (!expected && !failed) {
+                response.reset();
+                callback.succeeded();
+                return true;
+            }
             response.write(true, ByteBuffer.wrap("\n\r".getBytes()), callback);
 
             callback.failed(new ProcessingException(""));
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/OutboundMessageContextTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/OutboundMessageContextTest.java
index bd6e3c5..817170c 100644
--- a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/OutboundMessageContextTest.java
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/OutboundMessageContextTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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,9 @@
 
 package org.glassfish.jersey.tests.e2e.common.message.internal;
 
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.text.ParseException;
@@ -33,10 +36,13 @@
 import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.ext.RuntimeDelegate;
 
+import org.glassfish.jersey.io.spi.FlushedCloseable;
 import org.glassfish.jersey.message.internal.CookieProvider;
 import org.glassfish.jersey.message.internal.OutboundMessageContext;
 import org.glassfish.jersey.tests.e2e.common.TestRuntimeDelegate;
 
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 import static org.hamcrest.Matchers.contains;
@@ -271,4 +277,38 @@
         newCtx.setMediaType(MediaType.APPLICATION_XML_TYPE); // new value
         Assertions.assertEquals(MediaType.APPLICATION_XML_TYPE, newCtx.getMediaType());
     }
+
+    @Test
+    public void OutboundMessageContextFlushTest() throws IOException {
+        FlushCountOutputStream os = new FlushCountOutputStream();
+        OutboundMessageContext ctx = new OutboundMessageContext((Configuration) null);
+        ctx.setEntity("Anything");
+        ctx.setEntityStream(os);
+        os.flush();
+        ctx.close();
+        MatcherAssert.assertThat(os.flushedCnt, Matchers.is(2));
+
+        os = new FlushedClosableOutputStream();
+        ctx = new OutboundMessageContext((Configuration) null);
+        ctx.setEntity("Anything2");
+        ctx.setEntityStream(os);
+        os.flush();
+        ctx.close();
+        MatcherAssert.assertThat(os.flushedCnt, Matchers.is(1));
+    }
+
+    private static class FlushCountOutputStream extends ByteArrayOutputStream {
+        private int flushedCnt = 0;
+
+        @Override
+        public void flush() throws IOException {
+            flushedCnt++;
+            super.flush();
+        }
+    }
+
+    private static class FlushedClosableOutputStream extends FlushCountOutputStream implements FlushedCloseable {
+
+    }
 }
+
diff --git a/tests/e2e-entity/pom.xml b/tests/e2e-entity/pom.xml
index 8a869fd..ae026c1 100644
--- a/tests/e2e-entity/pom.xml
+++ b/tests/e2e-entity/pom.xml
@@ -41,7 +41,6 @@
                     <forkCount>1</forkCount>
                     <reuseForks>false</reuseForks>
                     <enableAssertions>false</enableAssertions>
-                    <skipTests>${skip.e2e}</skipTests>
                 </configuration>
                 <executions>
                     <execution>
@@ -225,6 +224,15 @@
             </properties>
         </profile>
         <profile>
+            <id>test-suit</id> <!--due to the broadcast test failure after update to the latest junit platform suite -->
+            <activation>       <!--for JDK 11 (only in CI/CD job) it's required to used legacy junit version -->
+                <jdk>11</jdk>
+            </activation>
+            <properties>
+                <junit-platform-suite.version>${junit-platform-suite.legacy.version}</junit-platform-suite.version>
+            </properties>
+        </profile>
+        <profile>
             <id>sonar</id>
             <build>
                 <pluginManagement>
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/ChunkedInputReaderTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/ChunkedInputReaderTest.java
new file mode 100644
index 0000000..5c0c944
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/ChunkedInputReaderTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2024 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.e2e.entity;
+
+import org.glassfish.jersey.client.ChunkedInput;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.ClientRequestContext;
+import jakarta.ws.rs.client.ClientRequestFilter;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.GenericType;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.ext.ReaderInterceptor;
+import jakarta.ws.rs.ext.ReaderInterceptorContext;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class ChunkedInputReaderTest extends JerseyTest {
+
+    @Path("/")
+    public static class ChunkedInputReaderTestResource {
+        @GET
+        public String get() {
+            return "To_be_replaced_by_client_reader";
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(ChunkedInputReaderTestResource.class);
+    }
+
+    @Test
+    public void testChunkedInputStreamIsClosed() {
+        AtomicBoolean closed = new AtomicBoolean(false);
+        InputStream inputStream = new ByteArrayInputStream("TEST".getBytes()) {
+            @Override
+            public void close() throws IOException {
+                closed.set(true);
+                super.close();
+            }
+        };
+
+        final GenericType<ChunkedInput<String>> chunkedInputGenericType = new GenericType(new ParameterizedType() {
+            @Override
+            public Type[] getActualTypeArguments() {
+                return new Type[]{String.class};
+            }
+
+            @Override
+            public Type getRawType() {
+                return ChunkedInput.class;
+            }
+
+            @Override
+            public Type getOwnerType() {
+                return ChunkedInput.class;
+            }
+        });
+
+
+        ChunkedInput<String> response = target().register(new ReaderInterceptor() {
+                    @Override
+                    public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
+                        context.setInputStream(inputStream);
+                        return context.proceed();
+                    };
+                }).request().get(chunkedInputGenericType);
+        MatcherAssert.assertThat(response.read(), Matchers.is("TEST"));
+        response.close();
+        MatcherAssert.assertThat(closed.get(), Matchers.is(true));
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/EntityTypesTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/EntityTypesTest.java
index 77a74a6..9e872fc 100644
--- a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/EntityTypesTest.java
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/EntityTypesTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 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
@@ -72,6 +72,7 @@
 import org.glassfish.jersey.internal.util.collection.MultivaluedStringMap;
 import org.glassfish.jersey.jettison.JettisonFeature;
 import org.glassfish.jersey.message.internal.FileProvider;
+import org.glassfish.jersey.message.internal.PathProvider;
 import org.glassfish.jersey.server.ResourceConfig;
 
 import org.codehaus.jettison.json.JSONArray;
@@ -431,6 +432,20 @@
         _test(in, FileResource.class);
     }
 
+    @Path("PathResource")
+    public static class PathResource extends AResource<java.nio.file.Path> {
+    }
+
+    @Test
+    @Execution(ExecutionMode.CONCURRENT)
+    public void testPathRepresentation() throws IOException {
+        final var pp = new PathProvider();
+        final var in = pp.readFrom(java.nio.file.Path.class, java.nio.file.Path.class, null, null, null,
+                new ByteArrayInputStream("CONTENT".getBytes()));
+
+        _test(in, PathResource.class);
+    }
+
     @Produces("application/x-www-form-urlencoded")
     @Consumes("application/x-www-form-urlencoded")
     @Path("FormResource")
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/BroadcasterExecutorTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/BroadcasterExecutorTest.java
index 5b3d957..c97bea3 100644
--- a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/BroadcasterExecutorTest.java
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/BroadcasterExecutorTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2024 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
@@ -152,6 +152,7 @@
     @Test
     public void test() throws InterruptedException {
         final String[] onEventThreadName = {""};
+        final CountDownLatch onEventLatch = new CountDownLatch(1);
         SseEventSource eventSource = SseEventSource
                 .target(target().path("sse/events"))
                 .build();
@@ -159,6 +160,7 @@
         eventSource.register((event) -> {
                     LOGGER.info("Event: " + event + " from: " + Thread.currentThread().getName());
                     onEventThreadName[0] = Thread.currentThread().getName();
+                    onEventLatch.countDown();
                 }
         );
 
@@ -174,6 +176,7 @@
         Assertions.assertTrue(sendThreadOk, "send either not invoked at all or from wrong thread");
         Assertions.assertTrue(onCompleteThreadOk, "onComplete either not invoked at all or from wrong thread");
 
+        Assertions.assertTrue(onEventLatch.await(2_000L, TimeUnit.MILLISECONDS));
         Assertions.assertTrue(onEventThreadName[0].startsWith("custom-client-executor"),
                 "Client event called from wrong thread ( " + onEventThreadName[0] + ")");
     }
diff --git a/tests/e2e-inject/hk2/pom.xml b/tests/e2e-inject/hk2/pom.xml
index 40fd069..f4669f2 100644
--- a/tests/e2e-inject/hk2/pom.xml
+++ b/tests/e2e-inject/hk2/pom.xml
@@ -36,6 +36,11 @@
             <type>pom</type>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-client</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
 
diff --git a/tests/e2e-inject/hk2/src/test/java/org/glassfish/jersey/tests/e2e/inject/hk2/HK2FactoryBindingTest.java b/tests/e2e-inject/hk2/src/test/java/org/glassfish/jersey/tests/e2e/inject/hk2/HK2FactoryBindingTest.java
new file mode 100644
index 0000000..abc0981
--- /dev/null
+++ b/tests/e2e-inject/hk2/src/test/java/org/glassfish/jersey/tests/e2e/inject/hk2/HK2FactoryBindingTest.java
@@ -0,0 +1,340 @@
+/*
+ * Copyright (c) 2024 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.e2e.inject.hk2;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.InjectionManagerClientProvider;
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.internal.inject.Injectee;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.inject.InjectionResolver;
+import org.glassfish.jersey.process.internal.RequestScoped;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import jakarta.annotation.Priority;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
+import jakarta.ws.rs.Priorities;
+import jakarta.ws.rs.RuntimeType;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.ClientRequestContext;
+import jakarta.ws.rs.client.ClientRequestFilter;
+import jakarta.ws.rs.core.Configuration;
+import jakarta.ws.rs.core.Feature;
+import jakarta.ws.rs.core.FeatureContext;
+import jakarta.ws.rs.core.GenericType;
+import jakarta.ws.rs.core.Response;
+import java.io.IOException;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.Map;
+import java.util.function.Supplier;
+
+public class HK2FactoryBindingTest {
+
+    public static interface ConfigurationProperties {
+        Map<String, Object> getProperties();
+    }
+
+    public static class ConfigurationPropertiesFactory implements org.glassfish.hk2.api.Factory<ConfigurationProperties> {
+
+        private final Configuration configuration;
+
+        @Inject
+        public ConfigurationPropertiesFactory(Configuration configuration) {
+            this.configuration = configuration;
+        }
+
+        @Override
+        public ConfigurationProperties provide() {
+            return new ConfigurationProperties() {
+                @Override
+                public Map<String, Object> getProperties() {
+                    return configuration.getProperties();
+                }
+            };
+        }
+
+        @Override
+        public void dispose(ConfigurationProperties configurationProperties) {
+
+        }
+    }
+
+    @Priority(Priorities.USER)
+    public static class ConfigurationPropertiesFilter implements ClientRequestFilter {
+        @Inject
+        ConfigurationProperties properties;
+
+        @Override
+        public void filter(ClientRequestContext requestContext) throws IOException {
+            requestContext.abortWith(Response.ok(properties.getProperties().get(PROPERTY_NAME)).build());
+        }
+    }
+
+    private static final String PROPERTY_NAME = "TEST_PROPERTY";
+    private static final String PROPERTY_VALUE = "HELLO_PROPERTY";
+
+    @Test
+    public void testFactoryClassBinding() {
+        ClientConfig config = new ClientConfig();
+        config.property(PROPERTY_NAME, PROPERTY_VALUE);
+        String response = ClientBuilder.newClient(config).register(new AbstractBinder() {
+            @Override
+            protected void configure() {
+                bind(ConfigurationPropertiesFactory.class).to(ConfigurationProperties.class).proxy(true)
+                        .proxyForSameScope(false).in(RequestScoped.class);
+            }
+        }).register(ConfigurationPropertiesFilter.class).target("http://test.com").request().get(String.class);
+        Assertions.assertEquals(PROPERTY_VALUE, response);
+    }
+
+    @Test
+    public void testFactoryInstanceBinding() {
+        ClientConfig config = new ClientConfig();
+        config.property(PROPERTY_NAME, PROPERTY_VALUE);
+        String response = ClientBuilder.newClient(config).register(new AbstractBinder() {
+            @Override
+            protected void configure() {
+                bind(new ConfigurationPropertiesFactory(config)).to(ConfigurationProperties.class).proxy(true)
+                        .proxyForSameScope(false).in(RequestScoped.class);
+            }
+        }).register(ConfigurationPropertiesFilter.class).target("http://test.com").request().get(String.class);
+        Assertions.assertEquals(PROPERTY_VALUE, response);
+    }
+
+    static final class ConfigurationPropertiesImpl implements ConfigurationProperties {
+        private final Configuration configuration;
+        @Inject
+        public ConfigurationPropertiesImpl(Configuration configuration) {
+            this.configuration = configuration;
+        }
+
+        @Override
+        public Map<String, Object> getProperties() {
+            return configuration.getProperties();
+        }
+    }
+
+    static class ConfigurationPropertiesSupplier implements Supplier<ConfigurationPropertiesImpl> {
+        private final Configuration configuration;
+
+        @Inject
+        ConfigurationPropertiesSupplier(Configuration configuration) {
+            this.configuration = configuration;
+        }
+
+        @Override
+        public ConfigurationPropertiesImpl get() {
+            return new ConfigurationPropertiesImpl(configuration);
+        }
+    }
+
+    @Test
+    public void testSupplierJerseyInstanceBinding() {
+        ClientConfig config = new ClientConfig();
+        config.property(PROPERTY_NAME, PROPERTY_VALUE);
+        String response = ClientBuilder.newClient(config).register(new AbstractBinder() {
+            @Override
+            protected void configure() {
+                bindFactory(() -> new ConfigurationPropertiesImpl(config)).to(ConfigurationProperties.class).proxy(true)
+                        .proxyForSameScope(false).in(RequestScoped.class);
+            }
+        }).register(ConfigurationPropertiesFilter.class).target("http://test.com").request().get(String.class);
+        Assertions.assertEquals(PROPERTY_VALUE, response);
+    }
+
+    @Test
+    public void testSupplierJerseyClassBinding() {
+        ClientConfig config = new ClientConfig();
+        config.property(PROPERTY_NAME, PROPERTY_VALUE);
+        String response = ClientBuilder.newClient(config).register(new AbstractBinder() {
+            @Override
+            protected void configure() {
+                bindFactory(ConfigurationPropertiesSupplier.class).to(ConfigurationProperties.class).proxy(true)
+                        .proxyForSameScope(false).in(RequestScoped.class);
+            }
+        }).register(ConfigurationPropertiesFilter.class).target("http://test.com").request().get(String.class);
+        Assertions.assertEquals(PROPERTY_VALUE, response);
+    }
+
+    public static interface ConfigurationPropertiesProvider {
+        Map<String, Object> getProperties();
+
+        ConfigurationProperties getConfigurationProperties();
+    }
+
+    public static class ConfigurationPropertiesProviderImpl implements ConfigurationPropertiesProvider {
+        private final Configuration configuration;
+        @Inject
+        public ConfigurationPropertiesProviderImpl(Configuration configuration) {
+            this.configuration = configuration;
+        }
+
+        @Override
+        public Map<String, Object> getProperties() {
+            return configuration.getProperties();
+        }
+
+        @Override
+        public ConfigurationProperties getConfigurationProperties() {
+            return new ConfigurationPropertiesImpl(configuration);
+        }
+    }
+
+    public static class ConfigurationPropertiesProviderSupplier implements Supplier<ConfigurationProperties> {
+        final ConfigurationPropertiesProvider impl;
+
+        @Inject
+        public ConfigurationPropertiesProviderSupplier(ConfigurationPropertiesProvider impl) {
+            this.impl = impl;
+        }
+
+        @Override
+        public ConfigurationProperties get() {
+            return impl.getConfigurationProperties();
+        }
+    }
+
+    public static class ConfigurationProperties2Filter implements ClientRequestFilter {
+        @Inject
+        ConfigurationProperties properties;
+
+        @Override
+        public void filter(ClientRequestContext requestContext) throws IOException {
+            requestContext.abortWith(Response.ok(properties.getProperties().get(PROPERTY_NAME)).build());
+        }
+    }
+
+    @Test
+    public void testSupplierOfProviderClassBinding() {
+        ClientConfig config = new ClientConfig();
+        config.property(PROPERTY_NAME, PROPERTY_VALUE);
+        String response = ClientBuilder.newClient(config).register(new AbstractBinder() {
+            @Override
+            protected void configure() {
+                bind(ConfigurationPropertiesProviderImpl.class).to(ConfigurationPropertiesProvider.class).proxy(true)
+                        .proxyForSameScope(false).in(RequestScoped.class);
+                bindFactory(ConfigurationPropertiesProviderSupplier.class).to(ConfigurationProperties.class).proxy(true)
+                        .proxyForSameScope(false).in(RequestScoped.class);
+            }
+        }).register(ConfigurationProperties2Filter.class).target("http://test.com").request().get(String.class);
+        Assertions.assertEquals(PROPERTY_VALUE, response);
+    }
+
+    @Test
+    public void testFactoryHk2ClassBinding() {
+        ClientConfig config = new ClientConfig();
+        config.property(PROPERTY_NAME, PROPERTY_VALUE);
+        String response = ClientBuilder.newClient(config).register(new org.glassfish.hk2.utilities.binding.AbstractBinder() {
+            @Override
+            protected void configure() {
+                bindFactory(ConfigurationPropertiesFactory.class).to(ConfigurationProperties.class).proxy(true)
+                        .proxyForSameScope(false).in(RequestScoped.class);
+            }
+        }).register(ConfigurationPropertiesFilter.class).target("http://test.com").request().get(String.class);
+        Assertions.assertEquals(PROPERTY_VALUE, response);
+    }
+
+    @Test
+    public void testFactoryHk2InstanceBinding() {
+        ClientConfig config = new ClientConfig();
+        config.property(PROPERTY_NAME, PROPERTY_VALUE);
+        String response = ClientBuilder.newClient(config).register(new org.glassfish.hk2.utilities.binding.AbstractBinder() {
+            @Override
+            protected void configure() {
+                bindFactory(new ConfigurationPropertiesFactory(config)).to(ConfigurationProperties.class).proxy(true)
+                        .proxyForSameScope(false).in(RequestScoped.class);
+            }
+        }).register(ConfigurationPropertiesFilter.class).target("http://test.com").request().get(String.class);
+        Assertions.assertEquals(PROPERTY_VALUE, response);
+    }
+
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target(ElementType.FIELD)
+    static @interface ConfigurationPropertiesInject {
+
+    }
+
+    static class ConfigurationPropertiesInjectResolver implements InjectionResolver<ConfigurationPropertiesInject> {
+
+        private final InjectionManager injectionManager;
+
+        ConfigurationPropertiesInjectResolver(InjectionManager injectionManager) {
+            this.injectionManager = injectionManager;
+        }
+
+        @Override
+        public Object resolve(Injectee injectee) {
+            if (injectee.getRequiredType() == ConfigurationProperties.class) {
+                return new ConfigurationPropertiesImpl(injectionManager.getInstance(Configuration.class));
+            }
+            return null;
+        }
+
+        @Override
+        public boolean isConstructorParameterIndicator() {
+            return false;
+        }
+
+        @Override
+        public boolean isMethodParameterIndicator() {
+            return false;
+        }
+
+        @Override
+        public Class<ConfigurationPropertiesInject> getAnnotation() {
+            return ConfigurationPropertiesInject.class;
+        }
+    }
+
+    public static class ConfigurationPropertiesInjectFilter implements ClientRequestFilter {
+        @ConfigurationPropertiesInject
+        ConfigurationProperties properties;
+
+        @Override
+        public void filter(ClientRequestContext requestContext) throws IOException {
+            requestContext.abortWith(Response.ok(properties.getProperties().get(PROPERTY_NAME)).build());
+        }
+    }
+
+    @Test
+    public void testInjectionResolverBinding() {
+        ClientConfig config = new ClientConfig();
+        config.property(PROPERTY_NAME, PROPERTY_VALUE);
+        String response = ClientBuilder.newClient(config).register(new Feature() {
+            @Override
+            public boolean configure(FeatureContext context) {
+                final InjectionManager injectionManager = InjectionManagerClientProvider.getInjectionManager(context);
+                context.register(new AbstractBinder() {
+                    @Override
+                    protected void configure() {
+                        bind(new ConfigurationPropertiesInjectResolver(injectionManager))
+                                //.to(ConfigurationProperties.class)
+                                .to(new GenericType<ConfigurationProperties>(){})
+                                .in(Singleton.class);
+                    }
+                });
+                return true;
+            }
+        }).register(ConfigurationPropertiesInjectFilter.class).target("http://test.com").request().get(String.class);
+        Assertions.assertEquals(PROPERTY_VALUE, response);
+    }
+}
diff --git a/tests/e2e-jdk-specifics/pom.xml b/tests/e2e-jdk-specifics/pom.xml
new file mode 100644
index 0000000..b06de6a
--- /dev/null
+++ b/tests/e2e-jdk-specifics/pom.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2024 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">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests</groupId>
+        <artifactId>project</artifactId>
+        <version>3.5.99-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>e2e-jdk-specifics</artifactId>
+    <name>jersey-tests-e2e-specifics</name>
+    <packaging>jar</packaging>
+
+    <description>Jersey E2E tests for testing JDK 17+ specifics</description>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <forkCount>1</forkCount>
+                    <reuseForks>false</reuseForks>
+                    <enableAssertions>false</enableAssertions>
+                    <argLine>
+                        ${http.patch.addopens}
+                    </argLine>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework</groupId>
+            <artifactId>jersey-test-framework-util</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-suite</artifactId>
+            <version>${junit-platform-suite.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>jdk15-</id>
+            <activation>
+                <jdk>[8, 16)</jdk>
+            </activation>
+            <properties>
+                <http.patch.addopens> </http.patch.addopens>
+            </properties>
+        </profile>
+        <profile>
+            <id>jdk16+</id>
+            <activation>
+                <jdk>[16, )</jdk>
+            </activation>
+            <properties>
+                <http.patch.addopens>--add-opens java.base/java.net=ALL-UNNAMED</http.patch.addopens>
+            </properties>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/tests/e2e-jdk-specifics/src/test/java/org/glassfish/jersey/tests/e2e/jdk17/HttpPatchTest.java b/tests/e2e-jdk-specifics/src/test/java/org/glassfish/jersey/tests/e2e/jdk17/HttpPatchTest.java
new file mode 100644
index 0000000..c949958
--- /dev/null
+++ b/tests/e2e-jdk-specifics/src/test/java/org/glassfish/jersey/tests/e2e/jdk17/HttpPatchTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2024 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.e2e.jdk17;
+
+import org.glassfish.jersey.client.HttpUrlConnectorProvider;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.HttpMethod;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Response;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+public class HttpPatchTest extends JerseyTest {
+
+    public static final String PATCH_ENTITY = "HelloPatch";
+
+    @Target({ElementType.METHOD})
+    @Retention(RetentionPolicy.RUNTIME)
+    @HttpMethod(HttpMethod.PATCH)
+    public @interface PATCH {
+
+    }
+
+    @Path("/")
+    public static class HttpPatchResource {
+        @PATCH
+        public String patchEcho(String entity) {
+            return entity;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(HttpPatchResource.class);
+    }
+
+    @Test
+    void testPatchWithHttpUrlConnector() {
+        try (Response response = target()
+                .property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true)
+                .request().method(HttpMethod.PATCH, Entity.text(PATCH_ENTITY))) {
+            MatcherAssert.assertThat(200, Matchers.equalTo(response.getStatus()));
+            response.bufferEntity();
+            System.out.println(response.readEntity(String.class));
+            MatcherAssert.assertThat(PATCH_ENTITY, Matchers.equalTo(response.readEntity(String.class)));
+        }
+    }
+}
diff --git a/tests/e2e-jdk-specifics/src/test/java/org/glassfish/jersey/tests/e2e/jdk21/ThreadFactoryUsageTest.java b/tests/e2e-jdk-specifics/src/test/java/org/glassfish/jersey/tests/e2e/jdk21/ThreadFactoryUsageTest.java
new file mode 100644
index 0000000..6739ba9
--- /dev/null
+++ b/tests/e2e-jdk-specifics/src/test/java/org/glassfish/jersey/tests/e2e/jdk21/ThreadFactoryUsageTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2024 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.e2e.jdk21;
+
+import org.glassfish.jersey.CommonProperties;
+import org.glassfish.jersey.innate.VirtualThreadSupport;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.ClientRequestFilter;
+import jakarta.ws.rs.core.Response;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+public class ThreadFactoryUsageTest {
+    @Test
+    public void testThreadFactory() throws ExecutionException, InterruptedException {
+        CountDownLatch countDownLatch = new CountDownLatch(1);
+        ThreadFactory threadFactory = VirtualThreadSupport.allowVirtual(true).getThreadFactory();
+        ThreadFactory countDownThreadFactory = r -> {
+            countDownLatch.countDown();
+            return threadFactory.newThread(r);
+        };
+
+        CompletionStage<Response> r = ClientBuilder.newClient()
+                .property(CommonProperties.THREAD_FACTORY, countDownThreadFactory)
+                .property(CommonProperties.USE_VIRTUAL_THREADS, true)
+                .register((ClientRequestFilter) requestContext -> requestContext.abortWith(Response.ok().build()))
+                .target("http://localhost:58080/test").request().rx().get();
+
+        MatcherAssert.assertThat(r.toCompletableFuture().get().getStatus(), Matchers.is(200));
+        countDownLatch.await(10, TimeUnit.SECONDS);
+        MatcherAssert.assertThat(countDownLatch.getCount(), Matchers.is(0L));
+    }
+}
diff --git a/tests/e2e-server/pom.xml b/tests/e2e-server/pom.xml
index 7285152..8d5af4a 100644
--- a/tests/e2e-server/pom.xml
+++ b/tests/e2e-server/pom.xml
@@ -41,7 +41,6 @@
                     <forkCount>1</forkCount>
                     <reuseForks>false</reuseForks>
                     <enableAssertions>false</enableAssertions>
-                    <skipTests>${skip.e2e}</skipTests>
                 </configuration>
                 <executions>
                     <execution>
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ChunkedInputOutputTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ChunkedInputOutputTest.java
index 64c0422..f8dab62 100644
--- a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ChunkedInputOutputTest.java
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ChunkedInputOutputTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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,13 +61,33 @@
     @Path("/test")
     public static class TestResource {
         /**
+         * Get chunk stream with a queue capacity of 2.
+         *
+         * @return chunk stream.
+         */
+        @GET
+        @Path("/testWithBuilder")
+        public ChunkedOutput<String> getWithBuilder() {
+            return getOutput(ChunkedOutput.<String>builder(String.class).queueCapacity(2)
+                             .chunkDelimiter("\r\n".getBytes()).build());
+        }
+
+        /**
          * Get chunk stream.
          *
          * @return chunk stream.
          */
         @GET
         public ChunkedOutput<String> get() {
-            final ChunkedOutput<String> output = new ChunkedOutput<>(String.class, "\r\n");
+            return getOutput(new ChunkedOutput<>(String.class, "\r\n"));
+        }
+
+        /**
+         * Get chunk stream.
+         *
+         * @return chunk stream.
+         */
+        private ChunkedOutput<String> getOutput(ChunkedOutput<String> output) {
 
             new Thread() {
                 @Override
@@ -183,6 +203,19 @@
     }
 
     /**
+     * Test retrieving chunked response stream as a single response string, when a builder with capacity is used.
+     *
+     * @throws Exception in case of a failure during the test execution.
+     */
+    @Test
+    public void testChunkedOutputToSingleStringWithBuilder() throws Exception {
+        final String response = target().path("test/testWithBuilder").request().get(String.class);
+
+        assertEquals("test\r\ntest\r\ntest\r\n", response,
+                "Unexpected value of chunked response unmarshalled as a single string.");
+    }
+
+    /**
      * Test retrieving chunked response stream sequentially as individual chunks using chunked input.
      *
      * @throws Exception in case of a failure during the test execution.
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/WadlResourceTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/WadlResourceTest.java
index 749eb22..899d9ca 100644
--- a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/WadlResourceTest.java
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/WadlResourceTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 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 java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Properties;
 import java.util.concurrent.ExecutionException;
@@ -108,6 +109,7 @@
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
@@ -400,6 +402,20 @@
         }
 
         @Test
+        public void testLastModifiedGETOnJPLocale() {
+            Locale defaultLocale = Locale.getDefault();
+            try {
+                Locale.setDefault(new Locale("ja", "JP"));
+                final WebTarget target = target("/application.wadl");
+
+                final Response r = target.queryParam(WadlUtils.DETAILED_WADL_QUERY_PARAM, "true").request().get(Response.class);
+                assertDoesNotThrow(() -> r.getLastModified());
+            } finally {
+                Locale.setDefault(defaultLocale);
+            }
+        }
+
+        @Test
         public void testLastModifiedOPTIONS() {
             final WebTarget target = target("/widgets/3/verbose");
 
diff --git a/tests/e2e-testng/pom.xml b/tests/e2e-testng/pom.xml
index c871420..9946c71 100644
--- a/tests/e2e-testng/pom.xml
+++ b/tests/e2e-testng/pom.xml
@@ -46,7 +46,6 @@
                     <forkCount>1</forkCount>
                     <reuseForks>false</reuseForks>
                     <enableAssertions>false</enableAssertions>
-                    <skipTests>${skip.e2e}</skipTests>
                 </configuration>
                 <dependencies>
                     <dependency>
diff --git a/tests/e2e-tls/pom.xml b/tests/e2e-tls/pom.xml
index b787877..5c73498 100644
--- a/tests/e2e-tls/pom.xml
+++ b/tests/e2e-tls/pom.xml
@@ -40,8 +40,6 @@
                 <configuration>
                     <forkCount>1</forkCount>
                     <reuseForks>false</reuseForks>
-                    <enableAssertions>false</enableAssertions>
-                    <skipTests>${skip.e2e}</skipTests>
                     <systemPropertyVariables>
                         <sun.net.http.allowRestrictedHeaders>true</sun.net.http.allowRestrictedHeaders>
                         <jdk.httpclient.allowRestrictedHeaders>Host</jdk.httpclient.allowRestrictedHeaders>
@@ -51,6 +49,24 @@
                         </property>
                     </systemPropertyVariables>
                 </configuration>
+                <executions>
+                    <execution>
+                        <id>HttpsPatch</id>
+                        <goals>
+                            <goal>test</goal>
+                        </goals>
+                        <configuration>
+                            <includes>
+                                <include>**/HttpsPatchTest.java</include>
+                            </includes>
+                            <systemPropertyVariables>
+                                <jersey.added.opens>true</jersey.added.opens>
+                            </systemPropertyVariables>
+                            <!-- Allow HTTP PATCH by property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true) -->
+                            <argLine>${http.patch.addopens}</argLine>
+                        </configuration>
+                    </execution>
+                </executions>
             </plugin>
         </plugins>
     </build>
@@ -81,7 +97,7 @@
         <dependency>
             <groupId>io.specto</groupId>
             <artifactId>hoverfly-java-junit5</artifactId>
-            <version>0.14.0</version>
+            <version>0.18.1</version>
             <scope>test</scope>
         </dependency>
 
@@ -125,6 +141,15 @@
 
     <profiles>
         <profile>
+            <id>jdk8</id>
+            <activation>
+                <jdk>8</jdk>
+            </activation>
+            <properties>
+                <http.patch.addopens> </http.patch.addopens>
+            </properties>
+        </profile>
+        <profile>
             <id>jdk11+</id>
             <activation>
                 <jdk>[11,)</jdk>
@@ -139,6 +164,10 @@
             <properties>
                 <!-- https://bugs.openjdk.java.net/browse/JDK-8211426 -->
                 <surefire.security.argline>-Djdk.tls.server.protocols=TLSv1.2</surefire.security.argline>
+                <http.patch.addopens>
+                    --add-opens java.base/java.net=ALL-UNNAMED
+                    --add-opens java.base/sun.net.www.protocol.https=ALL-UNNAMED
+                </http.patch.addopens>
             </properties>
         </profile>
     </profiles>
diff --git a/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/SniTest.java b/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/SniTest.java
index 3964fce..7da4a6b 100644
--- a/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/SniTest.java
+++ b/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/SniTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2024 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
@@ -74,10 +74,29 @@
         ClientConfig clientConfig = new ClientConfig();
         clientConfig.connectorProvider(provider);
         clientConfig.property(ClientProperties.SNI_HOST_NAME, "www.host1.com");
-        serverTest(clientConfig, provider, "www.host1.com");
+        serverTest(clientConfig, provider, "www.host1.com", "www.host1.com");
     }
 
-    public void serverTest(ClientConfig clientConfig, ConnectorProvider provider, String hostName) {
+    @ParameterizedTest
+    @MethodSource("getConnectors")
+    public void sniHostNamePropertyTest(ConnectorProvider provider) {
+        ClientConfig clientConfig = new ClientConfig();
+        clientConfig.connectorProvider(provider);
+        clientConfig.property(ClientProperties.SNI_HOST_NAME, "www.host3.com");
+        serverTest(clientConfig, provider, "www.host4.com", "www.host3.com");
+    }
+
+    @ParameterizedTest
+    @MethodSource("getConnectors")
+    public void turnOffSniTest(ConnectorProvider provider) {
+        ClientConfig clientConfig = new ClientConfig();
+        clientConfig.connectorProvider(provider);
+        clientConfig.property(ClientProperties.SNI_HOST_NAME, LOCALHOST);
+        serverTest(clientConfig, provider, "www.host4.com", null);
+    }
+
+
+    public void serverTest(ClientConfig clientConfig, ConnectorProvider provider, String hostName, String resultHostName) {
         String newHostName = replaceWhenHostNotKnown(hostName);
         final List<SNIServerName> serverNames = new LinkedList<>();
         final String[] requestHostName = new String[1];
@@ -121,12 +140,14 @@
 
         server.stop();
 
-        if (serverNames.isEmpty()) {
+        if (resultHostName != null && serverNames.isEmpty()) {
             throw new IllegalStateException("ServerNames are empty");
+        } else if (resultHostName == null) {
+            return;
         }
 
         String clientSniName = new String(serverNames.get(0).getEncoded());
-        if (!hostName.equals(clientSniName)) {
+        if (!resultHostName.equals(clientSniName)) {
             throw new IllegalStateException("Unexpected client SNI name " + clientSniName);
         }
 
diff --git a/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/SslContextPerRequestTest.java b/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/SslContextPerRequestTest.java
index 944f284..d2564e9 100644
--- a/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/SslContextPerRequestTest.java
+++ b/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/SslContextPerRequestTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2024 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
@@ -53,10 +53,8 @@
 import java.util.function.Supplier;
 import java.util.stream.Stream;
 
-public class SslContextPerRequestTest extends JerseyTest {
+public class SslContextPerRequestTest extends SslParentTest {
 
-    private SSLContext serverSslContext;
-    private SSLParameters serverSslParameters;
     private static final String MESSAGE = "Message for Netty with SSL";
 
     @Override
@@ -77,33 +75,6 @@
         return new ResourceConfig(TestResource.class);
     }
 
-    @Override
-    protected URI getBaseUri() {
-        return UriBuilder
-                .fromUri("https://localhost")
-                .port(getPort())
-                .build();
-    }
-
-    @Override
-    protected Optional<SSLContext> getSslContext() {
-        if (serverSslContext == null) {
-            serverSslContext = SslUtils.createServerSslContext();
-        }
-
-        return Optional.of(serverSslContext);
-    }
-
-    @Override
-    protected Optional<SSLParameters> getSslParameters() {
-        if (serverSslParameters == null) {
-            serverSslParameters = new SSLParameters();
-            serverSslParameters.setNeedClientAuth(false);
-        }
-
-        return Optional.of(serverSslParameters);
-    }
-
     public static Stream<ConnectorProvider> connectorProviders() {
         return Stream.of(
                 new HttpUrlConnectorProvider(),
@@ -168,43 +139,4 @@
         String s = target.request().get(String.class);
         Assertions.assertEquals(MESSAGE, s);
     }
-
-    private static class SslUtils {
-
-        private static final String SERVER_IDENTITY_PATH = "server-identity.jks";
-        private static final char[] SERVER_IDENTITY_PASSWORD = "secret".toCharArray();
-
-        private static final String CLIENT_TRUSTSTORE_PATH = "client-truststore.jks";
-        private static final char[] CLIENT_TRUSTSTORE_PASSWORD = "secret".toCharArray();
-
-        private static final String KEYSTORE_TYPE = "PKCS12";
-
-        private SslUtils() {}
-
-        public static SSLContext createServerSslContext() {
-            return new SslContextClientBuilder()
-                    .keyStore(getKeyStore(SERVER_IDENTITY_PATH, SERVER_IDENTITY_PASSWORD), SERVER_IDENTITY_PASSWORD)
-                    .get();
-        }
-
-        public static Supplier<SSLContext> createClientSslContext() {
-            return new SslContextClientBuilder()
-                    .trustStore(getKeyStore(CLIENT_TRUSTSTORE_PATH, CLIENT_TRUSTSTORE_PASSWORD));
-
-        }
-
-        private static KeyStore getKeyStore(String path, char[] keyStorePassword) {
-            try (InputStream inputStream = getResource(path)) {
-                KeyStore keyStore = KeyStore.getInstance(KEYSTORE_TYPE);
-                keyStore.load(inputStream, keyStorePassword);
-                return keyStore;
-            } catch (Exception e) {
-                throw new ProcessingException(e);
-            }
-        }
-
-        private static InputStream getResource(String path) {
-            return SslUtils.class.getClassLoader().getResourceAsStream(path);
-        }
-    }
 }
diff --git a/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/SslParentTest.java b/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/SslParentTest.java
new file mode 100644
index 0000000..abfe245
--- /dev/null
+++ b/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/SslParentTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2023, 2024 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.e2e.tls;
+
+import org.glassfish.jersey.client.SslContextClientBuilder;
+import org.glassfish.jersey.test.JerseyTest;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLParameters;
+import jakarta.ws.rs.ProcessingException;
+import jakarta.ws.rs.core.UriBuilder;
+import java.io.InputStream;
+import java.net.URI;
+import java.security.KeyStore;
+import java.util.Optional;
+import java.util.function.Supplier;
+
+public class SslParentTest extends JerseyTest {
+
+    protected SSLContext serverSslContext;
+    protected SSLParameters serverSslParameters;
+
+    @Override
+    protected Optional<SSLContext> getSslContext() {
+        if (serverSslContext == null) {
+            serverSslContext = SslUtils.createServerSslContext();
+        }
+
+        return Optional.of(serverSslContext);
+    }
+
+    @Override
+    protected Optional<SSLParameters> getSslParameters() {
+        if (serverSslParameters == null) {
+            serverSslParameters = new SSLParameters();
+            serverSslParameters.setNeedClientAuth(false);
+        }
+
+        return Optional.of(serverSslParameters);
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        return UriBuilder
+                .fromUri("https://localhost")
+                .port(getPort())
+                .build();
+    }
+
+    protected static class SslUtils {
+
+        private static final String SERVER_IDENTITY_PATH = "server-identity.jks";
+        private static final char[] SERVER_IDENTITY_PASSWORD = "secret".toCharArray();
+
+        private static final String CLIENT_TRUSTSTORE_PATH = "client-truststore.jks";
+        private static final char[] CLIENT_TRUSTSTORE_PASSWORD = "secret".toCharArray();
+
+        private static final String KEYSTORE_TYPE = "PKCS12";
+
+        private SslUtils() {}
+
+        public static SSLContext createServerSslContext() {
+            return new SslContextClientBuilder()
+                    .keyStore(getKeyStore(SERVER_IDENTITY_PATH, SERVER_IDENTITY_PASSWORD), SERVER_IDENTITY_PASSWORD)
+                    .get();
+        }
+
+        public static Supplier<SSLContext> createClientSslContext() {
+            return new SslContextClientBuilder()
+                    .trustStore(getKeyStore(CLIENT_TRUSTSTORE_PATH, CLIENT_TRUSTSTORE_PASSWORD));
+
+        }
+
+        private static KeyStore getKeyStore(String path, char[] keyStorePassword) {
+            try (InputStream inputStream = getResource(path)) {
+                KeyStore keyStore = KeyStore.getInstance(KEYSTORE_TYPE);
+                keyStore.load(inputStream, keyStorePassword);
+                return keyStore;
+            } catch (Exception e) {
+                throw new ProcessingException(e);
+            }
+        }
+
+        private static InputStream getResource(String path) {
+            return SslUtils.class.getClassLoader().getResourceAsStream(path);
+        }
+    }
+}
diff --git a/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/patch/HttpsPatchTest.java b/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/patch/HttpsPatchTest.java
new file mode 100644
index 0000000..37b63c8
--- /dev/null
+++ b/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/patch/HttpsPatchTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2024 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.e2e.tls.patch;
+
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.client.HttpUrlConnectorProvider;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.tests.e2e.tls.SslParentTest;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.Test;
+
+import javax.net.ssl.SSLContext;
+import jakarta.ws.rs.HttpMethod;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Response;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.function.Supplier;
+
+public class HttpsPatchTest extends SslParentTest {
+
+    public static final String PATCH_ENTITY = "HelloPatch";
+
+    @Target({ElementType.METHOD})
+    @Retention(RetentionPolicy.RUNTIME)
+    @HttpMethod(HttpMethod.PATCH)
+    public @interface PATCH {
+
+    }
+
+    @Path("/")
+    public static class HttpPatchResource {
+        @PATCH
+        public String patchEcho(String entity) {
+            return entity;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(HttpPatchResource.class);
+    }
+
+    @Test
+    void testPatchWithHttpUrlConnector() {
+        String value = System.getProperty("jersey.added.opens");
+        if (null == value) {
+            System.out.println("JDK add-opens not set - exiting...");
+            return;
+        }
+
+        Supplier<SSLContext> clientSslContext = SslUtils.createClientSslContext();
+        try (Response response = target()
+                .property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true)
+                .property(ClientProperties.SSL_CONTEXT_SUPPLIER, clientSslContext)
+                .request().method(HttpMethod.PATCH, Entity.text(PATCH_ENTITY))) {
+            MatcherAssert.assertThat(200, Matchers.equalTo(response.getStatus()));
+            response.bufferEntity();
+            System.out.println(response.readEntity(String.class));
+            MatcherAssert.assertThat(PATCH_ENTITY, Matchers.equalTo(response.readEntity(String.class)));
+        }
+    }
+}
diff --git a/tests/e2e/pom.xml b/tests/e2e/pom.xml
index 05083e4..8378fe2 100644
--- a/tests/e2e/pom.xml
+++ b/tests/e2e/pom.xml
@@ -41,10 +41,10 @@
                     <forkCount>1</forkCount>
                     <reuseForks>false</reuseForks>
                     <enableAssertions>false</enableAssertions>
-                    <skipTests>${skip.e2e}</skipTests>
                     <excludes>
                         <!--TODO remove after jakartification-->
                         <exclude>org/glassfish/jersey/tests/e2e/server/wadl/NoJAXBNoWadlTest.java</exclude>
+                        <exclude>org/glassfish/jersey/tests/e2e/inject/SingleRequestScopeInjectionTest.java</exclude>
                     </excludes>
                 </configuration>
             </plugin>
@@ -211,6 +211,7 @@
                             <testExcludes>
                                 <testExclude>org/glassfish/jersey/tests/e2e/container/Jersey2462Test.java</testExclude>
                                 <testExclude>org/glassfish/jersey/tests/e2e/container/JettyEmptyHeaderParamTest.java</testExclude>
+                                <testExclude>org/glassfish/jersey/tests/e2e/inject/SingleRequestScopeInjectionTest.java</testExclude>
                             </testExcludes>
                         </configuration>
                     </plugin>
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ContentDispositionTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ContentDispositionTest.java
index c4bcd53..138466e 100644
--- a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ContentDispositionTest.java
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ContentDispositionTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2024 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
@@ -66,7 +66,7 @@
             contentDisposition = new ContentDisposition(header);
             assertNotNull(contentDisposition);
             assertEquals(contentDispositionType, contentDisposition.getType());
-            final String dateString = HttpDateFormat.getPreferredDateFormat().format(date);
+            final String dateString = HttpDateFormat.getPreferredDateFormatter().format(date);
             header = contentDispositionType + ";filename=\"test.file\";creation-date=\""
                     + dateString + "\";modification-date=\"" + dateString + "\";read-date=\""
                     + dateString + "\";size=1222";
@@ -89,11 +89,19 @@
     }
 
     @Test
+    void testContentDispositionEncoded() {
+        final Date date = new Date();
+        final ContentDisposition contentDisposition = ContentDisposition.type(contentDispositionType).fileName("\"rm\\ -rf\".sh")
+                .creationDate(date).modificationDate(date).readDate(date).size(312).build();
+        assertEquals("\\\"rm\\\\ -rf\\\".sh", contentDisposition.getFileName());
+    }
+
+    @Test
     public void testToString() {
         final Date date = new Date();
         final ContentDisposition contentDisposition = ContentDisposition.type(contentDispositionType).fileName("test.file")
                 .creationDate(date).modificationDate(date).readDate(date).size(1222).build();
-        final String dateString = HttpDateFormat.getPreferredDateFormat().format(date);
+        final String dateString = HttpDateFormat.getPreferredDateFormatter().format(date);
         final String header = contentDispositionType + "; filename=\"test.file\"; creation-date=\""
                 + dateString + "\"; modification-date=\"" + dateString + "\"; read-date=\"" + dateString + "\"; size=1222";
         assertEquals(header, contentDisposition.toString());
@@ -244,7 +252,7 @@
             final boolean decode
     ) throws ParseException {
         final Date date = new Date();
-        final String dateString = HttpDateFormat.getPreferredDateFormat().format(date);
+        final String dateString = HttpDateFormat.getPreferredDateFormatter().format(date);
         final String prefixHeader = contentDispositionType + ";filename=\"" + actualFileName + "\";"
                 + "creation-date=\"" + dateString + "\";modification-date=\"" + dateString + "\";read-date=\""
                 + dateString + "\";size=1222" + ";name=\"testData\";" + "filename*=\"";
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/FormDataContentDispositionTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/FormDataContentDispositionTest.java
index c931809..0595890 100644
--- a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/FormDataContentDispositionTest.java
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/FormDataContentDispositionTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2024 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
@@ -45,7 +45,7 @@
                 .modificationDate(date).readDate(date).size(1222).build();
         assertFormDataContentDisposition(contentDisposition, date);
         try {
-            final String dateString = HttpDateFormat.getPreferredDateFormat().format(date);
+            final String dateString = HttpDateFormat.getPreferredDateFormatter().format(date);
             final String header = contentDispositionType + ";filename=\"test.file\";creation-date=\"" + dateString
                     + "\";modification-date=\"" + dateString + "\";read-date=\"" + dateString + "\";size=1222"
                     + ";name=\"testData\"";
@@ -92,7 +92,7 @@
         final FormDataContentDisposition contentDisposition = FormDataContentDisposition.name("testData")
                 .fileName("test.file").creationDate(date).modificationDate(date)
                         .readDate(date).size(1222).build();
-        final String dateString = HttpDateFormat.getPreferredDateFormat().format(date);
+        final String dateString = HttpDateFormat.getPreferredDateFormatter().format(date);
         final String header = contentDispositionType + "; filename=\"test.file\"; creation-date=\"" + dateString
                 + "\"; modification-date=\"" + dateString + "\"; read-date=\"" + dateString + "\"; size=1222"
                 + "; name=\"testData\"";
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/HttpHeaderTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/HttpHeaderTest.java
index 63305db..4e1bfd3 100644
--- a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/HttpHeaderTest.java
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/HttpHeaderTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2024 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
@@ -100,8 +100,8 @@
     @Test
     public void testDateParsing() throws ParseException {
         final String date_RFC1123 = "Sun, 06 Nov 1994 08:49:37 GMT";
-        final String date_RFC1036 = "Sunday, 06-Nov-94 08:49:37 GMT";
-        final String date_ANSI_C = "Sun Nov  6 08:49:37 1994";
+        final String date_RFC1036 = "Sunday, 07-Nov-04 08:49:37 GMT";
+        final String date_ANSI_C = "Sun Nov 6 08:49:37 1994";
 
         HttpHeaderReader.readDate(date_RFC1123);
         HttpHeaderReader.readDate(date_RFC1036);
@@ -113,7 +113,7 @@
         final String date_RFC1123 = "Sun, 06 Nov 1994 08:49:37 GMT";
         final Date date = HttpHeaderReader.readDate(date_RFC1123);
 
-        final String date_formatted = HttpDateFormat.getPreferredDateFormat().format(date);
+        final String date_formatted = HttpDateFormat.getPreferredDateFormatter().format(date);
         assertEquals(date_RFC1123, date_formatted);
     }
 
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/inject/SingleRequestScopeInjectionTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/inject/SingleRequestScopeInjectionTest.java
new file mode 100644
index 0000000..2c4cc35
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/inject/SingleRequestScopeInjectionTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2024 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.e2e.inject;
+
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.util.collection.Ref;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.jetty.JettyTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.junit.jupiter.api.Test;
+
+import jakarta.inject.Inject;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.container.ContainerRequestContext;
+import jakarta.ws.rs.container.ContainerRequestFilter;
+import jakarta.ws.rs.container.DynamicFeature;
+import jakarta.ws.rs.container.ResourceInfo;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.FeatureContext;
+import jakarta.ws.rs.core.GenericType;
+
+import java.io.IOException;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class SingleRequestScopeInjectionTest extends JerseyTest {
+    @Path("hello")
+    public static class HelloResource {
+        @GET
+        public String getHello() {
+            return "Hello World!";
+        }
+    }
+    @Override
+    protected Application configure() {
+        ResourceConfig resourceConfig = new ResourceConfig(HelloResource.class);
+        resourceConfig.register(new InjectedFilterRegistrar(InjectedFilter.class));
+        return resourceConfig;
+    }
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new JettyTestContainerFactory();
+    }
+    @Test
+    public void test() {
+        final String hello = target("hello").request().get(String.class);
+        assertEquals("Hello World!", hello);
+    }
+    public static class InjectedFilter implements ContainerRequestFilter {
+        @Inject
+        private InjectionManager injectionManager;
+        @Override
+        public void filter(ContainerRequestContext requestContext) throws IOException {
+            Ref<HttpServletRequest> requestRef =
+                    injectionManager.getInstance((new GenericType<Ref<HttpServletRequest>>() {}).getType());
+            if (requestRef == null || requestRef.get() == null) {
+                throw new IllegalStateException("Request not injected");
+            }
+        }
+    }
+    public static class InjectedFilterRegistrar implements DynamicFeature {
+        private final Class<?> filterToRegister;
+        public InjectedFilterRegistrar(Class<?> filterToRegister) {
+            this.filterToRegister = filterToRegister;
+        }
+        @Override
+        public void configure(ResourceInfo resourceInfo, FeatureContext context) {
+            context.register(filterToRegister);
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/integration/async-jersey-filter/pom.xml b/tests/integration/async-jersey-filter/pom.xml
index b7ff2f0..b8fb128 100644
--- a/tests/integration/async-jersey-filter/pom.xml
+++ b/tests/integration/async-jersey-filter/pom.xml
@@ -66,6 +66,11 @@
             <artifactId>junit-jupiter-api</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/tests/integration/async-jersey-filter/src/test/java/module-info.java b/tests/integration/async-jersey-filter/src/test/java/module-info.java
index cd60c38..55d2d87 100644
--- a/tests/integration/async-jersey-filter/src/test/java/module-info.java
+++ b/tests/integration/async-jersey-filter/src/test/java/module-info.java
@@ -29,6 +29,7 @@
 
     requires org.junit.jupiter.api;
     requires org.junit.jupiter.engine;
+    requires org.junit.platform.engine;
 
     exports org.glassfish.jersey.tests.integration.async;
     exports org.glassfish.jersey.tests.integration.jersey2730;
diff --git a/tests/integration/cdi-integration/cdi-beanvalidation-webapp/pom.xml b/tests/integration/cdi-integration/cdi-beanvalidation-webapp/pom.xml
index 7de5ef3..9f53580 100644
--- a/tests/integration/cdi-integration/cdi-beanvalidation-webapp/pom.xml
+++ b/tests/integration/cdi-integration/cdi-beanvalidation-webapp/pom.xml
@@ -31,6 +31,16 @@
 
     <description>Jersey CDI/Bean Validation test web application</description>
 
+    <properties>
+        <surefire.coverage.argline>
+            --add-reads org.jboss.logging=weld.core.impl
+            --add-reads org.jboss.logging=weld.environment.common
+            --add-reads org.jboss.logging=weld.se.core
+            --add-reads org.jboss.logging=org.hibernate.validator
+            --add-modules=ALL-MODULE-PATH
+        </surefire.coverage.argline>
+    </properties>
+
     <dependencies>
         <dependency>
             <groupId>jakarta.ws.rs</groupId>
@@ -73,7 +83,13 @@
         <dependency>
             <groupId>org.jboss.weld.se</groupId>
             <artifactId>weld-se-core</artifactId>
-            <scope>provided</scope>
+<!--            <scope>provided</scope>-->
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.weld.environment</groupId>
+            <artifactId>weld-environment-common</artifactId>
+            <version>${weld.version}</version>
+<!--            <scope>provided</scope>-->
         </dependency>
         <dependency>
             <groupId>org.slf4j</groupId>
@@ -121,6 +137,11 @@
             <artifactId>jersey-container-grizzly2-http</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <profiles>
diff --git a/tests/integration/cdi-integration/cdi-client/pom.xml b/tests/integration/cdi-integration/cdi-client/pom.xml
index 3d640a6..f123376 100644
--- a/tests/integration/cdi-integration/cdi-client/pom.xml
+++ b/tests/integration/cdi-integration/cdi-client/pom.xml
@@ -92,4 +92,13 @@
             </plugin>
         </plugins>
     </build>
+    <properties>
+        <surefire.coverage.argline>
+            --add-opens weld.core.impl/org.jboss.weld.logging=org.jboss.logging
+            --add-reads org.jboss.logging=weld.core.impl
+            --add-reads org.jboss.logging=weld.environment.common
+            --add-reads org.jboss.logging=weld.se.core
+            --add-modules=ALL-MODULE-PATH
+        </surefire.coverage.argline>
+    </properties>
 </project>
diff --git a/tests/integration/cdi-integration/cdi-client/src/test/java/module-info.java b/tests/integration/cdi-integration/cdi-client/src/test/java/module-info.java
index 6ff1a4b..0567a2c 100644
--- a/tests/integration/cdi-integration/cdi-client/src/test/java/module-info.java
+++ b/tests/integration/cdi-integration/cdi-client/src/test/java/module-info.java
@@ -25,6 +25,8 @@
     requires weld.core.impl;
     requires weld.se.core;
 
+    requires org.jboss.logging;
+
     requires org.glassfish.jersey.core.common;
     requires org.glassfish.jersey.inject.hk2;
     requires org.glassfish.jersey.ext.cdi1x ;
diff --git a/tests/integration/cdi-integration/cdi-manually-bound/pom.xml b/tests/integration/cdi-integration/cdi-manually-bound/pom.xml
index e62d76d..a1d67ad 100644
--- a/tests/integration/cdi-integration/cdi-manually-bound/pom.xml
+++ b/tests/integration/cdi-integration/cdi-manually-bound/pom.xml
@@ -118,5 +118,21 @@
             </exclusions>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
+
+    <properties>
+        <surefire.coverage.argline>
+            --add-opens weld.core.impl/org.jboss.weld.logging=org.jboss.logging
+            --add-reads org.jboss.logging=weld.core.impl
+            --add-reads org.jboss.logging=weld.environment.common
+            --add-reads org.jboss.logging=weld.se.core
+            --add-modules=ALL-MODULE-PATH
+        </surefire.coverage.argline>
+    </properties>
+
 </project>
diff --git a/tests/integration/cdi-integration/cdi-manually-bound/src/test/java/module-info.java b/tests/integration/cdi-integration/cdi-manually-bound/src/test/java/module-info.java
index 4479aa6..4365145 100644
--- a/tests/integration/cdi-integration/cdi-manually-bound/src/test/java/module-info.java
+++ b/tests/integration/cdi-integration/cdi-manually-bound/src/test/java/module-info.java
@@ -34,6 +34,7 @@
 
     requires org.junit.jupiter.api;
     requires org.junit.jupiter.engine;
+    requires org.junit.platform.engine;
 
     requires org.glassfish.jersey.tests.framework.core;
     requires org.glassfish.jersey.tests.framework.provider.external;
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 ea440ac..e282fff 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
@@ -95,6 +95,21 @@
             <groupId>org.glassfish.jersey.containers</groupId>
             <artifactId>jersey-container-servlet-core</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
+    <properties>
+        <surefire.coverage.argline>
+            --add-opens weld.core.impl/org.jboss.weld.logging=org.jboss.logging
+            --add-reads org.jboss.logging=weld.core.impl
+            --add-reads org.jboss.logging=weld.environment.common
+            --add-reads org.jboss.logging=weld.se.core
+            --add-modules=ALL-MODULE-PATH
+        </surefire.coverage.argline>
+    </properties>
+
 </project>
diff --git a/tests/integration/cdi-integration/cdi-resource-with-at-context/src/test/java/module-info.java b/tests/integration/cdi-integration/cdi-resource-with-at-context/src/test/java/module-info.java
index 6a38acb..d3276bf 100644
--- a/tests/integration/cdi-integration/cdi-resource-with-at-context/src/test/java/module-info.java
+++ b/tests/integration/cdi-integration/cdi-resource-with-at-context/src/test/java/module-info.java
@@ -32,6 +32,7 @@
 
     requires org.junit.jupiter.api;
     requires org.junit.jupiter.engine;
+    requires org.junit.platform.engine;
 
     requires org.glassfish.jersey.tests.framework.core;
     requires org.glassfish.jersey.tests.integration.cdi.resource.context;
diff --git a/tests/integration/cdi-integration/cdi-singleton/pom.xml b/tests/integration/cdi-integration/cdi-singleton/pom.xml
index 6cf2624..0508502 100644
--- a/tests/integration/cdi-integration/cdi-singleton/pom.xml
+++ b/tests/integration/cdi-integration/cdi-singleton/pom.xml
@@ -99,6 +99,11 @@
             </exclusions>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
 </project>
diff --git a/tests/integration/cdi-integration/cdi-singleton/src/test/java/module-info.java b/tests/integration/cdi-integration/cdi-singleton/src/test/java/module-info.java
index e70f9f8..fdb0bc3 100644
--- a/tests/integration/cdi-integration/cdi-singleton/src/test/java/module-info.java
+++ b/tests/integration/cdi-integration/cdi-singleton/src/test/java/module-info.java
@@ -33,6 +33,7 @@
     requires org.glassfish.jersey.tests.framework.provider.grizzly;
     requires org.junit.jupiter.api;
     requires org.junit.jupiter.engine;
+    requires org.junit.platform.engine;
 
     exports org.glassfish.jersey.tests.cdi.singleton.test;
 }
\ No newline at end of file
diff --git a/tests/integration/cdi-integration/cdi-skipping-analyzer/pom.xml b/tests/integration/cdi-integration/cdi-skipping-analyzer/pom.xml
new file mode 100644
index 0000000..0d86799
--- /dev/null
+++ b/tests/integration/cdi-integration/cdi-skipping-analyzer/pom.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2024 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>cdi-integration-project</artifactId>
+        <groupId>org.glassfish.jersey.tests.integration.cdi</groupId>
+        <version>3.5.99-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>cdi-skipping-analyzer</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>jakarta.ws.rs</groupId>
+            <artifactId>jakarta.ws.rs-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.annotation</groupId>
+            <artifactId>jakarta.annotation-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.enterprise</groupId>
+            <artifactId>jakarta.enterprise.cdi-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext.cdi</groupId>
+            <artifactId>jersey-cdi1x</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.weld.se</groupId>
+            <artifactId>weld-se-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework</groupId>
+            <artifactId>jersey-test-framework-util</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.incubator</groupId>
+            <artifactId>jersey-injectless-client</artifactId>
+            <version>${jersey.version}</version>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/tests/integration/cdi-integration/cdi-skipping-analyzer/src/main/java/org/glassfish/jersey/tests/cdi/skippinganalyzer/CdiService.java b/tests/integration/cdi-integration/cdi-skipping-analyzer/src/main/java/org/glassfish/jersey/tests/cdi/skippinganalyzer/CdiService.java
new file mode 100644
index 0000000..44d56d2
--- /dev/null
+++ b/tests/integration/cdi-integration/cdi-skipping-analyzer/src/main/java/org/glassfish/jersey/tests/cdi/skippinganalyzer/CdiService.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2024 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.cdi.skippinganalyzer;
+
+public interface CdiService<T> {
+    void doService(T t);
+}
diff --git a/tests/integration/cdi-integration/cdi-skipping-analyzer/src/main/java/org/glassfish/jersey/tests/cdi/skippinganalyzer/CdiServiceExtension.java b/tests/integration/cdi-integration/cdi-skipping-analyzer/src/main/java/org/glassfish/jersey/tests/cdi/skippinganalyzer/CdiServiceExtension.java
new file mode 100644
index 0000000..a7e5130
--- /dev/null
+++ b/tests/integration/cdi-integration/cdi-skipping-analyzer/src/main/java/org/glassfish/jersey/tests/cdi/skippinganalyzer/CdiServiceExtension.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2024 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.cdi.skippinganalyzer;
+
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.event.Observes;
+import jakarta.enterprise.inject.spi.AfterBeanDiscovery;
+import jakarta.enterprise.inject.spi.Extension;
+import java.io.IOException;
+
+public class CdiServiceExtension implements Extension {
+    public void observe(@Observes AfterBeanDiscovery event) throws IOException, ClassNotFoundException {
+        event.addBean()
+                .addType(CdiService.class)
+                .beanClass(CdiService.class)
+                .scope(ApplicationScoped.class)
+                .createWith(context -> new CdiServiceImpl());
+    }
+
+}
diff --git a/tests/integration/cdi-integration/cdi-skipping-analyzer/src/main/java/org/glassfish/jersey/tests/cdi/skippinganalyzer/CdiServiceImpl.java b/tests/integration/cdi-integration/cdi-skipping-analyzer/src/main/java/org/glassfish/jersey/tests/cdi/skippinganalyzer/CdiServiceImpl.java
new file mode 100644
index 0000000..6406346
--- /dev/null
+++ b/tests/integration/cdi-integration/cdi-skipping-analyzer/src/main/java/org/glassfish/jersey/tests/cdi/skippinganalyzer/CdiServiceImpl.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2024 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.cdi.skippinganalyzer;
+
+import jakarta.enterprise.inject.spi.BeanManager;
+import jakarta.inject.Inject;
+
+public class CdiServiceImpl implements CdiService<StringBuilder> {
+
+    @Inject
+    BeanManager beanManager;
+
+    @Override
+    public void doService(StringBuilder sb) {
+        sb.append(getClass().getSimpleName());
+    }
+}
diff --git a/tests/integration/cdi-integration/cdi-skipping-analyzer/src/main/java/org/glassfish/jersey/tests/cdi/skippinganalyzer/WeldDiscoveredBean.java b/tests/integration/cdi-integration/cdi-skipping-analyzer/src/main/java/org/glassfish/jersey/tests/cdi/skippinganalyzer/WeldDiscoveredBean.java
new file mode 100644
index 0000000..ce459d6
--- /dev/null
+++ b/tests/integration/cdi-integration/cdi-skipping-analyzer/src/main/java/org/glassfish/jersey/tests/cdi/skippinganalyzer/WeldDiscoveredBean.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2024 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.cdi.skippinganalyzer;
+
+import jakarta.enterprise.context.ApplicationScoped;
+
+@ApplicationScoped
+public class WeldDiscoveredBean {
+
+}
diff --git a/tests/integration/cdi-integration/cdi-skipping-analyzer/src/main/resources/META-INF/beans.xml b/tests/integration/cdi-integration/cdi-skipping-analyzer/src/main/resources/META-INF/beans.xml
new file mode 100644
index 0000000..0850fd8
--- /dev/null
+++ b/tests/integration/cdi-integration/cdi-skipping-analyzer/src/main/resources/META-INF/beans.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2024 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
+
+-->
+
+
+<beans xmlns="https://jakarta.ee/xml/ns/jakartaee"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/beans_4_0.xsd"
+       version="4.0" bean-discovery-mode="annotated">
+</beans>
\ No newline at end of file
diff --git a/tests/integration/cdi-integration/cdi-skipping-analyzer/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension b/tests/integration/cdi-integration/cdi-skipping-analyzer/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension
new file mode 100644
index 0000000..7bfc71c
--- /dev/null
+++ b/tests/integration/cdi-integration/cdi-skipping-analyzer/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension
@@ -0,0 +1 @@
+org.glassfish.jersey.tests.cdi.skippinganalyzer.CdiServiceExtension
\ No newline at end of file
diff --git a/tests/integration/cdi-integration/cdi-skipping-analyzer/src/test/java/org/glassfish/jersey/tests/cdi/skippinganalyzer/SkippingAnalyzerTest.java b/tests/integration/cdi-integration/cdi-skipping-analyzer/src/test/java/org/glassfish/jersey/tests/cdi/skippinganalyzer/SkippingAnalyzerTest.java
new file mode 100644
index 0000000..698e496
--- /dev/null
+++ b/tests/integration/cdi-integration/cdi-skipping-analyzer/src/test/java/org/glassfish/jersey/tests/cdi/skippinganalyzer/SkippingAnalyzerTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2024 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.cdi.skippinganalyzer;
+
+import org.glassfish.hk2.api.ClassAnalyzer;
+import org.glassfish.jersey.ext.cdi1x.internal.CdiComponentProvider;
+import org.glassfish.jersey.ext.cdi1x.internal.InjecteeSkippingAnalyzer;
+import org.glassfish.jersey.inject.hk2.Hk2InjectionManagerFactory;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.inject.Injections;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.jboss.weld.environment.se.Weld;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assumptions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import jakarta.enterprise.inject.spi.BeanManager;
+import jakarta.enterprise.inject.spi.CDI;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.Set;
+
+public class SkippingAnalyzerTest {
+    private Weld weld;
+
+    @BeforeEach
+    public void setup() {
+        Assumptions.assumeTrue(Hk2InjectionManagerFactory.isImmediateStrategy());
+    }
+
+    @BeforeEach
+    public void setUp() throws Exception {
+        if (Hk2InjectionManagerFactory.isImmediateStrategy()) {
+            weld = new Weld();
+            weld.initialize();
+        }
+    }
+
+    @AfterEach
+    public void tearDown() throws Exception {
+        weld.shutdown();
+    }
+
+    @Test
+    public void testInjecteeSkippingAnalyzerWithZeroFieldsToSkip() throws Exception {
+        BeanManager beanManager = CDI.current().getBeanManager();
+        CdiComponentProvider provider = beanManager.getExtension(CdiComponentProvider.class);
+        Method method = provider.getClass().getDeclaredMethod("getFieldsToSkip");
+        method.setAccessible(true);
+        Map fieldMap = (Map) method.invoke(provider);
+        MatcherAssert.assertThat(0, Matchers.is(fieldMap.size()));
+
+        InjectionManager injectionManager = Injections.createInjectionManager();
+        provider.initialize(injectionManager);
+        injectionManager.completeRegistration();
+        ClassAnalyzer analyzer = injectionManager.getInstance(ClassAnalyzer.class, CdiComponentProvider.CDI_CLASS_ANALYZER);
+        MatcherAssert.assertThat(InjecteeSkippingAnalyzer.class, Matchers.is(analyzer.getClass()));
+
+        Set<Field> fieldSet = analyzer.getFields(CdiServiceImpl.class);
+        MatcherAssert.assertThat(0, Matchers.is(fieldSet.size()));
+    }
+}
diff --git a/tests/integration/cdi-integration/cdi-test-webapp/pom.xml b/tests/integration/cdi-integration/cdi-test-webapp/pom.xml
index a024fcd..7414097 100644
--- a/tests/integration/cdi-integration/cdi-test-webapp/pom.xml
+++ b/tests/integration/cdi-integration/cdi-test-webapp/pom.xml
@@ -93,6 +93,11 @@
             <version>${jboss.logging.version}</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/tests/integration/cdi-integration/cdi-test-webapp/src/test/java/module-info.java b/tests/integration/cdi-integration/cdi-test-webapp/src/test/java/module-info.java
index 37b7d03..00ac6c5 100644
--- a/tests/integration/cdi-integration/cdi-test-webapp/src/test/java/module-info.java
+++ b/tests/integration/cdi-integration/cdi-test-webapp/src/test/java/module-info.java
@@ -48,6 +48,7 @@
     requires org.junit.jupiter.api;
     requires org.junit.jupiter.engine;
     requires org.junit.jupiter.params;
+    requires org.junit.platform.engine;
 
     exports org.glassfish.jersey.tests.cdi.resources.test;
 
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 735217f..3c688aa 100644
--- a/tests/integration/cdi-integration/context-inject-on-server/pom.xml
+++ b/tests/integration/cdi-integration/context-inject-on-server/pom.xml
@@ -84,6 +84,11 @@
             <groupId>org.glassfish.jersey.containers</groupId>
             <artifactId>jersey-container-servlet-core</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
     <profiles>
         <profile>
diff --git a/tests/integration/cdi-integration/context-inject-on-server/src/test/java/module-info.java b/tests/integration/cdi-integration/context-inject-on-server/src/test/java/module-info.java
index 90c500e..ac34e67 100644
--- a/tests/integration/cdi-integration/context-inject-on-server/src/test/java/module-info.java
+++ b/tests/integration/cdi-integration/context-inject-on-server/src/test/java/module-info.java
@@ -40,6 +40,7 @@
     requires org.glassfish.jersey.ext.cdi1x;
 
     requires org.junit.jupiter.api;
+    requires org.junit.platform.engine;
     requires org.glassfish.jersey.tests.framework.core;
     requires org.glassfish.jersey.tests.framework.provider.grizzly;
 
diff --git a/tests/integration/cdi-integration/pom.xml b/tests/integration/cdi-integration/pom.xml
index 126835f..21a6edf 100644
--- a/tests/integration/cdi-integration/pom.xml
+++ b/tests/integration/cdi-integration/pom.xml
@@ -31,6 +31,18 @@
     <groupId>org.glassfish.jersey.tests.integration.cdi</groupId>
     <artifactId>cdi-integration-project</artifactId>
     <name>cdi-integration-project</name>
+
+    <properties>
+        <surefire.coverage.argline>
+            --add-opens weld.core.impl/org.jboss.weld.logging=org.jboss.logging
+            --add-reads org.jboss.logging=weld.core.impl
+            --add-reads org.jboss.logging=weld.environment.common
+            --add-reads org.jboss.logging=weld.se.core
+            --add-modules=ALL-MODULE-PATH
+        </surefire.coverage.argline>
+    </properties>
+
+
     <modules>
         <module>cdi-beanvalidation-webapp</module>
         <module>cdi-client</module>
@@ -43,6 +55,7 @@
         <module>cdi-multipart-webapp</module>
         <module>cdi-resource-with-at-context</module>
         <module>cdi-singleton</module>
+        <module>cdi-skipping-analyzer</module>
         <module>cdi-test-webapp</module>
         <module>cdi-with-jersey-injection-custom-cfg-webapp</module>
         <module>cdi-with-jersey-injection-custom-hk2-banned-webapp</module>
diff --git a/tests/integration/client-connector-provider/pom.xml b/tests/integration/client-connector-provider/pom.xml
index 16e85e7..45ad9ca 100644
--- a/tests/integration/client-connector-provider/pom.xml
+++ b/tests/integration/client-connector-provider/pom.xml
@@ -57,6 +57,11 @@
             <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
     <build>
         <finalName>${project.artifactId}</finalName>
diff --git a/tests/integration/client-connector-provider/src/test/java/module-info.java b/tests/integration/client-connector-provider/src/test/java/module-info.java
index 7fa0966..c306f6a 100644
--- a/tests/integration/client-connector-provider/src/test/java/module-info.java
+++ b/tests/integration/client-connector-provider/src/test/java/module-info.java
@@ -24,6 +24,7 @@
 
     requires org.junit.jupiter.api;
     requires org.junit.jupiter.engine;
+    requires org.junit.platform.engine;
     requires org.glassfish.jersey.tests.framework.core;
     requires org.glassfish.jersey.tests.framework.provider.grizzly;
 
diff --git a/tests/integration/jersey-1604/pom.xml b/tests/integration/jersey-1604/pom.xml
index d2ce39c..2a7c964 100644
--- a/tests/integration/jersey-1604/pom.xml
+++ b/tests/integration/jersey-1604/pom.xml
@@ -67,6 +67,8 @@
         </plugins>
     </build>
     <properties>
-        <failsafe.coverage.argline>--add-opens org.glassfish.jersey.core.client/org.glassfish.jersey.client.innate=ALL-UNNAMED --add-modules=ALL-MODULE-PATH</failsafe.coverage.argline>
+        <failsafe.coverage.argline>
+            --add-opens org.glassfish.jersey.core.client/org.glassfish.jersey.client.innate=ALL-UNNAMED
+            --add-exports org.glassfish.jersey.core.common/org.glassfish.jersey.innate.io=ALL-UNNAMED --add-modules=ALL-MODULE-PATH</failsafe.coverage.argline>
     </properties>
 </project>
diff --git a/tests/integration/jersey-1829/pom.xml b/tests/integration/jersey-1829/pom.xml
index 6e905b3..2775fcf 100644
--- a/tests/integration/jersey-1829/pom.xml
+++ b/tests/integration/jersey-1829/pom.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
 
-    Copyright (c) 2013, 2023 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2013, 2024 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/tests/integration/jersey-2255/pom.xml b/tests/integration/jersey-2255/pom.xml
index 114c363..8e30834 100644
--- a/tests/integration/jersey-2255/pom.xml
+++ b/tests/integration/jersey-2255/pom.xml
@@ -57,6 +57,11 @@
             <version>${jaxb.ri.version}</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/tests/integration/jersey-2255/src/test/java/module-info.java b/tests/integration/jersey-2255/src/test/java/module-info.java
index cdb0cb5..5d4f2fd 100644
--- a/tests/integration/jersey-2255/src/test/java/module-info.java
+++ b/tests/integration/jersey-2255/src/test/java/module-info.java
@@ -19,6 +19,7 @@
     requires jakarta.xml.bind;
 
     requires org.junit.jupiter.api;
+    requires org.junit.platform.engine;
     requires org.hamcrest;
     requires org.glassfish.jersey.tests.framework.core;
     requires org.glassfish.jersey.tests.framework.provider.external;
diff --git a/tests/integration/jersey-2421/src/test/java/org/glassfish/jersey/tests/integration/jersey2421/Jersey2421Test.java b/tests/integration/jersey-2421/src/test/java/org/glassfish/jersey/tests/integration/jersey2421/Jersey2421Test.java
index bae7758..79be107 100644
--- a/tests/integration/jersey-2421/src/test/java/org/glassfish/jersey/tests/integration/jersey2421/Jersey2421Test.java
+++ b/tests/integration/jersey-2421/src/test/java/org/glassfish/jersey/tests/integration/jersey2421/Jersey2421Test.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2023 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -38,7 +38,6 @@
 import org.glassfish.jersey.media.multipart.FormDataMultiPart;
 import org.glassfish.jersey.media.multipart.MultiPart;
 import org.glassfish.jersey.media.multipart.MultiPartFeature;
-import org.glassfish.jersey.message.internal.NullOutputStream;
 import org.glassfish.jersey.message.internal.OutboundMessageContext;
 
 import org.junit.jupiter.api.Test;
@@ -61,7 +60,7 @@
                 request.setStreamProvider(new OutboundMessageContext.StreamProvider() {
                     @Override
                     public OutputStream getOutputStream(final int contentLength) throws IOException {
-                        return new NullOutputStream();
+                        return OutputStream.nullOutputStream();
                     }
                 });
                 request.writeEntity();
diff --git a/tests/integration/jersey-2776/pom.xml b/tests/integration/jersey-2776/pom.xml
index 7135902..d976ab6 100644
--- a/tests/integration/jersey-2776/pom.xml
+++ b/tests/integration/jersey-2776/pom.xml
@@ -42,7 +42,7 @@
         <dependency>
             <groupId>org.apache.cxf</groupId>
             <artifactId>cxf-rt-rs-client</artifactId>
-            <version>3.0.3</version>
+            <version>3.5.8</version>
             <scope>test</scope>
         </dependency>
 
diff --git a/tests/integration/jersey-2794/src/test/java/org/glassfish/jersey/tests/integration/jersey2794/Jersey2794ITCase.java b/tests/integration/jersey-2794/src/test/java/org/glassfish/jersey/tests/integration/jersey2794/Jersey2794ITCase.java
index 80eca2d..ce9ccdc 100644
--- a/tests/integration/jersey-2794/src/test/java/org/glassfish/jersey/tests/integration/jersey2794/Jersey2794ITCase.java
+++ b/tests/integration/jersey-2794/src/test/java/org/glassfish/jersey/tests/integration/jersey2794/Jersey2794ITCase.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -23,7 +23,6 @@
 import java.nio.file.FileVisitResult;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.nio.file.SimpleFileVisitor;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -104,7 +103,7 @@
 
     private int matchingTempFiles(final String tempDir) throws IOException {
         AtomicInteger count = new AtomicInteger(0);
-        Files.walkFileTree(Paths.get(tempDir), new SimpleFileVisitor<Path>() {
+        Files.walkFileTree(Path.of(tempDir), new SimpleFileVisitor<Path>() {
             @Override
             public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                 if (file.getFileName().startsWith("MIME") && file.getFileName().endsWith("tmp")) {
diff --git a/tests/integration/jersey-2846/src/test/java/org/glassfish/jersey/tests/integration/jersey2846/Jersey2846ITCase.java b/tests/integration/jersey-2846/src/test/java/org/glassfish/jersey/tests/integration/jersey2846/Jersey2846ITCase.java
index 4286aef..6b37da0 100644
--- a/tests/integration/jersey-2846/src/test/java/org/glassfish/jersey/tests/integration/jersey2846/Jersey2846ITCase.java
+++ b/tests/integration/jersey-2846/src/test/java/org/glassfish/jersey/tests/integration/jersey2846/Jersey2846ITCase.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -20,7 +20,6 @@
 import java.nio.file.FileVisitResult;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.nio.file.SimpleFileVisitor;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.util.Arrays;
@@ -124,7 +123,7 @@
 
     private int matchingTempFiles(final String tempDir) throws IOException {
         AtomicInteger count = new AtomicInteger(0);
-        Files.walkFileTree(Paths.get(tempDir), new SimpleFileVisitor<Path>() {
+        Files.walkFileTree(Path.of(tempDir), new SimpleFileVisitor<Path>() {
             @Override
             public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                 Path name = file.getFileName();
diff --git a/tests/integration/jersey-4099/pom.xml b/tests/integration/jersey-4099/pom.xml
index 2a13e4d..9b1127e 100644
--- a/tests/integration/jersey-4099/pom.xml
+++ b/tests/integration/jersey-4099/pom.xml
@@ -85,6 +85,11 @@
             <groupId>org.glassfish.jersey.ext.cdi</groupId>
             <artifactId>jersey-cdi1x</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
@@ -97,7 +102,11 @@
     </build>
     <properties>
         <surefire.coverage.argline>
-            --add-reads org.jboss.logging=java.logging --add-modules=ALL-MODULE-PATH
+            --add-opens weld.core.impl/org.jboss.weld.logging=org.jboss.logging
+            --add-reads org.jboss.logging=weld.core.impl
+            --add-reads org.jboss.logging=weld.environment.common
+            --add-reads org.jboss.logging=weld.se.core
+            --add-modules=ALL-MODULE-PATH
         </surefire.coverage.argline>
     </properties>
 </project>
diff --git a/tests/integration/jersey-4099/src/test/java/module-info.java b/tests/integration/jersey-4099/src/test/java/module-info.java
index 428bc79..9a2560c 100644
--- a/tests/integration/jersey-4099/src/test/java/module-info.java
+++ b/tests/integration/jersey-4099/src/test/java/module-info.java
@@ -26,6 +26,7 @@
     requires org.glassfish.jersey.core.server;
 
     requires org.junit.jupiter.api;
+    requires org.junit.platform.engine;
     requires org.glassfish.jersey.tests.framework.core;
     requires org.glassfish.jersey.tests.framework.provider.grizzly;
 
diff --git a/tests/integration/property-check/pom.xml b/tests/integration/property-check/pom.xml
index 42c9810..e2a75d4 100644
--- a/tests/integration/property-check/pom.xml
+++ b/tests/integration/property-check/pom.xml
@@ -74,6 +74,16 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-apache5-connector</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-jnh-connector</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.glassfish.jersey.security</groupId>
             <artifactId>oauth1-server</artifactId>
             <version>${project.version}</version>
@@ -109,28 +119,4 @@
             </plugin>
         </plugins>
     </build>
-
-    <profiles>
-        <profile>
-            <id>jdk19+</id>
-            <activation>
-                <jdk>[19,)</jdk>
-            </activation>
-            <build>
-                <pluginManagement>
-                    <plugins>
-                        <plugin>
-                            <groupId>org.apache.maven.plugins</groupId>
-                            <artifactId>maven-surefire-plugin</artifactId>
-                            <configuration>
-                                <excludes>
-                                    <exclude>**/SystemPropertiesConfigurationModelTest.java</exclude>
-                                </excludes>
-                            </configuration>
-                        </plugin>
-                    </plugins>
-                </pluginManagement>
-            </build>
-        </profile>
-    </profiles>
 </project>
diff --git a/tests/integration/property-check/src/test/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationModelTest.java b/tests/integration/property-check/src/test/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationModelTest.java
index ddb8e08..00701ff 100644
--- a/tests/integration/property-check/src/test/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationModelTest.java
+++ b/tests/integration/property-check/src/test/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationModelTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2024 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
@@ -32,12 +32,14 @@
 
 import org.glassfish.jersey.CommonProperties;
 import org.glassfish.jersey.apache.connector.ApacheClientProperties;
+import org.glassfish.jersey.apache5.connector.Apache5ClientProperties;
 import org.glassfish.jersey.client.ClientProperties;
 import org.glassfish.jersey.internal.InternalProperties;
 import org.glassfish.jersey.internal.util.JdkVersion;
 import org.glassfish.jersey.internal.util.PropertiesClass;
 import org.glassfish.jersey.internal.util.Property;
 import org.glassfish.jersey.jetty.connector.JettyClientProperties;
+import org.glassfish.jersey.jnh.connector.JavaNetHttpClientProperties;
 import org.glassfish.jersey.media.multipart.MultiPartProperties;
 import org.glassfish.jersey.message.MessageProperties;
 import org.glassfish.jersey.server.ServerProperties;
@@ -79,8 +81,13 @@
     @Test
     public void propertyLoadedWhenSecurityException() {
         final String TEST_STRING = "test";
-        SecurityManager sm = System.getSecurityManager();
-        String policy = System.getProperty("java.security.policy");
+        final boolean isSm = JdkVersion.getJdkVersion().getMajor() < 19;
+        SecurityManager sm = null;
+        String policy = null;
+        if (isSm) {
+            sm = System.getSecurityManager();
+            policy = System.getProperty("java.security.policy");
+        }
         try {
             System.setProperty(CommonProperties.ALLOW_SYSTEM_PROPERTIES_PROVIDER, Boolean.TRUE.toString());
             System.setProperty(ServerProperties.APPLICATION_NAME, TEST_STRING);
@@ -88,15 +95,21 @@
             System.setProperty(ServletProperties.JAXRS_APPLICATION_CLASS, TEST_STRING);
             System.setProperty(MessageProperties.IO_BUFFER_SIZE, TEST_STRING);
             System.setProperty(ApacheClientProperties.DISABLE_COOKIES, TEST_STRING);
+            System.setProperty(Apache5ClientProperties.DISABLE_COOKIES, TEST_STRING);
             System.setProperty(JettyClientProperties.ENABLE_SSL_HOSTNAME_VERIFICATION, TEST_STRING);
+            System.setProperty(JavaNetHttpClientProperties.DISABLE_COOKIES, TEST_STRING);
             System.setProperty(MultiPartProperties.TEMP_DIRECTORY, TEST_STRING);
             System.setProperty(OAuth1ServerProperties.REALM, TEST_STRING);
             JerseySystemPropertiesConfigurationModel model = new JerseySystemPropertiesConfigurationModel();
             assertTrue(model.as(CommonProperties.ALLOW_SYSTEM_PROPERTIES_PROVIDER, Boolean.class));
-            String securityPolicy = SystemPropertiesConfigurationModelTest.class.getResource("/server.policy").getFile();
-            System.setProperty("java.security.policy", securityPolicy);
-            SecurityManager manager = new SecurityManager();
-            System.setSecurityManager(manager);
+
+            if (isSm) {
+                String securityPolicy = SystemPropertiesConfigurationModelTest.class.getResource("/server.policy").getFile();
+                System.setProperty("java.security.policy", securityPolicy);
+                SecurityManager manager = new SecurityManager();
+                System.setSecurityManager(manager);
+            }
+
             Map<String, Object> properties = model.getProperties();
             assertEquals(TEST_STRING, properties.get(ServerProperties.APPLICATION_NAME));
             assertEquals(Boolean.TRUE.toString(), properties.get(CommonProperties.ALLOW_SYSTEM_PROPERTIES_PROVIDER));
@@ -109,9 +122,13 @@
             assertEquals(TEST_STRING, properties.get(MessageProperties.IO_BUFFER_SIZE));
             assertFalse(properties.containsKey(MessageProperties.DEFLATE_WITHOUT_ZLIB));
             assertEquals(TEST_STRING, properties.get(ApacheClientProperties.DISABLE_COOKIES));
+            assertEquals(TEST_STRING, properties.get(Apache5ClientProperties.DISABLE_COOKIES));
             assertFalse(properties.containsKey(ApacheClientProperties.CONNECTION_MANAGER));
+            assertFalse(properties.containsKey(Apache5ClientProperties.CONNECTION_MANAGER));
             assertEquals(TEST_STRING, properties.get(JettyClientProperties.ENABLE_SSL_HOSTNAME_VERIFICATION));
             assertFalse(properties.containsKey(JettyClientProperties.DISABLE_COOKIES));
+            assertEquals(TEST_STRING, properties.get(JavaNetHttpClientProperties.DISABLE_COOKIES));
+            assertFalse(properties.containsKey(JavaNetHttpClientProperties.SSL_PARAMETERS));
             assertEquals(TEST_STRING, properties.get(MultiPartProperties.TEMP_DIRECTORY));
             assertFalse(properties.containsKey(MultiPartProperties.BUFFER_THRESHOLD));
             assertEquals(TEST_STRING, properties.get(OAuth1ServerProperties.REALM));
@@ -120,7 +137,9 @@
             if (policy != null) {
                 System.setProperty("java.security.policy", policy);
             }
-            System.setSecurityManager(sm);
+            if (isSm) {
+                System.setSecurityManager(sm);
+            }
         }
     }
 
diff --git a/tests/integration/servlet-tests/pom.xml b/tests/integration/servlet-tests/pom.xml
index a9ef073..2332122 100644
--- a/tests/integration/servlet-tests/pom.xml
+++ b/tests/integration/servlet-tests/pom.xml
@@ -48,6 +48,7 @@
         <dependency>
             <groupId>org.glassfish.jersey.test-framework.providers</groupId>
             <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
         </dependency>
     </dependencies>
 
diff --git a/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/PostProcessingErrorFilter.java b/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/PostProcessingErrorFilter.java
new file mode 100644
index 0000000..856e2ef
--- /dev/null
+++ b/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/PostProcessingErrorFilter.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2024 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.servlettests;
+
+import jakarta.servlet.Filter;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+
+import java.io.IOException;
+
+import static org.glassfish.jersey.tests.integration.servlettests.PostProcessingErrorResource.ERROR_MESSAGE;
+
+public class PostProcessingErrorFilter implements Filter {
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+        Filter.super.init(filterConfig);
+    }
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+            throws IOException {
+
+        try {
+            chain.doFilter(request, response);
+        } catch (ServletException ex) {
+            //post-processing attempt
+            final Throwable orig = ex.getRootCause();
+            if (orig.getMessage().equalsIgnoreCase(ERROR_MESSAGE)) {
+                response.getWriter().print(ERROR_MESSAGE);
+                response.getWriter().flush();
+            }
+        }
+    }
+
+    @Override
+    public void destroy() {
+        Filter.super.destroy();
+    }
+}
diff --git a/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/PostProcessingErrorResource.java b/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/PostProcessingErrorResource.java
new file mode 100644
index 0000000..b9d3451
--- /dev/null
+++ b/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/PostProcessingErrorResource.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2024 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.servlettests;
+
+import jakarta.servlet.http.HttpServlet;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.ProcessingException;
+import jakarta.ws.rs.core.Response;
+
+@Path("postprocessing")
+public class PostProcessingErrorResource extends HttpServlet {
+
+    static final String ERROR_MESSAGE = "Must be post processed";
+    @GET
+    public Response getException() {
+        throw new ProcessingException(ERROR_MESSAGE);
+    }
+
+}
diff --git a/tests/integration/servlet-tests/src/main/webapp/WEB-INF/web.xml b/tests/integration/servlet-tests/src/main/webapp/WEB-INF/web.xml
index 5d9d99e..813b221 100644
--- a/tests/integration/servlet-tests/src/main/webapp/WEB-INF/web.xml
+++ b/tests/integration/servlet-tests/src/main/webapp/WEB-INF/web.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
 
-    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2010, 2024 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
@@ -157,4 +157,31 @@
         <filter-name>custom404</filter-name>
         <url-pattern>/custom404/*</url-pattern>
     </filter-mapping>
+
+    <!-- post process errors (40*, 50*) -->
+    <filter>
+        <filter-name>PostProcessFilter</filter-name>
+        <filter-class>org.glassfish.jersey.tests.integration.servlettests.PostProcessingErrorFilter</filter-class>
+    </filter>
+    <servlet>
+        <servlet-name>PostProcessServlet</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>jersey.config.server.provider.classnames</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.servlettests.PostProcessingErrorResource</param-value>
+        </init-param>
+        <init-param>
+            <param-name>jersey.config.server.response.setStatusOverSendError</param-name>
+            <param-value>true</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>PostProcessServlet</servlet-name>
+        <url-pattern>/postProcess/*</url-pattern>
+    </servlet-mapping>
+    <filter-mapping>
+        <filter-name>PostProcessFilter</filter-name>
+        <url-pattern>/postProcess/*</url-pattern>
+    </filter-mapping>
 </web-app>
diff --git a/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/PostProcesingITCase.java b/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/PostProcesingITCase.java
new file mode 100644
index 0000000..dbab315
--- /dev/null
+++ b/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/PostProcesingITCase.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2024 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.servlettests;
+
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Response;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.junit.jupiter.api.Test;
+
+import java.util.Locale;
+
+import static org.glassfish.jersey.tests.integration.servlettests.PostProcessingErrorResource.ERROR_MESSAGE;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class PostProcesingITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        // dummy resource config
+        return new ResourceConfig();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testPostProcessingLocale() {
+        final Response response = target()
+                .path("postProcess/postprocessing")
+                .request().get();
+        assertEquals(ERROR_MESSAGE, response.readEntity(String.class));
+    }
+
+}
diff --git a/tests/integration/tracing-support/pom.xml b/tests/integration/tracing-support/pom.xml
index 8ea0e49..c519174 100644
--- a/tests/integration/tracing-support/pom.xml
+++ b/tests/integration/tracing-support/pom.xml
@@ -43,12 +43,27 @@
             <artifactId>jersey-test-framework-provider-external</artifactId>
             <scope>test</scope>
         </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
         <plugins>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <systemPropertyVariables>
+                        <java.util.logging.config.file>${project.build.testOutputDirectory}/logging.properties</java.util.logging.config.file>
+                    </systemPropertyVariables>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
             </plugin>
             <plugin>
@@ -87,4 +102,34 @@
             </plugin>
         </plugins>
     </build>
+    <profiles>
+        <profile>
+            <id>jdk11+</id>
+            <activation>
+                <jdk>[11,)</jdk>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-compiler-plugin</artifactId>
+                        <configuration>
+                            <!--
+                            Since Jetty 11 for some reason is not possibly to properly set responseHeaderSize property
+                            which should be jetty.response.header.size with adjusted value 16192. The property however
+                            is never recognized by the plugin so resulting in the
+                                            org.eclipse.jetty.http.BadMessageException: 500: Response header too large
+                            exception. For this reason the test is being excluded.
+                              -->
+                            <testExcludes>
+                                <testExclude>
+                                    org/glassfish/jersey/tests/integration/tracing/AllTracingSupportITCase.java
+                                </testExclude>
+                            </testExcludes>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
 </project>
diff --git a/tests/integration/tracing-support/src/test/java/org/glassfish/jersey/tests/integration/tracing/TracingMatchResourceMethodTest.java b/tests/integration/tracing-support/src/test/java/org/glassfish/jersey/tests/integration/tracing/TracingMatchResourceMethodTest.java
new file mode 100644
index 0000000..6afcd91
--- /dev/null
+++ b/tests/integration/tracing-support/src/test/java/org/glassfish/jersey/tests/integration/tracing/TracingMatchResourceMethodTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2024 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.tracing;
+
+import org.glassfish.jersey.message.internal.TracingLogger;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.server.TracingConfig;
+import org.glassfish.jersey.server.internal.ServerTraceEvent;
+import org.glassfish.jersey.test.JerseyTest;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+public class TracingMatchResourceMethodTest extends JerseyTest {
+    GatheringHandler handler = new GatheringHandler();
+    Logger logger;
+
+    @Path("/echo")
+    public static class TracingMatchResourceMethodResource {
+        @Path("echo")
+        @POST
+        public String echo(String entity) {
+            return entity;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(TracingMatchResourceMethodResource.class)
+                .property(ServerProperties.TRACING, TracingConfig.ALL.name())
+                .property(ServerProperties.TRACING_THRESHOLD, TracingLogger.Level.VERBOSE.name());
+    }
+
+    @Test
+    public void testEcho() {
+        logger = Logger.getLogger("org.glassfish.jersey.tracing.general");
+        logger.addHandler(handler);
+
+        try (Response r = target("echo").path("echo").request().post(Entity.entity("ECHO", MediaType.TEXT_PLAIN_TYPE))) {
+            MatcherAssert.assertThat(r.getStatus(), Matchers.equalTo(200));
+            MatcherAssert.assertThat(r.readEntity(String.class), Matchers.equalTo("ECHO"));
+        }
+
+        List<LogRecord> matched = handler.logRecords.stream()
+                .filter(logRecord -> logRecord.getMessage().startsWith(ServerTraceEvent.MATCH_RESOURCE_METHOD.name()))
+                .collect(Collectors.toList());
+        MatcherAssert.assertThat(matched.size(), Matchers.equalTo(1));
+    }
+
+    private static class GatheringHandler extends Handler {
+
+        List<LogRecord> logRecords = new ArrayList<>();
+
+        @Override
+        public void publish(LogRecord record) {
+            logRecords.add(record);
+        }
+
+        @Override
+        public void flush() {
+        }
+
+        @Override
+        public void close() throws SecurityException {
+        }
+    }
+}
diff --git a/tests/osgi/functional/pom.xml b/tests/osgi/functional/pom.xml
index 7f38592..77e9b1b 100644
--- a/tests/osgi/functional/pom.xml
+++ b/tests/osgi/functional/pom.xml
@@ -151,6 +151,12 @@
             <groupId>org.ops4j.pax.exam</groupId>
             <artifactId>pax-exam-container-forked</artifactId>
             <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-api</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
         <dependency>
             <groupId>org.ops4j.pax.exam</groupId>
@@ -170,7 +176,13 @@
         <dependency>
             <groupId>org.ops4j.pax.url</groupId>
             <artifactId>pax-url-aether</artifactId>
-            <version>2.6.2</version>
+            <version>2.6.14</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>jcl-over-slf4j</artifactId>
+                </exclusion>
+            </exclusions>
             <scope>test</scope>
         </dependency>
         <dependency>
@@ -204,7 +216,7 @@
             <groupId>org.ops4j.pax.logging</groupId>
             <artifactId>pax-logging-api</artifactId>
             <scope>test</scope>
-            <version>1.11.5</version>
+            <version>1.11.17</version>
         </dependency>
         <dependency>
             <groupId>org.apache.httpcomponents</groupId>
@@ -305,8 +317,8 @@
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>org.glassfish</groupId>
-            <artifactId>jakarta.el</artifactId>
+            <groupId>org.glassfish.expressly</groupId>
+            <artifactId>expressly</artifactId>
             <scope>test</scope>
         </dependency>
         <dependency>
@@ -371,6 +383,12 @@
             <scope>test</scope>
         </dependency>
 
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <version>${slf4j.version}</version>
+        </dependency>
+
     </dependencies>
 
     <profiles>
diff --git a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/BeanValidationTest.java b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/BeanValidationTest.java
index 7486f72..2c4ba83 100644
--- a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/BeanValidationTest.java
+++ b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/BeanValidationTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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
@@ -72,7 +72,7 @@
                 mavenBundle().groupId("org.jboss.logging").artifactId("jboss-logging").versionAsInProject(),
                 mavenBundle().groupId("com.fasterxml").artifactId("classmate").versionAsInProject(),
                 mavenBundle().groupId("jakarta.el").artifactId("jakarta.el-api").versionAsInProject(),
-                mavenBundle().groupId("org.glassfish").artifactId("jakarta.el").versionAsInProject()
+                mavenBundle().groupId("org.glassfish.expressly").artifactId("expressly").versionAsInProject()
 
         ));
 
diff --git a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonMoxyTest.java b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonMoxyTest.java
index 4b2876b..2ca286c 100644
--- a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonMoxyTest.java
+++ b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonMoxyTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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
@@ -56,14 +56,14 @@
                 mavenBundle().groupId("org.glassfish.jersey.ext").artifactId("jersey-entity-filtering").versionAsInProject(),
                 mavenBundle().groupId("org.eclipse.persistence").artifactId("org.eclipse.persistence.moxy").versionAsInProject(),
                 mavenBundle().groupId("org.eclipse.persistence").artifactId("org.eclipse.persistence.core").versionAsInProject(),
-                mavenBundle().groupId("org.ow2.asm").artifactId("asm").versionAsInProject(),
-                mavenBundle().groupId("jakarta.json").artifactId("jakarta.json-api").versionAsInProject(),
+                mavenBundle().groupId("org.eclipse.persistence").artifactId("org.eclipse.persistence.asm").versionAsInProject(),
+                mavenBundle().groupId("org.eclipse.parsson").artifactId("parsson").versionAsInProject(),
 
                 // validation
                 mavenBundle().groupId("org.hibernate.validator").artifactId("hibernate-validator").versionAsInProject(),
                 mavenBundle().groupId("org.jboss.logging").artifactId("jboss-logging").versionAsInProject(),
                 mavenBundle().groupId("com.fasterxml").artifactId("classmate").versionAsInProject(),
-                mavenBundle().groupId("org.glassfish").artifactId("jakarta.el").versionAsInProject()
+                mavenBundle().groupId("org.glassfish.expressly").artifactId("expressly").versionAsInProject()
         ));
 
         return Helper.asArray(options);
diff --git a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/PackageScanningTest.java b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/PackageScanningTest.java
index c3a2fca..369c2b1 100644
--- a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/PackageScanningTest.java
+++ b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/PackageScanningTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 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
@@ -79,7 +79,8 @@
                         .versionAsInProject(),
 
                 // MBR/MBW for JSON-P is on the classpath.
-                mavenBundle().groupId("jakarta.json").artifactId("jakarta.json-api").versionAsInProject()
+                mavenBundle().groupId("jakarta.json").artifactId("jakarta.json-api").versionAsInProject(),
+                mavenBundle().groupId("org.eclipse.parsson").artifactId("parsson").versionAsInProject()
         ));
 
         options = Helper.addPaxExamMavenLocalRepositoryProperty(options);
diff --git a/tests/performance/jmx-client/src/main/java/org/glassfish/jersey/tests/performance/jmxclient/Main.java b/tests/performance/jmx-client/src/main/java/org/glassfish/jersey/tests/performance/jmxclient/Main.java
index 05391bc..ba18aae 100644
--- a/tests/performance/jmx-client/src/main/java/org/glassfish/jersey/tests/performance/jmxclient/Main.java
+++ b/tests/performance/jmx-client/src/main/java/org/glassfish/jersey/tests/performance/jmxclient/Main.java
@@ -80,6 +80,6 @@
     private static void writeResult(double resultValue, String propertiesFile) throws IOException {
         Properties resultProps = new Properties();
         resultProps.put("YVALUE", Double.toString(resultValue));
-        resultProps.store(Files.newOutputStream(Paths.get(propertiesFile)), null);
+        resultProps.store(Files.newOutputStream(Path.of(propertiesFile)), null);
     }
 }
diff --git a/tests/performance/runners/jersey-grizzly-runner/pom.xml b/tests/performance/runners/jersey-grizzly-runner/pom.xml
index eda85ad..4942065 100644
--- a/tests/performance/runners/jersey-grizzly-runner/pom.xml
+++ b/tests/performance/runners/jersey-grizzly-runner/pom.xml
@@ -49,7 +49,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-jar-plugin</artifactId>
-                <version>2.4</version>
+                <version>${jar.mvn.plugin.version}</version>
                 <configuration>
                     <archive>
                         <index>true</index>
diff --git a/tests/performance/test-cases/monitoring/pom.xml b/tests/performance/test-cases/monitoring/pom.xml
index c4076c2..d5b84bd 100644
--- a/tests/performance/test-cases/monitoring/pom.xml
+++ b/tests/performance/test-cases/monitoring/pom.xml
@@ -37,13 +37,13 @@
         <dependency>
             <groupId>com.yammer.metrics</groupId>
             <artifactId>metrics-core</artifactId>
-            <version>2.1.2</version>
+            <version>2.2.0</version>
         </dependency>
 
         <dependency>
             <groupId>org.junit.jupiter</groupId>
             <artifactId>junit-jupiter</artifactId>
-            <version>5.9.1</version>
+            <version>5.10.2</version>
             <scope>test</scope>
         </dependency>
 
@@ -56,13 +56,13 @@
         <dependency>
             <groupId>commons-codec</groupId>
             <artifactId>commons-codec</artifactId>
-            <version>1.5</version>
+            <version>1.17.0</version>
         </dependency>
 
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-jdk14</artifactId>
-            <version>1.6.1</version>
+            <version>2.0.13</version>
         </dependency>
 
        <dependency>
@@ -97,11 +97,11 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
-                <version>3.1</version>
+                <version>3.13.0</version>
                 <inherited>true</inherited>
                 <configuration>
-                    <source>1.7</source>
-                    <target>1.7</target>
+                    <source>1.8</source>
+                    <target>1.8</target>
                     <showWarnings>false</showWarnings>
                     <fork>false</fork>
                 </configuration>
diff --git a/tests/pom.xml b/tests/pom.xml
index 7445824..acb5d61 100644
--- a/tests/pom.xml
+++ b/tests/pom.xml
@@ -42,6 +42,7 @@
         <module>e2e-core-common</module>
         <module>e2e-entity</module>
         <module>e2e-inject</module>
+        <module>e2e-jdk-specifics</module>
         <module>e2e-server</module>
         <module>e2e-testng</module>
         <module>e2e-tls</module>
diff --git a/tests/release-test/pom.xml b/tests/release-test/pom.xml
index d705728..35d5133 100644
--- a/tests/release-test/pom.xml
+++ b/tests/release-test/pom.xml
@@ -47,7 +47,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-surefire-plugin</artifactId>
-                <version>3.0.0-M7</version>
+                <version>${surefire.mvn.plugin.version}</version>
                 <configuration>
                     <forkCount>1</forkCount>
                     <reuseForks>false</reuseForks>
@@ -117,7 +117,7 @@
         <dependency>
             <groupId>org.apache.maven.plugin-testing</groupId>
             <artifactId>maven-plugin-testing-harness</artifactId>
-            <version>3.3.0</version>
+            <version>${harness.testing.mvn.plugin.version}</version>
             <scope>test</scope>
         </dependency>
         <dependency>
@@ -156,7 +156,7 @@
                     <plugin>
                         <groupId>org.apache.maven.plugins</groupId>
                         <artifactId>maven-surefire-plugin</artifactId>
-                        <version>3.0.0-M7</version>
+                        <version>${surefire.mvn.plugin.version}</version>
                         <configuration>
                             <forkCount>1</forkCount>
                             <reuseForks>false</reuseForks>
diff --git a/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/ClassVersionChecker.java b/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/ClassVersionChecker.java
index 743d609..8a97941 100644
--- a/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/ClassVersionChecker.java
+++ b/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/ClassVersionChecker.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2024 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
@@ -27,7 +27,14 @@
 class ClassVersionChecker {
     static TestResult checkClassVersion(JarFile jar, JarEntry entry, Properties properties) throws IOException {
         final String jerseyVersion = MavenUtil.getJerseyVersion(properties);
-        final int minVersion = jerseyVersion.startsWith("3.1") ? 11 : 8;
+        final int minVersion;
+        if (jerseyVersion.startsWith("4")) {
+            minVersion = 17;
+        } else if (jerseyVersion.startsWith("3.1")) {
+            minVersion = 11;
+        } else {
+            minVersion = 8;
+        }
         return checkClassVersion(jar.getInputStream(entry), jar.getName() + File.separator + entry.getName(), minVersion);
     }
 
diff --git a/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/DependencyResolver.java b/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/DependencyResolver.java
index b1a7ee3..eeea982 100644
--- a/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/DependencyResolver.java
+++ b/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/DependencyResolver.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2024 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
@@ -42,4 +42,28 @@
         request.setRepositories(remoteRepos);
         return repoSystem.resolveArtifact(repoSession, request).getArtifact();
     }
+
+    static Artifact resolveSource(org.apache.maven.model.Dependency d, List<RemoteRepository> remoteRepos,
+                                    RepositorySystem repoSystem, RepositorySystemSession repoSession)
+            throws ArtifactResolutionException {
+        DefaultArtifact artifact = new DefaultArtifact(
+                d.getGroupId(), d.getArtifactId(), "sources", d.getType(), d.getVersion()
+        );
+        ArtifactRequest request = new ArtifactRequest();
+        request.setArtifact(artifact);
+        request.setRepositories(remoteRepos);
+        return repoSystem.resolveArtifact(repoSession, request).getArtifact();
+    }
+
+    static Artifact resolveJavadoc(org.apache.maven.model.Dependency d, List<RemoteRepository> remoteRepos,
+                                  RepositorySystem repoSystem, RepositorySystemSession repoSession)
+            throws ArtifactResolutionException {
+        DefaultArtifact artifact = new DefaultArtifact(
+                d.getGroupId(), d.getArtifactId(), "javadoc", d.getType(), d.getVersion()
+        );
+        ArtifactRequest request = new ArtifactRequest();
+        request.setArtifact(artifact);
+        request.setRepositories(remoteRepos);
+        return repoSystem.resolveArtifact(repoSession, request).getArtifact();
+    }
 }
\ No newline at end of file
diff --git a/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/MavenUtil.java b/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/MavenUtil.java
index 20e7737..98d4616 100644
--- a/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/MavenUtil.java
+++ b/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/MavenUtil.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2024 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
@@ -29,6 +29,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Properties;
+import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 public final class MavenUtil {
@@ -37,7 +38,7 @@
     private static final String PROJECT_VERSION = "project.version";
 
     static File getArtifactJar(File repositoryRoot, Dependency dependency, Properties properties) {
-        return getArtifactFile(repositoryRoot, dependency, properties, "jar");
+        return getArtifactFile(repositoryRoot, dependency, properties, dependency.getType());
     }
 
     private static File getArtifactFile(File repositoryRoot, Dependency dependency, Properties properties, String extension) {
@@ -52,7 +53,11 @@
         }
         String version = MavenUtil.getDependencyVersion(dependency, properties);
         fileSuffix.append(version).append(File.separator);
-        fileSuffix.append(dependency.getArtifactId()).append('-').append(version).append(".").append(extension);
+        fileSuffix.append(dependency.getArtifactId()).append('-').append(version);
+        if (dependency.getClassifier() != null) {
+            fileSuffix.append('-').append(dependency.getClassifier());
+        }
+        fileSuffix.append(".").append(extension);
         return new File(repositoryRoot, fileSuffix.toString());
     }
 
@@ -103,7 +108,16 @@
     static Stream<Dependency> streamJerseyJars() throws IOException, XmlPullParserException {
         Model model = getModelFromFile("pom.xml");
         List<Dependency> deps = getBomPomDependencies(model);
+        return streamJerseyJars(deps);
+    }
 
+    static Stream<Dependency> streamJerseySources() throws IOException, XmlPullParserException {
+        Model model = getModelFromFile("pom.xml");
+        List<Dependency> deps = getBomPomSources(model);
+        return streamJerseyJars(deps);
+    }
+
+    private static Stream<Dependency> streamJerseyJars(List<Dependency> deps) throws IOException, XmlPullParserException {
         return deps.stream()
                 .filter(dep -> dep.getGroupId().startsWith("org.glassfish.jersey"))
                 .filter(dep -> dep.getType().equals("jar"));
@@ -139,6 +153,15 @@
         return bomPomModel.getDependencyManagement().getDependencies();
     }
 
+    private static List<Dependency> getBomPomSources(Model model) throws XmlPullParserException, IOException {
+        return getBomPomDependencies(model).stream()
+                .map(dependency -> {
+                    dependency.setClassifier("sources");
+                    return dependency;
+                })
+                .collect(Collectors.toList());
+    }
+
     static String getJerseyVersion(Properties properties) {
         String property = properties.getProperty(JERSEY_VERSION); // when it is in the pom.file
         if (property == null || property.startsWith("${")) {
diff --git a/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/ArchetypesTest.java b/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/ArchetypesTest.java
index b42830b..5be312c 100644
--- a/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/ArchetypesTest.java
+++ b/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/ArchetypesTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2024 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
@@ -52,16 +52,21 @@
                     continue;
                 }
                 // Update the names with the ones in Jersey
-                Map.Entry<Object, Object> updatedEntry = updateEntry(pomEntry);
                 // Check the properties are there
-                if (properties.getProperty(updatedEntry.getKey().toString()) == null) {
+                final String key = pomEntry.getKey().toString();
+
+                if (properties.getProperty(key) == null) {
                     testResult.ok().append("Property ")
                             .append(pomEntry.getKey().toString())
                             .append(" from ").append(pom).println(" not in Jersey");
                     failed = true;
                 }
                 // check the values
-                else if (!properties.getProperty(updatedEntry.getKey().toString()).equals(updatedEntry.getValue())) {
+                else if (
+                        //archetype property value can be a variable from the main pom.xml - check and exclude if so
+                        !(properties.containsKey(key) && pomEntry.getValue().toString().contains(key))
+                        && !properties.getProperty(key).equals(pomEntry.getValue())
+                ) {
                     testResult.exception().append("The property ")
                             .append(pomEntry.getKey().toString())
                             .append(" in archetype pom ")
@@ -81,25 +86,4 @@
         }
     }
 
-    private Map.Entry<Object, Object> updateEntry(Map.Entry<Object, Object> pomEntry) {
-        if (pomEntry.getKey().equals("junit-jupiter.version")) {
-            return new Map.Entry<Object, Object>() {
-                @Override
-                public Object getKey() {
-                    return "junit5.version";
-                }
-
-                @Override
-                public Object getValue() {
-                    return pomEntry.getValue();
-                }
-
-                @Override
-                public Object setValue(Object value) {
-                    return value;
-                }
-            };
-        }
-        return pomEntry;
-    }
 }
diff --git a/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/DownloadBomPomDependencies.java b/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/DownloadBomPomDependencies.java
index 99c94ee..6a74f07 100644
--- a/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/DownloadBomPomDependencies.java
+++ b/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/DownloadBomPomDependencies.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2024 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,6 +58,12 @@
             Artifact m = mavenEnvironment.resolveArtifact(member);
             System.out.append("Resolved ").append(member.getGroupId()).append(":").append(member.getArtifactId()).append(":")
                     .append(member.getVersion()).append(" to ").println(m.getFile().getName());
+            m = mavenEnvironment.resolveSource(member);
+            System.out.append("Resolved sources ").append(member.getGroupId()).append(":").append(member.getArtifactId())
+                    .append(":").append(member.getVersion()).append(" to ").println(m.getFile().getName());
+            m = mavenEnvironment.resolveJavadoc(member);
+            System.out.append("Resolved javadoc ").append(member.getGroupId()).append(":").append(member.getArtifactId())
+                    .append(":").append(member.getVersion()).append(" to ").println(m.getFile().getName());
         }
     }
 
@@ -74,6 +80,14 @@
             System.out.append("Resolved ").append(dependency.getGroupId()).append(":")
                     .append(dependency.getArtifactId()).append(":")
                     .append(dependency.getVersion()).append(" to ").println(m.getFile().getName());
+            m = mavenEnvironment.resolveSource(dependency);
+            System.out.append("Resolved source ").append(dependency.getGroupId()).append(":")
+                    .append(dependency.getArtifactId()).append(":")
+                    .append(dependency.getVersion()).append(" to ").println(m.getFile().getName());
+            m = mavenEnvironment.resolveJavadoc(dependency);
+            System.out.append("Resolved javadoc ").append(dependency.getGroupId()).append(":")
+                    .append(dependency.getArtifactId()).append(":")
+                    .append(dependency.getVersion()).append(" to ").println(m.getFile().getName());
         }
     }
 
@@ -102,6 +116,16 @@
             return DependencyResolver.resolveArtifact(dependency, remoteRepos, repositorySystem, repoSession);
         }
 
+        Artifact resolveSource(Dependency dependency) throws ArtifactResolutionException {
+            dependency.setVersion(jerseyVersion);
+            return DependencyResolver.resolveSource(dependency, remoteRepos, repositorySystem, repoSession);
+        }
+
+        Artifact resolveJavadoc(Dependency dependency) throws ArtifactResolutionException {
+            dependency.setVersion(jerseyVersion);
+            return DependencyResolver.resolveJavadoc(dependency, remoteRepos, repositorySystem, repoSession);
+        }
+
         private List<RemoteRepository> getRemoteRepositories() throws Exception {
             MavenProject project = getMavenProjectForResourceFile("/release-test-pom.xml");
             List<RemoteRepository> remoteArtifactRepositories = project.getRemoteProjectRepositories();
diff --git a/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/LegalDocsIncludedTest.java b/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/LegalDocsIncludedTest.java
index 74e0281..8c784af 100644
--- a/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/LegalDocsIncludedTest.java
+++ b/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/LegalDocsIncludedTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2024 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,7 +41,15 @@
         List<File> jars = MavenUtil.streamJerseyJars()
                 .map(dependency -> MavenUtil.getArtifactJar(localRepository, dependency, properties))
                 .collect(Collectors.toList());
+        testLegalFiles(jars, testResult);
 
+        jars = MavenUtil.streamJerseySources()
+                .map(dependency -> MavenUtil.getArtifactJar(localRepository, dependency, properties))
+                .collect(Collectors.toList());
+        testLegalFiles(jars, testResult);
+    }
+
+    private void testLegalFiles(List<File> jars, TestResult testResult) throws IOException {
         for (File jar : jars) {
             for (String filename : new String[]{LICENSE_FILE, NOTICE_FILE}) {
                 JarFile jarFile = new JarFile(jar);
diff --git a/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/ManifestTest.java b/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/ManifestTest.java
index 0c77d69..a520868 100644
--- a/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/ManifestTest.java
+++ b/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/ManifestTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2024 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
@@ -25,6 +25,7 @@
 import java.util.Properties;
 import java.util.jar.JarFile;
 import java.util.stream.Collectors;
+import java.util.zip.ZipEntry;
 
 public class ManifestTest {
     private static final File localRepository = MavenUtil.getLocalMavenRepository();
@@ -63,6 +64,25 @@
             }
         }
 
+        for (File jar : jars) {
+            JarFile jarFile = new JarFile(jar);
+            String value = jarFile.getManifest().getMainAttributes().getValue("Multi-Release");
+//            System.out.append("Accessing META-INF/versions").append(" of ").println(jar.getName());
+            ZipEntry versions = jarFile.getEntry("META-INF/versions/");
+            if (versions != null) {
+                if (!"true".equals(value)) {
+                    testResult.exception().append("'Multi-Release: true' not set for ").println(jar.getName());
+                } else {
+                    testResult.ok().append("'Multi-Release: true' set for ").println(jar.getName());
+                }
+            } else {
+                if ("true".equals(value)) {
+                    testResult.exception().append("'Multi-Release: true' SET for ").println(jar.getName());
+                }
+            }
+
+        }
+
         //Assertions.assertTrue(testResult.result(), "Some error occurred, see previous messages");
         Assert.assertTrue("Some error occurred, see previous messages", testResult.result());
     }
diff --git a/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/MultiReleaseTest.java b/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/MultiReleaseTest.java
index 08be020..11a417e 100644
--- a/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/MultiReleaseTest.java
+++ b/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/MultiReleaseTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2024 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,12 +40,14 @@
     private static final DependencyPair[] jdk11multiRelease = jdk11multiRelease(properties);
     private static final DependencyPair[] jdk12multiRelease = jdk12multiRelease(properties);
     private static final DependencyPair[] jdk17multiRelease = jdk17multiRelease(properties);
+    private static final DependencyPair[] jdk21multiRelease = jdk21multiRelease(properties);
 
     @Test
     public void testIsJdkMultiRelease() throws IOException, XmlPullParserException {
         TestResult result = testJdkVersions("11", jdk11multiRelease);
         result.append(testJdkVersions("12", jdk12multiRelease));
         result.append(testJdkVersions("17", jdk17multiRelease));
+        result.append(testJdkVersions("21", jdk21multiRelease));
         //Assertions.assertTrue(result.result(), "Some error occurred, see previous messages");
         Assert.assertTrue("Some error occurred, see previous messages", result.result());
     }
@@ -54,6 +56,7 @@
             throws XmlPullParserException, IOException {
         final TestResult result = new TestResult();
         if (dependencies == null || dependencies.length == 0) {
+            System.out.append("No dependencies found for jdk ").println(version);
             return result;
         }
 
@@ -81,6 +84,7 @@
                 result.exception().append("Not a multirelease jar ").append(jar.getName()).println("!");
             }
             ZipEntry versions = jarFile.getEntry("META-INF/versions/" + version);
+            System.out.append("Accessing META-INF/versions/").append(version).append(" of ").println(jar.getName());
             if (versions == null) {
                 result.exception().append("No classes for JDK ").append(version).append(" for ").println(jar.getName());
             }
@@ -95,6 +99,26 @@
             result.append(ClassVersionChecker.checkClassVersion(jarFile, jarEntry, properties));
         }
 
+        // Verify that number of multirelease jars matches the expected dependencies
+        StringBuilder multi = new StringBuilder();
+        int multiCnt = 0;
+        List<File> allFiles = MavenUtil.streamJerseyJars()
+                .map(dependency -> MavenUtil.getArtifactJar(localRepository, dependency, properties))
+                .collect(Collectors.toList());
+        for (File jar : files) {
+            JarFile jarFile = new JarFile(jar);
+            if (jarFile.isMultiRelease()) {
+                multiCnt++;
+                multi.append("Multirelease jar ").append(jar.getName()).append('\n');
+            }
+        }
+        if (files.size() == multiCnt) {
+            result.ok().println("There is expected number of multirelease jars");
+        } else {
+            result.exception().println("There is unexpected number of multirelease jars:");
+            result.exception().append(multi).println("");
+        }
+
         return result;
     }
 
@@ -136,14 +160,40 @@
 
     private static DependencyPair[] jdk17multiRelease(Properties properties) {
         String jerseyVersion = MavenUtil.getJerseyVersion(properties);
-        if (jerseyVersion.startsWith("3")) {
+        if (jerseyVersion.startsWith("3.0")) {
             return new DependencyPair[] {
                     new DependencyPair("org.glassfish.jersey.connectors", "jersey-helidon-connector"),
                     new DependencyPair("org.glassfish.jersey.connectors", "jersey-jetty-connector"),
                     new DependencyPair("org.glassfish.jersey.containers", "jersey-container-jetty-http"),
                     new DependencyPair("org.glassfish.jersey.ext", "jersey-spring6")
             };
+        } else if (jerseyVersion.startsWith("3")) {
+            return new DependencyPair[] {
+                    new DependencyPair("org.glassfish.jersey.connectors", "jersey-helidon-connector"),
+                    new DependencyPair("org.glassfish.jersey.connectors", "jersey-jetty-connector"),
+                    new DependencyPair("org.glassfish.jersey.connectors", "jersey-jetty-http2-connector"),
+                    new DependencyPair("org.glassfish.jersey.containers", "jersey-container-jetty-http"),
+                    new DependencyPair("org.glassfish.jersey.containers", "jersey-container-jetty-http2"),
+                    new DependencyPair("org.glassfish.jersey.test-framework.providers", "jersey-test-framework-provider-jetty"),
+                    new DependencyPair("org.glassfish.jersey.test-framework.providers",
+                            "jersey-test-framework-provider-jetty-http2"),
+                    new DependencyPair("org.glassfish.jersey.ext", "jersey-spring6")
+            };
         }
         return new DependencyPair[]{};
     }
+
+    private static DependencyPair[] jdk21multiRelease(Properties properties) {
+        String jerseyVersion = MavenUtil.getJerseyVersion(properties);
+        if (jerseyVersion.startsWith("4")) {
+            return new DependencyPair[]{
+                    new DependencyPair("org.glassfish.jersey.core", "jersey-common")
+            };
+        } else {
+            return new DependencyPair[]{
+                    new DependencyPair("org.glassfish.jersey.bundles", "jaxrs-ri"),
+                    new DependencyPair("org.glassfish.jersey.core", "jersey-common")
+            };
+        }
+    }
 }
diff --git a/tests/release-test/src/test/resources/non-bom-pom-deps.xml b/tests/release-test/src/test/resources/non-bom-pom-deps.xml
index 7ced6b0..e3aca57 100644
--- a/tests/release-test/src/test/resources/non-bom-pom-deps.xml
+++ b/tests/release-test/src/test/resources/non-bom-pom-deps.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
 
-    Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2023, 2024 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
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.eclipse.ee4j</groupId>
         <artifactId>project</artifactId>
-        <version>1.0.8</version>
+        <version>1.0.9</version>
     </parent>
 
     <groupId>org.glassfish.jersey.tests</groupId>
diff --git a/tests/release-test/src/test/resources/release-test-pom.xml b/tests/release-test/src/test/resources/release-test-pom.xml
index 4fac652..58966b3 100644
--- a/tests/release-test/src/test/resources/release-test-pom.xml
+++ b/tests/release-test/src/test/resources/release-test-pom.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
 
-    Copyright (c) 2022, 2023 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2022, 2024 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
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.eclipse.ee4j</groupId>
         <artifactId>project</artifactId>
-        <version>1.0.8</version>
+        <version>1.0.9</version>
     </parent>
 
     <groupId>org.glassfish.jersey.tests</groupId>
diff --git a/tests/version-agnostic/pom.xml b/tests/version-agnostic/pom.xml
index aad0411..552ac47 100644
--- a/tests/version-agnostic/pom.xml
+++ b/tests/version-agnostic/pom.xml
@@ -38,7 +38,7 @@
         <maven.compiler.source>8</maven.compiler.source>
         <maven.compiler.target>8</maven.compiler.target>
         <hk2.version>3.0.3</hk2.version>
-        <jersey.version>3.5.99-SNAPSHOT</jersey.version>
+        <jersey.version>3.1.6</jersey.version>
     </properties>
 
     <build>
diff --git a/tests/version-agnostic/src/test/java/org/glassfish/jersey/test/agnostic/JettyHeaderTest.java b/tests/version-agnostic/src/test/java/org/glassfish/jersey/test/agnostic/JettyHeaderTest.java
index e720080..4eddfe4 100644
--- a/tests/version-agnostic/src/test/java/org/glassfish/jersey/test/agnostic/JettyHeaderTest.java
+++ b/tests/version-agnostic/src/test/java/org/glassfish/jersey/test/agnostic/JettyHeaderTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2024 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/tools/jersey-doc-modulelist-maven-plugin/pom.xml b/tools/jersey-doc-modulelist-maven-plugin/pom.xml
index 53df1f1..876adef 100644
--- a/tools/jersey-doc-modulelist-maven-plugin/pom.xml
+++ b/tools/jersey-doc-modulelist-maven-plugin/pom.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
 
-    Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2013, 2024 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
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.eclipse.ee4j</groupId>
         <artifactId>project</artifactId>
-        <version>1.0.8</version>
+        <version>1.0.9</version>
     </parent>
 
     <groupId>org.glassfish.jersey.tools.plugins</groupId>
diff --git a/tools/jersey-release-notes-maven-plugin/pom.xml b/tools/jersey-release-notes-maven-plugin/pom.xml
index e1f8091..fa51932 100644
--- a/tools/jersey-release-notes-maven-plugin/pom.xml
+++ b/tools/jersey-release-notes-maven-plugin/pom.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
 
-    Copyright (c) 2019, 2022 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2019, 2024 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
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.eclipse.ee4j</groupId>
         <artifactId>project</artifactId>
-        <version>1.0.8</version>
+        <version>1.0.9</version>
     </parent>
 
     <groupId>org.glassfish.jersey.tools.plugins</groupId>
@@ -67,7 +67,7 @@
         <dependency>
             <groupId>org.apache.maven.plugin-testing</groupId>
             <artifactId>maven-plugin-testing-harness</artifactId>
-            <version>3.3.0</version>
+            <version>${harness.testing.mvn.plugin.version}</version>
             <scope>test</scope>
         </dependency>
 
diff --git a/tools/jersey-release-notes-maven-plugin/src/main/java/org/glassfish/jersey/tools/plugins/releasenotes/ReleaseNotesMojo.java b/tools/jersey-release-notes-maven-plugin/src/main/java/org/glassfish/jersey/tools/plugins/releasenotes/ReleaseNotesMojo.java
index eec3df2..1ddf887 100644
--- a/tools/jersey-release-notes-maven-plugin/src/main/java/org/glassfish/jersey/tools/plugins/releasenotes/ReleaseNotesMojo.java
+++ b/tools/jersey-release-notes-maven-plugin/src/main/java/org/glassfish/jersey/tools/plugins/releasenotes/ReleaseNotesMojo.java
@@ -33,7 +33,7 @@
 import java.io.IOException;
 import java.nio.charset.Charset;
 import java.nio.file.Files;
-import java.nio.file.Paths;
+import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -172,12 +172,12 @@
                                           String releaseVersion, String releaseDate,
                                           String releaseNotesFilePath,
                                           Boolean dryRun, Log log) throws IOException {
-        if (Files.notExists(Paths.get(templateFilePath))) {
+        if (Files.notExists(Path.of(templateFilePath))) {
             log.warn(String.format("There is no source template file at the given location:%s", templateFilePath));
             return;
         }
         final List<String> notesLines = new ArrayList<>();
-        final List<String> lines = Files.readAllLines(Paths.get(templateFilePath), Charset.defaultCharset());
+        final List<String> lines = Files.readAllLines(Path.of(templateFilePath), Charset.defaultCharset());
         for (final String line : lines) {
             if (line.contains(RELEASE_DATE_PATTERN)) {
                 notesLines.add(line.replace(RELEASE_DATE_PATTERN, releaseDate));
@@ -197,8 +197,8 @@
         }
         if (Boolean.FALSE.equals(dryRun)) {
             log.info(String.format("Storing release notes to file %s/%s.html", releaseNotesFilePath, releaseVersion));
-            Files.createDirectories(Paths.get(releaseNotesFilePath));
-            Files.write(Paths.get(String.format("%s/%s.html", releaseNotesFilePath, releaseVersion)), notesLines, Charset.defaultCharset());
+            Files.createDirectories(Path.of(releaseNotesFilePath));
+            Files.write(Path.of(String.format("%s/%s.html", releaseNotesFilePath, releaseVersion)), notesLines, Charset.defaultCharset());
         } else {
             log.info("Prepared release notes are not stored to file due to dryRun mode");
             log.info(String.format("File path to store release notes is: %s/%s.html", releaseNotesFilePath, releaseVersion));