merge of the current 2.x into the 3.0

diff --git a/containers/jdk-http/pom.xml b/containers/jdk-http/pom.xml
index 659b4dc..f81c101 100644
--- a/containers/jdk-http/pom.xml
+++ b/containers/jdk-http/pom.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
 
-    Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2010, 2025 Oracle and/or its affiliates. All rights reserved.
 
     This program and the accompanying materials are made available under the
     terms of the Eclipse Public License v. 2.0, which is available at
@@ -34,12 +34,6 @@
 
     <dependencies>
         <dependency>
-            <groupId>commons-io</groupId>
-            <artifactId>commons-io</artifactId>
-            <scope>test</scope>
-            <version>${commons.io.version}</version>
-        </dependency>
-        <dependency>
             <groupId>org.hamcrest</groupId>
             <artifactId>hamcrest</artifactId>
             <scope>test</scope>
diff --git a/containers/jdk-http/src/test/java/org/glassfish/jersey/jdkhttp/JdkHttpsServerTest.java b/containers/jdk-http/src/test/java/org/glassfish/jersey/jdkhttp/JdkHttpsServerTest.java
index 01e9e34..d8a2798 100644
--- a/containers/jdk-http/src/test/java/org/glassfish/jersey/jdkhttp/JdkHttpsServerTest.java
+++ b/containers/jdk-http/src/test/java/org/glassfish/jersey/jdkhttp/JdkHttpsServerTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * 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,9 +30,9 @@
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLHandshakeException;
 
-import org.apache.commons.io.IOUtils;
 import org.glassfish.jersey.SslConfigurator;
 import org.glassfish.jersey.internal.util.JdkVersion;
+import org.glassfish.jersey.message.internal.ReaderWriter;
 import org.glassfish.jersey.server.ResourceConfig;
 
 import org.junit.jupiter.api.AfterEach;
@@ -195,9 +195,9 @@
 
 
         final SslConfigurator sslConfigClient = SslConfigurator.newInstance()
-                .trustStoreBytes(IOUtils.toByteArray(trustStore))
+                .trustStoreBytes(ReaderWriter.readFromAsBytes(trustStore))
                 .trustStorePassword(TRUSTSTORE_CLIENT_PWD)
-                .keyStoreBytes(IOUtils.toByteArray(keyStore))
+                .keyStoreBytes(ReaderWriter.readFromAsBytes(keyStore))
                 .keyPassword(KEYSTORE_CLIENT_PWD);
 
         return sslConfigClient.createSSLContext();
@@ -208,9 +208,9 @@
         final InputStream keyStore = JdkHttpsServerTest.class.getResourceAsStream(KEYSTORE_SERVER_FILE);
 
         final SslConfigurator sslConfigServer = SslConfigurator.newInstance()
-                .keyStoreBytes(IOUtils.toByteArray(keyStore))
+                .keyStoreBytes(ReaderWriter.readFromAsBytes(keyStore))
                 .keyPassword(KEYSTORE_SERVER_PWD)
