Merge remote-tracking branch 'origin/master' into origin/3.x

Signed-off-by: Maxim Nesen <maxim.nesen@oracle.com>
diff --git a/bom/pom.xml b/bom/pom.xml
index 1083d28..92404fc 100644
--- a/bom/pom.xml
+++ b/bom/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.eclipse.ee4j</groupId>
         <artifactId>project</artifactId>
-        <version>1.0.7</version>
+        <version>1.0.8</version>
         <relativePath/>
     </parent>
 
diff --git a/connectors/helidon-connector/pom.xml b/connectors/helidon-connector/pom.xml
index 0e06810..31b40cd 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>3.0.0-M1</version>
+            <version>${helidon.jersey.connector.version}</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
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 d3b1d9f..0a3ede0 100644
--- a/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/TransferEncodingParser.java
+++ b/connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/TransferEncodingParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -218,8 +218,9 @@
 
                         while (offset < limit) {
                             final byte b = input.get(offset);
-                            if (isSpaceOrTab(b) /*trailing spaces are not allowed by the spec, but some server put it there*/
-                                    || b == HttpParserUtils.CR || b == HttpParserUtils.SEMI_COLON) {
+                            if (isSpaceOrTab(b)
+                                    || /*trailing spaces are not allowed by the spec, but some server put it there*/
+                                    b == HttpParserUtils.CR || b == HttpParserUtils.SEMI_COLON) {
                                 headerParsingState.checkpoint = offset;
                             } else if (b == HttpParserUtils.LF) {
                                 contentParsingState.chunkContentStart = offset + 1;
diff --git a/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/internal/SslFilterTLS11Test.java b/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/internal/SslFilterTLS11Test.java
index 3d1ef6c..fc87b73 100644
--- a/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/internal/SslFilterTLS11Test.java
+++ b/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/internal/SslFilterTLS11Test.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2022 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
diff --git a/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/internal/SslFilterTLS1Test.java b/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/internal/SslFilterTLS1Test.java
index a3a9237..a7293b5 100644
--- a/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/internal/SslFilterTLS1Test.java
+++ b/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/internal/SslFilterTLS1Test.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2022 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
diff --git a/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/internal/SslFilterTest.java b/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/internal/SslFilterTest.java
index 7cef70b..5d205ce 100644
--- a/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/internal/SslFilterTest.java
+++ b/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/internal/SslFilterTest.java
@@ -24,22 +24,29 @@
 import java.net.ServerSocket;
 import java.net.SocketException;
 import java.nio.ByteBuffer;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
-
 import javax.net.ServerSocketFactory;
 import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLException;
 import javax.net.ssl.SSLServerSocket;
 import javax.net.ssl.SSLServerSocketFactory;
 import javax.net.ssl.SSLSession;
 import javax.net.ssl.SSLSocket;
-
 import org.glassfish.jersey.SslConfigurator;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ConditionEvaluationResult;
+import org.junit.jupiter.api.extension.ExecutionCondition;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.extension.ExtensionContext;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -48,6 +55,7 @@
 /**
  * @author Petr Janouch
  */
+@ExtendWith(SslFilterTest.DeprecatedTLSCondition.class)
 public abstract class SslFilterTest {
 
     private static final int PORT = 8321;
@@ -531,4 +539,32 @@
             socket.startHandshake();
         }
     }
+
+    public static class DeprecatedTLSCondition implements ExecutionCondition {
+
+        @Override
+        public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {
+            Class<?> test = context.getTestClass().get();
+            String required = null;
+            if (test == SslFilterTLS1Test.class) {
+                required = "TLSv1";
+            } else if (test == SslFilterTLS11Test.class) {
+                required = "TLSv1.1";
+            }
+            if (required != null) {
+                try {
+                    SSLContext context1 = SSLContext.getInstance("TLS");
+                    context1.init(null, null, null);
+                    List<String> supportedProtocols = Arrays.asList(context1.getDefaultSSLParameters().getProtocols());
+                    if (!supportedProtocols.contains(required)) {
+                        return ConditionEvaluationResult.disabled("JDK does not support " + required);
+                    }
+                } catch (KeyManagementException | NoSuchAlgorithmException e) {
+                    return ConditionEvaluationResult.disabled("JDK does not support TLS: " + e.getMessage());
+                }
+            }
+            return ConditionEvaluationResult.enabled("JDK is valid to run " + test.getCanonicalName());
+        }
+
+    }
 }
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebComponent.java b/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebComponent.java
index 3aaf758..59b1da9 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebComponent.java
+++ b/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebComponent.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -603,7 +603,11 @@
                 final String name = (String) parameterNames.nextElement();
                 final List<String> values = Arrays.asList(servletRequest.getParameterValues(name));
 
-                formMap.put(name, keepQueryParams ? values : filterQueryParams(name, values, queryParams));
+                final List<String> filteredValues = keepQueryParams ? values : filterQueryParams(name, values, queryParams);
+
+                if (!filteredValues.isEmpty()) {
+                    formMap.put(name, filteredValues);
+                }
             }
 
             if (!formMap.isEmpty()) {
diff --git a/containers/jetty-http/src/main/java11/org/glassfish/jersey/jetty/JettyHttpContainer.java b/containers/jetty-http/src/main/java11/org/glassfish/jersey/jetty/JettyHttpContainer.java
index 183fd0f..df67cb5 100644
--- a/containers/jetty-http/src/main/java11/org/glassfish/jersey/jetty/JettyHttpContainer.java
+++ b/containers/jetty-http/src/main/java11/org/glassfish/jersey/jetty/JettyHttpContainer.java
@@ -238,13 +238,9 @@
     }
 
 
