merge of the actual 2.x into the 3.0

diff --git a/NOTICE.md b/NOTICE.md
index 3596992..2818e63 100644
--- a/NOTICE.md
+++ b/NOTICE.md
@@ -70,7 +70,7 @@
 * Project: http://www.javassist.org/

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

 

-Jackson JAX-RS Providers Version 2.17.0

+Jackson JAX-RS Providers Version 2.17.1

 * License: Apache License, 2.0

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

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

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 dc88fe1..af777dd 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
@@ -127,6 +127,5 @@
     <properties>
         <java.version>1.8</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 176eefd..a3b4e18 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 00afb9c..3afa9e3 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>11.0.20</jetty.version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-        <surefire.mvn.plugin.version>3.2.5</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 c777ef4..b0a9775 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>2.5</version>
+                <version>${resources.mvn.plugin.version}</version>
                 <configuration>
                     <escapeString>\</escapeString>
                 </configuration>
diff --git a/archetypes/jersey-quickstart-grizzly2/src/main/resources/META-INF/archetype.xml b/archetypes/jersey-quickstart-grizzly2/src/main/resources/META-INF/archetype.xml
deleted file mode 100644
index 8a4f997..0000000
--- a/archetypes/jersey-quickstart-grizzly2/src/main/resources/META-INF/archetype.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-
-    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
-
-    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
-
--->
-
-<archetype>
-  <id>jersey-quickstart-grizzly2</id>
-  <sources>
-    <source>src/main/java/Main.java</source>
-    <source>src/main/java/MyResource.java</source>
-  </sources>
-  <testSources>
-    <source>src/test/java/MyResourceTest.java</source>
-  </testSources>
-</archetype>
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
new file mode 100644
index 0000000..54380e5
--- /dev/null
+++ b/archetypes/jersey-quickstart-grizzly2/src/main/resources/META-INF/maven/archetype-metadata.xml
@@ -0,0 +1,42 @@
+<?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
+
+-->
+
+<archetype-descriptor
+        xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0 http://maven.apache.org/xsd/archetype-descriptor-1.0.0.xsd"
+        name="jersey-quickstart-webapp"
+        xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+    <fileSets>
+        <fileSet filtered="true" packaged="true" encoding="UTF-8">
+            <directory>src/main/java</directory>
+            <includes>
+                <include>**/*</include>
+            </includes>
+        </fileSet>
+        <!--<fileSet filtered="true" encoding="UTF-8">
+            <directory>src/main/webapp</directory>
+        </fileSet>-->
+        <fileSet filtered="true" packaged="true" encoding="UTF-8">
+            <directory>src/test/java</directory>
+            <includes>
+                <include>**/*</include>
+            </includes>
+        </fileSet>
+    </fileSets>
+</archetype-descriptor>
\ No newline at end of file
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 779f080..087417f 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,7 +50,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>1.8</source>
@@ -60,7 +60,7 @@
             <plugin>
                 <groupId>org.codehaus.mojo</groupId>
                 <artifactId>exec-maven-plugin</artifactId>
-                <version>1.2.1</version>
+                <version>${exec.mvn.plugin.version}</version>
                 <executions>
                     <execution>
                         <goals>
@@ -83,8 +83,7 @@
 
     <properties>
         <jersey.version>${project.version}</jersey.version>
-        <junit-jupiter.version>5.10.2</junit-jupiter.version>
+        <junit5.version>${junit5.version}</junit5.version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-        <surefire.mvn.plugin.version>3.2.5</surefire.mvn.plugin.version>
     </properties>
 </project>
diff --git a/archetypes/jersey-quickstart-webapp/pom.xml b/archetypes/jersey-quickstart-webapp/pom.xml
index 33b1eca..32e2969 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>2.5</version>
+                <version>${resources.mvn.plugin.version}</version>
                 <configuration>
                     <escapeString>\</escapeString>
                 </configuration>