-                .trustStoreBytes(IOUtils.toByteArray(trustStore))
+                .trustStoreBytes(ReaderWriter.readFromAsBytes(trustStore))
                 .trustStorePassword(TRUSTSTORE_SERVER_PWD);
 
         return sslConfigServer.createSSLContext();
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/ClientResponse.java b/core-client/src/main/java/org/glassfish/jersey/client/ClientResponse.java
index 35fcef5..698fb39 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/ClientResponse.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/ClientResponse.java
@@ -63,8 +63,12 @@
      * @param response       JAX-RS response to be used to initialize the response context.
      */
     public ClientResponse(final ClientRequest requestContext, final Response response) {
-        this(response.getStatusInfo(), requestContext);
-        this.headers(OutboundJaxrsResponse.from(response, requestContext.getConfiguration()).getContext().getStringHeaders());
+        super(requestContext.getConfiguration(),
+                OutboundJaxrsResponse.from(response, requestContext.getConfiguration()).getContext().getStringHeaders(),
+                false
+        );
+        this.requestContext = requestContext;
+        init(response.getStatusInfo(), requestContext, requestContext.getUri());
 
         final Object entity = response.getEntity();
         if (entity != null) {
@@ -122,10 +126,13 @@
      */
     public ClientResponse(Response.StatusType status, ClientRequest requestContext, URI resolvedRequestUri) {
         super(requestContext.getConfiguration());
+        this.requestContext = requestContext;
+        init(status, requestContext, resolvedRequestUri);
+    }
+
+    private void init(Response.StatusType status, ClientRequest requestContext, URI resolvedRequestUri) {
         this.status = status;
         this.resolvedUri = resolvedRequestUri;
-        this.requestContext = requestContext;
-
         setWorkers(requestContext.getWorkers());
     }
 
diff --git a/core-client/src/test/java/org/glassfish/jersey/client/AbortTest.java b/core-client/src/test/java/org/glassfish/jersey/client/AbortTest.java
index caffa4d..ae487bf 100644
--- a/core-client/src/test/java/org/glassfish/jersey/client/AbortTest.java
+++ b/core-client/src/test/java/org/glassfish/jersey/client/AbortTest.java
@@ -16,19 +16,30 @@
 
 package org.glassfish.jersey.client;
 
+import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
+import jakarta.ws.rs.Priorities;
 import jakarta.ws.rs.Produces;
 import jakarta.ws.rs.WebApplicationException;
 import jakarta.ws.rs.client.Client;
 import jakarta.ws.rs.client.ClientBuilder;
 import jakarta.ws.rs.client.ClientRequestContext;
 import jakarta.ws.rs.client.ClientRequestFilter;
+import jakarta.ws.rs.client.ClientResponseContext;
+import jakarta.ws.rs.client.ClientResponseFilter;
+import jakarta.ws.rs.core.Application;
 import jakarta.ws.rs.core.GenericEntity;
+import jakarta.ws.rs.core.Link;
 import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.MultivaluedMap;
 import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.UriBuilder;
+import jakarta.ws.rs.core.Variant;
 import jakarta.ws.rs.ext.MessageBodyWriter;
+import jakarta.ws.rs.ext.ReaderInterceptor;
+import jakarta.ws.rs.ext.ReaderInterceptorContext;
+import jakarta.ws.rs.ext.RuntimeDelegate;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.lang.annotation.Annotation;
@@ -37,18 +48,25 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
 
-//import static java.nio.charset.StandardCharsets;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
 public class AbortTest {
     private static final String TEXT_CSV = "text/csv";
+    private static final String TEXT_HEADER = "text/header";
     private static final String EXPECTED_CSV = "hello;goodbye\nsalutations;farewell";
     private static final List<List<String>> CSV_LIST = Arrays.asList(
             Arrays.asList("hello", "goodbye"),
             Arrays.asList("salutations", "farewell")
     );
+    private final String entity = "HI";
+    private final String header = "CUSTOM_HEADER";
+
 
     @Test
     void testAbortWithGenericEntity() {
@@ -77,7 +95,6 @@
 
         @Override
         public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
-            System.out.println(genericType.getTypeName());
             return List.class.isAssignableFrom(type) && genericType instanceof ParameterizedType
                     && ((ParameterizedType) genericType).getActualTypeArguments()[0] instanceof ParameterizedType
                     && String.class.equals(((ParameterizedType) ((ParameterizedType) genericType).getActualTypeArguments()[0])
@@ -99,4 +116,172 @@
         }
     }
 
+    @Test
+    void testAbortWithMBWWritingHeaders() {
+        try (Response response = ClientBuilder.newClient().register(new ClientRequestFilter() {
+            @Override
+            public void filter(ClientRequestContext requestContext) throws IOException {
+                requestContext.abortWith(Response.ok(entity, TEXT_HEADER).build());
+            }
+        }).register(new MessageBodyWriter<String>() {
+
+            @Override
+            public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+                return mediaType.toString().equals(TEXT_HEADER);
+            }
+
+            @Override
+            public void writeTo(String s, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
+                                MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException,
+                    WebApplicationException {
+                httpHeaders.add(header, entity);
+                entityStream.write(s.getBytes());
+            }
+        }, Priorities.USER - 1).target("http://localhost:8080").request().get()) {
+            Assertions.assertEquals(entity, response.readEntity(String.class));
+            Assertions.assertEquals(entity, response.getHeaderString(header));
+        }
+    }
+
+    @Test
+    void testInterceptorHeaderAdd() {
+        final String header2 = "CUSTOM_HEADER_2";
+
+        try (Response response = ClientBuilder.newClient().register(new ClientRequestFilter() {
+            @Override
+            public void filter(ClientRequestContext requestContext) throws IOException {
+                requestContext.abortWith(Response.ok().entity(entity).build());
+            }
+        }).register(new ReaderInterceptor() {
+                    @Override
+                    public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
+                        MultivaluedMap<String, String> headers = context.getHeaders();
+                        headers.put(header, Collections.singletonList(entity));
+                        headers.add(header2, entity);
+                        return context.proceed();
+                    }
+                })
+                .target("http://localhost:8080").request().get()) {
+            Assertions.assertEquals(entity, response.readEntity(String.class));
+            Assertions.assertEquals(entity, response.getHeaderString(header));
+            Assertions.assertEquals(entity, response.getHeaderString(header2));
+        }
+    }
+
+    @Test
+    void testInterceptorHeaderIterate() {
+        final AtomicReference<String> originalHeader = new AtomicReference<>();
+
+        try (Response response = ClientBuilder.newClient().register(new ClientRequestFilter() {
+                    @Override
+                    public void filter(ClientRequestContext requestContext) throws IOException {
+                        requestContext.abortWith(Response.ok().header(header, header).entity(entity).build());
+                    }
+                }).register(new ReaderInterceptor() {
+                    @Override
+                    public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
+                        MultivaluedMap<String, String> headers = context.getHeaders();
+                        Iterator<Map.Entry<String, List<String>>> it = headers.entrySet().iterator();
+                        while (it.hasNext()) {
+                            Map.Entry<String, List<String>> next = it.next();
+                            if (header.equals(next.getKey())) {
+                                originalHeader.set(next.setValue(Collections.singletonList(entity)).get(0));
+                            }
+                        }
+                        return context.proceed();
+                    }
+                })
+                .target("http://localhost:8080").request().get()) {
+            Assertions.assertEquals(entity, response.readEntity(String.class));
+            Assertions.assertEquals(entity, response.getHeaderString(header));
+            Assertions.assertEquals(header, originalHeader.get());
+        }
+    }
+
+    @Test
+    void testNullHeader() {
+        final AtomicReference<String> originalHeader = new AtomicReference<>();
+        RuntimeDelegate.setInstance(new StringHeaderRuntimeDelegate(RuntimeDelegate.getInstance()));
+        try (Response response = ClientBuilder.newClient().register(new ClientRequestFilter() {
+                    @Override
+                    public void filter(ClientRequestContext requestContext) throws IOException {
+                        requestContext.abortWith(Response.ok()
+                                .header(header, new StringHeader())
+                                .entity(entity).build());
+                    }
+                }).register(new ClientResponseFilter() {
+                    @Override
+                    public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext)
+                            throws IOException {
+                        originalHeader.set(responseContext.getHeaderString(header));
+                    }
+                })
+                .target("http://localhost:8080").request().get()) {
+            Assertions.assertEquals(entity, response.readEntity(String.class));
+            Assertions.assertEquals("", originalHeader.get());
+        }
+    }
+
+    private static class StringHeader extends AtomicReference<String> {
+
+    }
+
+    private static class StringHeaderDelegate implements RuntimeDelegate.HeaderDelegate<StringHeader> {
+        @Override
+        public StringHeader fromString(String value) {
+            StringHeader stringHeader = new StringHeader();
+            stringHeader.set(value);
+            return stringHeader;
+        }
+
+        @Override
+        public String toString(StringHeader value) {
+            //on purpose
+            return null;
+        }
+    }
+
+    private static class StringHeaderRuntimeDelegate extends RuntimeDelegate {
+        private final RuntimeDelegate original;
+
+        private StringHeaderRuntimeDelegate(RuntimeDelegate original) {
+            this.original = original;
+        }
+
+        @Override
+        public UriBuilder createUriBuilder() {
+            return original.createUriBuilder();
+        }
+
+        @Override
+        public Response.ResponseBuilder createResponseBuilder() {
+            return original.createResponseBuilder();
+        }
+
+        @Override
+        public Variant.VariantListBuilder createVariantListBuilder() {
+            return original.createVariantListBuilder();
+        }
+
+        @Override
+        public <T> T createEndpoint(Application application, Class<T> endpointType)
+                throws IllegalArgumentException, UnsupportedOperationException {
+            return original.createEndpoint(application, endpointType);
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public <T> HeaderDelegate<T> createHeaderDelegate(Class<T> type) throws IllegalArgumentException {
+            if (StringHeader.class.equals(type)) {
+                return (HeaderDelegate<T>) new StringHeaderDelegate();
+            }
+            return original.createHeaderDelegate(type);
+        }
+
+        @Override
+        public Link.Builder createLinkBuilder() {
+            return original.createLinkBuilder();
+        }
+    }
+
 }
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Views.java b/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Views.java
index 4765056..75b1987 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Views.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Views.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -129,60 +129,107 @@
      * @return transformed map view.
      */
     public static <K, V, O> Map<K, V> mapView(Map<K, O> originalMap, Function<O, V> valuesTransformer) {
-        return new AbstractMap<K, V>() {
-            @Override
-            public Set<Entry<K, V>> entrySet() {
-                return new AbstractSet<Entry<K, V>>() {
+        return new KVOMap<K, V, O>(originalMap, valuesTransformer);
+    }
 
+    private static class KVOMap<K, V, O> extends AbstractMap<K, V> {
+            protected final Map<K, O> originalMap;
+            protected final Function<O, V> valuesTransformer;
 
-                    Set<Entry<K, O>> originalSet = originalMap.entrySet();
-                    Iterator<Entry<K, O>> original = originalSet.iterator();
+        private KVOMap(Map<K, O> originalMap, Function<O, V> valuesTransformer) {
+            this.originalMap = originalMap;
+            this.valuesTransformer = valuesTransformer;
+        }
 
-                    @Override
-                    public Iterator<Entry<K, V>> iterator() {
-                        return new Iterator<Entry<K, V>>() {
-                            @Override
-                            public boolean hasNext() {
-                                return original.hasNext();
-                            }
+        @Override
+        public Set<Entry<K, V>> entrySet() {
+            return new AbstractSet<Entry<K, V>>() {
 
-                            @Override
-                            public Entry<K, V> next() {
+                Set<Entry<K, O>> originalSet = originalMap.entrySet();
+                Iterator<Entry<K, O>> original = originalSet.iterator();
 
-                                Entry<K, O> next = original.next();
+                @Override
+                public Iterator<Entry<K, V>> iterator() {
+                    return new Iterator<Entry<K, V>>() {
+                        @Override
+                        public boolean hasNext() {
+                            return original.hasNext();
+                        }
 
-                                return new Entry<K, V>() {
-                                    @Override
-                                    public K getKey() {
-                                        return next.getKey();
-                                    }
+                        @Override
+                        public Entry<K, V> next() {
 
-                                    @Override
-                                    public V getValue() {
-                                        return valuesTransformer.apply(next.getValue());
-                                    }
+                            Entry<K, O> next = original.next();
 
-                                    @Override
-                                    public V setValue(V value) {
-                                        throw new UnsupportedOperationException("Not supported.");
-                                    }
-                                };
-                            }
+                            return new Entry<K, V>() {
+                                @Override
+                                public K getKey() {
+                                    return next.getKey();
+                                }
 
-                            @Override
-                            public void remove() {
-                                original.remove();
-                            }
-                        };
-                    }
+                                @Override
+                                public V getValue() {
+                                    return valuesTransformer.apply(next.getValue());
+                                }
 
-                    @Override
-                    public int size() {
-                        return originalSet.size();
-                    }
-                };
-            }
-        };
+                                @Override
+                                public V setValue(V value) {
+                                    return KVOMap.this.setValue(next, value);
+                                }
+                            };
+                        }
+
+                        @Override
+                        public void remove() {
+                            original.remove();
+                        }
+                    };
+                }
+
+                @Override
+                public int size() {
+                    return originalSet.size();
+                }
+            };
+        }
+
+        protected V setValue(Map.Entry<K, O> entry, V value) {
+            throw new UnsupportedOperationException("Not supported.");
+        }
+    }
+
+    /**
+     * Create a {@link Map} view, which transforms the values of provided original map.
+     * <p>
+     *
+     * @param originalMap       provided map.
+     * @param valuesTransformer values transformer.
+     * @return transformed map view.
+     */
+    public static Map<String, List<String>> mapObjectToStringView(Map<String, List<Object>> originalMap,
+                                                            Function<List<Object>, List<String>> valuesTransformer) {
+        return new ObjectToStringMap(originalMap, valuesTransformer);
+    }
+
+    private static class ObjectToStringMap extends KVOMap<String, List<String>, List<Object>> {
+
+        private ObjectToStringMap(Map<String, List<Object>> originalMap, Function<List<Object>, List<String>> valuesTransformer) {
+            super(originalMap, valuesTransformer);
+        }
+
+        @Override
+        protected List<String> setValue(Entry<String, List<Object>> entry, List<String> value) {
+            @SuppressWarnings("unchecked")
+            final List<Object> old = entry.setValue((List<Object>) (List<?>) value);
+            return valuesTransformer.apply(old);
+        }
+
+        @Override
+        public List<String> put(String key, List<String> value) {
+            @SuppressWarnings("unchecked")
+            final List<Object> old = originalMap.put(key, (List<Object>) (List<?>) value);
+            return valuesTransformer.apply(old);
+        }
     }
 
     /**
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/CookiesParser.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/CookiesParser.java
index f8bec0a..51bb99e 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/CookiesParser.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/CookiesParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -86,20 +86,29 @@
     }
 
     /**
-     * Check if a cookie with identical name had been parsed.
-     * If yes, the one with the longest string will be kept
+     * Check if a cookie with similar names had been parsed.
+     * If yes, the one with the longest path will be kept
+     * For similar paths the newest is stored
      * @param cookies : Map of cookies
      * @param cookie : Cookie to be checked
      */
     private static void checkSimilarCookieName(Map<String, Cookie> cookies, MutableCookie cookie) {
-        if (cookie != null) {
-            if (cookies.containsKey(cookie.name)){
-                if (cookie.value.length() > cookies.get(cookie.name).getValue().length()){
-                    cookies.put(cookie.name, cookie.getImmutableCookie());
-                }
-            } else {
-                cookies.put(cookie.name, cookie.getImmutableCookie());
-            }
+        if (cookie == null) {
+            return;
+        }
+
+        boolean alreadyPresent = cookies.containsKey(cookie.name);
+        boolean recordCookie = !alreadyPresent;
+
+        if (alreadyPresent) {
+            final String newPath = cookie.path == null ? "" : cookie.path;
+            final String existingPath = cookies.get(cookie.name).getPath() == null ? ""
+                    : cookies.get(cookie.name).getPath();
+            recordCookie = (newPath.length() >= existingPath.length());
+        }
+
+        if (recordCookie) {
+            cookies.put(cookie.name, cookie.getImmutableCookie());
         }
     }
 
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/HeaderUtils.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/HeaderUtils.java
index aeea686..c60386b 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/HeaderUtils.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/HeaderUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -210,7 +210,7 @@
         }
 
         return new AbstractMultivaluedMap<String, String>(
-                Views.mapView(headers, input -> HeaderUtils.asStringList(input, rd))
+                Views.mapObjectToStringView(headers, input -> HeaderUtils.asStringList(input, rd))
         ) {
         };
     }
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/InboundMessageContext.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/InboundMessageContext.java
index 29cb6e0..46446a4 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/InboundMessageContext.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/InboundMessageContext.java
@@ -157,8 +157,22 @@
      *                      as required by JAX-RS specification on the server side.
      */
     public InboundMessageContext(Configuration configuration, boolean translateNce) {
+        this(configuration, HeaderUtils.createInbound(), translateNce);
+    }
+
+    /**
+     * Create new inbound message context.
+     *
+     * @param configuration the related client/server side {@link Configuration}. If {@code null},
+     *                      the default behaviour is expected.
+     * @param httpHeaders   the http headers map.
+     * @param translateNce  if {@code true}, the {@link javax.ws.rs.core.NoContentException} thrown by a
+     *                      selected message body reader will be translated into a {@link javax.ws.rs.BadRequestException}
+     *                      as required by JAX-RS specification on the server side.
+     */
+    public InboundMessageContext(Configuration configuration, MultivaluedMap<String, String> httpHeaders, boolean translateNce) {
         super(configuration);
-        this.headers = new GuardianStringKeyMultivaluedMap<>(HeaderUtils.createInbound());
+        this.headers = new GuardianStringKeyMultivaluedMap<>(httpHeaders);
         this.entityContent = new EntityContent();
         this.translateNce = translateNce;
         this.configuration = configuration;
@@ -302,7 +316,11 @@
         }
 
         final Iterator<String> valuesIterator = values.iterator();