-    private URI getBaseUri(final Request request) {
-        try {
-            return new URI(request.getScheme(), null, request.getServerName(),
-                    request.getServerPort(), getBasePath(request), null, null);
-        } catch (final URISyntaxException ex) {
-            throw new IllegalArgumentException(ex);
-        }
+    private URI getBaseUri(final Request request) throws URISyntaxException {
+        return new URI(request.getScheme(), null, request.getServerName(),
+                request.getServerPort(), getBasePath(request), null, null);
     }
 
     private String getBasePath(final Request request) {
diff --git a/containers/jetty-http/src/test/java/org/glassfish/jersey/jetty/ExceptionTest.java b/containers/jetty-http/src/test/java/org/glassfish/jersey/jetty/ExceptionTest.java
index 4d7c43e..7c4c760 100644
--- a/containers/jetty-http/src/test/java/org/glassfish/jersey/jetty/ExceptionTest.java
+++ b/containers/jetty-http/src/test/java/org/glassfish/jersey/jetty/ExceptionTest.java
@@ -66,6 +66,19 @@
     }
 
     @Test
+    public void test400StatusCodeForIllegalHeaderValue() throws IOException {
+        startServer(ExceptionResource.class);
+        URI testUri = getUri().build();
+        BasicHttpRequest request = new BasicHttpRequest("GET", testUri.toString() + "/400");
+        request.addHeader("X-Forwarded-Host", "_foo.com");
+        CloseableHttpClient client = HttpClientBuilder.create().build();
+
+        CloseableHttpResponse response = client.execute(new HttpHost(testUri.getHost(), testUri.getPort()), request);
+
+        assertEquals(400, response.getStatusLine().getStatusCode());
+    }
+
+    @Test
     public void test400StatusCode() throws IOException {
         startServer(ExceptionResource.class);
         Client client = ClientBuilder.newClient();
diff --git a/core-client/src/test/java/org/glassfish/jersey/client/JerseyCompletionStageRxInvokerTest.java b/core-client/src/test/java/org/glassfish/jersey/client/JerseyCompletionStageRxInvokerTest.java
index b55f9e8..d7643aa 100644
--- a/core-client/src/test/java/org/glassfish/jersey/client/JerseyCompletionStageRxInvokerTest.java
+++ b/core-client/src/test/java/org/glassfish/jersey/client/JerseyCompletionStageRxInvokerTest.java
@@ -111,7 +111,7 @@
 
     @Test
     public void testNotFoundReadEntityViaGenericType() throws Throwable {
-        Assertions.assertThrows(NotFoundException.class, () ->{
+        Assertions.assertThrows(NotFoundException.class, () -> {
             try {
                 client.target("http://jersey.java.net")
                       .request()
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/inject/ParamConverters.java b/core-common/src/main/java/org/glassfish/jersey/internal/inject/ParamConverters.java
index 6a234e5..1548ef8 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/inject/ParamConverters.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/inject/ParamConverters.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2018 Payara Foundation and/or its affiliates.
  *
  * This program and the accompanying materials are made available under the
@@ -280,11 +280,15 @@
                     } else {
                         final List<ClassTypePair> ctps = ReflectionHelper.getTypeArgumentAndClass(genericType);
                         final ClassTypePair ctp = (ctps.size() == 1) ? ctps.get(0) : null;
-
+                        final boolean empty = value.isEmpty();
                         for (ParamConverterProvider provider : Providers.getProviders(manager, ParamConverterProvider.class)) {
                             final ParamConverter<?> converter = provider.getConverter(ctp.rawClass(), ctp.type(), annotations);
                             if (converter != null) {
-                                return (T) Optional.of(value).map(s -> converter.fromString(value));
+                                if (empty) {
+                                    return (T) Optional.empty();
+                                } else {
+                                    return (T) Optional.of(value).map(s -> converter.fromString(value));
+                                }
                             }
                         }
                         /*
@@ -323,7 +327,7 @@
 
                 @Override
                 public T fromString(String value) {
-                    if (value == null) {
+                    if (value == null || value.isEmpty()) {
                         return (T) optionals.empty();
                     } else {
                         return (T) optionals.of(value);
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/GuardianStringKeyMultivaluedMap.java b/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/GuardianStringKeyMultivaluedMap.java
new file mode 100644
index 0000000..d92ac56
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/GuardianStringKeyMultivaluedMap.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.internal.util.collection;
+
+import jakarta.ws.rs.core.MultivaluedMap;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * The {@link MultivaluedMap} wrapper that is able to set guards observing changes of values represented by a key.
+ * @param <V> The value type of the wrapped {@code MultivaluedMap}.
+ *
+ * @since 2.38
+ */
+public class GuardianStringKeyMultivaluedMap<V> implements MultivaluedMap<String, V> {
+
+    private final MultivaluedMap<String, V> inner;
+    private final Map<String, Boolean> guards = new HashMap<>();
+
+    public GuardianStringKeyMultivaluedMap(MultivaluedMap<String, V> inner) {
+        this.inner = inner;
+    }
+
+    @Override
+    public void putSingle(String key, V value) {
+        observe(key);
+        inner.putSingle(key, value);
+    }
+
+    @Override
+    public void add(String key, V value) {
+        observe(key);
+        inner.add(key, value);
+    }
+
+    @Override
+    public V getFirst(String key) {
+        return inner.getFirst(key);
+    }
+
+    @Override
+    public void addAll(String key, V... newValues) {
+        observe(key);
+        inner.addAll(key, newValues);
+    }
+
+    @Override
+    public void addAll(String key, List<V> valueList) {
+        observe(key);
+        inner.addAll(key, valueList);
+    }
+
+    @Override
+    public void addFirst(String key, V value) {
+        observe(key);
+        inner.addFirst(key, value);
+    }
+
+    @Override
+    public boolean equalsIgnoreValueOrder(MultivaluedMap<String, V> otherMap) {
+        return inner.equalsIgnoreValueOrder(otherMap);
+    }
+
+    @Override
+    public int size() {
+        return inner.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return inner.isEmpty();
+    }
+
+    @Override
+    public boolean containsKey(Object key) {
+        return inner.containsKey(key);
+    }
+
+    @Override
+    public boolean containsValue(Object value) {
+        return inner.containsValue(value);
+    }
+
+    @Override
+    public List<V> get(Object key) {
+        return inner.get(key);
+    }
+
+    @Override
+    public List<V> put(String key, List<V> value) {
+        observe(key);
+        return inner.put(key, value);
+    }
+
+    @Override
+    public List<V> remove(Object key) {
+        if (key != null) {
+            observe(key.toString());
+        }
+        return inner.remove(key);
+    }
+
+    @Override
+    public void putAll(Map<? extends String, ? extends List<V>> m) {
+        for (String key : m.keySet()) {
+            observe(key);
+        }
+        inner.putAll(m);
+    }
+
+    @Override
+    public void clear() {
+        observeAll();
+        inner.clear();
+    }
+
+    @Override
+    public Set<String> keySet() {
+        return inner.keySet();
+    }
+
+    @Override
+    public Collection<List<V>> values() {
+        return inner.values();
+    }
+
+    @Override
+    public Set<Entry<String, List<V>>> entrySet() {
+        return inner.entrySet();
+    }
+
+    /**
+     * Observe changes of a value represented by the key.
+     * @param key the key values to observe
+     */
+    public void setGuard(String key) {
+        guards.put(key, false);
+    }
+
+    /**
+     * Get all the guarded keys
+     * @return a {@link Set} of keys guarded.
+     */
+    public Set<String> getGuards() {
+        return guards.keySet();
+    }
+
+    /**
+     * Return true when the value represented by the key has changed. Resets any observation - the operation is not idempotent.
+     * @param key the Key observed.
+     * @return whether the value represented by the key has changed.
+     */
+    public boolean isObservedAndReset(String key) {
+        Boolean observed = guards.get(key);
+        guards.put(key, false);
+        return observed != null && observed;
+    }
+
+    private void observe(String key) {
+        for (Map.Entry<String, Boolean> guard : guards.entrySet()) {
+            if (guard.getKey().equals(key)) {
+                guard.setValue(true);
+            }
+        }
+    }
+
+    private void observeAll() {
+        for (Map.Entry<String, Boolean> guard : guards.entrySet()) {
+            guard.setValue(true);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return inner.toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        GuardianStringKeyMultivaluedMap<?> that = (GuardianStringKeyMultivaluedMap<?>) o;
+        return inner.equals(that.inner) && guards.equals(that.guards);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(inner, guards);
+    }
+}
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/LRU.java b/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/LRU.java
new file mode 100644
index 0000000..3338b00
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/LRU.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.internal.util.collection;
+
+import org.glassfish.jersey.internal.guava.Cache;
+import org.glassfish.jersey.internal.guava.CacheBuilder;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An abstract LRU interface wrapping an actual LRU implementation.
+ * @param <K> Key type
+ * @param <V> Value type
+ * @Since 2.38
+ */
+public abstract class LRU<K, V> {
+
+    /**
+     * Returns the value associated with {@code key} in this cache, or {@code null} if there is no
+     * cached value for {@code key}.
+     */
+    public abstract V getIfPresent(Object key);
+
+    /**
+     * Associates {@code value} with {@code key} in this cache. If the cache previously contained a
+     * value associated with {@code key}, the old value is replaced by {@code value}.
+     */
+    public abstract void put(K key, V value);
+
+    /**
+     * Create new LRU
+     * @return new LRU
+     */
+    public static <K, V> LRU<K, V> create() {
+        return LRUFactory.createLRU();
+    }
+
+    private static class LRUFactory {
+        // TODO configure via the Configuration
+        public static final int LRU_CACHE_SIZE = 128;
+        public static final long TIMEOUT = 5000L;
+        private static <K, V> LRU<K, V> createLRU() {
+            final Cache<K, V> CACHE = CacheBuilder.newBuilder()
+                    .maximumSize(LRU_CACHE_SIZE)
+                    .expireAfterAccess(TIMEOUT, TimeUnit.MILLISECONDS)
+                    .build();
+            return new LRU<K, V>() {
+                @Override
+                public V getIfPresent(Object key) {
+                    return CACHE.getIfPresent(key);
+                }
+
+                @Override
+                public void put(K key, V value) {
+                    CACHE.put(key, value);
+                }
+            };
+        }
+    }
+
+
+}
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 8064f66..aeea686 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, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -102,7 +102,7 @@
      *         if the supplied header value is {@code null}.
      */
     @SuppressWarnings("unchecked")
-    private static String asString(final Object headerValue, RuntimeDelegate rd) {
+    public static String asString(final Object headerValue, RuntimeDelegate rd) {
         if (headerValue == null) {
             return null;
         }
@@ -149,7 +149,7 @@
      *                     will be called for before element conversion.
      * @return String view of header values.
      */
-    private static List<String> asStringList(final List<Object> headerValues, final RuntimeDelegate rd) {
+    public static List<String> asStringList(final List<Object> headerValues, final RuntimeDelegate rd) {
         if (headerValues == null || headerValues.isEmpty()) {
             return Collections.emptyList();
         }
@@ -191,7 +191,24 @@
             return null;
         }
 
-        final RuntimeDelegate rd = RuntimeDelegateDecorator.configured(configuration);
+        return asStringHeaders(headers, RuntimeDelegateDecorator.configured(configuration));
+    }
+
+    /**
+     * Returns string view of passed headers. Any modifications to the headers are visible to the view, the view also
+     * supports removal of elements. Does not support other modifications.
+     *
+     * @param headers headers.
+     * @param rd     {@link RuntimeDelegate} instance or {@code null} (in that case {@link RuntimeDelegate#getInstance()}
+     *               will be called for before conversion of elements).
+     * @return String view of headers or {@code null} if {code headers} input parameter is {@code null}.
+     */
+    public static MultivaluedMap<String, String> asStringHeaders(
+            final MultivaluedMap<String, Object> headers, RuntimeDelegate rd) {
+        if (headers == null) {
+            return null;
+        }
+
         return new AbstractMultivaluedMap<String, String>(
                 Views.mapView(headers, input -> HeaderUtils.asStringList(input, rd))
         ) {
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpHeaderReader.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpHeaderReader.java
index c39a016..fe5a249 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpHeaderReader.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/HttpHeaderReader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -23,6 +23,7 @@
 import java.util.Date;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -31,6 +32,7 @@
 import jakarta.ws.rs.core.Cookie;
 import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.NewCookie;
+import org.glassfish.jersey.internal.util.collection.LRU;
 
 /**
  * An abstract pull-based reader of HTTP headers.
@@ -371,61 +373,25 @@
         return l;
     }
 
-    private static final ListElementCreator<MediaType> MEDIA_TYPE_CREATOR =
-            new ListElementCreator<MediaType>() {
-
-                @Override
-                public MediaType create(HttpHeaderReader reader) throws ParseException {
-                    return MediaTypeProvider.valueOf(reader);
-                }
-            };
-
     /**
      * TODO javadoc.
      */
     public static List<MediaType> readMediaTypes(List<MediaType> l, String header) throws ParseException {
-        return HttpHeaderReader.readList(
-                l,
-                MEDIA_TYPE_CREATOR,
-                header);
+        return MEDIA_TYPE_LIST_READER.readList(l, header);
     }
 
-    private static final ListElementCreator<AcceptableMediaType> ACCEPTABLE_MEDIA_TYPE_CREATOR =
-            new ListElementCreator<AcceptableMediaType>() {
-
-                @Override
-                public AcceptableMediaType create(HttpHeaderReader reader) throws ParseException {
-                    return AcceptableMediaType.valueOf(reader);
-                }
-            };
-
     /**
      * TODO javadoc.
      */
     public static List<AcceptableMediaType> readAcceptMediaType(String header) throws ParseException {
-        return HttpHeaderReader.readQualifiedList(
-                AcceptableMediaType.COMPARATOR,
-                ACCEPTABLE_MEDIA_TYPE_CREATOR,
-                header);
+        return ACCEPTABLE_MEDIA_TYPE_LIST_READER.readList(header);
     }
 
-    private static final ListElementCreator<QualitySourceMediaType> QUALITY_SOURCE_MEDIA_TYPE_CREATOR =
-            new ListElementCreator<QualitySourceMediaType>() {
-
-                @Override
-                public QualitySourceMediaType create(HttpHeaderReader reader) throws ParseException {
-                    return QualitySourceMediaType.valueOf(reader);
-                }
-            };
-
     /**
      * FIXME use somewhere in production code or remove.
      */
     public static List<QualitySourceMediaType> readQualitySourceMediaType(String header) throws ParseException {
-        return HttpHeaderReader.readQualifiedList(
-                QualitySourceMediaType.COMPARATOR,
-                QUALITY_SOURCE_MEDIA_TYPE_CREATOR,
-                header);
+        return QUALITY_SOURCE_MEDIA_TYPE_LIST_READER.readList(header);
     }
 
     /**
@@ -454,121 +420,246 @@
     public static List<AcceptableMediaType> readAcceptMediaType(
             final String header, final List<QualitySourceMediaType> priorityMediaTypes) throws ParseException {
 
-        return HttpHeaderReader.readQualifiedList(
-                new Comparator<AcceptableMediaType>() {
-
-                    @Override
-                    public int compare(AcceptableMediaType o1, AcceptableMediaType o2) {
-                        // FIXME what is going on here?
-                        boolean q_o1_set = false;
-                        int q_o1 = 0;
-                        boolean q_o2_set = false;
-                        int q_o2 = 0;
-                        for (QualitySourceMediaType priorityType : priorityMediaTypes) {
-                            if (!q_o1_set && MediaTypes.typeEqual(o1, priorityType)) {
-                                q_o1 = o1.getQuality() * priorityType.getQuality();
-                                q_o1_set = true;
-                            } else if (!q_o2_set && MediaTypes.typeEqual(o2, priorityType)) {
-                                q_o2 = o2.getQuality() * priorityType.getQuality();
-                                q_o2_set = true;
-                            }
-                        }
-                        int i = q_o2 - q_o1;
-                        if (i != 0) {
-                            return i;
-                        }
-
-                        i = o2.getQuality() - o1.getQuality();
-                        if (i != 0) {
-                            return i;
-                        }
-
-                        return MediaTypes.PARTIAL_ORDER_COMPARATOR.compare(o1, o2);
-                    }
-                },
-                ACCEPTABLE_MEDIA_TYPE_CREATOR,
-                header);
+        return new AcceptMediaTypeListReader(priorityMediaTypes).readList(header);
     }
-    private static final ListElementCreator<AcceptableToken> ACCEPTABLE_TOKEN_CREATOR =
-            new ListElementCreator<AcceptableToken>() {
-
-                @Override
-                public AcceptableToken create(HttpHeaderReader reader) throws ParseException {
-                    return new AcceptableToken(reader);
-                }
-            };
 
     /**
      * TODO javadoc.
      */
     public static List<AcceptableToken> readAcceptToken(String header) throws ParseException {
-        return HttpHeaderReader.readQualifiedList(ACCEPTABLE_TOKEN_CREATOR, header);
+        return ACCEPTABLE_TOKEN_LIST_READER.readList(header);
     }
 
-    private static final ListElementCreator<AcceptableLanguageTag> LANGUAGE_CREATOR =
-            new ListElementCreator<AcceptableLanguageTag>() {
-
-                @Override
-                public AcceptableLanguageTag create(HttpHeaderReader reader) throws ParseException {
-                    return new AcceptableLanguageTag(reader);
-                }
-            };
-
     /**
      * TODO javadoc.
      */
     public static List<AcceptableLanguageTag> readAcceptLanguage(String header) throws ParseException {
-        return HttpHeaderReader.readQualifiedList(LANGUAGE_CREATOR, header);
-    }
-
-    private static <T extends Qualified> List<T> readQualifiedList(ListElementCreator<T> c, String header)
-            throws ParseException {
-
-        List<T> l = readList(c, header);
-        Collections.sort(l, Quality.QUALIFIED_COMPARATOR);
-        return l;
-    }
-
-    private static <T> List<T> readQualifiedList(final Comparator<T> comparator, ListElementCreator<T> c, String header)
-            throws ParseException {
-
-        List<T> l = readList(c, header);
-        Collections.sort(l, comparator);
-        return l;
+        return ACCEPTABLE_LANGUAGE_TAG_LIST_READER.readList(header);
     }
 
     /**
      * TODO javadoc.
      */
     public static List<String> readStringList(String header) throws ParseException {
-        return readList(new ListElementCreator<String>() {
+        return STRING_LIST_READER.readList(header);
+    }
 
+    private static final MediaTypeListReader MEDIA_TYPE_LIST_READER = new MediaTypeListReader();
+    private static final AcceptableMediaTypeListReader ACCEPTABLE_MEDIA_TYPE_LIST_READER = new AcceptableMediaTypeListReader();
+    private static final QualitySourceMediaTypeListReader QUALITY_SOURCE_MEDIA_TYPE_LIST_READER =
+            new QualitySourceMediaTypeListReader();
+    private static final AcceptableTokenListReader ACCEPTABLE_TOKEN_LIST_READER = new AcceptableTokenListReader();
+    private static final AcceptableLanguageTagListReader ACCEPTABLE_LANGUAGE_TAG_LIST_READER =
+            new AcceptableLanguageTagListReader();
+    private static final StringListReader STRING_LIST_READER = new StringListReader();
+
+    private static class MediaTypeListReader extends ListReader<MediaType> {
+        private static final ListElementCreator<MediaType> MEDIA_TYPE_CREATOR =
+                new ListElementCreator<MediaType>() {
+
+                    @Override
+                    public MediaType create(HttpHeaderReader reader) throws ParseException {
+                        return MediaTypeProvider.valueOf(reader);
+                    }
+                };
+
+        List<MediaType> readList(List<MediaType> l, final String header) throws ParseException {
+            return super.readList(l, header);
+        }
+
+        private MediaTypeListReader() {
+            super(MEDIA_TYPE_CREATOR);
+        }
+    }
+
+    private static class AcceptableMediaTypeListReader extends QualifiedListReader<AcceptableMediaType> {
+        private static final ListElementCreator<AcceptableMediaType> ACCEPTABLE_MEDIA_TYPE_CREATOR =
+                new ListElementCreator<AcceptableMediaType>() {
+
+                    @Override
+                    public AcceptableMediaType create(HttpHeaderReader reader) throws ParseException {
+                        return AcceptableMediaType.valueOf(reader);
+                    }
+                };
+        private AcceptableMediaTypeListReader() {
+            super(ACCEPTABLE_MEDIA_TYPE_CREATOR, AcceptableMediaType.COMPARATOR);
+        }
+    }
+    /*
+     * TODO not used in production?
+     */
+    private static class QualitySourceMediaTypeListReader extends QualifiedListReader<QualitySourceMediaType> {
+        private static final ListElementCreator<QualitySourceMediaType> QUALITY_SOURCE_MEDIA_TYPE_CREATOR =
+                new ListElementCreator<QualitySourceMediaType>() {
+
+                    @Override
+                    public QualitySourceMediaType create(HttpHeaderReader reader) throws ParseException {
+                        return QualitySourceMediaType.valueOf(reader);
+                    }
+                };
+        private QualitySourceMediaTypeListReader() {
+            super(QUALITY_SOURCE_MEDIA_TYPE_CREATOR, QualitySourceMediaType.COMPARATOR);
+        }
+    }
+
+    /*
+     * TODO this is used in tests only
+     */
+    private static class AcceptMediaTypeListReader extends QualifiedListReader<AcceptableMediaType> {
+        AcceptMediaTypeListReader(List<QualitySourceMediaType> priorityMediaTypes) {
+            super(ACCEPTABLE_MEDIA_TYPE_CREATOR, new AcceptableMediaTypeComparator(priorityMediaTypes));
+        }
+
+        private static final ListElementCreator<AcceptableMediaType> ACCEPTABLE_MEDIA_TYPE_CREATOR =
+                new ListElementCreator<AcceptableMediaType>() {
+
+                    @Override
+                    public AcceptableMediaType create(HttpHeaderReader reader) throws ParseException {
+                        return AcceptableMediaType.valueOf(reader);
+                    }
+                };
+
+        private static class AcceptableMediaTypeComparator implements Comparator<AcceptableMediaType> {
+            private final List<QualitySourceMediaType> priorityMediaTypes;
+
+            private AcceptableMediaTypeComparator(List<QualitySourceMediaType> priorityMediaTypes) {
+                this.priorityMediaTypes = priorityMediaTypes;
+            }
+
+            @Override
+            public int compare(AcceptableMediaType o1, AcceptableMediaType o2) {
+                // FIXME what is going on here?
+                boolean q_o1_set = false;
+                int q_o1 = 0;
+                boolean q_o2_set = false;
+                int q_o2 = 0;
+                for (QualitySourceMediaType priorityType : priorityMediaTypes) {
+                    if (!q_o1_set && MediaTypes.typeEqual(o1, priorityType)) {
+                        q_o1 = o1.getQuality() * priorityType.getQuality();
+                        q_o1_set = true;
+                    } else if (!q_o2_set && MediaTypes.typeEqual(o2, priorityType)) {
+                        q_o2 = o2.getQuality() * priorityType.getQuality();
+                        q_o2_set = true;
+                    }
+                }
+                int i = q_o2 - q_o1;
+                if (i != 0) {
+                    return i;
+                }
+
+                i = o2.getQuality() - o1.getQuality();
+                if (i != 0) {
+                    return i;
+                }
+
+                return MediaTypes.PARTIAL_ORDER_COMPARATOR.compare(o1, o2);
+            }
+        };
+
+
+    }
+
+    private static class AcceptableTokenListReader extends QualifiedListReader<AcceptableToken> {
+        private static final ListElementCreator<AcceptableToken> ACCEPTABLE_TOKEN_CREATOR =
+                new ListElementCreator<AcceptableToken>() {
+
+                    @Override
+                    public AcceptableToken create(HttpHeaderReader reader) throws ParseException {
+                        return new AcceptableToken(reader);
+                    }
+                };
+        private AcceptableTokenListReader() {
+            super(ACCEPTABLE_TOKEN_CREATOR);
+        }
+    }
+
+    private static class AcceptableLanguageTagListReader extends QualifiedListReader<AcceptableLanguageTag> {
+        private static final ListElementCreator<AcceptableLanguageTag> LANGUAGE_CREATOR =
+                new ListElementCreator<AcceptableLanguageTag>() {
+
+                    @Override
+                    public AcceptableLanguageTag create(HttpHeaderReader reader) throws ParseException {
+                        return new AcceptableLanguageTag(reader);
+                    }
+                };
+        private AcceptableLanguageTagListReader() {
+            super(LANGUAGE_CREATOR);
+        }
+    }
+
+    private abstract static class QualifiedListReader<T extends Qualified> extends ListReader<T> {
+        @Override
+        public List<T> readList(String header) throws ParseException {
+            List<T> l = super.readList(header);
+            Collections.sort(l, comparator);
+            return l;
+        }
+
+        private final Comparator<T> comparator;
+        private QualifiedListReader(ListElementCreator<T> creator) {
+            this(creator, (Comparator<T>) Quality.QUALIFIED_COMPARATOR);
+        }
+
+        protected QualifiedListReader(ListElementCreator<T> creator, Comparator<T> comparator) {
+            super(creator);
+            this.comparator = comparator;
+        }
+    }
+
+    private static class StringListReader extends ListReader<String> {
+        private static final ListElementCreator<String> listElementCreator = new ListElementCreator<String>() {
             @Override
             public String create(HttpHeaderReader reader) throws ParseException {
                 reader.hasNext();
                 return reader.nextToken().toString();
             }
-        }, header);
+        };
+
+        private StringListReader() {
+            super(listElementCreator);
+        }
     }
 
-    private static <T> List<T> readList(final ListElementCreator<T> c, final String header) throws ParseException {
-        return readList(new ArrayList<T>(), c, header);
-    }
+    private abstract static class ListReader<T> {
+        private final LRU<String, List<T>> LIST_CACHE = LRU.create();
+        protected final ListElementCreator<T> creator;
 
-    private static <T> List<T> readList(final List<T> l, final ListElementCreator<T> c, final String header)
-            throws ParseException {
-
-        HttpHeaderReader reader = new HttpHeaderReaderImpl(header);
-        HttpHeaderListAdapter adapter = new HttpHeaderListAdapter(reader);
-
-        while (reader.hasNext()) {
-            l.add(c.create(adapter));
-            adapter.reset();
-            if (reader.hasNext()) {
-                reader.next();
-            }
+        protected ListReader(ListElementCreator<T> creator) {
+            this.creator = creator;
         }
 
-        return l;
+        protected List<T> readList(final String header) throws ParseException {
+            return readList(new ArrayList<T>(), header);
+        }
+
+        private List<T> readList(final List<T> l, final String header)
+                throws ParseException {
+
+//            List<T> list = null;
+            List<T> list = LIST_CACHE.getIfPresent(header);
+
+            if (list == null) {
+                synchronized (LIST_CACHE) {
+                    list = LIST_CACHE.getIfPresent(header);
+                    if (list == null) {
+                        HttpHeaderReader reader = new HttpHeaderReaderImpl(header);
+                        HttpHeaderListAdapter adapter = new HttpHeaderListAdapter(reader);
+                        list = new LinkedList<>();
+
+                        while (reader.hasNext()) {
+                            list.add(creator.create(adapter));
+                            adapter.reset();
+                            if (reader.hasNext()) {
+                                reader.next();
+                            }
+                        }
+                        LIST_CACHE.put(header, list);
+                    }
+                }
+            }
+
+            l.addAll(list);
+            return l;
+        }
     }
 }
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 7e70d3b..9eb4a40 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
@@ -50,11 +50,16 @@
 import jakarta.ws.rs.core.NewCookie;
 import jakarta.ws.rs.ext.ReaderInterceptor;
 
+import jakarta.ws.rs.ext.RuntimeDelegate;
 import javax.xml.transform.Source;
 
 import org.glassfish.jersey.internal.LocalizationMessages;
 import org.glassfish.jersey.internal.PropertiesDelegate;
 import org.glassfish.jersey.internal.RuntimeDelegateDecorator;
+import org.glassfish.jersey.internal.util.collection.GuardianStringKeyMultivaluedMap;
+import org.glassfish.jersey.internal.util.collection.LazyValue;
+import org.glassfish.jersey.internal.util.collection.Value;
+import org.glassfish.jersey.internal.util.collection.Values;
 import org.glassfish.jersey.message.MessageBodyWorkers;
 
 /**
@@ -90,11 +95,14 @@
     private static final List<AcceptableMediaType> WILDCARD_ACCEPTABLE_TYPE_SINGLETON_LIST =
             Collections.singletonList(MediaTypes.WILDCARD_ACCEPTABLE_TYPE);
 
-    private final MultivaluedMap<String, String> headers;
+    private final GuardianStringKeyMultivaluedMap<String> headers;
     private final EntityContent entityContent;
     private final boolean translateNce;
     private MessageBodyWorkers workers;
     private final Configuration configuration;
+    private final RuntimeDelegate runtimeDelegateDecorator;
+    private LazyValue<MediaType> contentTypeCache;
+    private LazyValue<List<AcceptableMediaType>> acceptTypeCache;
 
     /**
      * Input stream and its state. State is represented by the {@link Type Type enum} and
@@ -158,10 +166,16 @@
      *                      as required by JAX-RS specification on the server side.
      */
     public InboundMessageContext(Configuration configuration, boolean translateNce) {
-        this.headers = HeaderUtils.createInbound();
+        this.headers = new GuardianStringKeyMultivaluedMap<>(HeaderUtils.createInbound());
         this.entityContent = new EntityContent();
         this.translateNce = translateNce;
         this.configuration = configuration;
+        runtimeDelegateDecorator = RuntimeDelegateDecorator.configured(configuration);
+
+        contentTypeCache = contentTypeCache();
+        acceptTypeCache = acceptTypeCache();
+        headers.setGuard(HttpHeaders.CONTENT_TYPE);
+        headers.setGuard(HttpHeaders.ACCEPT);
     }
 
     /**
@@ -196,7 +210,7 @@
      * @return updated context.
      */
     public InboundMessageContext header(String name, Object value) {
-        getHeaders().add(name, HeaderUtils.asString(value, configuration));
+        getHeaders().add(name, HeaderUtils.asString(value, runtimeDelegateDecorator));
         return this;
     }
 
@@ -208,7 +222,7 @@
      * @return updated context.
      */
     public InboundMessageContext headers(String name, Object... values) {
-        this.getHeaders().addAll(name, HeaderUtils.asStringList(Arrays.asList(values), configuration));
+        this.getHeaders().addAll(name, HeaderUtils.asStringList(Arrays.asList(values), runtimeDelegateDecorator));
         return this;
     }
 
@@ -265,7 +279,7 @@
         final LinkedList<String> linkedList = new LinkedList<String>();
 
         for (Object element : values) {
-            linkedList.add(HeaderUtils.asString(element, configuration));
+            linkedList.add(HeaderUtils.asString(element, runtimeDelegateDecorator));
         }
 
         return linkedList;
@@ -332,7 +346,7 @@
         }
 
         try {
-            return converter.apply(HeaderUtils.asString(value, configuration));
+            return converter.apply(HeaderUtils.asString(value, runtimeDelegateDecorator));
         } catch (ProcessingException ex) {
             throw exception(name, value, ex);
         }
@@ -447,18 +461,26 @@
      * message entity).
      */
     public MediaType getMediaType() {
-        return singleHeader(HttpHeaders.CONTENT_TYPE, new Function<String, MediaType>() {
-            @Override
-            public MediaType apply(String input) {
-                try {
-                    return RuntimeDelegateDecorator.configured(configuration)
-                            .createHeaderDelegate(MediaType.class)
-                            .fromString(input);
-                } catch (IllegalArgumentException iae) {
-                    throw new ProcessingException(iae);
-                }
-            }
-        }, false);
+        if (headers.isObservedAndReset(HttpHeaders.CONTENT_TYPE) && contentTypeCache.isInitialized()) {
+            contentTypeCache = contentTypeCache(); // headers changed -> drop cache
+        }
+        return contentTypeCache.get();
+    }
+
+    private LazyValue<MediaType> contentTypeCache() {
+        return Values.lazy((Value<MediaType>) () -> singleHeader(
+                HttpHeaders.CONTENT_TYPE, new Function<String, MediaType>() {
+                    @Override
+                    public MediaType apply(String input) {
+                        try {
+                            return runtimeDelegateDecorator
+                                    .createHeaderDelegate(MediaType.class)
+                                    .fromString(input);
+                        } catch (IllegalArgumentException iae) {
+                            throw new ProcessingException(iae);
+                        }
+                    }
+                }, false));
     }
 
     /**
@@ -468,17 +490,26 @@
      * to their q-value, with highest preference first.
      */
     public List<AcceptableMediaType> getQualifiedAcceptableMediaTypes() {
-        final String value = getHeaderString(HttpHeaders.ACCEPT);
-
-        if (value == null || value.isEmpty()) {
-            return WILDCARD_ACCEPTABLE_TYPE_SINGLETON_LIST;
+        if (headers.isObservedAndReset(HttpHeaders.ACCEPT) && acceptTypeCache.isInitialized()) {
+            acceptTypeCache = acceptTypeCache();
         }
+        return acceptTypeCache.get();
+    }
 
-        try {
-            return Collections.unmodifiableList(HttpHeaderReader.readAcceptMediaType(value));
-        } catch (ParseException e) {
-            throw exception(HttpHeaders.ACCEPT, value, e);
-        }
+    private LazyValue<List<AcceptableMediaType>> acceptTypeCache() {
+        return Values.lazy((Value<List<AcceptableMediaType>>) () -> {
+            final String value = getHeaderString(HttpHeaders.ACCEPT);
+
+            if (value == null || value.isEmpty()) {
+                return WILDCARD_ACCEPTABLE_TYPE_SINGLETON_LIST;
+            }
+
+            try {
+                return Collections.unmodifiableList(HttpHeaderReader.readAcceptMediaType(value));
+            } catch (ParseException e) {
+                throw exception(HttpHeaders.ACCEPT, value, e);
+            }
+        });
     }
 
     /**
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java
index 3a7748c..b9ab253 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -47,11 +47,16 @@
 import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.MultivaluedMap;
 import jakarta.ws.rs.core.NewCookie;
+import jakarta.ws.rs.ext.RuntimeDelegate;
 
 import org.glassfish.jersey.CommonProperties;
 import org.glassfish.jersey.internal.RuntimeDelegateDecorator;
 import org.glassfish.jersey.internal.LocalizationMessages;
 import org.glassfish.jersey.internal.util.ReflectionHelper;
+import org.glassfish.jersey.internal.util.collection.GuardianStringKeyMultivaluedMap;
+import org.glassfish.jersey.internal.util.collection.LazyValue;
+import org.glassfish.jersey.internal.util.collection.Value;
+import org.glassfish.jersey.internal.util.collection.Values;
 
 /**
  * Base outbound message context implementation.
@@ -63,9 +68,11 @@
     private static final List<MediaType> WILDCARD_ACCEPTABLE_TYPE_SINGLETON_LIST =
             Collections.<MediaType>singletonList(MediaTypes.WILDCARD_ACCEPTABLE_TYPE);
 
-    private final MultivaluedMap<String, Object> headers;
+    private final GuardianStringKeyMultivaluedMap<Object> headers;
     private final CommittingOutputStream committingOutputStream;
     private Configuration configuration;
+    private RuntimeDelegate runtimeDelegateDecorator;
+    private LazyValue<MediaType> mediaTypeCache;
 
     private Object entity;
     private GenericType<?> entityType;
@@ -101,9 +108,13 @@
      */
     public OutboundMessageContext(Configuration configuration) {
         this.configuration = configuration;
-        this.headers = HeaderUtils.createOutbound();
+        this.headers = new GuardianStringKeyMultivaluedMap<>(HeaderUtils.createOutbound());
         this.committingOutputStream = new CommittingOutputStream();
         this.entityStream = committingOutputStream;
+        this.runtimeDelegateDecorator = RuntimeDelegateDecorator.configured(configuration);
+        this.mediaTypeCache = mediaTypeCache();
+
+        headers.setGuard(HttpHeaders.CONTENT_LENGTH);
     }
 
     /**
@@ -113,7 +124,8 @@
      * @param original the original outbound message context.
      */
     public OutboundMessageContext(OutboundMessageContext original) {
-        this.headers = HeaderUtils.createOutbound();
+        this.headers = new GuardianStringKeyMultivaluedMap<>(HeaderUtils.createOutbound());
+        this.headers.setGuard(HttpHeaders.CONTENT_LENGTH);
         this.headers.putAll(original.headers);
         this.committingOutputStream = new CommittingOutputStream();
         this.entityStream = committingOutputStream;
@@ -122,6 +134,8 @@
         this.entityType = original.entityType;
         this.entityAnnotations = original.entityAnnotations;
         this.configuration = original.configuration;
+        this.runtimeDelegateDecorator = original.runtimeDelegateDecorator;
+        this.mediaTypeCache = original.mediaTypeCache();
     }
 
     /**
@@ -153,7 +167,7 @@
      * @return multi-valued map of outbound message header names to their string-converted values.
      */
     public MultivaluedMap<String, String> getStringHeaders() {
-        return HeaderUtils.asStringHeaders(headers, configuration);
+        return HeaderUtils.asStringHeaders(headers, runtimeDelegateDecorator);
     }
 
     /**
@@ -173,7 +187,7 @@
      * character.
      */
     public String getHeaderString(String name) {
-        return HeaderUtils.asHeaderString(headers.get(name), RuntimeDelegateDecorator.configured(configuration));
+        return HeaderUtils.asHeaderString(headers.get(name), runtimeDelegateDecorator);
     }
 
     /**
@@ -209,7 +223,7 @@
             return valueType.cast(value);
         } else {
             try {
-                return converter.apply(HeaderUtils.asString(value, null));
+                return converter.apply(HeaderUtils.asString(value, runtimeDelegateDecorator));
             } catch (ProcessingException ex) {
                 throw exception(name, value, ex);
             }
@@ -267,8 +281,17 @@
      * message entity).
      */
     public MediaType getMediaType() {
-        return singleHeader(HttpHeaders.CONTENT_TYPE, MediaType.class, RuntimeDelegateDecorator.configured(configuration)
-                .createHeaderDelegate(MediaType.class)::fromString, false);
+        if (headers.isObservedAndReset(HttpHeaders.CONTENT_TYPE) && mediaTypeCache.isInitialized()) {
+            mediaTypeCache = mediaTypeCache(); // headers changed -> drop cache
+        }
+        return mediaTypeCache.get();
+    }
+
+    private LazyValue<MediaType> mediaTypeCache() {
+        return Values.lazy((Value<MediaType>) () ->
+                singleHeader(HttpHeaders.CONTENT_TYPE, MediaType.class, RuntimeDelegateDecorator.configured(configuration)
+                    .createHeaderDelegate(MediaType.class)::fromString, false)
+        );
     }
 
     /**
@@ -294,7 +317,7 @@
                     result.add(_value);
                 } else {
                     conversionApplied = true;
-                    result.addAll(HttpHeaderReader.readAcceptMediaType(HeaderUtils.asString(value, configuration)));
+                    result.addAll(HttpHeaderReader.readAcceptMediaType(HeaderUtils.asString(value, runtimeDelegateDecorator)));
                 }
             } catch (java.text.ParseException e) {
                 throw exception(HttpHeaders.ACCEPT, value, e);
@@ -333,7 +356,7 @@
             } else {
                 conversionApplied = true;
                 try {
-                    result.addAll(HttpHeaderReader.readAcceptLanguage(HeaderUtils.asString(value, configuration))
+                    result.addAll(HttpHeaderReader.readAcceptLanguage(HeaderUtils.asString(value, runtimeDelegateDecorator))
                                                   .stream()
                                                   .map(LanguageTag::getAsLocale)
                                                   .collect(Collectors.toList()));
@@ -366,7 +389,7 @@
         }
 
         Map<String, Cookie> result = new HashMap<String, Cookie>();
-        for (String cookie : HeaderUtils.asStringList(cookies, configuration)) {
+        for (String cookie : HeaderUtils.asStringList(cookies, runtimeDelegateDecorator)) {
             if (cookie != null) {
                 result.putAll(HttpHeaderReader.readCookies(cookie));
             }
@@ -454,7 +477,7 @@
         }
 
         Map<String, NewCookie> result = new HashMap<String, NewCookie>();
-        for (String cookie : HeaderUtils.asStringList(cookies, configuration)) {
+        for (String cookie : HeaderUtils.asStringList(cookies, runtimeDelegateDecorator)) {
             if (cookie != null) {
                 NewCookie newCookie = HttpHeaderReader.readNewCookie(cookie);
                 String cookieName = newCookie.getName();
@@ -542,7 +565,7 @@
             } else {
                 conversionApplied = true;
                 try {
-                    result.add(Link.valueOf(HeaderUtils.asString(value, configuration)));
+                    result.add(Link.valueOf(HeaderUtils.asString(value, runtimeDelegateDecorator)));
                 } catch (IllegalArgumentException e) {
                     throw exception(HttpHeaders.LINK, value, e);
                 }
@@ -863,6 +886,7 @@
 
     void setConfiguration(Configuration configuration) {
         this.configuration = configuration;
+        this.runtimeDelegateDecorator = RuntimeDelegateDecorator.configured(configuration);
     }
 
     /**
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 d4b4a05..f9ee35a 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, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -19,13 +19,15 @@
 import java.io.Closeable;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.io.OutputStreamWriter;
 import java.io.Reader;
 import java.io.Writer;
 import java.nio.charset.Charset;
 import java.security.AccessController;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -134,7 +136,7 @@
      * @throws IOException if there is an error reading from the input stream.
      */
     public static String readFromAsString(InputStream in, MediaType type) throws IOException {
-        return readFromAsString(new InputStreamReader(in, getCharset(type)));
+        return new String(readAllBytes(in), getCharset(type));
     }
 
     /**
@@ -154,6 +156,73 @@
         }
         return sb.toString();
     }
+    /**
+     * The maximum size of 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
+     */
+    private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
+
+    /**
+     * Java 9+ InputStream::readAllBytes
+     * TODO Replace when Java 8 not any longer supported (3.1+)
+     */
+    private static byte[] readAllBytes(InputStream inputStream) throws IOException {
+        List<byte[]> bufs = null;
+        byte[] result = null;
+        int total = 0;
+        int remaining = Integer.MAX_VALUE;
+        int n;
+        do {
+            byte[] buf = new byte[Math.min(remaining, BUFFER_SIZE)];
+            int nread = 0;
+
+            // read to EOF which may read more or less than buffer size
+            while ((n = inputStream.read(buf, nread,
+                    Math.min(buf.length - nread, remaining))) > 0) {
+                nread += n;
+                remaining -= n;
+            }
+
+            if (nread > 0) {
+                if (MAX_BUFFER_SIZE - total < nread) {
+                    throw new OutOfMemoryError("Required array size too large");
+                }
+                total += nread;
+                if (result == null) {
+                    result = buf;
+                } else {
+                    if (bufs == null) {
+                        bufs = new ArrayList<>();
+                        bufs.add(result);
+                    }
+                    bufs.add(buf);
+                }
+            }
+            // if the last call to read returned -1 or the number of bytes
+            // requested have been read then break
+        } while (n >= 0 && remaining > 0);
+
+        if (bufs == null) {
+            if (result == null) {
+                return new byte[0];
+            }
+            return result.length == total ? result : Arrays.copyOf(result, total);
+        }
+
+        result = new byte[total];
+        int offset = 0;
+        remaining = total;
+        for (byte[] b : bufs) {
+            int count = Math.min(b.length, remaining);
+            System.arraycopy(b, 0, result, offset, count);
+            offset += count;
+            remaining -= count;
+        }
+
+        return result;
+    }
 
     /**
      * Convert a string to bytes and write those bytes to an output stream.
diff --git a/core-server/src/test/java/org/glassfish/jersey/server/RequestContextBuilder.java b/core-server/src/test/java/org/glassfish/jersey/server/RequestContextBuilder.java
index bdf4ef5..99e5022 100644
--- a/core-server/src/test/java/org/glassfish/jersey/server/RequestContextBuilder.java
+++ b/core-server/src/test/java/org/glassfish/jersey/server/RequestContextBuilder.java
@@ -28,6 +28,7 @@
 import java.util.logging.Logger;
 
 import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.core.Configuration;
 import jakarta.ws.rs.core.Cookie;
 import jakarta.ws.rs.core.GenericEntity;
 import jakarta.ws.rs.core.GenericType;
@@ -36,7 +37,6 @@
 import jakarta.ws.rs.core.MultivaluedHashMap;
 import jakarta.ws.rs.core.MultivaluedMap;
 import jakarta.ws.rs.core.SecurityContext;
-import jakarta.ws.rs.ext.RuntimeDelegate;
 import jakarta.ws.rs.ext.WriterInterceptor;
 
 import org.glassfish.jersey.internal.MapPropertiesDelegate;
@@ -162,7 +162,7 @@
     }
 
     public RequestContextBuilder type(final MediaType contentType) {
-        request.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, HeaderUtils.asString(contentType, null));
+        request.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, HeaderUtils.asString(contentType, (Configuration) null));
         return this;
     }
 
@@ -186,7 +186,7 @@
             request.getHeaders().remove(name);
             return;
         }
-        request.header(name, HeaderUtils.asString(value, null));
+        request.header(name, HeaderUtils.asString(value, (Configuration) null));
     }
 
     private void putHeaders(final String name, final Object... values) {
@@ -194,7 +194,7 @@
             request.getHeaders().remove(name);
             return;
         }
-        request.getHeaders().addAll(name, HeaderUtils.asStringList(Arrays.asList(values), null));
+        request.getHeaders().addAll(name, HeaderUtils.asStringList(Arrays.asList(values), (Configuration) null));
     }
 
     private void putHeaders(final String name, final String... values) {
diff --git a/docs/src/main/docbook/getting-started.xml b/docs/src/main/docbook/getting-started.xml
index 47adf87..5553d50 100644
--- a/docs/src/main/docbook/getting-started.xml
+++ b/docs/src/main/docbook/getting-started.xml
@@ -51,10 +51,10 @@
                     configuration needs to be added to your Maven project pom:
 
                     <programlisting language="xml" linenumbering="unnumbered">&lt;snapshotRepository&gt;
-                        &lt;id&gt;ossrh&lt;/id&gt;
-                    &lt;name&gt;Sonatype Nexus Snapshots&lt;/name&gt;
-                &lt;url&gt;https://jakarta.oss.sonatype.org/content/repositories/snapshots/&lt;/url&gt;
-            &lt;/snapshotRepository&gt;</programlisting>
+    &lt;id&gt;ossrh&lt;/id&gt;
+    &lt;name&gt;Sonatype Nexus Snapshots&lt;/name&gt;
+    &lt;url&gt;https://jakarta.oss.sonatype.org/content/repositories/snapshots/&lt;/url&gt;
+&lt;/snapshotRepository&gt;</programlisting>
                 </para>
             </note>
         </para>
diff --git a/examples/extended-wadl-webapp/pom.xml b/examples/extended-wadl-webapp/pom.xml
index a6bf0d2..91c4d64 100644
--- a/examples/extended-wadl-webapp/pom.xml
+++ b/examples/extended-wadl-webapp/pom.xml
@@ -259,7 +259,7 @@
                     <dependency>
                         <groupId>org.apache.maven.surefire</groupId>
                         <artifactId>surefire-junit47</artifactId>
-                        <version>${surefire.version}</version>
+                        <version>${surefire.mvn.plugin.version}</version>
                     </dependency>
                 </dependencies>
             </plugin>
diff --git a/examples/groovy/pom.xml b/examples/groovy/pom.xml
index 2038f89..89ca79c 100644
--- a/examples/groovy/pom.xml
+++ b/examples/groovy/pom.xml
@@ -28,7 +28,7 @@
             <groupId>org.apache.groovy</groupId>
             <artifactId>groovy-all</artifactId>
             <type>pom</type>
-            <version>4.0.4</version>
+            <version>${groovy.version}</version>
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.test-framework.providers</groupId>
diff --git a/examples/helloworld-weld/src/test/java/org/glassfish/jersey/examples/helloworld/RequestScopedResourceTest.java b/examples/helloworld-weld/src/test/java/org/glassfish/jersey/examples/helloworld/RequestScopedResourceTest.java
index 0821761..f39e30e 100644
--- a/examples/helloworld-weld/src/test/java/org/glassfish/jersey/examples/helloworld/RequestScopedResourceTest.java
+++ b/examples/helloworld-weld/src/test/java/org/glassfish/jersey/examples/helloworld/RequestScopedResourceTest.java
@@ -102,6 +102,10 @@
     @AfterEach
     public void tearDown() throws Exception {
         super.tearDown();
+    }
+
+    @AfterAll
+    public void report() {
         System.out.printf("SYNC: %d, ASYNC: %d, STRAIGHT: %d%n",
                 parameterizedCounter.intValue(), parameterizedAsyncCounter.intValue(), straightCounter.intValue());
     }
diff --git a/examples/http-patch/pom.xml b/examples/http-patch/pom.xml
index 47b7302..2831595 100644
--- a/examples/http-patch/pom.xml
+++ b/examples/http-patch/pom.xml
@@ -43,6 +43,19 @@
             <groupId>com.github.fge</groupId>
             <artifactId>json-patch</artifactId>
             <version>1.9</version>
+            <!-- Remove when a new higher version depends on Guava 24+  -->
+            <exclusions>
+                <exclusion>
+                    <groupId>com.google.guava</groupId>
+                    <artifactId>guava</artifactId>
+                </exclusion>
+            </exclusions> 
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>${guava.version}</version>
+            <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.media</groupId>
diff --git a/examples/open-tracing/src/main/java/org/glassfish/jersey/examples/opentracing/App.java b/examples/open-tracing/src/main/java/org/glassfish/jersey/examples/opentracing/App.java
index 6b39f57..c92f440 100644
--- a/examples/open-tracing/src/main/java/org/glassfish/jersey/examples/opentracing/App.java
+++ b/examples/open-tracing/src/main/java/org/glassfish/jersey/examples/opentracing/App.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2022 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Distribution License v. 1.0, which is available at
@@ -113,7 +113,7 @@
         public void filter(ContainerRequestContext requestContext) throws IOException {
             Span span = OpenTracingUtils
                     .getRequestSpan(requestContext)
-                    .orElse(GlobalTracer.get().buildSpan("ad-hoc-span-reqA").startManual());
+                    .orElse(GlobalTracer.get().buildSpan("ad-hoc-span-reqA").start());
             span.log("ReqFilterA.filter() invoked");
         }
     }
@@ -128,7 +128,7 @@
         public void filter(ContainerRequestContext requestContext) throws IOException {
             Span span = OpenTracingUtils
                     .getRequestSpan(requestContext)
-                    .orElse(GlobalTracer.get().buildSpan("ad-hoc-span-reqB").startManual());
+                    .orElse(GlobalTracer.get().buildSpan("ad-hoc-span-reqB").start());
             span.log("ReqFilterB.filter() invoked");
         }
     }
@@ -141,7 +141,7 @@
         public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
             Span span = OpenTracingUtils
                     .getRequestSpan(requestContext)
-                    .orElse(GlobalTracer.get().buildSpan("ad-hoc-span-respA").startManual());
+                    .orElse(GlobalTracer.get().buildSpan("ad-hoc-span-respA").start());
             span.log("RespFilterA.filter() invoked");
         }
     }
@@ -154,7 +154,7 @@
         public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
             Span span = OpenTracingUtils
                     .getRequestSpan(requestContext)
-                    .orElse(GlobalTracer.get().buildSpan("ad-hoc-span-respB").startManual());
+                    .orElse(GlobalTracer.get().buildSpan("ad-hoc-span-respB").start());
             span.log("RespFilterB.filter() invoked");
         }
     }
diff --git a/examples/osgi-helloworld-webapp/functional-test/pom.xml b/examples/osgi-helloworld-webapp/functional-test/pom.xml
index 6103b1e..36b35c8 100644
--- a/examples/osgi-helloworld-webapp/functional-test/pom.xml
+++ b/examples/osgi-helloworld-webapp/functional-test/pom.xml
@@ -80,15 +80,6 @@
             <artifactId>javassist</artifactId>
             <scope>test</scope>
         </dependency>
-
-        <!-- logging -->
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-log4j12</artifactId>
-            <version>1.6.4</version>
-            <scope>test</scope>
-        </dependency>
-
         <!-- JUnit and Pax-Exam dependencies -->
         <dependency>
             <groupId>org.osgi</groupId>
diff --git a/ext/bean-validation/pom.xml b/ext/bean-validation/pom.xml
index 914251e..62f4686 100644
--- a/ext/bean-validation/pom.xml
+++ b/ext/bean-validation/pom.xml
@@ -100,9 +100,18 @@
                     <groupId>jakarta.el</groupId>
                     <artifactId>jakarta.el-api</artifactId>
                 </exclusion>
+                <exclusion>
+                    <groupId>org.jboss.logging</groupId>
+                    <artifactId>jboss-logging</artifactId>
+                </exclusion>
             </exclusions>
         </dependency>
         <dependency>
+            <groupId>org.jboss.logging</groupId>
+            <artifactId>jboss-logging</artifactId>
+            <version>${jboss.logging.version}</version>
+        </dependency>
+        <dependency>
             <groupId>jakarta.xml.bind</groupId>
             <artifactId>jakarta.xml.bind-api</artifactId>
             <optional>true</optional>
diff --git a/ext/microprofile/mp-rest-client/pom.xml b/ext/microprofile/mp-rest-client/pom.xml
index bf2013b..4ffb25a 100644
--- a/ext/microprofile/mp-rest-client/pom.xml
+++ b/ext/microprofile/mp-rest-client/pom.xml
@@ -85,7 +85,7 @@
         <dependency>
             <groupId>org.reactivestreams</groupId>
             <artifactId>reactive-streams</artifactId>
-            <version>1.0.3</version>
+            <version>${reactive.streams.version}</version>
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.media</groupId>
diff --git a/incubator/open-tracing/src/main/java/org/glassfish/jersey/opentracing/OpenTracingApplicationEventListener.java b/incubator/open-tracing/src/main/java/org/glassfish/jersey/opentracing/OpenTracingApplicationEventListener.java
index 40e2551..b6c6feb 100644
--- a/incubator/open-tracing/src/main/java/org/glassfish/jersey/opentracing/OpenTracingApplicationEventListener.java
+++ b/incubator/open-tracing/src/main/java/org/glassfish/jersey/opentracing/OpenTracingApplicationEventListener.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2022 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -25,6 +25,7 @@
 import jakarta.ws.rs.container.ContainerRequestFilter;
 import jakarta.ws.rs.container.ContainerResponseFilter;
 
+import io.opentracing.propagation.TextMapAdapter;
 import org.glassfish.jersey.server.ContainerRequest;
 import org.glassfish.jersey.server.ContainerResponse;
 import org.glassfish.jersey.server.monitoring.ApplicationEvent;
@@ -36,7 +37,6 @@
 import io.opentracing.SpanContext;
 import io.opentracing.Tracer;
 import io.opentracing.propagation.Format;
-import io.opentracing.propagation.TextMapExtractAdapter;
 import io.opentracing.tag.Tags;
 import io.opentracing.util.GlobalTracer;
 
@@ -84,7 +84,7 @@
                         (entry) -> OpenTracingUtils.formatList(entry.getValue())));
 
         final SpanContext extractedContext =
-                globalTracer.extract(Format.Builtin.HTTP_HEADERS, new TextMapExtractAdapter(mappedHeaders));
+                globalTracer.extract(Format.Builtin.HTTP_HEADERS, new TextMapAdapter(mappedHeaders));
 
         Tracer.SpanBuilder spanBuilder = globalTracer
                 .buildSpan(OpenTracingFeature.DEFAULT_REQUEST_SPAN_NAME)
@@ -99,7 +99,7 @@
             spanBuilder = spanBuilder.asChildOf(extractedContext);
         }
 
-        final Span span = spanBuilder.startManual();
+        final Span span = spanBuilder.start();
         request.setProperty(OpenTracingFeature.SPAN_CONTEXT_PROPERTY, span);
         span.log(LocalizationMessages.OPENTRACING_LOG_REQUEST_STARTED());
         return span;
@@ -157,7 +157,7 @@
 
                     resourceSpan = globalTracer.buildSpan(OpenTracingFeature.DEFAULT_RESOURCE_SPAN_NAME)
                                                .asChildOf(requestSpan)
-                                               .startManual();
+                                               .start();
 
                     event.getContainerRequest().setProperty(OpenTracingFeature.SPAN_CONTEXT_PROPERTY, resourceSpan);
                     break;
diff --git a/incubator/open-tracing/src/main/java/org/glassfish/jersey/opentracing/OpenTracingClientRequestFilter.java b/incubator/open-tracing/src/main/java/org/glassfish/jersey/opentracing/OpenTracingClientRequestFilter.java
index dc339a8..f384c91 100644
--- a/incubator/open-tracing/src/main/java/org/glassfish/jersey/opentracing/OpenTracingClientRequestFilter.java
+++ b/incubator/open-tracing/src/main/java/org/glassfish/jersey/opentracing/OpenTracingClientRequestFilter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2022 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -29,7 +29,7 @@
 import io.opentracing.SpanContext;
 import io.opentracing.Tracer;
 import io.opentracing.propagation.Format;
-import io.opentracing.propagation.TextMapInjectAdapter;
+import io.opentracing.propagation.TextMapAdapter;
 import io.opentracing.tag.Tags;
 import io.opentracing.util.GlobalTracer;
 
@@ -65,11 +65,11 @@
         if (property != null && property instanceof SpanContext) {
             spanBuilder = spanBuilder.asChildOf((SpanContext) property);
         }
-        Span span = spanBuilder.startManual();
+        Span span = spanBuilder.start();
 
         requestContext.setProperty(OpenTracingFeature.SPAN_CONTEXT_PROPERTY, span);
         Map<String, String> addedHeaders = new HashMap<>();
-        GlobalTracer.get().inject(span.context(), Format.Builtin.HTTP_HEADERS, new TextMapInjectAdapter(addedHeaders));
+        GlobalTracer.get().inject(span.context(), Format.Builtin.HTTP_HEADERS, new TextMapAdapter(addedHeaders));
         addedHeaders.forEach((key, value) -> requestContext.getHeaders().add(key, value));
     }
 }
diff --git a/incubator/open-tracing/src/main/java/org/glassfish/jersey/opentracing/OpenTracingUtils.java b/incubator/open-tracing/src/main/java/org/glassfish/jersey/opentracing/OpenTracingUtils.java
index c71ffac..3353be0 100644
--- a/incubator/open-tracing/src/main/java/org/glassfish/jersey/opentracing/OpenTracingUtils.java
+++ b/incubator/open-tracing/src/main/java/org/glassfish/jersey/opentracing/OpenTracingUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2022 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -89,7 +89,7 @@
                 spanBuilder = spanBuilder.asChildOf((Span) spanProperty);
             }
         }
-        return spanBuilder.startManual();
+        return spanBuilder.start();
     }
 
     /**
diff --git a/pom.xml b/pom.xml
index c3473a1..d3a76a2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -23,7 +23,7 @@
     <parent>
         <groupId>org.eclipse.ee4j</groupId>
         <artifactId>project</artifactId>
-        <version>1.0.7</version>
+        <version>1.0.8</version>
     </parent>
 
     <groupId>org.glassfish.jersey</groupId>
@@ -236,7 +236,7 @@
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-enforcer-plugin</artifactId>
-                    <version>${enforcer.version}</version>
+                    <version>${enforcer.mvn.plugin.version}</version>
                     <executions>
                         <execution>
                             <id>enforce-versions</id>
@@ -257,7 +257,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>
                             <phase>generate-sources</phase>
@@ -282,7 +282,7 @@
                 <plugin>
                     <groupId>com.sun.istack</groupId>
                     <artifactId>istack-commons-maven-plugin</artifactId>
-                    <version>3.0.8</version>
+                    <version>${istack.mvn.plugin.version}</version>
                     <executions>
                         <execution>
                             <phase>generate-sources</phase>
@@ -310,7 +310,7 @@
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-compiler-plugin</artifactId>
-                    <version>3.8.0</version>
+                    <version>${compiler.mvn.plugin.version}</version>
                     <inherited>true</inherited>
                     <configuration>
                         <source>${java.version}</source>
@@ -330,17 +330,17 @@
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-jar-plugin</artifactId>
-                    <version>3.2.0</version>
+                    <version>${jar.mvn.plugin.version}</version>
                 </plugin>
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-install-plugin</artifactId>
-                    <version>2.5.2</version>
+                    <version>${install.mvn.plugin.version}</version>
                 </plugin>
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-resources-plugin</artifactId>
-                    <version>2.6</version>
+                    <version>${resources.mvn.plugin.version}</version>
                     <extensions>true</extensions>
                     <!-- Add legal information, NOTICE.md and LINCENSE.md to jars -->
                     <executions>
@@ -412,7 +412,7 @@
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-surefire-plugin</artifactId>
-                    <version>${surefire.version}</version>
+                    <version>${surefire.mvn.plugin.version}</version>
                     <configuration>
                         <!-- print full stack trace if error -->
                         <trimStackTrace>false</trimStackTrace>
@@ -434,14 +434,14 @@
                         <dependency>
                             <groupId>org.apache.maven.surefire</groupId>
                             <artifactId>surefire-logger-api</artifactId>
-                            <version>${surefire.version}</version>
+                            <version>${surefire.mvn.plugin.version}</version>
                             <!-- to get around bug https://github.com/junit-team/junit5/issues/1367 -->
                             <optional>true</optional>
                         </dependency>
                         <dependency>
                             <groupId>org.apache.maven.surefire</groupId>
                             <artifactId>surefire-api</artifactId>
-                            <version>${surefire.version}</version>
+                            <version>${surefire.mvn.plugin.version}</version>
                             <optional>true</optional>
                         </dependency>
                     </dependencies>
@@ -449,20 +449,20 @@
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-assembly-plugin</artifactId>
-                    <version>2.4</version>
+                    <version>${assembly.mvn.plugin.version}</version>
                     <configuration>
-                        <tarLongFileMode>gnu</tarLongFileMode>
+                        <tarLongFileMode>posix</tarLongFileMode>
                     </configuration>
                 </plugin>
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-dependency-plugin</artifactId>
-                    <version>3.3.0</version>
+                    <version>${dependency.mvn.plugin.version}</version>
                 </plugin>
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-javadoc-plugin</artifactId>
-                    <version>3.2.0</version>
+                    <version>${javadoc.mvn.plugin.version}</version>
                     <configuration>
                         <doctitle>Jersey ${jersey.version} API Documentation</doctitle>
                         <windowtitle>Jersey ${jersey.version} API</windowtitle>
@@ -500,7 +500,7 @@
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-source-plugin</artifactId>
-                    <version>3.0.1</version>
+                    <version>${source.mvn.plugin.version}</version>
                     <executions>
                         <execution>
                             <id>attach-sources</id>
@@ -514,7 +514,7 @@
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-deploy-plugin</artifactId>
-                    <version>2.8.1</version>
+                    <version>${deploy.mvn.plugin.version}</version>
                     <configuration>
                         <retryFailedDeploymentCount>10</retryFailedDeploymentCount>
                     </configuration>
@@ -540,12 +540,12 @@
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-site-plugin</artifactId>
-                    <version>3.7.1</version>
+                    <version>${site.mvn.plugin.version}</version>
                 </plugin>
                 <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>
@@ -557,7 +557,7 @@
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-jxr-plugin</artifactId>
-                    <version>2.3</version>
+                    <version>${jxr.mvn.plugin.version}</version>
                     <executions>
                         <execution>
                             <goals>
@@ -594,7 +594,7 @@
                 <plugin>
                     <groupId>org.codehaus.mojo</groupId>
                     <artifactId>findbugs-maven-plugin</artifactId>
-                    <version>${findbugs.version}</version>
+                    <version>${findbugs.mvn.plugin.version}</version>
                     <configuration>
                         <skip>${findbugs.skip}</skip>
                         <threshold>${findbugs.threshold}</threshold>
@@ -622,7 +622,7 @@
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-failsafe-plugin</artifactId>
-                    <version>3.0.0-M3</version>
+                    <version>${failsafe.mvn.plugin.version}</version>
                     <configuration>
                         <!-- print full stack trace if error -->
                         <trimStackTrace>false</trimStackTrace>
@@ -642,7 +642,7 @@
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-war-plugin</artifactId>
-                    <version>3.3.1</version>
+                    <version>${war.mvn.plugin.version}</version>
                     <configuration>
                         <failOnMissingWebXml>false</failOnMissingWebXml>
                     </configuration>
@@ -650,12 +650,12 @@
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-ear-plugin</artifactId>
-                    <version>2.8</version>
+                    <version>${ear.mvn.plugin.version}</version>
                 </plugin>
                 <plugin>
                     <groupId>org.glassfish.embedded</groupId>
                     <artifactId>maven-embedded-glassfish-plugin</artifactId>
-                    <version>3.1.2.2</version>
+                    <version>${gfembedded.mvn.plugin.version}</version>
                 </plugin>
                 <plugin>
                     <groupId>org.glassfish.copyright</groupId>
@@ -681,7 +681,7 @@
                 <plugin>
                     <groupId>org.apache.felix</groupId>
                     <artifactId>maven-bundle-plugin</artifactId>
-                    <version>3.5.0</version>
+                    <version>${felix.mvn.plugin.version}</version>
                     <extensions>true</extensions>
                     <configuration>
                         <instructions>
@@ -703,7 +703,7 @@
                 <plugin>
                     <groupId>org.codehaus.mojo</groupId>
                     <artifactId>xml-maven-plugin</artifactId>
-                    <version>1.0</version>
+                    <version>${xml.mvn.plugin.version}</version>
                 </plugin>
                 <plugin>
                     <groupId>com.sun.tools.xjc.maven2</groupId>
@@ -713,7 +713,7 @@
                 <plugin>
                     <groupId>org.codehaus.mojo</groupId>
                     <artifactId>buildnumber-maven-plugin</artifactId>
-                    <version>1.1</version>
+                    <version>${buildnumber.mvn.plugin.version}</version>
                 </plugin>
                 <!-- TODO: remove the old jetty plugin dependencies -->
                 <plugin>
@@ -750,7 +750,7 @@
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-shade-plugin</artifactId>
-                    <version>2.4.3</version>
+                    <version>${shade.mvn.plugin.version}</version>
                     <executions>
                         <execution>
                             <id>shade-archive</id>
@@ -781,24 +781,24 @@
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-antrun-plugin</artifactId>
-                    <version>3.0.0</version>
+                    <version>${antrun.mvn.plugin.version}</version>
                     <dependencies>
                         <dependency>
                             <groupId>org.apache.ant</groupId>
                             <artifactId>ant</artifactId>
-                            <version>1.10.11</version>
+                            <version>1.10.12</version>
                         </dependency>
                     </dependencies>
                 </plugin>
                 <plugin>
                     <groupId>org.fortasoft</groupId>
                     <artifactId>gradle-maven-plugin</artifactId>
-                    <version>1.0.5</version>
+                    <version>1.0.8</version>
                 </plugin>
                 <plugin>
                     <groupId>com.github.wvengen</groupId>
                     <artifactId>proguard-maven-plugin</artifactId>
-                    <version>2.0.8</version>
+                    <version>${proguard.mvn.plugin.version}</version>
                     <dependencies>
                         <dependency>
                             <groupId>net.sf.proguard</groupId>
@@ -869,6 +869,10 @@
             <activation>
                 <jdk>1.8</jdk>
             </activation>
+            <properties>
+                <checkstyle.version>9.3</checkstyle.version>
+                <istack.mvn.plugin.version>3.0.9</istack.mvn.plugin.version>
+            </properties>
             <build>
                 <pluginManagement>
                     <plugins>
@@ -1412,7 +1416,7 @@
             <plugin>
                 <groupId>org.codehaus.mojo</groupId>
                 <artifactId>findbugs-maven-plugin</artifactId>
-                <version>${findbugs.version}</version>
+                <version>${findbugs.mvn.plugin.version}</version>
                 <reportSets>
                     <reportSet>
                         <configuration>
@@ -1858,11 +1862,6 @@
 
             <!-- HV OSGi dependencies. -->
             <dependency>
-                <groupId>org.jboss.logging</groupId>
-                <artifactId>jboss-logging</artifactId>
-                <version>${jboss.logging.version}</version>
-            </dependency>
-            <dependency>
                 <groupId>com.fasterxml</groupId>
                 <artifactId>classmate</artifactId>
                 <version>${fasterxml.classmate.version}</version>
@@ -1935,7 +1934,7 @@
             <dependency>
                 <groupId>commons-logging</groupId>
                 <artifactId>commons-logging</artifactId>
-                <version>1.2</version>
+                <version>${commons.logging.version}</version>
             </dependency>
 
             <!-- Weld -->
@@ -2015,21 +2014,16 @@
             </dependency>
 
             <dependency>
-                <groupId>org.junit.jupiter</groupId>
-                <artifactId>junit-jupiter</artifactId>
+                <groupId>org.junit</groupId>
+                <artifactId>junit-bom</artifactId>
                 <version>${junit5.version}</version>
-                <scope>test</scope>
-            </dependency>
-            <dependency>
-                <groupId>org.junit.jupiter</groupId>
-                <artifactId>junit-jupiter-engine</artifactId>
-                <version>${junit5.version}</version>
-                <scope>test</scope>
+                <type>pom</type>
+                <scope>import</scope>
             </dependency>
             <dependency>
                 <groupId>org.testng</groupId>
                 <artifactId>testng</artifactId>
-                <version>6.9.13.6</version>
+                <version>${testng.version}</version>
                 <scope>test</scope>
             </dependency>
 
@@ -2073,21 +2067,21 @@
             <dependency>
                 <groupId>org.apache.felix</groupId>
                 <artifactId>org.apache.felix.framework</artifactId>
-                <version>7.0.0</version>
+                <version>${felix.framework.version}</version>
                 <scope>test</scope>
             </dependency>
 
             <dependency>
                 <groupId>org.apache.felix</groupId>
                 <artifactId>org.apache.felix.eventadmin</artifactId>
-                <version>1.5.0</version>
+                <version>${felix.eventadmin.version}</version>
                 <scope>test</scope>
             </dependency>
 
             <dependency>
                 <groupId>org.apache.felix</groupId>
                 <artifactId>org.apache.felix.framework.security</artifactId>
-                <version>2.6.1</version>
+                <version>${felix.framework.security.version}</version>
                 <scope>test</scope>
             </dependency>
 
@@ -2125,7 +2119,7 @@
     </dependencyManagement>
 
     <properties>
-        <archetype.mvn.plugin.version>2.4</archetype.mvn.plugin.version>
+        <archetype.mvn.plugin.version>3.2.1</archetype.mvn.plugin.version>
 
         <findbugs.skip>false</findbugs.skip>
         <findbugs.threshold>Low</findbugs.threshold>
@@ -2142,6 +2136,7 @@
         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 <!--        <release.tests.args>-Dmaven.test.skip=false</release.tests.args>-->
 <!--        <release.preparationGoals>clean install</release.preparationGoals>-->
+        <skip.e2e>false</skip.e2e>
         <skip.tests>false</skip.tests>
         <xdk.absolute.path />
         <surefire.security.argline />
@@ -2150,9 +2145,44 @@
         <failsafe.coverage.argline />
         <server.coverage.argline>${failsafe.coverage.argline}</server.coverage.argline>
 
+        <!-- Versions of Maven plugins -->
+        <antrun.mvn.plugin.version>3.1.0</antrun.mvn.plugin.version>
+        <assembly.mvn.plugin.version>3.4.2</assembly.mvn.plugin.version>
+        <enforcer.mvn.plugin.version>3.1.0</enforcer.mvn.plugin.version>
+        <exec.mvn.plugin.version>3.1.0</exec.mvn.plugin.version>
+        <buildhelper.mvn.plugin.version>3.3.0</buildhelper.mvn.plugin.version>
+        <buildnumber.mvn.plugin.version>3.0.0</buildnumber.mvn.plugin.version>
+        <checkstyle.mvn.plugin.version>3.1.2</checkstyle.mvn.plugin.version>
+        <checkstyle.version>10.3.2</checkstyle.version>
+        <compiler.mvn.plugin.version>3.8.0</compiler.mvn.plugin.version>
+        <dependency.mvn.plugin.version>3.3.0</dependency.mvn.plugin.version>
+        <deploy.mvn.plugin.version>3.0.0-M2</deploy.mvn.plugin.version>
+        <ear.mvn.plugin.version>3.2.0</ear.mvn.plugin.version>
+        <failsafe.mvn.plugin.version>3.0.0-M7</failsafe.mvn.plugin.version>
+        <felix.mvn.plugin.version>5.1.8</felix.mvn.plugin.version>
+        <findbugs.mvn.plugin.version>3.0.5</findbugs.mvn.plugin.version>
+        <gfembedded.mvn.plugin.version>5.1</gfembedded.mvn.plugin.version>
+        <install.mvn.plugin.version>3.0.1</install.mvn.plugin.version>
+        <istack.mvn.plugin.version>4.1.1</istack.mvn.plugin.version>
+        <jar.mvn.plugin.version>3.2.2</jar.mvn.plugin.version>
+        <javadoc.mvn.plugin.version>3.4.0</javadoc.mvn.plugin.version>
+        <jxr.mvn.plugin.version>3.2.0</jxr.mvn.plugin.version>
+        <paxexam.mvn.plugin.version>1.2.4</paxexam.mvn.plugin.version>
+        <proguard.mvn.plugin.version>2.6.0</proguard.mvn.plugin.version>
+        <resources.mvn.plugin.version>3.3.0</resources.mvn.plugin.version>
+        <shade.mvn.plugin.version>3.3.0</shade.mvn.plugin.version>
+        <site.mvn.plugin.version>3.9.1</site.mvn.plugin.version>
+        <source.mvn.plugin.version>3.2.1</source.mvn.plugin.version>
+        <surefire.mvn.plugin.version>3.0.0-M7</surefire.mvn.plugin.version>
+        <war.mvn.plugin.version>3.3.2</war.mvn.plugin.version>
+        <wiremock.mvn.plugin.version>2.9.0</wiremock.mvn.plugin.version>
+        <xml.mvn.plugin.version>1.0.2</xml.mvn.plugin.version>
+        <!-- END of Versions of Maven plugins -->
 
         <!-- Dependency versions -->
         <jersey.version>${project.version}</jersey.version>
+        <arquillian.version>1.7.0.Alpha13</arquillian.version>
+        <arquillian.weld.version>3.0.1.Final</arquillian.weld.version>
         <!-- asm is now source integrated - keeping this property to see the version -->
         <!-- see core-server/src/main/java/jersey/repackaged/asm/.. -->
         <asm.version>9.4</asm.version>
@@ -2161,22 +2191,19 @@
         <bouncycastle.version>1.68</bouncycastle.version>
         <commons.io.version>2.11.0</commons.io.version>
         <commons-lang3.version>3.3.2</commons-lang3.version>
-        <checkstyle.mvn.plugin.version>3.1.0</checkstyle.mvn.plugin.version>
-        <checkstyle.version>8.28</checkstyle.version>
-        <easymock.version>3.3</easymock.version>
-
-        <fasterxml.classmate.version>1.3.3</fasterxml.classmate.version>
+        <commons.logging.version>1.2</commons.logging.version>
+        <fasterxml.classmate.version>1.5.1</fasterxml.classmate.version>
+        <felix.eventadmin.version>1.6.4</felix.eventadmin.version>
+        <felix.framework.security.version>2.8.3</felix.framework.security.version>
+        <felix.framework.version>7.0.5</felix.framework.version>
         <findbugs.glassfish.version>1.7</findbugs.glassfish.version>
-        <findbugs.version>3.0.4</findbugs.version>
-        <freemarker.version>2.3.27-incubating</freemarker.version>
-        <gae.version>1.9.59</gae.version>
-        <grizzly.client.version>1.16</grizzly.client.version>
-        <enforcer.version>3.1.0</enforcer.version>
+        <freemarker.version>2.3.31</freemarker.version>
+        <gae.version>1.9.98</gae.version>
+        <groovy.version>4.0.6</groovy.version>
+        <gson.version>2.9.0</gson.version>
 
         <!--versions, extracted here due to maven-enforcer-plugin -->
-        <slf4j.version>2.0.3</slf4j.version>
         <commons.codec.version>1.15</commons.codec.version>
-        <reactive.streams.version>1.0.3</reactive.streams.version>
         <com.uber.jaeger.version>0.27.0</com.uber.jaeger.version>
         <org.codehaus.gmavenplus.version>1.13.1</org.codehaus.gmavenplus.version>
         <!-- end of versions extracted here due to maven-enforcer-plugin -->
@@ -2187,56 +2214,51 @@
         <helidon.config.version>3.0.0-M1</helidon.config.version>
         <smallrye.config.version>3.0.0-RC1</smallrye.config.version>
 
-
-        <grizzly2.version>2.4.4</grizzly2.version>
         <guava.version>31.1-jre</guava.version>
         <hamcrest.version>2.2</hamcrest.version>
-        <!--<helidon.version>1.0.3</helidon.version>-->
+        <!--<helidon.version>1.4.9</helidon.version>-->
+        <helidon.jersey.connector.version>3.0.2</helidon.jersey.connector.version>
         <xmlunit.version>2.9.0</xmlunit.version>
         <hk2.osgi.version>org.glassfish.hk2.*;version="[2.5,4)"</hk2.osgi.version>
         <hk2.jvnet.osgi.version>org.jvnet.hk2.*;version="[2.5,4)"</hk2.jvnet.osgi.version>
         <httpclient.version>4.5.13</httpclient.version>
-        <httpclient5.version>5.1.2</httpclient5.version>
+        <httpclient5.version>5.1.3</httpclient5.version>
         <jackson.version>2.13.3</jackson.version>
-        <javassist.version>3.28.0-GA</javassist.version>
-        <jboss.logging.version>3.3.0.Final</jboss.logging.version>
+        <javassist.version>3.29.0-GA</javassist.version>
+        <jboss.logging.version>3.4.2.Final</jboss.logging.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 -->
-        <jmh.version>1.10.2</jmh.version>
+        <jmh.version>1.35</jmh.version>
         <jmockit.version>1.49</jmockit.version>
         <junit4.version>4.13.2</junit4.version>
         <junit5.version>5.9.1</junit5.version>
         <junit-platform-suite.version>1.9.1</junit-platform-suite.version>
         <kryo.version>4.0.2</kryo.version>
         <mockito.version>3.12.4</mockito.version> <!-- CQ 17673 -->
-        <mustache.version>0.8.17</mustache.version>
-        <netty.version>4.1.75.Final</netty.version>
-        <nexus-staging.mvn.plugin.version>1.6.7</nexus-staging.mvn.plugin.version>
-        <opentracing.version>0.30.0</opentracing.version>
+        <mustache.version>0.9.10</mustache.version>
+        <netty.version>4.1.79.Final</netty.version>
+        <opentracing.version>0.33.0</opentracing.version>
         <osgi.version>6.0.0</osgi.version>
         <osgi.framework.version>1.10.0</osgi.framework.version>
         <osgi.compendium.version>5.0.0</osgi.compendium.version>
         <osgi.service.cm.version>1.6.0</osgi.service.cm.version>
         <pax.exam.version>4.13.4</pax.exam.version>
         <pax.web.version>0.7.4</pax.web.version><!-- TODO: UPGRADE! -->
-        <paxexam.mvn.plugin.version>1.2.4</paxexam.mvn.plugin.version>
-        <rxjava.version>1.2.5</rxjava.version>
-        <rxjava2.version>2.0.4</rxjava2.version>
-        <servlet3.version>3.0.1</servlet3.version>
-        <servlet4.version>4.0.3</servlet4.version>
+        <reactive.streams.version>1.0.4</reactive.streams.version>
+        <rxjava.version>1.3.8</rxjava.version>
+        <rxjava2.version>2.2.21</rxjava2.version>
         <simple.version>6.0.1</simple.version>
-        <skip.e2e>false</skip.e2e>
+        <slf4j.version>2.0.3</slf4j.version>
         <spring6.version>6.0.0-M3</spring6.version>
-        <surefire.version>3.0.0-M7</surefire.version>
-        <validation.impl.version>6.2.0.Final</validation.impl.version>
-        <gson.version>2.9.0</gson.version>
-
+        <testng.version>7.6.1</testng.version>
+        <testng6.version>6.9.13.6</testng6.version>
         <!-- Jakartified, eligible for CQ -->
         <weld.version>4.0.2.Final</weld.version>
         <weld3.version>3.1.7.SP1</weld3.version>
-        <validation.impl.version>7.0.1.Final</validation.impl.version>
+        <validation.impl.version>7.0.5.Final</validation.impl.version>
         <!-- END of Jakartified, eligible for CQ -->
+        <wiremock.version>2.27.2</wiremock.version>
         <xerces.version>2.12.2</xerces.version>
 
         <!-- Graal VM       -->
@@ -2250,6 +2272,7 @@
         <cdi.osgi.version>jakarta.enterprise.*;version="[3.0,5)"</cdi.osgi.version>
         <ejb.version>4.0.1</ejb.version>
         <grizzly2.version>3.0.1</grizzly2.version>
+        <grizzly.client.version>1.16</grizzly.client.version>
         <grizzly.npn.version>2.0.0</grizzly.npn.version>
         <hk2.version>3.0.3</hk2.version>
         <jsp.version>3.0.0</jsp.version>
diff --git a/test-framework/core/pom.xml b/test-framework/core/pom.xml
index 2cc71a0..37cbab0 100644
--- a/test-framework/core/pom.xml
+++ b/test-framework/core/pom.xml
@@ -67,6 +67,7 @@
         <dependency>
             <groupId>org.testng</groupId>
             <artifactId>testng</artifactId>
+            <version>${testng6.version}</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
diff --git a/test-framework/maven/custom-enforcer-rules/pom.xml b/test-framework/maven/custom-enforcer-rules/pom.xml
index b99519e..a3d77c0 100644
--- a/test-framework/maven/custom-enforcer-rules/pom.xml
+++ b/test-framework/maven/custom-enforcer-rules/pom.xml
@@ -34,7 +34,7 @@
         <dependency>
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-enforcer-plugin</artifactId>
-            <version>${enforcer.version}</version>
+            <version>${enforcer.mvn.plugin.version}</version>
             <exclusions>
                 <exclusion>
                     <groupId>junit</groupId>
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/HeaderUtilsTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/HeaderUtilsTest.java
index f252906..03446f9 100644
--- a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/HeaderUtilsTest.java
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/HeaderUtilsTest.java
@@ -25,6 +25,7 @@
 import java.util.List;
 
 import jakarta.ws.rs.core.AbstractMultivaluedMap;
+import jakarta.ws.rs.core.Configuration;
 import jakarta.ws.rs.core.MultivaluedMap;
 import jakarta.ws.rs.core.NewCookie;
 import jakarta.ws.rs.ext.RuntimeDelegate;
@@ -101,19 +102,19 @@
 
     @Test
     public void testAsString() throws Exception {
-        assertNull(HeaderUtils.asString(null, null));
+        assertNull(HeaderUtils.asString(null, (Configuration) null));
 
         final String value = "value";
-        assertSame(value, HeaderUtils.asString(value, null));
+        assertSame(value, HeaderUtils.asString(value, (Configuration) null));
 
         final URI uri = new URI("test");
-        assertEquals(uri.toASCIIString(), HeaderUtils.asString(uri, null));
+        assertEquals(uri.toASCIIString(), HeaderUtils.asString(uri, (Configuration) null));
     }
 
     @Test
     public void testAsStringList() throws Exception {
-        assertNotNull(HeaderUtils.asStringList(null, null));
-        assertTrue(HeaderUtils.asStringList(null, null).isEmpty());
+        assertNotNull(HeaderUtils.asStringList(null, (Configuration) null));
+        assertTrue(HeaderUtils.asStringList(null, (Configuration) null).isEmpty());
 
         final URI uri = new URI("test");
         final List<Object> values = new LinkedList<Object>() {{
@@ -123,7 +124,7 @@
         }};
 
         // test string values
-        final List<String> stringList = HeaderUtils.asStringList(values, null);
+        final List<String> stringList = HeaderUtils.asStringList(values, (Configuration) null);
         assertEquals(Arrays.asList("value", "[null]", uri.toASCIIString()),
                      stringList);
 
@@ -138,7 +139,7 @@
 
     @Test
     public void testAsStringHeaders() throws Exception {
-        assertNull(HeaderUtils.asStringHeaders(null, null));
+        assertNull(HeaderUtils.asStringHeaders(null, (Configuration) null));
 
         final AbstractMultivaluedMap<String, Object> headers = HeaderUtils.createOutbound();
 
@@ -150,7 +151,7 @@
 
         headers.putSingle("k3", "value3");
 
-        final MultivaluedMap<String, String> stringHeaders = HeaderUtils.asStringHeaders(headers, null);
+        final MultivaluedMap<String, String> stringHeaders = HeaderUtils.asStringHeaders(headers, (Configuration) null);
 
         // test string values
         assertEquals(Arrays.asList("value", "value2"),
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/header/HeaderDelegateProviderTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/header/HeaderDelegateProviderTest.java
index bc1058a..8fe509d 100644
--- a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/header/HeaderDelegateProviderTest.java
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/header/HeaderDelegateProviderTest.java
@@ -16,6 +16,7 @@
 
 package org.glassfish.jersey.tests.e2e.header;
 
+
 import org.glassfish.jersey.CommonProperties;
 import org.glassfish.jersey.client.ClientConfig;
 import org.glassfish.jersey.internal.ServiceFinder;
@@ -36,6 +37,7 @@
 import jakarta.ws.rs.container.ContainerRequestContext;
 import jakarta.ws.rs.container.ContainerResponseContext;
 import jakarta.ws.rs.container.ContainerResponseFilter;
+import jakarta.ws.rs.core.Configuration;
 import jakarta.ws.rs.core.Context;
 import jakarta.ws.rs.core.HttpHeaders;
 import jakarta.ws.rs.core.MediaType;
@@ -155,7 +157,7 @@
     public void testHeaderDelegateIsUsedWhenRuntimeDelegateDecoratorIsUsed() {
         MultivaluedHashMap headers = new MultivaluedHashMap();
         headers.put(HEADER_NAME, Arrays.asList(new BeanForHeaderDelegateProviderTest()));
-        MultivaluedMap<String, String> converted = HeaderUtils.asStringHeaders(headers, null);
+        MultivaluedMap<String, String> converted = HeaderUtils.asStringHeaders(headers, (Configuration) null);
         testMap(converted, BeanForHeaderDelegateProviderTest.getValue());
 
         Client client = ClientBuilder.newClient().property(CommonProperties.METAINF_SERVICES_LOOKUP_DISABLE, false);
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/OptionalParamConverterTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/OptionalParamConverterTest.java
index 444475b..c1bfab3 100644
--- a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/OptionalParamConverterTest.java
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/OptionalParamConverterTest.java
@@ -16,6 +16,9 @@
 
 package org.glassfish.jersey.tests.e2e.server;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
 import java.text.ParseException;
@@ -23,7 +26,7 @@
 import java.util.Date;
 import java.util.List;
 import java.util.Optional;
-
+import jakarta.validation.constraints.NotNull;
 import jakarta.ws.rs.GET;
 import jakarta.ws.rs.Path;
 import jakarta.ws.rs.QueryParam;
@@ -39,13 +42,24 @@
 import org.glassfish.jersey.test.JerseyTest;
 import org.junit.jupiter.api.Test;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-
 public class OptionalParamConverterTest extends JerseyTest {
 
     private static final String PARAM_NAME = "paramName";
 
+    @Path("/IntegerResource")
+    public static class IntegerResource {
+        @GET
+        @Path("/fromInteger")
+        public Response fromInteger(@QueryParam(PARAM_NAME) Integer data) {
+            return Response.ok(0).build();
+        }
+        @GET
+        @Path("/fromIntegerNotNull")
+        public Response fromIntegerNotNull(@NotNull @QueryParam(PARAM_NAME) Integer data) {
+            return Response.ok(0).build();
+        }
+    }
+
     @Path("/OptionalResource")
     public static class OptionalResource {
 
@@ -62,6 +76,12 @@
         }
 
         @GET
+        @Path("/fromIntegerNotNull")
+        public Response fromIntegerNotNull(@NotNull @QueryParam(PARAM_NAME) Optional<Integer> data) {
+            return Response.ok(data.orElse(0)).build();
+        }
+
+        @GET
         @Path("/fromDate")
         public Response fromDate(@QueryParam(PARAM_NAME) Optional<Date> data) throws ParseException {
             return Response.ok(data.orElse(new Date(1609459200000L))).build();
@@ -118,7 +138,7 @@
 
     @Override
     protected Application configure() {
-        return new ResourceConfig(OptionalResource.class, InstantParamConverterProvider.class);
+        return new ResourceConfig(OptionalResource.class, IntegerResource.class, InstantParamConverterProvider.class);
     }
 
     @Test
@@ -137,6 +157,7 @@
         Response empty = target("/OptionalResource/fromInteger").queryParam(PARAM_NAME, "").request().get();
         Response notEmpty = target("/OptionalResource/fromInteger").queryParam(PARAM_NAME, 1).request().get();
         Response invalid = target("/OptionalResource/fromInteger").queryParam(PARAM_NAME, "invalid").request().get();
+        Response missingNotNull = target("/OptionalResource/fromIntegerNotNull").request().get();
         assertEquals(200, missing.getStatus());
         assertEquals(Integer.valueOf(0), missing.readEntity(Integer.class));
         assertEquals(200, empty.getStatus());
@@ -145,6 +166,17 @@
         assertEquals(Integer.valueOf(1), notEmpty.readEntity(Integer.class));
         assertEquals(404, invalid.getStatus());
         assertFalse(invalid.hasEntity());
+        assertEquals(200, missingNotNull.getStatus());
+        assertEquals(Integer.valueOf(0), missingNotNull.readEntity(Integer.class));
+    }
+
+    @Test
+    public void fromInteger() {
+        Response missing = target("/IntegerResource/fromInteger").request().get();
+        Response missingNotNull = target("/IntegerResource/fromIntegerNotNull").request().get();
+        assertEquals(200, missing.getStatus());
+        assertEquals(Integer.valueOf(0), missing.readEntity(Integer.class));
+        assertEquals(400, missingNotNull.getStatus());
     }
 
     @Test
@@ -156,8 +188,8 @@
         Response invalid = target("/OptionalResource/fromDate").queryParam(PARAM_NAME, "invalid").request().get();
         assertEquals(200, missing.getStatus());
         assertEquals(new Date(1609459200000L), missing.readEntity(Date.class));
-        assertEquals(404, empty.getStatus());
-        assertFalse(empty.hasEntity());
+        assertEquals(200, empty.getStatus());
+        assertEquals(new Date(1609459200000L), empty.readEntity(Date.class));
         assertEquals(200, notEmpty.getStatus());
         assertEquals(new Date(1619870400000L), notEmpty.readEntity(Date.class));
         assertEquals(404, invalid.getStatus());
@@ -173,8 +205,8 @@
         Response invalid = target("/OptionalResource/fromInstant").queryParam(PARAM_NAME, "invalid").request().get();
         assertEquals(200, missing.getStatus());
         assertEquals("2021-01-01T00:00:00Z", missing.readEntity(String.class));
-        assertEquals(404, empty.getStatus());
-        assertFalse(empty.hasEntity());
+        assertEquals(200, empty.getStatus());
+        assertEquals("2021-01-01T00:00:00Z", empty.readEntity(String.class));
         assertEquals(200, notEmpty.getStatus());
         assertEquals("2021-05-01T12:00:00Z", notEmpty.readEntity(String.class));
         assertEquals(404, invalid.getStatus());
diff --git a/tests/e2e-testng/pom.xml b/tests/e2e-testng/pom.xml
index 98fad18..74d089b 100644
--- a/tests/e2e-testng/pom.xml
+++ b/tests/e2e-testng/pom.xml
@@ -52,7 +52,7 @@
                     <dependency>
                         <groupId>org.apache.maven.surefire</groupId>
                         <artifactId>surefire-testng</artifactId>
-                        <version>${surefire.version}</version>
+                        <version>${surefire.mvn.plugin.version}</version>
                     </dependency>
                 </dependencies>
             </plugin>
@@ -70,6 +70,7 @@
         <dependency>
             <groupId>org.testng</groupId>
             <artifactId>testng</artifactId>
+            <version>${testng6.version}</version>
             <scope>test</scope>
         </dependency>
     </dependencies>
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ResponseTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ResponseTest.java
index bf8c847..c9ac68c 100644
--- a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ResponseTest.java
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ResponseTest.java
@@ -24,6 +24,7 @@
 import java.util.Locale;
 import java.util.Set;
 
+import jakarta.ws.rs.core.Configuration;
 import jakarta.ws.rs.core.Cookie;
 import jakarta.ws.rs.core.HttpHeaders;
 import jakarta.ws.rs.core.MediaType;
@@ -247,7 +248,7 @@
         }
 
         MultivaluedMap<String, String> mvp = HeaderUtils.asStringHeaders(
-                resp.getMetadata(), null);
+                resp.getMetadata(), (Configuration) null);
 
         for (String key : mvp.keySet()) {
             sb.append(indent + "Processing Key found in response: ").append(key).append(": ").append(mvp.get(key)).append("; ")
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/JerseyContainerTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/JerseyContainerTest.java
index 7f9bd49..b1f5cfc 100644
--- a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/JerseyContainerTest.java
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/JerseyContainerTest.java
@@ -52,7 +52,7 @@
         super(testContainerFactory);
     }
 
-    protected static List<TestContainerFactory> listContainerFactories(TestContainerFactory...factories) {
+    protected static List<TestContainerFactory> listContainerFactories(TestContainerFactory... factories) {
         final JdkVersion version = JdkVersion.getJdkVersion();
         boolean isJDK8 = version.getMajor() == 1;
         final List<TestContainerFactory> filtered = new LinkedList<>();
diff --git a/tests/integration/cdi-integration/gf-cdi-inject/src/test/java/org/glassfish/jersey/tests/cdi/gf/GFTest.java b/tests/integration/cdi-integration/gf-cdi-inject/src/test/java/org/glassfish/jersey/tests/cdi/gf/GFTest.java
index a224bc8..5946c36 100644
--- a/tests/integration/cdi-integration/gf-cdi-inject/src/test/java/org/glassfish/jersey/tests/cdi/gf/GFTest.java
+++ b/tests/integration/cdi-integration/gf-cdi-inject/src/test/java/org/glassfish/jersey/tests/cdi/gf/GFTest.java
@@ -39,7 +39,7 @@
         );
     }
 
-    private static WebArchive createDeployment(String archiveName, Class<?>...classes) {
+    private static WebArchive createDeployment(String archiveName, Class<?>... classes) {
         WebArchive archive = ShrinkWrap.create(WebArchive.class, archiveName + ".war");
         archive.addClasses(classes);
         return archive;
diff --git a/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/TraceResponseWrapper.java b/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/TraceResponseWrapper.java
index de01bf9..c4c1bad 100644
--- a/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/TraceResponseWrapper.java
+++ b/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/TraceResponseWrapper.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -17,6 +17,7 @@
 package org.glassfish.jersey.tests.integration.jersey2176;
 
 import jakarta.servlet.ServletOutputStream;
+import jakarta.servlet.WriteListener;
 import jakarta.servlet.http.HttpServletResponse;
 import jakarta.servlet.http.HttpServletResponseWrapper;
 import java.io.ByteArrayOutputStream;
diff --git a/tests/integration/microprofile/rest-client-tck/pom.xml b/tests/integration/microprofile/rest-client-tck/pom.xml
index 947348b..e5f263e 100644
--- a/tests/integration/microprofile/rest-client-tck/pom.xml
+++ b/tests/integration/microprofile/rest-client-tck/pom.xml
@@ -50,11 +50,11 @@
             <artifactId>weld-se-core</artifactId>
             <scope>test</scope>
         </dependency>
-        <dependency>
+        <!--<dependency>
             <groupId>javax.servlet</groupId>
             <artifactId>javax.servlet-api</artifactId>
             <version>4.0.1</version>
-        </dependency>
+        </dependency>-->
         <dependency>
             <groupId>io.smallrye.config</groupId>
             <artifactId>smallrye-config</artifactId>
@@ -70,12 +70,13 @@
         <dependency>
             <groupId>org.testng</groupId>
             <artifactId>testng</artifactId>
+            <version>${testng6.version}</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>com.github.tomakehurst</groupId>
             <artifactId>wiremock</artifactId>
-            <version>2.27.2</version>
+            <version>${wiremock.version}</version>
             <scope>test</scope>
             <exclusions>
                 <exclusion>
@@ -103,7 +104,7 @@
         <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
-            <version>20.0</version>
+            <version>${guava.version}</version>
         </dependency>
         <dependency>
             <groupId>org.eclipse.jetty</groupId>
@@ -138,19 +139,19 @@
         <dependency>
             <groupId>org.jboss.arquillian.testng</groupId>
             <artifactId>arquillian-testng-container</artifactId>
-            <version>1.7.0.Alpha10</version>
+            <version>${arquillian.version}</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.jboss.arquillian.container</groupId>
             <artifactId>arquillian-container-test-spi</artifactId>
-            <version>1.7.0.Alpha10</version>
+            <version>${arquillian.version}</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.jboss.arquillian.container</groupId>
             <artifactId>arquillian-weld-embedded</artifactId>
-            <version>3.0.0.Final</version>
+            <version>${arquillian.weld.version}</version>
             <scope>test</scope>
         </dependency>
         <dependency>
@@ -199,12 +200,12 @@
                             <dependency>
                                 <groupId>org.apache.maven.surefire</groupId>
                                 <artifactId>surefire-junit47</artifactId>
-                                <version>${surefire.version}</version>
+                                <version>${surefire.mvn.plugin.version}</version>
                             </dependency>
                             <dependency>
                                 <groupId>org.apache.maven.surefire</groupId>
                                 <artifactId>surefire-testng</artifactId>
-                                <version>${surefire.version}</version>
+                                <version>${surefire.mvn.plugin.version}</version>
                             </dependency>
                         </dependencies>
                         <configuration>
diff --git a/tests/integration/microprofile/rest-client-tck/tck-suite.xml b/tests/integration/microprofile/rest-client-tck/tck-suite.xml
index a317e79..05b1e48 100644
--- a/tests/integration/microprofile/rest-client-tck/tck-suite.xml
+++ b/tests/integration/microprofile/rest-client-tck/tck-suite.xml
@@ -1,7 +1,7 @@
 <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
 <!--
 
-    Copyright (c) 2019, 2021 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2019, 2022 Oracle and/or its affiliates. All rights reserved.
 
     This program and the accompanying materials are made available under the
     terms of the Eclipse Public License v. 2.0, which is available at
@@ -24,14 +24,6 @@
             <package name="org.eclipse.microprofile.rest.client.tck.*">
             </package>
         </packages>
-        <classes>
-            <class name="org.eclipse.microprofile.rest.client.tck.ProxyServerTest">
-                <methods>
-                    <!--https://github.com/eclipse/microprofile-rest-client/pull/298-->
-                    <exclude name="testProxy"></exclude>
-                </methods>
-            </class>
-        </classes>
     </test>
 
 </suite>
diff --git a/tests/integration/property-check/pom.xml b/tests/integration/property-check/pom.xml
index d4ee4ad..89cee9e 100644
--- a/tests/integration/property-check/pom.xml
+++ b/tests/integration/property-check/pom.xml
@@ -109,7 +109,7 @@
             </plugin>
         </plugins>
     </build>
-    
+
     <profiles>
         <profile>
             <id>jdk19+</id>
diff --git a/tests/integration/property-check/src/test/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationModelTest.java b/tests/integration/property-check/src/test/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationModelTest.java
index 2a07280..ddb8e08 100644
--- a/tests/integration/property-check/src/test/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationModelTest.java
+++ b/tests/integration/property-check/src/test/java/org/glassfish/jersey/internal/config/SystemPropertiesConfigurationModelTest.java
@@ -137,7 +137,7 @@
                         return Void.class;
                     }
                 });
-        steam = steam.filter(Arrays.stream(predicates).reduce(x->true, Predicate::and));
+        steam = steam.filter(Arrays.stream(predicates).reduce(x -> true, Predicate::and));
         return steam.collect(Collectors.toList());
     }
 
diff --git a/tests/integration/reactive-streams/sse/pom.xml b/tests/integration/reactive-streams/sse/pom.xml
index 7e1d7f1..2eb2f4b 100644
--- a/tests/integration/reactive-streams/sse/pom.xml
+++ b/tests/integration/reactive-streams/sse/pom.xml
@@ -36,7 +36,7 @@
         <dependency>
             <groupId>org.reactivestreams</groupId>
             <artifactId>reactive-streams-tck</artifactId>
-            <version>1.0.3</version>
+            <version>${reactive.streams.version}</version>
             <scope>test</scope>
         </dependency>
         <dependency>
@@ -47,6 +47,7 @@
         <dependency>
             <groupId>org.testng</groupId>
             <artifactId>testng</artifactId>
+            <version>${testng6.version}</version>
             <scope>test</scope>
         </dependency>
         <dependency>
@@ -93,7 +94,7 @@
                     <dependency>
                         <groupId>org.apache.maven.surefire</groupId>
                         <artifactId>surefire-testng</artifactId>
-                        <version>3.0.0-M3</version>
+                        <version>${surefire.mvn.plugin.version}</version>
                     </dependency>
                 </dependencies>
             </plugin>
diff --git a/tests/jmockit/pom.xml b/tests/jmockit/pom.xml
index 75afd57..e5e8246 100644
--- a/tests/jmockit/pom.xml
+++ b/tests/jmockit/pom.xml
@@ -92,7 +92,7 @@
                     <dependency>
                         <groupId>org.apache.maven.surefire</groupId>
                         <artifactId>surefire-junit47</artifactId>
-                        <version>${surefire.version}</version>
+                        <version>${surefire.mvn.plugin.version}</version>
                     </dependency>
                 </dependencies>
             </plugin>
diff --git a/tests/osgi/functional/pom.xml b/tests/osgi/functional/pom.xml
index d5d4740..1f11e07 100644
--- a/tests/osgi/functional/pom.xml
+++ b/tests/osgi/functional/pom.xml
@@ -84,7 +84,7 @@
                     <dependency>
                         <groupId>org.apache.maven.surefire</groupId>
                         <artifactId>surefire-junit47</artifactId>
-                        <version>${surefire.version}</version>
+                        <version>${surefire.mvn.plugin.version}</version>
                     </dependency>
                 </dependencies>
             </plugin>
@@ -301,11 +301,6 @@
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>org.jboss.logging</groupId>
-            <artifactId>jboss-logging</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
             <groupId>com.fasterxml</groupId>
             <artifactId>classmate</artifactId>
             <scope>test</scope>
@@ -377,13 +372,6 @@
             <scope>test</scope>
         </dependency>
 
-        <!-- logging -->
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-log4j12</artifactId>
-            <version>1.6.4</version>
-            <scope>test</scope>
-        </dependency>
     </dependencies>
 
     <profiles>
diff --git a/tests/performance/benchmarks/pom.xml b/tests/performance/benchmarks/pom.xml
index 565c998..2b18b61 100644
--- a/tests/performance/benchmarks/pom.xml
+++ b/tests/performance/benchmarks/pom.xml
@@ -94,6 +94,10 @@
             <type>pom</type>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-jdk-http</artifactId>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/AllBenchmarks.java b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/AllBenchmarks.java
index af66d45..bccc7c4 100644
--- a/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/AllBenchmarks.java
+++ b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/AllBenchmarks.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -36,6 +36,7 @@
 //                .include(JacksonBenchmark.class.getSimpleName())
                 .include(LocatorBenchmark.class.getSimpleName())
                 .include(JerseyUriBuilderBenchmark.class.getSimpleName())
+                .include(HeadersServerBenchmark.class.getName())
                 // Measure throughput in seconds (ops/s).
                 .mode(Mode.Throughput)
                 .timeUnit(TimeUnit.SECONDS)
diff --git a/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/HeadersClientBenchmark.java b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/HeadersClientBenchmark.java
new file mode 100644
index 0000000..2c71a82
--- /dev/null
+++ b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/HeadersClientBenchmark.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.tests.performance.benchmark;
+
+import com.sun.net.httpserver.HttpServer;
+import org.glassfish.jersey.CommonProperties;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.jdkhttp.JdkHttpServerFactory;
+import org.glassfish.jersey.tests.performance.benchmark.headers.HeadersMBRW;
+import org.glassfish.jersey.tests.performance.benchmark.headers.HeadersResource;
+import org.glassfish.jersey.tests.performance.benchmark.headers.HeadersApplication;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Threads;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.RunnerException;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import java.io.IOException;
+import java.net.URI;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@BenchmarkMode(Mode.Throughput)
+@OutputTimeUnit(TimeUnit.SECONDS)
+@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS)
+@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS)
+@Fork(1)
+@Threads(4)
+@State(Scope.Benchmark)
+public class HeadersClientBenchmark {
+
+    static final String BASE_URI = "http://localhost:9009/headers";
+
+    private static final AtomicInteger counter = new AtomicInteger();
+    private static final MediaType MEDIA_PLAIN = MediaType.valueOf(HeadersResource.MEDIA_PLAIN);
+    private static final MediaType MEDIA_JSON = MediaType.valueOf(HeadersResource.MEDIA_JSON);
+
+    private static final boolean INCLUDE_INIT = false;
+
+    private volatile WebTarget webTarget;
+
+    @Setup
+    public void setUp() {
+        if (!INCLUDE_INIT) {
+            webTarget = ClientBuilder.newClient(config()).target(BASE_URI);
+        }
+    }
+
+    private WebTarget webTarget() {
+        return INCLUDE_INIT ? ClientBuilder.newClient(config()).target(BASE_URI) : webTarget;
+    }
+
+    private static class JdkServer {
+        private HttpServer server;
+        void start() {
+            server = JdkHttpServerFactory.createHttpServer(URI.create(BASE_URI), new HeadersApplication(), null, false);
+            server.start();
+            try {
+                TimeUnit.SECONDS.sleep(1L);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+
+        void stop() {
+            server.stop(1);
+        }
+    }
+
+    private static class GrizzlyServer {
+        private org.glassfish.grizzly.http.server.HttpServer httpServer;
+        void start() {
+            httpServer = GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), new HeadersApplication(), null, false);
+            try {
+                httpServer.start();
+                TimeUnit.SECONDS.sleep(1L);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+
+        void stop() {
+            httpServer.shutdownNow();
+        }
+    }
+
+    @Benchmark
+    public void testGetPlainTextClient() {
+        WebTarget target = webTarget().path("headers/getPlain");
+        try (Response r = target.request(MEDIA_PLAIN).get()) {
+            consume(r, HeadersResource.CONTENT_PLAIN, MEDIA_PLAIN);
+        }
+    }
+
+    @Benchmark
+    public void testGetJsonClient() {
+        WebTarget target = webTarget().path("headers/getJson");
+        try (Response r = target.request(MEDIA_JSON).get()) {
+            consume(r, HeadersResource.CONTENT_PLAIN, MEDIA_JSON);
+        }
+    }
+
+    @Benchmark
+    public void testPostPlainTextClient() {
+        WebTarget target = webTarget().path("headers/postPlain");
+        try (Response r = target.request(MEDIA_PLAIN).post(Entity.entity(HeadersResource.CONTENT_PLAIN, MEDIA_PLAIN))) {
+            consume(r, HeadersResource.CONTENT_PLAIN, MEDIA_PLAIN);
+        }
+    }
+
+    @Benchmark
+    public void testPostJsonClient() {
+        WebTarget target = webTarget().path("headers/postJson");
+        try (Response r = target.request(MEDIA_JSON).post(Entity.entity(HeadersResource.CONTENT_PLAIN, MEDIA_JSON))) {
+            consume(r, HeadersResource.CONTENT_PLAIN, MEDIA_JSON);
+        }
+    }
+
+    @Benchmark
+    public void testRandomClient() {
+        switch (counter.incrementAndGet() % 4) {
+            case 0:
+                testGetJsonClient();
+                break;
+            case 1:
+                testGetPlainTextClient();
+                break;
+            case 2:
+                testPostJsonClient();
+                break;
+            case 3:
+                testPostPlainTextClient();
+                break;
+        }
+    }
+
+    private ClientConfig config() {
+        ClientConfig config = new ClientConfig();
+        config.property(CommonProperties.PROVIDER_DEFAULT_DISABLE, "ALL");
+        config.register(HeadersMBRW.class);
+        return config;
+    }
+
+    private void consume(Response response, String expectedContent, MediaType expectedMedia) {
+        if (response.getStatus() != 200) {
+            throw new IllegalStateException("Status:" + response.getStatus());
+        }
+        String content = response.readEntity(String.class);
+        if (!expectedContent.equals(content)) {
+            throw new IllegalStateException("Content:" + content);
+        }
+        if (!expectedMedia.equals(response.getMediaType())) {
+            throw new IllegalStateException("ContentType:" + response.getMediaType());
+        }
+    }
+
+    public static void main(String[] args) throws RunnerException {
+//        JdkServer server = new JdkServer();
+        GrizzlyServer server = new GrizzlyServer();
+        server.start();
+
+        final Options opt = new OptionsBuilder()
+                // Register our benchmarks.
+                .include(HeadersClientBenchmark.class.getSimpleName())
+//               .addProfiler(org.openjdk.jmh.profile.JavaFlightRecorderProfiler.class)
+                .build();
+
+        try {
+            new Runner(opt).run();
+            //new HeadersBenchmark().testGetJsonClient();
+        } finally {
+            server.stop();
+        }
+
+    }
+}
diff --git a/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/HeadersServerBenchmark.java b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/HeadersServerBenchmark.java
new file mode 100644
index 0000000..a72feef
--- /dev/null
+++ b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/HeadersServerBenchmark.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.tests.performance.benchmark;
+
+import org.glassfish.jersey.server.ApplicationHandler;
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.ContainerResponse;
+import org.glassfish.jersey.test.util.server.ContainerRequestBuilder;
+import org.glassfish.jersey.tests.performance.benchmark.headers.HeadersApplication;
+import org.glassfish.jersey.tests.performance.benchmark.headers.HeadersResource;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.TearDown;
+import org.openjdk.jmh.annotations.Threads;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.RunnerException;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+
+import jakarta.ws.rs.core.MediaType;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@BenchmarkMode(Mode.Throughput)
+@OutputTimeUnit(TimeUnit.SECONDS)
+@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS)
+@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS)
+@Fork(1)
+@Threads(4)
+@State(Scope.Benchmark)
+public class HeadersServerBenchmark {
+    private static final AtomicInteger counter = new AtomicInteger();
+    private static final MediaType MEDIA_PLAIN = MediaType.valueOf(HeadersResource.MEDIA_PLAIN);
+    private static final MediaType MEDIA_JSON = MediaType.valueOf(HeadersResource.MEDIA_JSON);
+
+    private volatile ApplicationHandler handler;
+
+    @Setup
+    public void start() throws Exception {
+        handler = new ApplicationHandler(new HeadersApplication());
+    }
+
+    @TearDown
+    public void shutdown() {
+        if (counter.get() != 0) {
+            System.out.append("Executed ").append(String.valueOf(counter.get())).append(" requests");
+        }
+    }
+
+    @Benchmark
+    public void testGetPlainText() throws ExecutionException, InterruptedException {
+        ContainerRequest request = ContainerRequestBuilder
+                .from("headers/getPlain", "GET", handler.getConfiguration())
+                .accept(MEDIA_PLAIN)
+                .build();
+
+        ContainerResponse response = handler.apply(request).get();
+        consume(response, HeadersResource.CONTENT_PLAIN, MEDIA_PLAIN);
+    }
+
+    @Benchmark
+    public void testGetJson() throws ExecutionException, InterruptedException {
+        ContainerRequest request = ContainerRequestBuilder
+                .from("headers/getJson", "GET", handler.getConfiguration())
+                .accept(MEDIA_JSON)
+                .build();
+
+        ContainerResponse response = handler.apply(request).get();
+        consume(response, HeadersResource.CONTENT_PLAIN, MEDIA_JSON);
+    }
+
+    @Benchmark
+    public void testPostPlainText() throws ExecutionException, InterruptedException {
+        ContainerRequest request = ContainerRequestBuilder
+                .from("headers/postPlain", "POST", handler.getConfiguration())
+                .accept(MEDIA_PLAIN)
+                .type(MEDIA_PLAIN)
+                .entity(HeadersResource.CONTENT_PLAIN, handler)
+                .build();
+
+        ContainerResponse response = handler.apply(request).get();
+        consume(response, HeadersResource.CONTENT_PLAIN, MEDIA_PLAIN);
+    }
+
+    @Benchmark
+    public void testPostJson() throws ExecutionException, InterruptedException {
+        ContainerRequest request = ContainerRequestBuilder
+                .from("headers/postJson", "POST", handler.getConfiguration())
+                .accept(MEDIA_JSON)
+                .type(MEDIA_JSON)
+                .entity(HeadersResource.CONTENT_PLAIN, handler)
+                .build();
+
+        ContainerResponse response = handler.apply(request).get();
+        consume(response, HeadersResource.CONTENT_PLAIN, MEDIA_JSON);
+    }
+
+    @Benchmark
+    public void testRandomClient() throws ExecutionException, InterruptedException {
+        switch (counter.incrementAndGet() % 4) {
+            case 0:
+                testGetJson();
+                break;
+            case 1:
+                testGetPlainText();
+                break;
+            case 2:
+                testPostJson();
+                break;
+            case 3:
+                testPostPlainText();
+                break;
+        }
+    }
+
+    private void consume(ContainerResponse response, String expectedContent, MediaType expectedMedia) {
+        if (response.getStatus() != 200) {
+            throw new IllegalStateException("Status:" + response.getStatus());
+        }
+        String content = response.getEntity().toString();
+        if (!expectedContent.equals(content)) {
+            throw new IllegalStateException("Content:" + content);
+        }
+        if (!expectedMedia.equals(response.getMediaType())) {
+            throw new IllegalStateException("ContentType:" + response.getMediaType());
+        }
+    }
+
+    public static void main(String[] args) throws RunnerException {
+        final Options opt = new OptionsBuilder()
+                // Register our benchmarks.
+                .include(HeadersServerBenchmark.class.getSimpleName())
+//                .addProfiler(org.openjdk.jmh.profile.JavaFlightRecorderProfiler.class)
+                .build();
+
+        new Runner(opt).run();
+
+// DEBUG:
+
+//        try {
+//            HeadersServerBenchmark benchmark = new HeadersServerBenchmark();
+//            benchmark.start();
+//            for (int i = 0; i != 5; i++) {
+//                benchmark.testPostPlainText();
+//            }
+//        } catch (Exception e) {
+//            e.printStackTrace();
+//        }
+    }
+
+}
diff --git a/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/headers/HeadersApplication.java b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/headers/HeadersApplication.java
new file mode 100644
index 0000000..6ec127d
--- /dev/null
+++ b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/headers/HeadersApplication.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.tests.performance.benchmark.headers;
+
+import org.glassfish.jersey.CommonProperties;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+
+
+public class HeadersApplication extends ResourceConfig {
+    public HeadersApplication() {
+        register(HeadersResource.class).register(HeadersMBRW.class);
+
+        // Turn off Monitoring to not affect benchmarks.
+        property(ServerProperties.MONITORING_ENABLED, false);
+        property(ServerProperties.MONITORING_STATISTICS_ENABLED, false);
+        property(ServerProperties.MONITORING_STATISTICS_MBEANS_ENABLED, false);
+
+        property(ServerProperties.WADL_FEATURE_DISABLE, true);
+        property(CommonProperties.PROVIDER_DEFAULT_DISABLE, "ALL");
+    }
+}
diff --git a/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/headers/HeadersMBRW.java b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/headers/HeadersMBRW.java
new file mode 100644
index 0000000..9aeda0c
--- /dev/null
+++ b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/headers/HeadersMBRW.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.tests.performance.benchmark.headers;
+
+import org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.JacksonJsonProvider;
+import org.glassfish.jersey.message.internal.ReaderWriter;
+
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.MultivaluedMap;
+import jakarta.ws.rs.ext.MessageBodyReader;
+import jakarta.ws.rs.ext.MessageBodyWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+public class HeadersMBRW implements MessageBodyReader<String>, MessageBodyWriter<String> {
+
+    private static final JacksonJsonProvider jackson = new JacksonJsonProvider();
+
+    @Override
+    public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+        return (type == String.class && HeadersResource.MEDIA_PLAIN.equals(mediaType.toString()))
+                || (type == String.class && HeadersResource.MEDIA_JSON.equals(mediaType.toString()));
+    }
+
+    @Override
+    public String readFrom(Class<String> type, Type genericType, Annotation[] annotations, MediaType mediaType,
+                           MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
+            throws IOException, WebApplicationException {
+        switch (mediaType.toString()) {
+            case HeadersResource.MEDIA_PLAIN:
+                return ReaderWriter.readFromAsString(entityStream, MediaType.TEXT_PLAIN_TYPE);
+            case HeadersResource.MEDIA_JSON:
+                return jackson.readFrom((Class<Object>) (Class) type, genericType, annotations, mediaType,
+                        httpHeaders, entityStream).toString();
+        }
+        return null;
+    }
+
+    @Override
+    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+        return isReadable(type, genericType, annotations, mediaType);
+    }
+
+    @Override
+    public void writeTo(String s, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
+                        MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
+            throws IOException, WebApplicationException {
+        switch (mediaType.toString()) {
+            case HeadersResource.MEDIA_PLAIN:
+                ReaderWriter.writeToAsString(s, entityStream, MediaType.TEXT_PLAIN_TYPE);
+                break;
+            case HeadersResource.MEDIA_JSON:
+                jackson.writeTo(s, type, genericType, annotations, MediaType.APPLICATION_JSON_TYPE, httpHeaders, entityStream);
+                break;
+        }
+    }
+}
diff --git a/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/headers/HeadersResource.java b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/headers/HeadersResource.java
new file mode 100644
index 0000000..f7f3cee
--- /dev/null
+++ b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/headers/HeadersResource.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.tests.performance.benchmark.headers;
+
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.core.Response;
+
+@Path("/headers")
+public class HeadersResource {
+    public static final String MEDIA_PLAIN = "myapplication/someplaintext+someothertoolongtype";
+    public static final String MEDIA_JSON = "myapplication/somejson+someradiculouslylongtype";
+
+    public static final String CONTENT_PLAIN = "some plain text content does not matter";
+    public static final String CONTENT_JSON = "\"" + CONTENT_PLAIN + "\"";
+
+    @GET
+    @Produces(MEDIA_PLAIN)
+    @Path("getPlain")
+    public String getMediaPlain() {
+        return CONTENT_PLAIN;
+    }
+
+    @POST
+    @Produces(MEDIA_PLAIN)
+    @Consumes(MEDIA_PLAIN)
+    @Path("postPlain")
+    public String postMediaPlain(String content) {
+        if (!CONTENT_PLAIN.equals(content)) {
+            throw new WebApplicationException(Response.Status.EXPECTATION_FAILED);
+        }
+        return CONTENT_PLAIN;
+    }
+
+    @GET
+    @Produces(MEDIA_JSON)
+    @Path("getJson")
+    public Response getJson() {
+        return Response.ok(CONTENT_PLAIN, MEDIA_JSON).build();
+    }
+
+    @POST
+    @Produces(MEDIA_JSON)
+    @Consumes(MEDIA_JSON)
+    @Path("postJson")
+    public Response postJson(String json) {
+        if (!CONTENT_PLAIN.equals(json)) {
+            throw new WebApplicationException(Response.Status.EXPECTATION_FAILED);
+        }
+        return Response.ok(CONTENT_PLAIN, MEDIA_JSON).build();
+    }
+
+}
diff --git a/tests/performance/test-cases/pom.xml b/tests/performance/test-cases/pom.xml
index 60c2b73..31f2c11 100644
--- a/tests/performance/test-cases/pom.xml
+++ b/tests/performance/test-cases/pom.xml
@@ -75,7 +75,7 @@
                             <phase>package</phase>
                             <!-- append to the packaging phase. -->
                             <goals>
-                                <goal>attached</goal>
+                                <goal>single</goal>
                                 <!-- goals == mojos -->
                             </goals>
                             <configuration>
diff --git a/tests/release-test/README.MD b/tests/release-test/README.MD
new file mode 100644
index 0000000..395ec2a
--- /dev/null
+++ b/tests/release-test/README.MD
@@ -0,0 +1,20 @@
+[//]: # " Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved. "
+[//]: # "  "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Public License v. 2.0, which is available at "
+[//]: # " http://www.eclipse.org/legal/epl-2.0. "
+[//]: # "  "
+[//]: # " This Source Code may also be made available under the following Secondary "
+[//]: # " Licenses when the conditions for such availability set forth in the "
+[//]: # " Eclipse Public License v. 2.0 are satisfied: GNU General Public License, "
+[//]: # " version 2 with the GNU Classpath Exception, which is available at "
+[//]: # " https://www.gnu.org/software/classpath/license.html. "
+[//]: # "  "
+[//]: # " SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 "
+
+This module is to check the built artifacts before release.
+
+The module can check Jersey 2.x, 3.0.x, and 3.1.x versions.
+
+Usage:
+ * mvn test -Djersey.version=<version> [-Pstaging]
diff --git a/tests/release-test/pom.xml b/tests/release-test/pom.xml
new file mode 100644
index 0000000..33eb1cf
--- /dev/null
+++ b/tests/release-test/pom.xml
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.eclipse.ee4j</groupId>
+        <artifactId>project</artifactId>
+        <version>1.0.8</version>
+        <relativePath>../../../pom.xml</relativePath>
+    </parent>
+
+    <groupId>org.glassfish.jersey.tests</groupId>
+    <artifactId>release-test</artifactId>
+    <version>2.37-SNAPSHOT</version>
+    <packaging>jar</packaging>
+    <name>jersey-release-test</name>
+
+    <description>Jersey post-release validation tests</description>
+
+    <properties>
+        <jersey.version>${jersey.version}</jersey.version> <!-- Must pass using -Djersey.version= -->
+        <maven.compiler.source>11</maven.compiler.source>
+        <maven.compiler.target>11</maven.compiler.target>
+        <maven.version>3.8.6</maven.version>
+        <maven.resolver.version>1.7.3</maven.resolver.version> <!-- 1.8.2 does not work-->
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>3.0.0-M7</version>
+                <configuration>
+                    <forkCount>1</forkCount>
+                    <reuseForks>false</reuseForks>
+                    <enableAssertions>false</enableAssertions>
+                    <includes>
+                        <include>**/DownloadBomPomDependencies.java</include>
+                        <include>**/*Test.class</include>
+                    </includes>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.glassfish.jersey</groupId>
+                <artifactId>jersey-bom</artifactId>
+                <version>${jersey.version}</version>
+                <scope>import</scope>
+                <type>pom</type>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.13.2</version>
+            <scope>test</scope>
+            <!-- maven-plugin-testing-harness does not work with junit 5 -->
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-core</artifactId>
+            <version>${maven.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-model</artifactId>
+            <version>${maven.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-compat</artifactId>
+            <version>${maven.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.maven.plugin-testing</groupId>
+            <artifactId>maven-plugin-testing-harness</artifactId>
+            <version>3.3.0</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven.resolver</groupId>
+            <artifactId>maven-resolver-connector-basic</artifactId>
+            <version>${maven.resolver.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven.resolver</groupId>
+            <artifactId>maven-resolver-transport-http</artifactId>
+            <version>${maven.resolver.version}</version>
+        </dependency>
+    </dependencies>
+</project>
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
new file mode 100644
index 0000000..43e5327
--- /dev/null
+++ b/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/ClassVersionChecker.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.glassfish.jersey.test.artifacts;
+
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+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;
+        return checkClassVersion(jar.getInputStream(entry), jar.getName() + File.separator + entry.getName(), minVersion);
+    }
+
+    private static TestResult checkClassVersion(InputStream inputStream, String filename, int version) throws IOException {
+        TestResult result = new TestResult();
+        DataInputStream in = new DataInputStream(inputStream);
+
+        int magic = in.readInt();
+        if (magic != -889275714) {
+            result.exception().append(filename).println(" is not a valid class!");
+        }
+
+        int minor = in.readUnsignedShort();
+        int major = in.readUnsignedShort();
+        int classVersion = convertMajorMinorToSE(major, minor);
+        TestResult.MessageBuilder builder =  classVersion <= version ? result.ok() : result.exception();
+        builder.append(filename).append(": ").append(major).append(".").append(minor).append(" = JDK ")
+                .println(String.valueOf(classVersion));
+        in.close();
+        return result;
+    }
+
+    private static int convertMajorMinorToSE(int major, int minor) {
+        int comp = (major - 44 + minor);
+        return comp;
+    }
+}
diff --git a/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/DependencyPair.java b/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/DependencyPair.java
new file mode 100644
index 0000000..dd31588
--- /dev/null
+++ b/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/DependencyPair.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.glassfish.jersey.test.artifacts;
+
+import java.util.Arrays;
+
+class DependencyPair {
+    private final String groupId;
+    private final String artifactId;
+
+    DependencyPair(String groupId, String artifactId) {
+        this.groupId = groupId;
+        this.artifactId = artifactId;
+    }
+
+    String artifactId() {
+        return artifactId;
+    }
+
+    String groupId() {
+        return groupId;
+    }
+
+    static DependencyPair[] concat(DependencyPair[] first, DependencyPair[] second) {
+        DependencyPair[] result = Arrays.copyOf(first, first.length + second.length);
+        System.arraycopy(second, 0, result, first.length, second.length);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return groupId + ':' + artifactId;
+    }
+}
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
new file mode 100644
index 0000000..5df3bc1
--- /dev/null
+++ b/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/DependencyResolver.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.glassfish.jersey.test.artifacts;
+
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.artifact.DefaultArtifact;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.resolution.ArtifactRequest;
+import org.eclipse.aether.resolution.ArtifactResolutionException;
+
+import java.util.List;
+
+/**
+ *
+ * @author lukas
+ */
+final class DependencyResolver {
+    static Artifact resolveArtifact(org.apache.maven.model.Dependency d, List<RemoteRepository> remoteRepos,
+                                           RepositorySystem repoSystem, RepositorySystemSession repoSession)
+            throws ArtifactResolutionException {
+        DefaultArtifact artifact = new DefaultArtifact(
+                d.getGroupId(), d.getArtifactId(), d.getClassifier(), 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
new file mode 100644
index 0000000..2c5b3a2
--- /dev/null
+++ b/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/MavenUtil.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+package org.glassfish.jersey.test.artifacts;
+
+import org.apache.maven.model.Dependency;
+import org.apache.maven.model.Model;
+import org.apache.maven.model.Profile;
+import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Properties;
+import java.util.stream.Stream;
+
+public final class MavenUtil {
+
+    static final String JERSEY_VERSION = "jersey.version";
+    private static final String PROJECT_VERSION = "project.version";
+
+    static File getArtifactJar(File repositoryRoot, Dependency dependency, Properties properties) {
+        return getArtifactFile(repositoryRoot, dependency, properties, "jar");
+    }
+
+    private static File getArtifactFile(File repositoryRoot, Dependency dependency, Properties properties, String extension) {
+        StringBuilder fileSuffix = new StringBuilder();
+        String groupIdParts[] = dependency.getGroupId().split("\\.");
+        for (String groupIdPart : groupIdParts) {
+            fileSuffix.append(groupIdPart).append(File.separator);
+        }
+        String artifactIdParts[] = dependency.getArtifactId().split("\\.");
+        for (String artifactIdPart : artifactIdParts) {
+            fileSuffix.append(artifactIdPart).append(File.separator);
+        }
+        String version = MavenUtil.getDependencyVersion(dependency, properties);
+        fileSuffix.append(version).append(File.separator);
+        fileSuffix.append(dependency.getArtifactId()).append('-').append(version).append(".").append(extension);
+        return new File(repositoryRoot, fileSuffix.toString());
+    }
+
+    static String getDependencyVersion(Dependency dependency, Properties properties) {
+        String version = dependency.getVersion();
+        if (version.startsWith("${") && version.endsWith("}")) {
+            String property = version.substring(2, version.length() - 1);
+            final String value;
+            switch (property) {
+                case JERSEY_VERSION: // in pom.xml
+                case PROJECT_VERSION: // in bom.pom
+                    value = getJerseyVersion(properties);
+                    break;
+                default:
+                    value = properties.getProperty(property);
+                    break;
+            }
+            version = value == null ? version : value;
+        }
+        return version;
+    }
+
+    static File getLocalMavenRepository() {
+        String folder = System.getProperty("localRepository");
+        return new File(folder);
+    }
+
+    static Properties getMavenProperties() {
+        try {
+            Model model = getModelFromFile("pom.xml");
+            return model.getProperties();
+        } catch (XmlPullParserException | IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    static Stream<Dependency> keepJerseyJars(Stream<Dependency> stream, DependencyPair... keep) {
+        return stream.filter(dependency -> {
+            for (DependencyPair pair : keep) {
+                if (dependency.getGroupId().equals(pair.groupId()) && dependency.getArtifactId().equals(pair.artifactId())) {
+                    return true;
+                }
+            }
+            return false;
+        });
+    }
+
+    static Stream<Dependency> streamJerseyJars() throws IOException, XmlPullParserException {
+        Model model = getModelFromFile("pom.xml");
+        List<Dependency> deps = getBomPomDependencies(model);
+
+        return deps.stream()
+                .filter(dep -> dep.getGroupId().startsWith("org.glassfish.jersey"))
+                .filter(dep -> dep.getType().equals("jar"));
+    }
+
+    private static Model getModelFromFile(String fileName) throws IOException, XmlPullParserException {
+        File pomFile = new File(fileName);
+        return getModelFromFile(pomFile);
+    }
+
+    private static Model getModelFromFile(File file) throws IOException, XmlPullParserException {
+        MavenXpp3Reader mavenReader = new MavenXpp3Reader();
+        try (FileReader fileReader = new FileReader(file)) {
+            Model model = mavenReader.read(fileReader);
+            return model;
+        }
+    }
+
+    private static List<Dependency> getBomPomDependencies(Model model) throws IOException, XmlPullParserException {
+        Dependency bomPom = null;
+        List<Dependency> dependencies = model.getDependencyManagement().getDependencies();
+        for (Dependency dependency : dependencies) {
+            if (dependency.getGroupId().equals("org.glassfish.jersey") && dependency.getArtifactId().equals("jersey-bom")) {
+                bomPom = dependency;
+                break;
+            }
+        }
+        if (bomPom == null) {
+            throw new IllegalStateException("Bom pom not found");
+        }
+        File pom = getArtifactFile(getLocalMavenRepository(), bomPom, model.getProperties(), "pom");
+        Model bomPomModel = getModelFromFile(pom);
+        return bomPomModel.getDependencyManagement().getDependencies();
+    }
+
+    static String getJerseyVersion(Properties properties) {
+        String property = properties.getProperty(JERSEY_VERSION); // when it is in the pom.file
+        if (property == null || property.startsWith("${")) {
+            property = System.getProperty(JERSEY_VERSION);        // not in pom, but -Djersey.version
+        }
+        if (property == null || property.startsWith("${")) {
+            throw new IllegalStateException("Property " + JERSEY_VERSION + " not set (-Djersey.version=)");
+        }
+        return property;
+    }
+
+    /* Unused at the moment, but could be useful in the future in the case of profiles are needed */
+    private static List<Dependency> getProfileDependency(Model model) {
+        List<Dependency> profileDependencies = Collections.EMPTY_LIST;
+        List<Profile> profiles = model.getProfiles();
+        String activeProfile = getActiveProfile();
+        for (Profile profile : profiles) {
+            if (activeProfile.equals(profile.getId())) {
+                profileDependencies = profile.getDependencies();
+                break;
+            }
+        }
+        return profileDependencies;
+    }
+
+    private static String getActiveProfile() {
+        String profileId = System.getProperty("profileId"); // set this to the surefire plugin
+        return profileId;
+    }
+}
diff --git a/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/TestResult.java b/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/TestResult.java
new file mode 100644
index 0000000..172c596
--- /dev/null
+++ b/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/TestResult.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+package org.glassfish.jersey.test.artifacts;
+
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+
+class TestResult {
+    private List<String> oks = new LinkedList<>();
+    private List<String> exceptions = new LinkedList<>();
+
+    MessageBuilder ok() {
+        return new MessageBuilder(oks);
+    }
+
+    MessageBuilder exception() {
+        return new MessageBuilder(exceptions);
+    }
+    boolean result() {
+        for (String ok : oks) {
+            System.out.append("(pass) ").print(ok);
+        }
+
+        for (String exception : exceptions) {
+            System.out.append("\u001b[31;1m(FAIL) ").append(exception).print("\u001b[0m");
+        }
+
+        return exceptions.isEmpty();
+    }
+
+    public TestResult append(TestResult result) throws IOException {
+        oks.addAll(result.oks);
+        exceptions.addAll(result.exceptions);
+        return this;
+    }
+
+    class MessageBuilder implements Appendable {
+        final List<String> list;
+        final StringBuilder builder = new StringBuilder();
+
+        MessageBuilder(List<String> list) {
+            this.list = list;
+        }
+
+        @Override
+        public MessageBuilder append(CharSequence csq) throws IOException {
+            builder.append(csq);
+            return this;
+        }
+
+        public MessageBuilder append(int i) throws IOException {
+            builder.append(i);
+            return this;
+        }
+
+        @Override
+        public MessageBuilder append(CharSequence csq, int start, int end) throws IOException {
+            builder.append(csq, start, end);
+            return this;
+        }
+
+        @Override
+        public MessageBuilder append(char c) throws IOException {
+            builder.append(c);
+            return this;
+        }
+
+        public TestResult println(String message) {
+            builder.append(message).append('\n');
+            list.add(builder.toString());
+            return TestResult.this;
+        }
+    }
+}
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
new file mode 100644
index 0000000..ef31d4e
--- /dev/null
+++ b/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/DownloadBomPomDependencies.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+package org.glassfish.jersey.test.artifacts;
+
+import org.eclipse.aether.DefaultRepositorySystemSession;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.impl.DefaultServiceLocator;
+import org.eclipse.aether.repository.LocalRepository;
+import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
+import org.eclipse.aether.spi.connector.transport.TransporterFactory;
+import org.eclipse.aether.transport.http.HttpTransporterFactory;
+
+import org.apache.maven.execution.DefaultMavenExecutionRequest;
+import org.apache.maven.execution.MavenExecutionRequest;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.plugin.testing.AbstractMojoTestCase;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.project.ProjectBuilder;
+import org.apache.maven.project.ProjectBuildingRequest;
+import org.apache.maven.project.ProjectBuildingResult;
+import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
+
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
+import org.junit.Test;
+
+import java.io.File;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.List;
+import java.util.Properties;
+import java.util.stream.Collectors;
+
+public class DownloadBomPomDependencies extends AbstractMojoTestCase {
+
+    @Test
+    public void testDownloadBomPomDependencies() throws Exception {
+//        RepositorySystem repositorySystem = (RepositorySystem) lookup(RepositorySystem.class.getName());
+        DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();
+        locator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class);
+        locator.addService(TransporterFactory.class, HttpTransporterFactory.class);
+        RepositorySystem repositorySystem = locator.getService(RepositorySystem.class);
+
+        RepositorySystemSession repoSession = getRepoSession(repositorySystem);
+        List<RemoteRepository> remoteRepos = getRemoteRepositories(repoSession);
+
+        Properties properties = MavenUtil.getMavenProperties();
+        String jerseyVersion = MavenUtil.getJerseyVersion(properties);
+        List<Dependency> memberDeps = MavenUtil.streamJerseyJars().collect(Collectors.toList());
+        for (Dependency member : memberDeps) {
+            member.setVersion(jerseyVersion);
+            Artifact m = DependencyResolver.resolveArtifact(member, remoteRepos, repositorySystem, repoSession);
+            System.out.append("Resolved ").append(member.getGroupId()).append(":").append(member.getArtifactId()).append(":")
+                    .append(member.getVersion()).append(" to ").println(m.getFile().getName());
+        }
+    }
+
+    private List<RemoteRepository> getRemoteRepositories(RepositorySystemSession session) throws Exception {
+        File pom = lookupResourcesPom("/release-test-pom.xml");
+        MavenExecutionRequest request = new DefaultMavenExecutionRequest();
+        request.setPom(pom);
+        request.addActiveProfile("staging");
+        ProjectBuildingRequest buildingRequest = request
+                .getProjectBuildingRequest()
+                .setRepositorySession(session)
+                .setResolveDependencies(true);
+
+        ProjectBuilder projectBuilder = lookup(ProjectBuilder.class);
+        ProjectBuildingResult projectBuildingResult = projectBuilder.build(pom, buildingRequest);
+        MavenProject project = projectBuildingResult.getProject();
+
+        List<RemoteRepository> remoteArtifactRepositories = project.getRemoteProjectRepositories();
+        return remoteArtifactRepositories;
+    }
+
+    private static RepositorySystemSession getRepoSession(RepositorySystem repositorySystem) {
+        DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
+        LocalRepository localRepo = new LocalRepository(MavenUtil.getLocalMavenRepository().getAbsolutePath());
+        session.setLocalRepositoryManager(repositorySystem.newLocalRepositoryManager(session, localRepo));
+        return session;
+    }
+
+    private static File lookupResourcesPom(String pomFile) throws URISyntaxException {
+        URL resource = DownloadBomPomDependencies.class.getResource(pomFile);
+        if (resource == null) {
+            throw new IllegalStateException("Pom file " + pomFile + " was not located on classpath!");
+        }
+        File file = new File(resource.toURI());
+        if (!file.exists()) {
+            throw new IllegalStateException("Cannot locate test pom xml file!");
+        }
+        return file;
+    }
+}
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
new file mode 100644
index 0000000..8da0835
--- /dev/null
+++ b/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/ManifestTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+package org.glassfish.jersey.test.artifacts;
+
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Properties;
+import java.util.jar.JarFile;
+import java.util.stream.Collectors;
+
+public class ManifestTest {
+    private static final File localRepository = MavenUtil.getLocalMavenRepository();
+    private static final Properties properties = MavenUtil.getMavenProperties();
+
+    private static final String BUNDLE_NAME_ATTRIBUTE = "Bundle-Name";
+    private static final String BUNDLE_VERSION_ATTRIBUTE = "Bundle-Version";
+    private static final String [] EXCLUDED_JARS = {"test", "rx-client", "oauth", "weld2-se", "spring",
+            "servlet-portability", /* obsolete */
+            "helidon-connector", /* Helidon does not contain OSGi headers */
+            "grizzly-connector", /* Limited maintenance */
+    };
+
+    @Test
+    public void testHasOsgiManifest() throws IOException, XmlPullParserException {
+        TestResult testResult = new TestResult();
+        List<File> jars = MavenUtil.streamJerseyJars()
+                .filter(dependency -> {
+                    for (String excluded : EXCLUDED_JARS) {
+                        if (dependency.getArtifactId().contains(excluded)) {
+                            return false;
+                        }
+                    }
+                    return true;
+                })
+                .map(dependency -> MavenUtil.getArtifactJar(localRepository, dependency, properties))
+                .collect(Collectors.toList());
+
+        for (String ATTRIBUTE : new String[]{BUNDLE_NAME_ATTRIBUTE, BUNDLE_VERSION_ATTRIBUTE}) {
+            for (File jar : jars) {
+                JarFile jarFile = new JarFile(jar);
+                String value = jarFile.getManifest().getMainAttributes().getValue(ATTRIBUTE);
+                TestResult.MessageBuilder builder = value != null ? testResult.ok() : testResult.exception();
+                builder.append(jar.getName()).append(value == null ? " DOES NOT CONTAIN " : " CONTAINS ")
+                        .append(ATTRIBUTE).println(" attribute");
+            }
+        }
+
+        //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
new file mode 100644
index 0000000..deef29a
--- /dev/null
+++ b/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/MultiReleaseTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.glassfish.jersey.test.artifacts;
+
+import org.apache.maven.model.Dependency;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Optional;
+import java.util.Properties;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.zip.ZipEntry;
+
+public class MultiReleaseTest {
+    private static final String s = "";
+    private static final File localRepository = MavenUtil.getLocalMavenRepository();
+    private static final Properties properties = MavenUtil.getMavenProperties();
+
+    private static final DependencyPair[] jdk11multiRelease = jdk11multiRelease(properties);
+    private static final DependencyPair[] jdk12multiRelease = jdk12multiRelease(properties);
+    private static final DependencyPair[] jdk17multiRelease = jdk17multiRelease(properties);
+
+    @Test
+    public void testIsJdkMultiRelease() throws IOException, XmlPullParserException {
+        TestResult result = testJdkVersions("11", jdk11multiRelease);
+        result.append(testJdkVersions("12", jdk12multiRelease));
+        result.append(testJdkVersions("17", jdk17multiRelease));
+        //Assertions.assertTrue(result.result(), "Some error occurred, see previous messages");
+        Assert.assertTrue("Some error occurred, see previous messages", result.result());
+    }
+
+    private static TestResult testJdkVersions(String version, DependencyPair... dependencies)
+            throws XmlPullParserException, IOException {
+        final TestResult result = new TestResult();
+        if (dependencies == null || dependencies.length == 0) {
+            return result;
+        }
+
+        Stream<Dependency> deps = MavenUtil.streamJerseyJars();
+        List<File> files = MavenUtil.keepJerseyJars(deps, dependencies)
+                .map(dependency -> MavenUtil.getArtifactJar(localRepository, dependency, properties))
+                .collect(Collectors.toList());
+
+        //Assertions.assertEquals(dependencies.length, files.size(), "Some jdk " + version + " dependencies not found");
+        if (dependencies.length != files.size()) {
+            System.out.println("Expected:");
+            for (DependencyPair pair : dependencies) {
+                System.out.println(pair);
+            }
+            System.out.println("Resolved:");
+            for (File file : files) {
+                System.out.println(file.getName());
+            }
+            Assert.assertEquals("Some jdk " + version + " dependencies not found", dependencies.length, files.size());
+        }
+
+        for (File jar : files) {
+            JarFile jarFile = new JarFile(jar);
+            if (!jarFile.isMultiRelease()) {
+                result.exception().append("Not a multirelease jar ").append(jar.getName()).println("!");
+            }
+            ZipEntry versions = jarFile.getEntry("META-INF/versions/" + version);
+            if (versions == null) {
+                result.exception().append("No classes for JDK ").append(version).append(" for ").println(jar.getName());
+            }
+            result.ok().append("Classes for JDK ").append(version).append(" found for ").println(jar.getName());
+
+            Optional<JarEntry> file = jarFile.stream()
+                    .filter(entry -> !entry.isDirectory())
+                    .filter(entry -> !entry.getName().contains("versions"))
+                    .filter(entry -> entry.getName().endsWith(".class"))
+                    .findAny();
+            JarEntry jarEntry = file.get();
+            result.append(ClassVersionChecker.checkClassVersion(jarFile, jarEntry, properties));
+        }
+
+        return result;
+    }
+
+    private static DependencyPair[] jdk11multiRelease(Properties properties) {
+        String jerseyVersion = MavenUtil.getJerseyVersion(properties);
+        if (jerseyVersion.startsWith("3.0")) {
+            return jdk11multiRelease30();
+        } else if (jerseyVersion.startsWith("3")) {
+            return jdk11multiRelease31();
+        }
+        return jdk11multiRelease2();
+    }
+
+    private static DependencyPair[] jdk11multiRelease2() {
+        return new DependencyPair[] {
+                new DependencyPair("org.glassfish.jersey.bundles", "jaxrs-ri"),
+                new DependencyPair("org.glassfish.jersey.core", "jersey-common")
+        };
+    }
+
+    private static DependencyPair[] jdk11multiRelease30() {
+        DependencyPair[] pairs30 = {
+                new DependencyPair("org.glassfish.jersey.containers", "jersey-container-jetty-http"),
+                new DependencyPair("org.glassfish.jersey.containers", "jersey-container-jetty-servlet"),
+                new DependencyPair("org.glassfish.jersey.test-framework.providers", "jersey-test-framework-provider-jetty")
+        };
+        return DependencyPair.concat(jdk11multiRelease2(), pairs30);
+    }
+
+    private static DependencyPair[] jdk11multiRelease31() {
+        return new DependencyPair[]{};
+    }
+
+    private static DependencyPair[] jdk12multiRelease(Properties properties) {
+        return new DependencyPair[]{
+                new DependencyPair("org.glassfish.jersey.ext", "jersey-wadl-doclet")
+        };
+    }
+
+    private static DependencyPair[] jdk17multiRelease(Properties properties) {
+        String jerseyVersion = MavenUtil.getJerseyVersion(properties);
+        if (jerseyVersion.startsWith("3")) {
+            return new DependencyPair[] {
+                    new DependencyPair("org.glassfish.jersey.connectors", "jersey-helidon-connector"),
+                    new DependencyPair("org.glassfish.jersey.ext", "jersey-spring6")
+            };
+        }
+        return new DependencyPair[]{};
+    }
+}
diff --git a/tests/release-test/src/test/resources/release-test-pom.xml b/tests/release-test/src/test/resources/release-test-pom.xml
new file mode 100644
index 0000000..d8aa486
--- /dev/null
+++ b/tests/release-test/src/test/resources/release-test-pom.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.eclipse.ee4j</groupId>
+        <artifactId>project</artifactId>
+        <version>1.0.7</version>
+    </parent>
+
+    <groupId>org.glassfish.jersey.tests</groupId>
+    <artifactId>release-test-test</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+
+</project>
\ No newline at end of file
diff --git a/tools/jersey-doc-modulelist-maven-plugin/pom.xml b/tools/jersey-doc-modulelist-maven-plugin/pom.xml
index dd83a1c..b316d42 100644
--- a/tools/jersey-doc-modulelist-maven-plugin/pom.xml
+++ b/tools/jersey-doc-modulelist-maven-plugin/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.eclipse.ee4j</groupId>
         <artifactId>project</artifactId>
-        <version>1.0.7</version>
+        <version>1.0.8</version>
     </parent>
 
     <groupId>org.glassfish.jersey.tools.plugins</groupId>
diff --git a/tools/jersey-release-notes-maven-plugin/pom.xml b/tools/jersey-release-notes-maven-plugin/pom.xml
index 3ede899..195212a 100644
--- a/tools/jersey-release-notes-maven-plugin/pom.xml
+++ b/tools/jersey-release-notes-maven-plugin/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.eclipse.ee4j</groupId>
         <artifactId>project</artifactId>
-        <version>1.0.6</version>
+        <version>1.0.8</version>
     </parent>
 
     <groupId>org.glassfish.jersey.tools.plugins</groupId>