diff --git a/archetypes/jersey-quickstart-webapp/src/main/resources/META-INF/archetype.xml b/archetypes/jersey-quickstart-webapp/src/main/resources/META-INF/archetype.xml
deleted file mode 100644
index cea1eea..0000000
--- a/archetypes/jersey-quickstart-webapp/src/main/resources/META-INF/archetype.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-
-    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
-
-    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
-
--->
-
-<archetype>
-  <id>jersey-quickstart-webapp</id>
-  <sources>
-    <source>src/main/java/MyResource.java</source>
-  </sources>
-  <resources>
-    <resource>src/main/webapp/index.jsp</resource>
-    <resource>src/main/webapp/WEB-INF/web.xml</resource>
-  </resources>
-</archetype>
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
new file mode 100644
index 0000000..bea65b3
--- /dev/null
+++ b/archetypes/jersey-quickstart-webapp/src/main/resources/META-INF/maven/archetype-metadata.xml
@@ -0,0 +1,42 @@
+<?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
+
+-->
+
+<archetype-descriptor
+        xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0 http://maven.apache.org/xsd/archetype-descriptor-1.0.0.xsd"
+        name="jersey-quickstart-webapp"
+        xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+    <fileSets>
+        <fileSet filtered="true" packaged="true" encoding="UTF-8">
+            <directory>src/main/java</directory>
+        </fileSet>
+        <fileSet filtered="true" encoding="UTF-8">
+            <directory>src/main/webapp</directory>
+            <includes>
+                <include>**/*</include>
+            </includes>
+        </fileSet>
+        <fileSet filtered="true" packaged="true" encoding="UTF-8">
+            <directory>src/test/java</directory>
+            <includes>
+                <include>**/*</include>
+            </includes>
+        </fileSet>
+    </fileSets>
+</archetype-descriptor>
\ No newline at end of file
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 fc70d51..341fb67 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.8.1</version>
+                <version>${compiler.mvn.plugin.version}</version>
                 <inherited>true</inherited>
                 <configuration>
                     <source>1.8</source>
@@ -63,6 +63,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/bundles/apidocs/pom.xml b/bundles/apidocs/pom.xml
index 709d10c..b7c9921 100644
--- a/bundles/apidocs/pom.xml
+++ b/bundles/apidocs/pom.xml
@@ -125,7 +125,7 @@
         <dependency>
             <groupId>org.glassfish</groupId>
             <artifactId>javax.servlet</artifactId>
-            <version>3.1</version>
+            <version>3.1.1</version>
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
diff --git a/bundles/jaxrs-ri/pom.xml b/bundles/jaxrs-ri/pom.xml
index d3eb536..e6a7a7f 100644
--- a/bundles/jaxrs-ri/pom.xml
+++ b/bundles/jaxrs-ri/pom.xml
@@ -424,7 +424,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>
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 d53401e..dd3c4c4 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
@@ -60,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;
@@ -890,7 +891,7 @@
         }
     }
 
-    private static class CancellableInputStream extends InputStream {
+    private static class CancellableInputStream extends InputStreamWrapper {
         private final InputStream in;
         private final Supplier<Boolean> isCancelled;
 
@@ -899,58 +900,17 @@
             this.isCancelled = isCancelled;
         }
 
-        public int read(byte b[]) throws IOException {
-            checkAborted();
-            return in.read();
-        }
-
-        public int read(byte b[], int off, int len) throws IOException {
-            checkAborted();
-            return in.read(b, off, len);
+        @Override
+        protected InputStream getWrapped() {
+            return in;
         }
 
         @Override
-        public int read() throws IOException {
-            checkAborted();
-            return in.read();
-        }
-
-        public boolean markSupported() {
-            return in.markSupported();
-        }
-
-        @Override
-        public long skip(long n) throws IOException {
-            checkAborted();
-            return in.skip(n);
-        }
-
-        @Override
-        public int available() throws IOException {
-            checkAborted();
-            return in.available();
-        }
-
-        @Override
-        public void close() throws IOException {
-            in.close();
-        }
-
-        @Override
-        public void mark(int readlimit) {
-            in.mark(readlimit);
-        }
-
-        @Override
-        public void reset() throws IOException {
-            checkAborted();
-            in.reset();
-        }
-
-        private void checkAborted() throws IOException {
+        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 56f8dd5..d797f11 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
@@ -105,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;
@@ -521,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()));
                 }
 
@@ -894,7 +895,7 @@
         }
     }
 