-        StringBuilder buffer = new StringBuilder(valuesIterator.next());
+        String next = valuesIterator.next();
+        if (next == null) {
+            next = "";
+        }
+        StringBuilder buffer = new StringBuilder(next);
         while (valuesIterator.hasNext()) {
             buffer.append(',').append(valuesIterator.next());
         }
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderWriter.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderWriter.java
index 018c9f0..38b5013 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderWriter.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderWriter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -160,11 +160,23 @@
         }
         return sb.toString();
     }
+
     /**
-     * The maximum size of array to allocate.
+     * Read/convert stream to the byte array.
+     *
+     * @param in stream to be converted to the byte array
+     * @return the byte array
+     * @throws IOException if there is an error reading from the stream
+     * @since 2.47
+     */
+    public static byte[] readFromAsBytes(InputStream in) throws IOException {
+        return readAllBytes(in);
+    }
+    /**
+     * The maximum size of an array to allocate.
      * Some VMs reserve some header words in an array.
      * Attempts to allocate larger arrays may result in
-     * OutOfMemoryError: Requested array size exceeds VM limit
+     * OutOfMemoryError: Requested array size exceeds the VM limit
      */
     private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
 
diff --git a/pom.xml b/pom.xml
index eb7f65a..7aa01a2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2205,7 +2205,6 @@
         <aspectj.weaver.version>1.9.22.1</aspectj.weaver.version>
         <!--        <bnd.plugin.version>2.3.6</bnd.plugin.version>-->
         <bouncycastle.version>1.70</bouncycastle.version>
-        <commons.io.version>2.19.0</commons.io.version>
         <commons.codec.version>1.16.1</commons.codec.version>
 <!--        <commons-lang3.version>3.3.2</commons-lang3.version>-->
         <commons.logging.version>1.3.5</commons.logging.version>
diff --git a/test-framework/maven/container-runner-maven-plugin/pom.xml b/test-framework/maven/container-runner-maven-plugin/pom.xml
index 21bbfd3..b84c7b3 100644
--- a/test-framework/maven/container-runner-maven-plugin/pom.xml
+++ b/test-framework/maven/container-runner-maven-plugin/pom.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
 
-    Copyright (c) 2015, 2024 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2015, 2025 Oracle and/or its affiliates. All rights reserved.
 
     This program and the accompanying materials are made available under the
     terms of the Eclipse Public License v. 2.0, which is available at
@@ -168,10 +168,10 @@
                     <groupId>org.codehaus.plexus</groupId>
                     <artifactId>plexus-archiver</artifactId>
                 </exclusion>
-                <exclusion>
+                <!--<exclusion>
                     <groupId>commons-io</groupId>
                     <artifactId>commons-io</artifactId>
-                </exclusion>
+                </exclusion>-->
                 <exclusion>
                     <groupId>org.junit.jupiter</groupId>
                     <artifactId>junit-jupiter-api</artifactId>