-    private static class CancellableInputStream extends InputStream {
+    private static class CancellableInputStream extends InputStreamWrapper {
         private final InputStream in;
         private final Supplier<Boolean> isCancelled;
 
@@ -903,58 +904,17 @@
             this.isCancelled = isCancelled;
         }
 
-        public int read(byte b[]) throws IOException {
-            checkAborted();
-            return in.read();
-        }
-
-        public int read(byte b[], int off, int len) throws IOException {
-            checkAborted();
-            return in.read(b, off, len);
+        @Override
+        protected InputStream getWrapped() {
+            return in;
         }
 
         @Override
-        public int read() throws IOException {
-            checkAborted();
-            return in.read();
-        }
-
-        public boolean markSupported() {
-            return in.markSupported();
-        }
-
-        @Override
-        public long skip(long n) throws IOException {
-            checkAborted();
-            return in.skip(n);
-        }
-
-        @Override
-        public int available() throws IOException {
-            checkAborted();
-            return in.available();
-        }
-
-        @Override
-        public void close() throws IOException {
-            in.close();
-        }
-
-        @Override
-        public void mark(int readlimit) {
-            in.mark(readlimit);
-        }
-
-        @Override
-        public void reset() throws IOException {
-            checkAborted();
-            in.reset();
-        }
-
-        private void checkAborted() throws IOException {
+        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 515e3b3..8453f9b 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/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 4070300..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
@@ -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 7dccd87..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
@@ -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/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 5d1d2f2..adf8e03 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
@@ -99,6 +99,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"));
        }
@@ -187,21 +189,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/NettyConnector.java b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java
index 4fbec4c..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
@@ -89,6 +89,9 @@
 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");
@@ -525,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/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 147ce8c..abde2ac 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
@@ -318,13 +318,33 @@
         final Response.Status badRequest = Response.Status.BAD_REQUEST;
         if (webComponent.configSetStatusOverSendError) {
             response.reset();
-            //noinspection deprecation
-            response.setStatus(badRequest.getStatusCode(), badRequest.getReasonPhrase());
+            setStatus(response, badRequest.getStatusCode(), badRequest.getReasonPhrase());
         } else {
             response.sendError(badRequest.getStatusCode(), badRequest.getReasonPhrase());
         }
     }
 
+    /**
+     * <p>
+     *     Set status and reason-phrase if the API still contains the method. Otherwise, only a status is sent.
+     * </p>
+     * <p>
+     *     It can happen the Servlet 6 API is used and the method is not there any longer. A proprietary API can be used,
+     *     or the class is transformed to Jakarta using some transformer means.
+     * </p>
+     * @param response the servlet {@link HttpServletResponse}
+     * @param statusCode the status code
+     * @param reasonPhrase the reason phrase
+     */
+    public static void setStatus(HttpServletResponse response, int statusCode, String reasonPhrase) {
+        try {
+            // noinspection deprecation
+            response.setStatus(statusCode, reasonPhrase);
+        } catch (NoSuchMethodError noSuchMethodError) {
+            response.setStatus(statusCode);
+        }
+    }
+
     @Override
     public void destroy() {
         super.destroy();
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 59b1da9..6e795f1 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, 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
@@ -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;
@@ -401,8 +404,7 @@
 
             if (configSetStatusOverSendError) {
                 servletResponse.reset();
-                //noinspection deprecation
-                servletResponse.setStatus(status.getStatusCode(), status.getReasonPhrase());
+                ServletContainer.setStatus(servletResponse, status.getStatusCode(), status.getReasonPhrase());
             } else {
                 servletResponse.sendError(status.getStatusCode(), status.getReasonPhrase());
             }
@@ -413,7 +415,7 @@
     }
 
     /**
-     * Initialize {@code ContainerRequest} instance to used used to handle {@code servletRequest}.
+     * Initialize {@code ContainerRequest} instance to used to handle {@code servletRequest}.
      */
     private void initContainerRequest(
             final ContainerRequest requestContext,
@@ -421,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 31b8f93..5188a1a 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
@@ -37,6 +37,7 @@
 import org.glassfish.jersey.server.ContainerResponse;
 import org.glassfish.jersey.server.internal.JerseyRequestTimeoutHandler;
 import org.glassfish.jersey.server.spi.ContainerResponseWriter;
+import org.glassfish.jersey.servlet.ServletContainer;
 import org.glassfish.jersey.servlet.spi.AsyncContextDelegate;
 
 /**
@@ -144,7 +145,7 @@
 
         final String reasonPhrase = responseContext.getStatusInfo().getReasonPhrase();
         if (reasonPhrase != null) {
-            response.setStatus(responseContext.getStatus(), reasonPhrase);
+            ServletContainer.setStatus(response, responseContext.getStatus(), reasonPhrase);
         } else {
             response.setStatus(responseContext.getStatus());
         }
@@ -214,12 +215,12 @@
         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(), "Request failed.");
+                        ServletContainer.setStatus(response, statusCode, "Request failed.");
                     } 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/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 b5b28f3..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
@@ -66,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;
@@ -83,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 = {
@@ -114,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();
@@ -143,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.
@@ -342,16 +355,23 @@
         }
     }
 
-    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 {
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/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-common/pom.xml b/core-common/pom.xml
index 56f0d05..46315aa 100644
--- a/core-common/pom.xml
+++ b/core-common/pom.xml
@@ -674,7 +674,6 @@
                     <plugin>
                         <groupId>org.apache.maven.plugins</groupId>
                         <artifactId>maven-source-plugin</artifactId>
-                        <version>3.0.1</version>
                         <executions>
                             <execution>
                                 <id>attach-sources</id>
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/message/internal/ReaderInterceptorExecutor.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderInterceptorExecutor.java
index 91738c1..a0e5dfc 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
@@ -38,6 +38,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,7 +249,7 @@
      * {@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;
@@ -259,43 +260,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 +270,6 @@
                 LOGGER.log(Level.FINE, LocalizationMessages.MBR_TRYING_TO_CLOSE_STREAM(reader.getClass()));
             }
         }
-
-        private InputStream unwrap() {
-            return original;
-        }
     }
 
     /**
@@ -320,7 +282,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-server/pom.xml b/core-server/pom.xml
index f0af2e8..263ab48 100644
--- a/core-server/pom.xml
+++ b/core-server/pom.xml
@@ -233,7 +233,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>
 
@@ -247,6 +253,16 @@
 
     <profiles>
         <profile>
+            <id>jdk8</id>
+            <activation>
+                <jdk>1.8</jdk>
+            </activation>
+            <properties>
+                <jboss.vfs.version>${jboss.vfs.jdk8.version}</jboss.vfs.version>
+                <jboss.logging.version>${jboss.logging.8.version}</jboss.logging.version>
+            </properties>
+        </profile>
+        <profile>
             <id>securityOff</id>
             <properties>
                 <surefire.security.argline />
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..8c116ca 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
@@ -24,6 +24,7 @@
 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 +110,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/wadl/internal/WadlResource.java b/core-server/src/main/java/org/glassfish/jersey/server/wadl/internal/WadlResource.java
index 5a7188a..9867681 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,7 @@
 import java.net.URI;
 import java.text.SimpleDateFormat;
 import java.util.Date;
+import java.util.Locale;
 
 import jakarta.ws.rs.GET;
 import jakarta.ws.rs.Path;
@@ -62,7 +63,7 @@
 
 
     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) {
@@ -81,7 +82,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);
diff --git a/examples/NOTICE.md b/examples/NOTICE.md
index e982511..034205c 100644
--- a/examples/NOTICE.md
+++ b/examples/NOTICE.md
@@ -71,7 +71,7 @@
 * Project: http://www.javassist.org/
 * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
 
-Jackson JAX-RS Providers Version 2.17.0
+Jackson JAX-RS Providers Version 2.17.1
 * 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/examples/extended-wadl-webapp/pom.xml b/examples/extended-wadl-webapp/pom.xml
index 3feed37..0b5f9f2 100644
--- a/examples/extended-wadl-webapp/pom.xml
+++ b/examples/extended-wadl-webapp/pom.xml
@@ -109,7 +109,7 @@
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-log4j12</artifactId>
-            <version>1.6.4</version>
+            <version>2.0.13</version>
             <scope>test</scope>
         </dependency>
 
diff --git a/examples/groovy/pom.xml b/examples/groovy/pom.xml
index 8d7a370..0252257 100644
--- a/examples/groovy/pom.xml
+++ b/examples/groovy/pom.xml
@@ -140,7 +140,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/jaxb/pom.xml b/examples/jaxb/pom.xml
index f8e72a7..4430309 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/osgi-http-service/functional-test/pom.xml b/examples/osgi-http-service/functional-test/pom.xml
index 5ec3b1c..05b2447 100644
--- a/examples/osgi-http-service/functional-test/pom.xml
+++ b/examples/osgi-http-service/functional-test/pom.xml
@@ -145,7 +145,7 @@
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-log4j12</artifactId>
-            <version>1.6.4</version>
+            <version>2.0.13</version>
             <scope>test</scope>
         </dependency>
 
diff --git a/examples/pom.xml b/examples/pom.xml
index 1798d4f..8d25b08 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -182,7 +182,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>
@@ -199,7 +199,7 @@
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-resources-plugin</artifactId>
-                    <version>2.6</version>
+                    <version>${resources.mvn.plugin.version}</version>
                     <!-- Add legal information, NOTICE.md and LINCENSE.md to jars -->
                     <executions>
                         <execution>
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/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/incubator/declarative-linking/pom.xml b/incubator/declarative-linking/pom.xml
index 181bb67..cdb5680 100644
--- a/incubator/declarative-linking/pom.xml
+++ b/incubator/declarative-linking/pom.xml
@@ -88,7 +88,7 @@
         <dependency>
             <groupId>org.skyscreamer</groupId>
             <artifactId>jsonassert</artifactId>
-            <version>1.4.0</version>
+            <version>1.5.1</version>
             <scope>test</scope>
         </dependency>
     </dependencies>
diff --git a/media/jaxb/pom.xml b/media/jaxb/pom.xml
index 75a49a0..6586b38 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/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 88e3168..5d328da 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.17.0", "com.fasterxml.jackson.jaxrs", "jackson-jaxrs-json-provider");
+        "2.17.1", "com.fasterxml.jackson.jaxrs", "jackson-jaxrs-json-provider");
 
     @Override
     public Version version() {
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 6034c04..4edecfc 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.17.0

+Jackson JAX-RS Providers version 2.17.1

 * 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/moxy/pom.xml b/media/moxy/pom.xml
index 335f80e..5ae722e 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>
 
diff --git a/pom.xml b/pom.xml
index 980af5d..b37f5c0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2234,7 +2234,7 @@
         <microprofile.config.version>3.0.3</microprofile.config.version>
         <microprofile.rest.client.version>3.0.1</microprofile.rest.client.version>
         <helidon.config.version>3.2.6</helidon.config.version>
-        <helidon.jersey.connector.version>3.2.6</helidon.jersey.connector.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>
 
@@ -2245,13 +2245,15 @@
         <hk2.jvnet.osgi.version>org.jvnet.hk2.*;version="[2.5,4)"</hk2.jvnet.osgi.version>
         <httpclient.version>4.5.14</httpclient.version>
         <httpclient5.version>5.3.1</httpclient5.version>
-        <jackson.version>2.17.0</jackson.version>
+        <jackson.version>2.17.1</jackson.version>
         <javassist.version>3.30.2-GA</javassist.version>
-        <jboss.logging.version>3.5.3.Final</jboss.logging.version>
         <jboss.logging.8.version>3.4.3.Final</jboss.logging.8.version>
         <jersey1.version>1.19.3</jersey1.version>
         <jersey1.last.final.version>${jersey1.version}</jersey1.last.final.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.vfs.jdk8.version>3.2.17.Final</jboss.vfs.jdk8.version>
+        <jboss.logging.version>3.6.0.Final</jboss.logging.version>
         <jmh.version>1.37</jmh.version>
         <jmockit.version>1.49</jmockit.version>
         <junit4.version>4.13.2</junit4.version>
@@ -2321,7 +2323,7 @@
         <jaxrs.api.spec.version>3.0</jaxrs.api.spec.version>
         <jaxrs.api.impl.version>3.0.0</jaxrs.api.impl.version>
         <jetty.osgi.version>org.eclipse.jetty.*;version="[11,15)"</jetty.osgi.version>
-        <jetty.version>11.0.20</jetty.version>
+        <jetty.version>11.0.22</jetty.version>
         <jetty.tracing.version>11.0.15</jetty.tracing.version> <!-- special version for tracing support tests, applied before JDK 21-->
         <jetty9.version>9.4.54.v20240208</jetty9.version>
         <jetty.plugin.version>11.0.20</jetty.plugin.version>
diff --git a/tests/e2e-client/pom.xml b/tests/e2e-client/pom.xml
index e0a036b..a8e9f41 100644
--- a/tests/e2e-client/pom.xml
+++ b/tests/e2e-client/pom.xml
@@ -237,6 +237,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/MultiPartTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/MultiPartTest.java
index 3cb45b9..55376fa 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,12 +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.internal.util.JdkVersion;
 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;
@@ -33,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;
@@ -41,18 +49,26 @@
 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;
-import java.util.stream.Stream;
 
 public class MultiPartTest {
 
@@ -132,5 +148,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-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 6c4907f..f342c5f 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, 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
@@ -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-tls/pom.xml b/tests/e2e-tls/pom.xml
index a7a98de..3f2bf1a 100644
--- a/tests/e2e-tls/pom.xml
+++ b/tests/e2e-tls/pom.xml
@@ -96,7 +96,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>
 
diff --git a/tests/integration/jersey-2776/pom.xml b/tests/integration/jersey-2776/pom.xml
index c9fdda3..82b05bb 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/performance/runners/jersey-grizzly-runner/pom.xml b/tests/performance/runners/jersey-grizzly-runner/pom.xml
index 1d26c29..03f7645 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/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 288932a..317118d 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,12 +160,38 @@
 
     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.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")
+            };
+        }
+    }
 }