diff --git a/test-framework/maven/custom-enforcer-rules/pom.xml b/test-framework/maven/custom-enforcer-rules/pom.xml
index 5f702fc..e7ccf4e 100644
--- a/test-framework/maven/custom-enforcer-rules/pom.xml
+++ b/test-framework/maven/custom-enforcer-rules/pom.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
 
-    Copyright (c) 2015, 2024 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2015, 2025 Oracle and/or its affiliates. All rights reserved.
 
     This program and the accompanying materials are made available under the
     terms of the Eclipse Public License v. 2.0, which is available at
@@ -49,14 +49,14 @@
                     <artifactId>plexus-utils</artifactId>
                 </exclusion>
                 <exclusion>
-                    <groupId>commons-io</groupId>
-                    <artifactId>commons-io</artifactId>
-                </exclusion>
-                <exclusion>
                     <groupId>commons-codec</groupId>
                     <artifactId>commons-codec</artifactId>
                 </exclusion>
                 <exclusion>
+                    <groupId>commons-io</groupId>
+                    <artifactId>commons-io</artifactId>
+                </exclusion>
+                <exclusion>
                     <groupId>org.apache.maven</groupId>
                     <artifactId>maven-core</artifactId>
                 </exclusion>
@@ -68,12 +68,6 @@
         </dependency>
 
         <dependency>
-            <groupId>commons-io</groupId>
-            <artifactId>commons-io</artifactId>
-            <version>${commons.io.version}</version>
-        </dependency>
-
-        <dependency>
             <groupId>org.junit.jupiter</groupId>
             <artifactId>junit-jupiter</artifactId>
             <scope>compile</scope>
diff --git a/test-framework/maven/custom-enforcer-rules/src/main/java/org/glassfish/jersey/test/maven/rule/FilePatternDoesNotExistRule.java b/test-framework/maven/custom-enforcer-rules/src/main/java/org/glassfish/jersey/test/maven/rule/FilePatternDoesNotExistRule.java
index f9c8fde..ee275ba 100644
--- a/test-framework/maven/custom-enforcer-rules/src/main/java/org/glassfish/jersey/test/maven/rule/FilePatternDoesNotExistRule.java
+++ b/test-framework/maven/custom-enforcer-rules/src/main/java/org/glassfish/jersey/test/maven/rule/FilePatternDoesNotExistRule.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * 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,17 +17,19 @@
 package org.glassfish.jersey.test.maven.rule;
 
 import java.io.File;
-import java.io.FileFilter;
+import java.io.IOException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.Arrays;
 import java.util.Set;
 import java.util.TreeSet;
 
-import org.apache.commons.io.filefilter.WildcardFileFilter;
 import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
 import org.apache.maven.enforcer.rules.AbstractStandardEnforcerRule;
 
 /**
- * Maven enforcer rule to enforce that given set of files does not exist.<br/>
+ * Maven enforcer rule to enforce that a given set of files does not exist.<br/>
  * This rule is similar to apache {@code requireFilesDontExist} with a support for wildcards.
  *
  * @author Stepan Vavra
@@ -55,8 +57,13 @@
             }
 
             final Set<File> matchedFiles = new TreeSet<>();
-            for (File matchedFile : dir.listFiles((FileFilter) new WildcardFileFilter(fileItselfPattern))) {
-                matchedFiles.add(matchedFile);
+            try {
+                final DirectoryStream<Path> directoryStream
+                        = Files.newDirectoryStream(dir.toPath(), fileItselfPattern);
+                directoryStream.forEach(path -> matchedFiles.add(path.toFile()));
+                directoryStream.close();
+            } catch (IOException e) {
+                throw new EnforcerRuleException(e);
             }
 
             if (!matchedFiles.isEmpty()) {
diff --git a/tests/e2e-client/pom.xml b/tests/e2e-client/pom.xml
index b01f938..22104d4 100644
--- a/tests/e2e-client/pom.xml
+++ b/tests/e2e-client/pom.xml
@@ -225,11 +225,7 @@
             <version>${junit-platform-suite.version}</version>
             <scope>test</scope>
         </dependency>
-        <dependency>
-            <groupId>commons-io</groupId>
-            <artifactId>commons-io</artifactId>
-            <version>${commons.io.version}</version>
-        </dependency>
+
     </dependencies>
 
     <profiles>
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/AbstractConnectorServerTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/AbstractConnectorServerTest.java
index a458eb9..6564024 100644
--- a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/AbstractConnectorServerTest.java
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/AbstractConnectorServerTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -18,7 +18,6 @@
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.Arrays;
 import java.util.stream.Stream;
 
 import javax.net.ssl.SSLContext;
@@ -32,11 +31,10 @@
 import org.glassfish.jersey.internal.util.JdkVersion;
 import org.glassfish.jersey.jetty.connector.JettyConnectorProvider;
 
+import org.glassfish.jersey.message.internal.ReaderWriter;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 
-import org.apache.commons.io.IOUtils;
-
 /**
  * SSL connector hostname verification tests.
  *
@@ -96,9 +94,9 @@
         final InputStream trustStore = SslConnectorConfigurationTest.class.getResourceAsStream(clientTrustStore());
         final InputStream keyStore = SslConnectorConfigurationTest.class.getResourceAsStream(CLIENT_KEY_STORE);
         return SslConfigurator.newInstance()
-                .trustStoreBytes(IOUtils.toByteArray(trustStore))
+                .trustStoreBytes(ReaderWriter.readFromAsBytes(trustStore))
                 .trustStorePassword("asdfgh")
-                .keyStoreBytes(IOUtils.toByteArray(keyStore))
+                .keyStoreBytes(ReaderWriter.readFromAsBytes(keyStore))
                 .keyPassword("asdfgh")
                 .createSSLContext();
     }
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/Server.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/Server.java
index 20795c3..b66ff73 100644
--- a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/Server.java
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/Server.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * 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,9 +23,9 @@
 
 import jakarta.ws.rs.core.UriBuilder;
 
-import org.apache.commons.io.IOUtils;
 import org.glassfish.jersey.logging.LoggingFeature;
 import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.message.internal.ReaderWriter;
 import org.glassfish.jersey.server.ResourceConfig;
 
 import org.glassfish.grizzly.http.server.HttpServer;
@@ -86,9 +86,9 @@
         SSLContextConfigurator sslContext = new SSLContextConfigurator();
 
         // set up security context
-        sslContext.setKeyStoreBytes(IOUtils.toByteArray(keyStore));  // contains server key pair
+        sslContext.setKeyStoreBytes(ReaderWriter.readFromAsBytes(keyStore));  // contains server key pair
         sslContext.setKeyStorePass("asdfgh");
-        sslContext.setTrustStoreBytes(IOUtils.toByteArray(trustStore)); // contains client certificate
+        sslContext.setTrustStoreBytes(ReaderWriter.readFromAsBytes(trustStore)); // contains client certificate
         sslContext.setTrustStorePass("asdfgh");
 
         ResourceConfig rc = new ResourceConfig();
diff --git a/tests/e2e-server/pom.xml b/tests/e2e-server/pom.xml
index e0d7298..73c9d23 100644
--- a/tests/e2e-server/pom.xml
+++ b/tests/e2e-server/pom.xml
@@ -217,12 +217,6 @@
         </dependency>
 
         <dependency>
-            <groupId>commons-io</groupId>
-            <artifactId>commons-io</artifactId>
-            <version>${commons.io.version}</version>
-        </dependency>
-
-        <dependency>
             <groupId>org.hamcrest</groupId>
             <artifactId>hamcrest</artifactId>
             <scope>test</scope>
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/SimilarInputStreamTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/SimilarInputStreamTest.java
index cedad3a..58e180c 100644
--- a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/SimilarInputStreamTest.java
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/SimilarInputStreamTest.java
@@ -16,7 +16,6 @@
 
 package org.glassfish.jersey.tests.e2e.server;
 
-import org.apache.commons.io.IOUtils;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
 import org.eclipse.jetty.servlet.ServletContextHandler;
@@ -25,6 +24,7 @@
 import org.glassfish.jersey.internal.InternalProperties;
 import org.glassfish.jersey.jackson.JacksonFeature;
 import org.glassfish.jersey.jetty.JettyHttpContainerFactory;
+import org.glassfish.jersey.message.internal.ReaderWriter;
 import org.glassfish.jersey.server.ResourceConfig;
 import org.glassfish.jersey.servlet.ServletContainer;
 import org.glassfish.jersey.test.JerseyTest;
@@ -203,7 +203,7 @@
 
         private void cacheInputStream() throws IOException {
             // Cache the inputstream in order to read it multiple times.
-            cachedBytes = IOUtils.toByteArray(super.getInputStream());
+            cachedBytes = ReaderWriter.readFromAsBytes(super.getInputStream());
         }
 
 
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/CookieImplTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/CookieImplTest.java
index 1068272..5438e71 100644
--- a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/CookieImplTest.java
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/CookieImplTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -151,27 +151,46 @@
     @Test
     public void testMultipleCookiesWithSameName(){
 
-        String cookieHeader = "kobe=longeststring; kobe=shortstring";
+        String cookieHeader = "kobe=oldeststring; kobe=neweststring";
         Map<String, Cookie> cookies = HttpHeaderReader.readCookies(cookieHeader);
         assertEquals(cookies.size(), 1);
         Cookie c = cookies.get("kobe");
         assertEquals(c.getVersion(), 0);
         assertEquals("kobe", c.getName());
-        assertEquals("longeststring", c.getValue());
+        assertEquals("neweststring", c.getValue());
 
-        cookieHeader = "bryant=longeststring; bryant=shortstring; fred=shortstring ;fred=longeststring;$Path=/path";
+        cookieHeader = "bryant=longeststring; bryant=neweststring; fred=oldeststring ;fred=neweststring;$Path=/path";
         cookies = HttpHeaderReader.readCookies(cookieHeader);
         assertEquals(cookies.size(), 2);
         c = cookies.get("bryant");
         assertEquals(c.getVersion(), 0);
         assertEquals("bryant", c.getName());
-        assertEquals("longeststring", c.getValue());
+        assertEquals("neweststring", c.getValue());
         c = cookies.get("fred");
         assertEquals(c.getVersion(), 0);
         assertEquals("fred", c.getName());
-        assertEquals("longeststring", c.getValue());
+        assertEquals("neweststring", c.getValue());
         assertEquals("/path", c.getPath());
 
+        cookieHeader = "cookiewithpath=longeststring;$Path=/path; cookiewithpath=string1;$Path=/path;"
+                + " cookiewithpath=string2;$Path=/path ;cookiewithpath=string3;$Path=/path";
+        cookies = HttpHeaderReader.readCookies(cookieHeader);
+        assertEquals(cookies.size(), 1);
+        c = cookies.get("cookiewithpath");
+        assertEquals(c.getVersion(), 0);
+        assertEquals("cookiewithpath", c.getName());
+        assertEquals("string3", c.getValue());
+
+        cookieHeader = "cookiewithpath=longeststring;$Path=/path/added/path; cookiewithpath=string1;$Path=/path;"
+                + " cookiewithpath=string2;$Path=/path ;cookiewithpath=string3;$Path=/path";
+        cookies = HttpHeaderReader.readCookies(cookieHeader);
+        assertEquals(cookies.size(), 1);
+        c = cookies.get("cookiewithpath");
+        assertEquals(c.getVersion(), 0);
+        assertEquals("cookiewithpath", c.getName());
+        assertEquals("longeststring", c.getValue());
+        assertEquals("/path/added/path", c.getPath());
+
     }
 
     @Test
diff --git a/tools/jersey-release-notes-maven-plugin/pom.xml b/tools/jersey-release-notes-maven-plugin/pom.xml
index 1522089..dfd342b 100644
--- a/tools/jersey-release-notes-maven-plugin/pom.xml
+++ b/tools/jersey-release-notes-maven-plugin/pom.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
 
-    Copyright (c) 2019, 2024 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2019, 2025 Oracle and/or its affiliates. All rights reserved.
 
     This program and the accompanying materials are made available under the
     terms of the Eclipse Public License v. 2.0, which is available at
@@ -56,7 +56,7 @@
         <dependency>
             <groupId>org.apache.maven.plugin-tools</groupId>
             <artifactId>maven-plugin-annotations</artifactId>
-            <version>3.6.0</version>
+            <version>3.15.1</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
@@ -74,7 +74,8 @@
         <dependency>
             <groupId>commons-io</groupId>
             <artifactId>commons-io</artifactId>
-            <version>2.17.0</version>
+            <version>${commons.io.version}</version>
+            <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.maven</groupId>
@@ -83,9 +84,15 @@
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <version>4.13.2</version>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-api</artifactId>
+            <version>${junit.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter</artifactId>
+            <version>${junit.version}</version>
             <scope>test</scope>
         </dependency>
 
@@ -96,7 +103,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-plugin-plugin</artifactId>
-                <version>3.6.0</version>
+                <version>3.15.1</version>
                 <configuration>
                     <skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>
                 </configuration>
@@ -113,7 +120,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
-                <version>3.1</version>
+                <version>3.14.0</version>
                 <inherited>true</inherited>
                 <configuration>
                     <source>${java.version}</source>
@@ -127,6 +134,8 @@
 
     <properties>
         <java.version>1.8</java.version>
-        <maven.version>3.8.1</maven.version>
+        <maven.version>3.9.9</maven.version>
+        <commons.io.version>2.19.0</commons.io.version>
+        <junit.version>5.12.2</junit.version>
     </properties>
 </project>
diff --git a/tools/jersey-release-notes-maven-plugin/src/test/java/org/glassfish/jersey/tools/plugins/releasenotes/GenerateReleaseNotesMojoTest.java b/tools/jersey-release-notes-maven-plugin/src/test/java/org/glassfish/jersey/tools/plugins/releasenotes/GenerateReleaseNotesMojoTest.java
index 93287ee..1826730 100644
--- a/tools/jersey-release-notes-maven-plugin/src/test/java/org/glassfish/jersey/tools/plugins/releasenotes/GenerateReleaseNotesMojoTest.java
+++ b/tools/jersey-release-notes-maven-plugin/src/test/java/org/glassfish/jersey/tools/plugins/releasenotes/GenerateReleaseNotesMojoTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -23,17 +23,14 @@
 import org.apache.maven.project.ProjectBuilder;
 import org.apache.maven.project.ProjectBuildingRequest;
 import org.eclipse.aether.DefaultRepositorySystemSession;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
 
 import java.io.File;
 
-@RunWith(JUnit4.class)
 public class GenerateReleaseNotesMojoTest extends AbstractMojoTestCase {
 
-    @Before
+    @BeforeEach
     public void setUp() throws Exception {
 
         // required for mojo lookups to work