Merge commit 'c42a6a214b9cbee8b4bd9b27e3693215403adadb' into 3.x

Signed-off-by: Maxim Nesen <maxim.nesen@oracle.com>
diff --git a/connectors/helidon-connector/src/test/java/org/glassfish/jersey/helidon/connector/MetaInfOverrideTest.java b/connectors/helidon-connector/src/test/java/org/glassfish/jersey/helidon/connector/MetaInfOverrideTest.java
new file mode 100644
index 0000000..0483bdd
--- /dev/null
+++ b/connectors/helidon-connector/src/test/java/org/glassfish/jersey/helidon/connector/MetaInfOverrideTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * 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.helidon.connector;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.client.HttpUrlConnectorProvider;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.ClientRequestContext;
+import jakarta.ws.rs.client.ClientRequestFilter;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.Response;
+import java.io.IOException;
+
+// The Helidon jar has META-INF set
+// Test of override
+class MetaInfOverrideTest extends JerseyTest {
+
+    @Path("/origin")
+    public static class UserAgentServer {
+        @GET
+        public String get(@Context HttpHeaders headers) {
+            return headers.getHeaderString(HttpHeaders.USER_AGENT);
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(UserAgentServer.class);
+    }
+
+    @Test
+    void defaultMetaInfTest() {
+        try (Response r = target("origin").request().get()) {
+            Assertions.assertEquals(200, r.getStatus());
+            Assertions.assertTrue(r.readEntity(String.class).contains("Helidon"));
+        }
+    }
+
+    @Test
+    void overrideMetaInfTest() {
+        ClientConfig config = new ClientConfig();
+        config.connectorProvider(new HttpUrlConnectorProvider());
+        try (Response r = ClientBuilder.newClient(config).target(target("origin").getUri()).request().get()) {
+            Assertions.assertEquals(200, r.getStatus());
+            r.bufferEntity();
+            System.out.println(r.readEntity(String.class));
+            Assertions.assertTrue(r.readEntity(String.class).contains("HttpUrlConnection"));
+        }
+    }
+
+    @Test
+    void overrideMetaInfByOtherConfigPropertyTest() {
+        ClientConfig config = new ClientConfig();
+        config.property(ClientProperties.CONNECTOR_PROVIDER, "org.glassfish.jersey.client.HttpUrlConnectorProvider");
+        try (Response r = ClientBuilder.newClient(config).target(target("origin").getUri()).request().get()) {
+            Assertions.assertEquals(200, r.getStatus());
+            r.bufferEntity();
+            System.out.println(r.readEntity(String.class));
+            Assertions.assertTrue(r.readEntity(String.class).contains("HttpUrlConnection"));
+        }
+    }
+
+    @Test
+    void overrideMetaInfByThePropertyTest() {
+        try (Response r = ClientBuilder.newBuilder()
+                .property(ClientProperties.CONNECTOR_PROVIDER, "org.glassfish.jersey.client.HttpUrlConnectorProvider")
+                .build()
+                .target(target("origin").getUri()).request().get()) {
+            Assertions.assertEquals(200, r.getStatus());
+            r.bufferEntity();
+            System.out.println(r.readEntity(String.class));
+            Assertions.assertTrue(r.readEntity(String.class).contains("HttpUrlConnection"));
+        }
+    }
+}
diff --git a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyClientHandler.java b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyClientHandler.java
index 71f38ea..d460867 100644
--- a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyClientHandler.java
+++ b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyClientHandler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2023 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -189,6 +189,9 @@
 
             if (content.isReadable()) {
                 content.retain();
+                if (nis == null) {
+                    nis = new NettyInputStream();
+                }
                 nis.publish(content);
             }
 
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java b/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java
index 909af8d..cd90578 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -24,6 +24,8 @@
 import org.glassfish.jersey.internal.util.PropertiesHelper;
 import org.glassfish.jersey.internal.util.PropertyAlias;
 
+import jakarta.ws.rs.client.ClientBuilder;
+
 /**
  * Jersey client implementation configuration properties.
  *
@@ -444,7 +446,7 @@
             EXPECT_100_CONTINUE_THRESHOLD_SIZE = "jersey.config.client.request.expect.100.continue.threshold.size";
 
     /**
-     * Default threshold size (64kb) after which which Expect:100-Continue header would be applied before
+     * Default threshold size (64kb) after which Expect:100-Continue header would be applied before
      * the main request.
      *
      * @since 2.32
@@ -463,6 +465,22 @@
      */
     public static final String QUERY_PARAM_STYLE = "jersey.config.client.uri.query.param.style";
 
+    /**
+     * Sets the {@link org.glassfish.jersey.client.spi.ConnectorProvider} class. Overrides the value from META-INF/services.
+     *
+     * <p>
+     *     The value MUST be an instance of {@code String}.
+     * </p>
+     * <p>
+     *     The property is recognized by {@link ClientBuilder}.
+     * </p>
+     * <p>
+     *     The name of the configuration property is <tt>{@value}</tt>.
+     * </p>
+     * @since 2.40
+     */
+    public static final String CONNECTOR_PROVIDER = "jersey.config.client.connector.provider";
+
     private ClientProperties() {
         // prevents instantiation
     }
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/JerseyClientBuilder.java b/core-client/src/main/java/org/glassfish/jersey/client/JerseyClientBuilder.java
index 69b6265..9277919 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/JerseyClientBuilder.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/JerseyClientBuilder.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,6 +16,7 @@
 
 package org.glassfish.jersey.client;
 
+import java.security.AccessController;
 import java.security.KeyStore;
 import java.util.Collections;
 import java.util.LinkedList;
@@ -32,9 +33,13 @@
 import javax.net.ssl.SSLContext;
 
 import org.glassfish.jersey.SslConfigurator;
+import org.glassfish.jersey.client.innate.inject.NonInjectionManager;
 import org.glassfish.jersey.client.internal.LocalizationMessages;
 import org.glassfish.jersey.client.spi.ClientBuilderListener;
+import org.glassfish.jersey.client.spi.ConnectorProvider;
 import org.glassfish.jersey.internal.ServiceFinder;
+import org.glassfish.jersey.internal.config.ExternalPropertiesConfigurationFactory;
+import org.glassfish.jersey.internal.util.ReflectionHelper;
 import org.glassfish.jersey.internal.util.collection.UnsafeValue;
 import org.glassfish.jersey.internal.util.collection.Values;
 import org.glassfish.jersey.model.internal.RankedComparator;
@@ -186,6 +191,9 @@
 
     @Override
     public JerseyClient build() {
+        ExternalPropertiesConfigurationFactory.configure(this.config);
+        setConnectorFromProperties();
+
         if (sslContext != null) {
             return new JerseyClient(config, sslContext, hostnameVerifier, null);
         } else if (sslConfigurator != null) {
@@ -204,6 +212,20 @@
         }
     }
 
+    private void setConnectorFromProperties() {
+        final Object connectorClass = config.getProperty(ClientProperties.CONNECTOR_PROVIDER);
+        if (connectorClass != null) {
+            if (String.class.isInstance(connectorClass)) {
+                Class<? extends ConnectorProvider> clazz
+                        = AccessController.doPrivileged(ReflectionHelper.classForNamePA((String) connectorClass));
+                final ConnectorProvider connectorProvider = new NonInjectionManager().justCreate(clazz);
+                config.connectorProvider(connectorProvider);
+            } else {
+                throw new IllegalArgumentException();
+            }
+        }
+    }
+
     @Override
     public ClientConfig getConfiguration() {
         return config;
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SniConfigurator.java b/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SniConfigurator.java
index 39a339d..596c587 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SniConfigurator.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SniConfigurator.java
@@ -16,6 +16,8 @@
 
 package org.glassfish.jersey.client.innate.http;
 
+import org.glassfish.jersey.client.internal.LocalizationMessages;
+
 import javax.net.ssl.SNIHostName;
 import javax.net.ssl.SNIServerName;
 import javax.net.ssl.SSLEngine;
@@ -27,12 +29,14 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.logging.Logger;
 
 /**
  * A unified routines to set {@link SNIHostName} for the {@link javax.net.ssl.SSLContext}.
  * To be reused in connectors.
  */
 final class SniConfigurator {
+    private static final Logger LOGGER = Logger.getLogger(SniConfigurator.class.getName());
     private final String hostName;
     private SniConfigurator(String hostName) {
         this.hostName = hostName;
@@ -85,6 +89,7 @@
         SSLParameters sslParameters = sslEngine.getSSLParameters();
         updateSSLParameters(sslParameters);
         sslEngine.setSSLParameters(sslParameters);
+        LOGGER.fine(LocalizationMessages.SNI_ON_SSLENGINE());
     }
 
     /**
@@ -95,6 +100,7 @@
         SSLParameters sslParameters = sslSocket.getSSLParameters();
         updateSSLParameters(sslParameters);
         sslSocket.setSSLParameters(sslParameters);
+        LOGGER.fine(LocalizationMessages.SNI_ON_SSLSOCKET());
     }
 
     private SSLParameters updateSSLParameters(SSLParameters sslParameters) {
@@ -103,6 +109,7 @@
         serverNames.add(serverName);
 
         sslParameters.setServerNames(serverNames);
+        LOGGER.finer(LocalizationMessages.SNI_UPDATE_SSLPARAMS(hostName));
 
         return sslParameters;
     }
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/innate/inject/NonInjectionManager.java b/core-client/src/main/java/org/glassfish/jersey/client/innate/inject/NonInjectionManager.java
index 7062bab..047bba8 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/innate/inject/NonInjectionManager.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/innate/inject/NonInjectionManager.java
@@ -418,10 +418,31 @@
         }
 
         ClassBindings<T> classBindings = classBindings(createMe);
-        return classBindings.create();
+        return classBindings.create(true);
     }
 
-    private <T> T justCreate(Class<T> createMe) {
+    @Override
+    public <T> T createAndInitialize(Class<T> createMe) {
+        checkShutdown();
+
+        if (InjectionManager.class.equals(createMe)) {
+            return (T) this;
+        }
+        if (RequestScope.class.equals(createMe)) {
+            if (!isRequestScope) {
+                isRequestScope = true;
+                return (T) new NonInjectionRequestScope();
+            } else {
+                throw new IllegalStateException(LocalizationMessages.NONINJECT_REQUESTSCOPE_CREATED());
+            }
+        }
+
+        ClassBindings<T> classBindings = classBindings(createMe);
+        T t = classBindings.create(false);
+        return t != null ? t : justCreate(createMe);
+    }
+
+    public <T> T justCreate(Class<T> createMe) {
         T result = null;
         try {
             Constructor<T> mostArgConstructor = findConstructor(createMe);
@@ -493,11 +514,6 @@
     }
 
     @Override
-    public <T> T createAndInitialize(Class<T> createMe) {
-        return justCreate(createMe);
-    }
-
-    @Override
     public void inject(Object injectMe) {
         Method postConstruct = getAnnotatedMethod(injectMe, PostConstruct.class);
         if (postConstruct != null) {
@@ -750,7 +766,7 @@
             return t;
         }
 
-        X create() {
+        X create(boolean throwWhenNoBinding) {
             _checkUnique();
             if (!instanceBindings.isEmpty()) {
                 return _getInstance(instanceBindings.get(0));
@@ -762,7 +778,11 @@
                 return _create(supplierClassBindings.get(0));
             }
 
-            throw new IllegalStateException(LocalizationMessages.NONINJECT_NO_BINDING(type));
+            if (throwWhenNoBinding) {
+                throw new IllegalStateException(LocalizationMessages.NONINJECT_NO_BINDING(type));
+            } else {
+                return null;
+            }
         }
 
         protected X getInstance() {
@@ -770,7 +790,7 @@
             if (instance != null) {
                 return instance;
             }
-            return create();
+            return create(true);
         }
 
         List<X> allInstances() {
@@ -931,7 +951,7 @@
 
         @SuppressWarnings("unchecked")
         @Override
-        T create() {
+        T create(boolean throwWhenNoBinding) {
             if (ParameterizedType.class.isInstance(type)) {
                 ParameterizedType pt = (ParameterizedType) type;
                 if (Provider.class.equals(pt.getRawType())) {
@@ -955,7 +975,7 @@
                     };
                 }
             }
-            return super.create();
+            return super.create(throwWhenNoBinding);
         }
     }
 
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java b/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java
index c7e0ac2..2fdb7e9 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java
@@ -345,7 +345,13 @@
         final HttpURLConnection uc;
         final Optional<ClientProxy> proxy = ClientProxy.proxyFromRequest(request);
         final SSLParamConfigurator sniConfig = SSLParamConfigurator.builder().request(request).build();
-        final URI sniUri = sniConfig.isSNIRequired() ? sniConfig.toIPRequestUri() : request.getUri();
+        final URI sniUri;
+        if (sniConfig.isSNIRequired()) {
+            sniUri = sniConfig.toIPRequestUri();
+            LOGGER.fine(LocalizationMessages.SNI_URI_REPLACED(sniUri.getHost(), request.getUri().getHost()));
+        } else {
+            sniUri = request.getUri();
+        }
 
         proxy.ifPresent(clientProxy -> ClientProxy.setBasicAuthorizationHeader(request.getHeaders(), proxy.get()));
         uc = this.connectionFactory.getConnection(sniUri.toURL(), proxy.isPresent() ? proxy.get().proxy() : null);
diff --git a/core-client/src/main/resources/org/glassfish/jersey/client/internal/jdkconnector/localization.properties b/core-client/src/main/resources/org/glassfish/jersey/client/internal/jdkconnector/localization.properties
deleted file mode 100644
index e8f0342..0000000
--- a/core-client/src/main/resources/org/glassfish/jersey/client/internal/jdkconnector/localization.properties
+++ /dev/null
@@ -1,73 +0,0 @@
-#
-# Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
-#
-# This program and the accompanying materials are made available under the
-# terms of the Eclipse Public License v. 2.0, which is available at
-# http://www.eclipse.org/legal/epl-2.0.
-#
-# This Source Code may also be made available under the following Secondary
-# Licenses when the conditions for such availability set forth in the
-# Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
-# version 2 with the GNU Classpath Exception, which is available at
-# https://www.gnu.org/software/classpath/license.html.
-#
-# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
-#
-
-read.listener.set.only.once="Read listener can be set only once."
-async.operation.not.supported="Operation not supported in synchronous mode."
-sync.operation.not.supported="Operation not supported in asynchronous mode."
-write.when.not.ready="Asynchronous write called when stream is in non-ready state."
-stream.closed.for.input="This stream has already been closed for input."
-write.listener.set.only.once="Write listener can be set only once."
-stream.closed="The stream has been closed."
-writing.failed="Writing data failed"
-buffer.incorrect.length="Buffer passed for encoding is neither a multiple of chunkSize nor smaller than chunkSize."
-connector.configuration="Connector configuration: {0}."
-negative.chunk.size="Configured chunk size is negative: {0}, using default value: {1}."
-timeout.receiving.response="Timeout receiving response."
-timeout.receiving.response.body="Timeout receiving response body."
-closed.while.sending.request="Connection closed by the server while sending request".
-closed.while.receiving.response="Connection closed by the server while receiving response."
-closed.while.receiving.body="Connection closed by the server while receiving response body."
-connection.closed="Connection closed by the server."
-closed.by.client.while.sending="Connection closed by the client while sending request."
-closed.by.client.while.receiving="Connection closed by the client while receiving response."
-closed.by.client.while.receiving.body="Connection closed by the client while receiving response body."
-connection.timeout="Connection timed out."
-connection.changing.state="HTTP connection {0}:{1} changing state {2} -> {3}."
-unexpected.data.in.buffer="Unexpected data remain in the buffer after the HTTP response has been parsed."
-http.initial.line.overflow="HTTP packet initial line is too large."
-http.packet.header.overflow="HTTP packet header is too large."
-http.negative.content.length="Content length cannot be less than 0."
-http.invalid.content.length="Invalid format of content length code."
-http.request.no.body="This HTTP request does not have a body."
-http.request.no.buffered.body="Buffered body is available only in buffered body mode."
-http.request.body.size.not.available="Body size is not available in chunked body mode."
-proxy.user.name.missing="User name is missing"
-proxy.password.missing="Password is missing"
-proxy.qop.no.supported="The 'qop' (quality of protection) = {0} extension requested by the server is not supported. Cannot authenticate against the server using Http Digest Authentication."
-proxy.407.twice="Received 407 for the second time."
-proxy.fail.auth.header="Creating authorization header failed."
-proxy.connect.fail="Connecting to proxy failed with status {0}."
-proxy.missing.auth.header="Proxy-Authenticate header value is missing or empty."
-proxy.unsupported.scheme="Unsupported scheme: {0}."
-redirect.no.location="Received redirect that does not contain a location or the location is empty."
-redirect.error.determining.location="Error determining redirect location."
-redirect.infinite.loop="Infinite loop in chained redirects detected."
-redirect.limit.reached="Max chained redirect limit ({0}) exceeded."
-ssl.session.closed="SSL session has been closed."
-http.body.size.overflow="Body size exceeds declared size"
-http.invalid.chunk.size.hex.value="Invalid byte representing a hex value within a chunk length encountered : {0}"
-http.unexpected.chunk.header="Unexpected HTTP chunk header."
-http.chunk.encoding.prefix.overflow="The chunked encoding length prefix is too large."
-http.trailer.header.overflow="The chunked encoding trailer header is too large."
-transport.connection.not.closed="Could not close a connection."
-transport.set.class.loader.failed="Cannot set thread context class loader."
-transport.executor.closed="Cannot set thread context class loader."
-transport.executor.queue.limit.reached="A limit of client thread pool queue has been reached."
-thread.pool.max.size.too.small="Max thread pool size cannot be smaller than 3."
-thread.pool.core.size.too.small="Core thread pool size cannot be smaller than 0."
-http.connection.establishing.illegal.state="Cannot try to establish connection if the connection is in other than CREATED state\
-  . Current state: {0}.
-http.connection.not.idle="Http request cannot be sent over a connection that is in other state than IDLE. Current state: {0}" 
diff --git a/core-client/src/main/resources/org/glassfish/jersey/client/internal/localization.properties b/core-client/src/main/resources/org/glassfish/jersey/client/internal/localization.properties
index ee80184..291ac98 100644
--- a/core-client/src/main/resources/org/glassfish/jersey/client/internal/localization.properties
+++ b/core-client/src/main/resources/org/glassfish/jersey/client/internal/localization.properties
@@ -83,6 +83,10 @@
 restricted.header.property.setting.true=Restricted headers are enabled using [{0}] system property(setting only takes effect on\
    connections created after the property has been set/changed).
 request.entity.already.written=The entity was already written in this request. The entity can be written (serialized into the output stream) only once per a request.
+sni.on.sslsocket=Setting SNIServerName on SSLSocket
+sni.on.sslengine=Setting SNIServerName on SSLEngine
+sni.uri.replaced=HTTP Request sent with request to IP address {0} rather than the hostname {1}.
+sni.update.sslparams=Updating SSLParameters for SNIServerName={0}.
 unexpected.error.response.processing=Unexpected error during response processing.
 use.encoding.ignored=Value {1} of {0} client property will be ignored as it is not a valid supported encoding. \
   Valid supported encodings are: {2}
diff --git a/core-common/src/main/java/org/glassfish/jersey/CommonProperties.java b/core-common/src/main/java/org/glassfish/jersey/CommonProperties.java
index d5a1658..e71e5e1 100644
--- a/core-common/src/main/java/org/glassfish/jersey/CommonProperties.java
+++ b/core-common/src/main/java/org/glassfish/jersey/CommonProperties.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2023 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -305,6 +305,22 @@
     public static final String JSON_JACKSON_DISABLED_MODULES_SERVER = "jersey.config.server.json.jackson.disabled.modules";
 
     /**
+     * <p>
+     *  Force the {@link jakarta.ws.rs.ext.ParamConverter} to throw {@link IllegalArgumentException} as mandated in javadoc.
+     *  Must be convertible to {@link Boolean} value.
+     * </p>
+     * <p>
+     *  Internally the {@code Exception} is caught by Jersey and usually converted to {@code null}.
+     *  Therefore, the default value is set to {@code false} to speed-up the conversion.
+     * </p>
+     * <p>
+     *  The name of the configuration property is <tt>{@value}</tt>.
+     * </p>
+     * @since 2.40
+     */
+    public static final String PARAM_CONVERTERS_THROW_IAE = "jersey.config.paramconverters.throw.iae";
+
+    /**
      * Prevent instantiation.
      */
     private CommonProperties() {
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/config/ExternalPropertiesAutoDiscoverable.java b/core-common/src/main/java/org/glassfish/jersey/internal/config/ExternalPropertiesAutoDiscoverable.java
deleted file mode 100644
index 8665e2c..0000000
--- a/core-common/src/main/java/org/glassfish/jersey/internal/config/ExternalPropertiesAutoDiscoverable.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package org.glassfish.jersey.internal.config;
-
-import org.glassfish.jersey.internal.spi.AutoDiscoverable;
-
-import jakarta.annotation.Priority;
-import jakarta.ws.rs.ConstrainedTo;
-import jakarta.ws.rs.RuntimeType;
-import jakarta.ws.rs.core.FeatureContext;
-
-@ConstrainedTo(RuntimeType.CLIENT) //server is configured directly in ResourceConfig
-@Priority(AutoDiscoverable.DEFAULT_PRIORITY)
-public class ExternalPropertiesAutoDiscoverable implements AutoDiscoverable {
-    @Override
-    public void configure(FeatureContext context) {
-        if (!context.getConfiguration().isRegistered(ExternalPropertiesConfigurationFeature.class)) {
-            context.register(ExternalPropertiesConfigurationFeature.class);
-        }
-    }
-}
\ No newline at end of file
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/config/ExternalPropertiesConfigurationFeature.java b/core-common/src/main/java/org/glassfish/jersey/internal/config/ExternalPropertiesConfigurationFeature.java
deleted file mode 100644
index 1c10c1c..0000000
--- a/core-common/src/main/java/org/glassfish/jersey/internal/config/ExternalPropertiesConfigurationFeature.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package org.glassfish.jersey.internal.config;
-
-import jakarta.ws.rs.core.Feature;
-import jakarta.ws.rs.core.FeatureContext;
-
-public class ExternalPropertiesConfigurationFeature implements Feature {
-
-    @Override
-    public boolean configure(FeatureContext configurableContext) {
-        return ExternalPropertiesConfigurationFactory.configure(configurableContext);
-    }
-
-}
\ No newline at end of file
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 b0984f5..0a9aaba 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
@@ -35,10 +35,12 @@
 import jakarta.ws.rs.WebApplicationException;
 import jakarta.ws.rs.ext.ParamConverter;
 import jakarta.ws.rs.ext.ParamConverterProvider;
+import jakarta.ws.rs.core.Configuration;
 import jakarta.ws.rs.core.Context;
 import jakarta.inject.Inject;
 import jakarta.inject.Singleton;
 
+import org.glassfish.jersey.CommonProperties;
 import org.glassfish.jersey.internal.LocalizationMessages;
 import org.glassfish.jersey.internal.util.ReflectionHelper;
 import org.glassfish.jersey.internal.util.collection.ClassTypePair;
@@ -55,12 +57,32 @@
 @Singleton
 public class ParamConverters {
 
-    private abstract static class AbstractStringReader<T> implements ParamConverter<T> {
+    private static class ParamConverterCompliance {
+        protected final boolean canReturnNull;
+
+        private ParamConverterCompliance(boolean canReturnNull) {
+            this.canReturnNull = canReturnNull;
+        }
+
+        protected <T> T nullOrThrow() {
+            if (canReturnNull) {
+                return null;
+            } else {
+                throw new IllegalArgumentException(LocalizationMessages.METHOD_PARAMETER_CANNOT_BE_NULL("value"));
+            }
+        }
+    }
+
+    private abstract static class AbstractStringReader<T> extends ParamConverterCompliance implements ParamConverter<T> {
+
+        private AbstractStringReader(boolean canReturnNull) {
+            super(canReturnNull);
+        }
 
         @Override
         public T fromString(final String value) {
             if (value == null) {
-                throw new IllegalArgumentException(LocalizationMessages.METHOD_PARAMETER_CANNOT_BE_NULL("value"));
+                return nullOrThrow();
             }
             try {
                 return _fromString(value);
@@ -85,7 +107,7 @@
         @Override
         public String toString(final T value) throws IllegalArgumentException {
             if (value == null) {
-                throw new IllegalArgumentException(LocalizationMessages.METHOD_PARAMETER_CANNOT_BE_NULL("value"));
+                return nullOrThrow();
             }
             return value.toString();
         }
@@ -97,7 +119,11 @@
      * by invoking a single {@code String} parameter constructor on the target type.
      */
     @Singleton
-    public static class StringConstructor implements ParamConverterProvider {
+    public static class StringConstructor extends ParamConverterCompliance implements ParamConverterProvider {
+
+        private StringConstructor(boolean canReturnNull) {
+            super(canReturnNull);
+        }
 
         @Override
         public <T> ParamConverter<T> getConverter(final Class<T> rawType,
@@ -106,7 +132,7 @@
 
             final Constructor constructor = AccessController.doPrivileged(ReflectionHelper.getStringConstructorPA(rawType));
 
-            return (constructor == null) ? null : new AbstractStringReader<T>() {
+            return (constructor == null) ? null : new AbstractStringReader<T>(canReturnNull) {
 
                 @Override
                 protected T _fromString(final String value) throws Exception {
@@ -122,7 +148,11 @@
      * by invoking a static {@code valueOf(String)} method on the target type.
      */
     @Singleton
-    public static class TypeValueOf implements ParamConverterProvider {
+    public static class TypeValueOf extends ParamConverterCompliance implements ParamConverterProvider {
+
+        private TypeValueOf(boolean canReturnNull) {
+            super(canReturnNull);
+        }
 
         @Override
         public <T> ParamConverter<T> getConverter(final Class<T> rawType,
@@ -131,7 +161,7 @@
 
             final Method valueOf = AccessController.doPrivileged(ReflectionHelper.getValueOfStringMethodPA(rawType));
 
-            return (valueOf == null) ? null : new AbstractStringReader<T>() {
+            return (valueOf == null) ? null : new AbstractStringReader<T>(canReturnNull) {
 
                 @Override
                 public T _fromString(final String value) throws Exception {
@@ -146,7 +176,11 @@
      * by invoking a static {@code fromString(String)} method on the target type.
      */
     @Singleton
-    public static class TypeFromString implements ParamConverterProvider {
+    public static class TypeFromString extends ParamConverterCompliance implements ParamConverterProvider {
+
+        private TypeFromString(boolean canReturnNull) {
+            super(canReturnNull);
+        }
 
         @Override
         public <T> ParamConverter<T> getConverter(final Class<T> rawType,
@@ -155,7 +189,7 @@
 
             final Method fromStringMethod = AccessController.doPrivileged(ReflectionHelper.getFromStringStringMethodPA(rawType));
 
-            return (fromStringMethod == null) ? null : new AbstractStringReader<T>() {
+            return (fromStringMethod == null) ? null : new AbstractStringReader<T>(canReturnNull) {
 
                 @Override
                 public T _fromString(final String value) throws Exception {
@@ -172,6 +206,10 @@
     @Singleton
     public static class TypeFromStringEnum extends TypeFromString {
 
+        private TypeFromStringEnum(boolean canReturnNull) {
+            super(canReturnNull);
+        }
+
         @Override
         public <T> ParamConverter<T> getConverter(final Class<T> rawType,
                                                   final Type genericType,
@@ -181,7 +219,11 @@
     }
 
     @Singleton
-    public static class CharacterProvider implements ParamConverterProvider {
+    public static class CharacterProvider extends ParamConverterCompliance implements ParamConverterProvider {
+
+        private CharacterProvider(boolean canReturnNull) {
+            super(canReturnNull);
+        }
 
         @Override
         public <T> ParamConverter<T> getConverter(final Class<T> rawType,
@@ -192,8 +234,7 @@
                     @Override
                     public T fromString(String value) {
                         if (value == null || value.isEmpty()) {
-                            return null;
-                            // throw new IllegalStateException(LocalizationMessages.METHOD_PARAMETER_CANNOT_BE_NULL("value"));
+                            return CharacterProvider.this.nullOrThrow();
                         }
 
                         if (value.length() == 1) {
@@ -206,7 +247,7 @@
                     @Override
                     public String toString(T value) {
                         if (value == null) {
-                            throw new IllegalArgumentException(LocalizationMessages.METHOD_PARAMETER_CANNOT_BE_NULL("value"));
+                            return CharacterProvider.this.nullOrThrow();
                         }
                         return value.toString();
                     }
@@ -223,7 +264,11 @@
      * {@link HttpDateFormat http date formatter} utility class.
      */
     @Singleton
-    public static class DateProvider implements ParamConverterProvider {
+    public static class DateProvider extends ParamConverterCompliance implements ParamConverterProvider {
+
+        private DateProvider(boolean canReturnNull) {
+            super(canReturnNull);
+        }
 
         @Override
         public <T> ParamConverter<T> getConverter(final Class<T> rawType,
@@ -234,7 +279,7 @@
                 @Override
                 public T fromString(final String value) {
                     if (value == null) {
-                        throw new IllegalArgumentException(LocalizationMessages.METHOD_PARAMETER_CANNOT_BE_NULL("value"));
+                        return DateProvider.this.nullOrThrow();
                     }
                     try {
                         return rawType.cast(HttpDateFormat.readDate(value));
@@ -246,7 +291,7 @@
                 @Override
                 public String toString(final T value) throws IllegalArgumentException {
                     if (value == null) {
-                        throw new IllegalArgumentException(LocalizationMessages.METHOD_PARAMETER_CANNOT_BE_NULL("value"));
+                        return DateProvider.this.nullOrThrow();
                     }
                     return value.toString();
                 }
@@ -259,12 +304,13 @@
      * by invoking {@link ParamConverterProvider}.
      */
     @Singleton
-    public static class OptionalCustomProvider implements ParamConverterProvider {
+    public static class OptionalCustomProvider extends ParamConverterCompliance implements ParamConverterProvider {
 
         // Delegates to this provider when the type of Optional is extracted.
         private final InjectionManager manager;
 
-        public OptionalCustomProvider(InjectionManager manager) {
+        public OptionalCustomProvider(InjectionManager manager, boolean canReturnNull) {
+            super(canReturnNull);
             this.manager = manager;
         }
 
@@ -294,7 +340,7 @@
                          *  In this case we don't send Optional.empty() because 'value' is not null.
                          *  But we return null because the provider didn't find how to parse it.
                          */
-                        return null;
+                        return nullOrThrow();
                     }
                 }
 
@@ -409,17 +455,20 @@
          * Create new aggregated {@link ParamConverterProvider param converter provider}.
          */
         @Inject
-        public AggregatedProvider(@Context InjectionManager manager) {
+        public AggregatedProvider(@Context InjectionManager manager, @Context Configuration configuration) {
+            boolean canThrowNull = !CommonProperties.getValue(configuration.getProperties(),
+                    CommonProperties.PARAM_CONVERTERS_THROW_IAE,
+                    Boolean.FALSE);
             this.providers = new ParamConverterProvider[] {
                     // ordering is important (e.g. Date provider must be executed before String Constructor
                     // as Date has a deprecated String constructor
-                    new DateProvider(),
-                    new TypeFromStringEnum(),
-                    new TypeValueOf(),
-                    new CharacterProvider(),
-                    new TypeFromString(),
-                    new StringConstructor(),
-                    new OptionalCustomProvider(manager),
+                    new DateProvider(canThrowNull),
+                    new TypeFromStringEnum(canThrowNull),
+                    new TypeValueOf(canThrowNull),
+                    new CharacterProvider(canThrowNull),
+                    new TypeFromString(canThrowNull),
+                    new StringConstructor(canThrowNull),
+                    new OptionalCustomProvider(manager, canThrowNull),
                     new OptionalProvider()
             };
         }
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/util/ReflectionHelper.java b/core-common/src/main/java/org/glassfish/jersey/internal/util/ReflectionHelper.java
index 2bd5e39..6383095 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/util/ReflectionHelper.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/util/ReflectionHelper.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2023 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -23,6 +23,7 @@
 import java.lang.reflect.AnnotatedElement;
 import java.lang.reflect.Array;
 import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
 import java.lang.reflect.Field;
 import java.lang.reflect.GenericArrayType;
 import java.lang.reflect.Member;
@@ -444,7 +445,7 @@
      * @see AccessController#doPrivileged(java.security.PrivilegedAction)
      */
     public static PrivilegedAction setAccessibleMethodPA(final Method m) {
-        if (Modifier.isPublic(m.getModifiers())) {
+        if (isPublic(m)) {
             return NoOpPrivilegedACTION;
         }
 
@@ -461,6 +462,24 @@
     }
 
     /**
+     * Return {@code true} iff the method is public.
+     * @param clazz The method in question
+     * @return {@code true} if mod includes the public modifier; {@code false} otherwise.
+     */
+    public static boolean isPublic(Class<?> clazz) {
+        return Modifier.isPublic(clazz.getModifiers());
+    }
+
+    /**
+     * Return {@code true} iff the executable is public.
+     * @param executable The executable in question
+     * @return {@code true} if the executable includes the public modifier; {@code false} otherwise.
+     */
+    public static boolean isPublic(Executable executable) {
+        return Modifier.isPublic(executable.getModifiers());
+    }
+
+    /**
      * Get the list of classes that represent the type arguments of a
      * {@link ParameterizedType parameterized} input type.
      * <p/>
@@ -879,8 +898,7 @@
      * @return {@code true} if the method is {@code getter}, {@code false} otherwise.
      */
     public static boolean isGetter(final Method method) {
-        if (method.getParameterTypes().length == 0
-                && Modifier.isPublic(method.getModifiers())) {
+        if (method.getParameterTypes().length == 0 && isPublic(method)) {
             final String methodName = method.getName();
 
             if (methodName.startsWith("get") && methodName.length() > 3) {
@@ -921,7 +939,7 @@
      * @return {@code true} if the method is {@code setter}, {@code false} otherwise.
      */
     public static boolean isSetter(final Method method) {
-        return Modifier.isPublic(method.getModifiers())
+        return isPublic(method)
                 && void.class.equals(method.getReturnType())
                 && method.getParameterTypes().length == 1
                 && method.getName().startsWith("set");
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
index 358a329..1ca2549 100644
--- 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
@@ -381,6 +381,24 @@
             final List<V> sublist = guarded.subList(fromIndex, toIndex);
             return sublist != null ? new GuardianList<>(sublist, guard) : sublist;
         }
+
+        @Override
+        public String toString() {
+            return guarded.toString();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (GuardianList.class.isInstance(obj)) {
+                return guarded.equals(((GuardianList) obj).guarded);
+            }
+            return guarded.equals(obj);
+        }
+
+        @Override
+        public int hashCode() {
+            return guarded.hashCode();
+        }
     }
 
     private static class GuardianIterator<V> extends MutableGuardian<V> implements Iterator<V> {
@@ -411,6 +429,11 @@
         public void forEachRemaining(Consumer<? super V> action) {
             guarded.forEachRemaining(action);
         }
+
+        @Override
+        public String toString() {
+            return guarded.toString();
+        }
     }
 
     private static class GuardianListIterator<V> extends GuardianIterator<V> implements ListIterator<V> {
diff --git a/core-common/src/main/resources/META-INF/services/org.glassfish.jersey.internal.spi.AutoDiscoverable b/core-common/src/main/resources/META-INF/services/org.glassfish.jersey.internal.spi.AutoDiscoverable
index abc1825..371b37b 100644
--- a/core-common/src/main/resources/META-INF/services/org.glassfish.jersey.internal.spi.AutoDiscoverable
+++ b/core-common/src/main/resources/META-INF/services/org.glassfish.jersey.internal.spi.AutoDiscoverable
@@ -1,2 +1 @@
-org.glassfish.jersey.logging.LoggingFeatureAutoDiscoverable
-org.glassfish.jersey.internal.config.ExternalPropertiesAutoDiscoverable
\ No newline at end of file
+org.glassfish.jersey.logging.LoggingFeatureAutoDiscoverable
\ No newline at end of file
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ContainerResponse.java b/core-server/src/main/java/org/glassfish/jersey/server/ContainerResponse.java
index 0c7020d..bd7f7a3 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/ContainerResponse.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/ContainerResponse.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -21,6 +21,8 @@
 import java.lang.annotation.Annotation;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.lang.reflect.WildcardType;
 import java.net.URI;
 import java.util.Date;
 import java.util.Locale;
@@ -308,6 +310,18 @@
             if (parameterizedType.getRawType().equals(GenericEntity.class)) {
                 t = parameterizedType.getActualTypeArguments()[0];
             }
+        } else if (type instanceof TypeVariable) {
+           final TypeVariable typeVariable = (TypeVariable) type;
+           final Type[] bounds = typeVariable.getBounds();
+           if (bounds.length == 1) {
+               t = bounds[0];
+           }
+        } else if (type instanceof WildcardType) {
+            final WildcardType wildcardType = (WildcardType) type;
+            final Type[] bounds = wildcardType.getUpperBounds();
+            if (bounds.length == 1) {
+                t = bounds[0];
+            }
         }
 
         messageContext.setEntityType(t);
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/model/internal/AbstractJavaResourceMethodDispatcher.java b/core-server/src/main/java/org/glassfish/jersey/server/model/internal/AbstractJavaResourceMethodDispatcher.java
index 0a3d6a3..b54300a 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/model/internal/AbstractJavaResourceMethodDispatcher.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/model/internal/AbstractJavaResourceMethodDispatcher.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2023 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -32,6 +32,7 @@
 import jakarta.ws.rs.core.Response;
 import jakarta.ws.rs.core.SecurityContext;
 
+import org.glassfish.jersey.internal.util.ReflectionHelper;
 import org.glassfish.jersey.message.internal.OutboundJaxrsResponse;
 import org.glassfish.jersey.message.internal.OutboundMessageContext;
 import org.glassfish.jersey.message.internal.TracingLogger;
@@ -68,12 +69,23 @@
     AbstractJavaResourceMethodDispatcher(final Invocable resourceMethod,
                                          final InvocationHandler methodHandler,
                                          final ConfiguredValidator validator) {
-        this.method = resourceMethod.getDefinitionMethod();
+        this.method = getPublic(resourceMethod.getHandlingMethod(), resourceMethod.getDefinitionMethod());
         this.methodHandler = methodHandler;
         this.resourceMethod = resourceMethod;
         this.validator = validator;
     }
 
+    private Method getPublic(Method handlingMethod, Method definitionMethod) {
+        if (handlingMethod == definitionMethod) {
+            return handlingMethod;
+        }
+
+        boolean publicHandling = ReflectionHelper.isPublic(handlingMethod)
+                && ReflectionHelper.isPublic(handlingMethod.getDeclaringClass());
+
+        return publicHandling ? handlingMethod : definitionMethod;
+    }
+
     @Override
     public final Response dispatch(Object resource, ContainerRequest request) throws ProcessingException {
         Response response = null;
diff --git a/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/ParamConverterInternalTest.java b/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/ParamConverterInternalTest.java
index b1f1ff8..8413a07 100644
--- a/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/ParamConverterInternalTest.java
+++ b/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/ParamConverterInternalTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2018 Payara Foundation and/or its affiliates.
  *
  * This program and the accompanying materials are made available under the
@@ -35,6 +35,7 @@
 import jakarta.ws.rs.Path;
 import jakarta.ws.rs.PathParam;
 import jakarta.ws.rs.QueryParam;
+import jakarta.ws.rs.core.Configuration;
 import jakarta.ws.rs.core.UriBuilder;
 import jakarta.ws.rs.ext.ParamConverter;
 import jakarta.ws.rs.ext.ParamConverterProvider;
@@ -43,6 +44,8 @@
 import org.glassfish.jersey.internal.inject.ParamConverters;
 import org.glassfish.jersey.internal.util.ReflectionHelper;
 import org.glassfish.jersey.internal.util.collection.ClassTypePair;
+import org.glassfish.jersey.model.internal.CommonConfig;
+import org.glassfish.jersey.model.internal.ComponentBag;
 import org.glassfish.jersey.server.ApplicationHandler;
 import org.glassfish.jersey.server.ContainerResponse;
 import org.glassfish.jersey.server.RequestContextBuilder;
@@ -284,8 +287,9 @@
     @Test
     public void testDateParamConverterIsChosenForDateString() {
         initiateWebApplication();
+        final Configuration configuration = new CommonConfig(null, ComponentBag.EXCLUDE_EMPTY);
         final ParamConverter<Date> converter =
-                new ParamConverters.AggregatedProvider(null).getConverter(Date.class, Date.class, null);
+                new ParamConverters.AggregatedProvider(null, configuration).getConverter(Date.class, Date.class, null);
 
         assertEquals(ParamConverters.DateProvider.class, converter.getClass().getEnclosingClass(),
                 "Unexpected date converter provider class");
diff --git a/core-server/src/test/java/org/glassfish/jersey/server/model/ResourceMethodDispatcherInheritanceTest.java b/core-server/src/test/java/org/glassfish/jersey/server/model/ResourceMethodDispatcherInheritanceTest.java
new file mode 100644
index 0000000..d614263
--- /dev/null
+++ b/core-server/src/test/java/org/glassfish/jersey/server/model/ResourceMethodDispatcherInheritanceTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.server.model;
+
+import org.glassfish.jersey.server.ApplicationHandler;
+import org.glassfish.jersey.server.ContainerResponse;
+import org.glassfish.jersey.server.RequestContextBuilder;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import java.util.concurrent.ExecutionException;
+
+public class ResourceMethodDispatcherInheritanceTest {
+    public interface ResourceIfc1 {
+        @GET
+        public void get();
+    }
+
+    @Path("/")
+    static class ResourceClass1 implements ResourceIfc1 {
+        public void get() {
+
+        }
+    }
+
+    interface ResourceIfc2 {
+        @GET
+        public void get();
+    }
+
+    @Path("/")
+    public static class ResourceClass2 implements ResourceIfc2 {
+        public void get() {
+
+        }
+    }
+
+    @Test
+    public void testInheritedMethodPublicClass() throws ExecutionException, InterruptedException {
+        ApplicationHandler app = new ApplicationHandler(new ResourceConfig(ResourceClass2.class));
+        ContainerResponse response;
+        response = app.apply(RequestContextBuilder.from("/", "GET").accept("text/plain").build()).get();
+        Assertions.assertEquals(204, response.getStatus());
+    }
+
+    @Test
+    public void testInheritedMethodPublicIface() throws ExecutionException, InterruptedException {
+        ApplicationHandler app = new ApplicationHandler(new ResourceConfig(ResourceClass1.class));
+        ContainerResponse response;
+        response = app.apply(RequestContextBuilder.from("/", "GET").accept("text/plain").build()).get();
+        Assertions.assertEquals(204, response.getStatus());
+    }
+}
diff --git a/docs/src/main/docbook/appendix-properties.xml b/docs/src/main/docbook/appendix-properties.xml
index 938e440..716f9b9 100644
--- a/docs/src/main/docbook/appendix-properties.xml
+++ b/docs/src/main/docbook/appendix-properties.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <!--
 
-    Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2013, 2023 Oracle and/or its affiliates. All rights reserved.
 
     This program and the accompanying materials are made available under the
     terms of the Eclipse Public License v. 2.0, which is available at
@@ -184,6 +184,21 @@
                         </entry>
                     </row>
                     <row>
+                        <entry>&jersey.common.CommonProperties.PARAM_CONVERTERS_THROW_IAE;(Jersey 2.40 or later)
+                        </entry>
+                        <entry>
+                            <literal>jersey.config.paramconverters.throw.iae</literal>
+                        </entry>
+                        <entry>
+                            Force the &jaxrs.ext.ParamConverter; to throw <literal>IllegalArgumentException</literal> as mandated in javadoc.
+                            Must be convertible to &lit.jdk6.Boolean; value.
+                        </entry>
+                        <entry>
+                            Internally the <literal>Exception</literal> is caught by Jersey and usually converted to <literal>null</literal>
+                            Therefore, the default value is set to &lit.false; to speed up the conversion.
+                        </entry>
+                    </row>
+                    <row>
                         <entry>&jersey.logging.LoggingFeature.LOGGING_FEATURE_LOGGER_NAME;
                         </entry>
                         <entry>
@@ -917,6 +932,53 @@
                         </entry>
                     </row>
                     <row>
+                        <entry>&jersey.client.ClientProperties.CONNECTOR_PROVIDER;(Jersey 2.40 or later)</entry>
+                        <entry><literal>jersey.config.client.connector.provider</literal></entry>
+                        <entry>
+                            <para>
+                                Sets the &jersey.client.ConnectorProvider; class. Overrides the value from META-INF/services.
+                            </para>
+                            <para>
+                                The value MUST be an instance of &lit.jdk6.String;.
+                            </para>
+                            <para>
+                                The property is recognized by &lit.jaxrs.client.ClientBuilder;.
+                            </para>
+                        </entry>
+                    </row>
+                    <row>
+                        <entry>&jersey.client.ClientProperties.DIGESTAUTH_URI_CACHE_SIZELIMIT;</entry>
+                        <entry><literal>jersey.config.client.digestAuthUriCacheSizeLimit</literal></entry>
+                        <entry>
+                            <para>
+                                The property defines a URI of a HTTP proxy the client connector should use.
+                            </para>
+                        </entry>
+                    </row>
+                    <row>
+                        <entry>&jersey.client.ClientProperties.EXPECT_100_CONTINUE;</entry>
+                        <entry><literal>jersey.config.client.request.expect.100.continue.processing</literal></entry>
+                        <entry>
+                            <para>
+                                Allows for HTTP Expect:100-Continue being handled by the HttpUrlConnector (default Jersey
+                                connector). <literal>Since 2.32</literal>
+                            </para>
+                        </entry>
+                    </row>
+                    <row>
+                        <entry>&jersey.client.ClientProperties.EXPECT_100_CONTINUE_THRESHOLD_SIZE;</entry>
+                        <entry><literal>jersey.config.client.request.expect.100.continue.threshold.size</literal></entry>
+                        <entry>
+                            <para>
+                                Property for threshold size for content length after which Expect:100-Continue header would be applied
+                                before the main request.
+                                Default threshold size (64kb) after which which Expect:100-Continue header would be applied before
+                                the main request.
+                                <literal>Since 2.32</literal>
+                            </para>
+                        </entry>
+                    </row>
+                    <row>
                         <entry>&jersey.client.ClientProperties.FEATURE_AUTO_DISCOVERY_DISABLE;</entry>
                         <entry><literal>jersey.config.client.disableAutoDiscovery</literal></entry>
                         <entry>
@@ -1056,38 +1118,6 @@
                         </entry>
                     </row>
                     <row>
-                        <entry>&jersey.client.ClientProperties.DIGESTAUTH_URI_CACHE_SIZELIMIT;</entry>
-                        <entry><literal>jersey.config.client.digestAuthUriCacheSizeLimit</literal></entry>
-                        <entry>
-                            <para>
-                                The property defines a URI of a HTTP proxy the client connector should use.
-                            </para>
-                        </entry>
-                    </row>
-                    <row>
-                        <entry>&jersey.client.ClientProperties.EXPECT_100_CONTINUE;</entry>
-                        <entry><literal>jersey.config.client.request.expect.100.continue.processing</literal></entry>
-                        <entry>
-                            <para>
-                                Allows for HTTP Expect:100-Continue being handled by the HttpUrlConnector (default Jersey
-                                connector). <literal>Since 2.32</literal>
-                            </para>
-                        </entry>
-                    </row>
-                    <row>
-                        <entry>&jersey.client.ClientProperties.EXPECT_100_CONTINUE_THRESHOLD_SIZE;</entry>
-                        <entry><literal>jersey.config.client.request.expect.100.continue.threshold.size</literal></entry>
-                        <entry>
-                            <para>
-                                Property for threshold size for content length after which Expect:100-Continue header would be applied
-                                before the main request.
-                                Default threshold size (64kb) after which which Expect:100-Continue header would be applied before
-                                the main request.
-                                <literal>Since 2.32</literal>
-                            </para>
-                        </entry>
-                    </row>
-                    <row>
                         <entry>&jersey.logging.LoggingFeature.LOGGING_FEATURE_LOGGER_NAME_CLIENT;
                         </entry>
                         <entry>
diff --git a/docs/src/main/docbook/client.xml b/docs/src/main/docbook/client.xml
index e19ffe0..1c0e17f 100644
--- a/docs/src/main/docbook/client.xml
+++ b/docs/src/main/docbook/client.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <!--
 
-    Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2010, 2023 Oracle and/or its affiliates. All rights reserved.
 
     This program and the accompanying materials are made available under the
     terms of the Eclipse Public License v. 2.0, which is available at
@@ -761,6 +761,16 @@
             register &lit.jersey.client.Connector; instances in the Jersey &jersey.client.ClientConfig;. The new
             &lit.jersey.client.ConnectorProvider; SPI must be used instead to configure a custom client-side transport connector.
         </para>
+        <para>
+            A &jersey.client.ConnectorProvider; can also be set by a property on a &lit.jaxrs.client.ClientBuilder; starting with
+            Jersey 2.40. The following example shows how to setup the custom Grizzly Asynchronous HTTP Client based
+            &lit.jersey.client.ConnectorProvider; in a Jersey client instance:
+            <programlisting language="java" linenumbering="numbered">Client client = ClientBuilder.newBuilder()
+    .property(ClientProperties.CONNECTOR_PROVIDER, "org.glassfish.jersey.grizzly.connector.GrizzlyConnectorProvider")
+    .build();</programlisting>
+
+            For more information about the property see <xref linkend="appendix-properties"/>.
+        </para>
         <section>
             <title>Client Connectors Properties</title>
             <para>
diff --git a/docs/src/main/docbook/jersey.ent b/docs/src/main/docbook/jersey.ent
index e947990..99dc355 100644
--- a/docs/src/main/docbook/jersey.ent
+++ b/docs/src/main/docbook/jersey.ent
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="iso-8859-1" ?>
 <!--
 
-    Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2010, 2023 Oracle and/or its affiliates. All rights reserved.
 
     This program and the accompanying materials are made available under the
     terms of the Eclipse Public License v. 2.0, which is available at
@@ -242,6 +242,7 @@
 <!ENTITY jaxrs.ext.ExceptionMapper "<link xlink:href='&jaxrs.javadoc.uri;/ext/ExceptionMapper.html'>ExceptionMapper&lt;E extends Throwable&gt;</link>">
 <!ENTITY jaxrs.ext.MessageBodyReader "<link xlink:href='&jaxrs.javadoc.uri;/ext/MessageBodyReader.html'>MessageBodyReader&lt;T&gt;</link>">
 <!ENTITY jaxrs.ext.MessageBodyWriter "<link xlink:href='&jaxrs.javadoc.uri;/ext/MessageBodyWriter.html'>MessageBodyWriter&lt;T&gt;</link>">
+<!ENTITY jaxrs.ext.ParamConverter "<link xlink:href='&jaxrs.javadoc.uri;/ext/ParamConverter.html'>ParamConverter&lt;T&gt;</link>">
 <!ENTITY jaxrs.ext.Provider "<link xlink:href='&jaxrs.javadoc.uri;/ext/Provider.html'>@Provider</link>">
 <!ENTITY jaxrs.ext.Providers "<link xlink:href='&jaxrs.javadoc.uri;/ext/Providers.html'>Providers</link>">
 <!ENTITY jaxrs.ext.RuntimeDelegate "<link xlink:href='&jaxrs.javadoc.uri;/ext/RuntimeDelegate.html'>RuntimeDelegate</link>">
@@ -338,6 +339,7 @@
 <!ENTITY jersey.client.ClientProperties.BUFFER_RESPONSE_ENTITY_ON_EXCEPTION "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#BUFFER_RESPONSE_ENTITY_ON_EXCEPTION'>ClientProperties.BUFFER_RESPONSE_ENTITY_ON_EXCEPTION</link>" >
 <!ENTITY jersey.client.ClientProperties.CHUNKED_ENCODING_SIZE "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#CHUNKED_ENCODING_SIZE'>ClientProperties.CHUNKED_ENCODING_SIZE</link>" >
 <!ENTITY jersey.client.ClientProperties.CONNECT_TIMEOUT "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#CONNECT_TIMEOUT'>ClientProperties.CONNECT_TIMEOUT</link>" >
+<!ENTITY jersey.client.ClientProperties.CONNECTOR_PROVIDER "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#CONNECTOR_PROVIDER'>ClientProperties.CONNECTOR_PROVIDER</link>" >
 <!ENTITY jersey.client.ClientProperties.DEFAULT_CHUNK_SIZE "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#DEFAULT_CHUNK_SIZE'>ClientProperties.DEFAULT_CHUNK_SIZE</link>" >
 <!ENTITY jersey.client.ClientProperties.FEATURE_AUTO_DISCOVERY_DISABLE "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#FEATURE_AUTO_DISCOVERY_DISABLE'>ClientProperties.FEATURE_AUTO_DISCOVERY_DISABLE</link>" >
 <!ENTITY jersey.client.ClientProperties.FOLLOW_REDIRECTS "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#FOLLOW_REDIRECTS'>ClientProperties.FOLLOW_REDIRECTS</link>" >
@@ -402,6 +404,7 @@
 <!ENTITY jersey.common.CommonProperties.JSON_JACKSON_DISABLED_MODULES "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#JSON_JACKSON_DISABLED_MODULES'>CommonProperties.JSON_JACKSON_DISABLED_MODULES</link>" >
 <!ENTITY jersey.common.CommonProperties.JSON_JACKSON_DISABLED_MODULES_CLIENT "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#JSON_JACKSON_DISABLED_MODULES'>CommonProperties.JSON_JACKSON_DISABLED_MODULES_CLIENT</link>" >
 <!ENTITY jersey.common.CommonProperties.JSON_JACKSON_DISABLED_MODULES_SERVER "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#JSON_JACKSON_DISABLED_MODULES'>CommonProperties.JSON_JACKSON_DISABLED_MODULES_SERVER</link>" >
+<!ENTITY jersey.common.CommonProperties.PARAM_CONVERTERS_THROW_IAE "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#PARAM_CONVERTERS_THROW_IAE'>CommonProperties.PARAM_CONVERTERS_THROW_IAE</link>" >
 <!ENTITY jersey.common.internal.inject.DisposableSupplier "<link xlink:href='&jersey.javadoc.uri.prefix;/internal/inject/DisposableSupplier.html'>DisposableSupplier</link>">
 <!ENTITY jersey.common.internal.inject.InjectionManager "<link xlink:href='&jersey.javadoc.uri.prefix;/internal/inject/InjectionManager.html'>InjectionManager</link>">
 <!ENTITY jersey.common.internal.inject.AbstractBinder "<link xlink:href='&jersey.javadoc.uri.prefix;/internal/inject/AbstractBinder.html'>AbstractBinder</link>">
diff --git a/examples/configured-client/README.MD b/examples/configured-client/README.MD
new file mode 100644
index 0000000..94a66be
--- /dev/null
+++ b/examples/configured-client/README.MD
@@ -0,0 +1,40 @@
+[//]: # " Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+Client Configured by Property File Example
+==========================================
+
+This example demonstrates configuration of a Client using property file.
+The property file microprofile-config.properties is consumed by 
+Microprofile Config implementation and the properties from the 
+property file are set to Jersey Configuration.
+
+The following properties are defined in `microprofile-config.properties` property file:
+  * jersey.config.client.connector.provider - jersey property name for the connector to be used
+  * entity.value - user defined property to be be sent as an echo message  
+
+
+Contents
+--------
+
+The mapping of the URI path space is presented in the following table:
+
+URI path             | Resource class      | HTTP methods | Notes
+-------------------- | ------------------- |--------------| --------------------------------------------------------
+**_/helloworld_**    | HelloWorldResource  | POST         |  Echoes the message sent
+
+Running the Example
+-------------------
+
+Run the example as follows:
+
+>     mvn clean compile exec:java
+
+This deploys the example using [Grizzly](http://grizzly.java.net/) container.
+
+-   <http://localhost:8080/base/helloworld>
diff --git a/examples/configured-client/pom.xml b/examples/configured-client/pom.xml
new file mode 100644
index 0000000..ff4aa99
--- /dev/null
+++ b/examples/configured-client/pom.xml
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>3.0.99-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>configured-client</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-examples-configured-client</name>
+
+    <description>Jersey client configured by a property file example.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-apache5-connector</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.ext.microprofile</groupId>
+            <artifactId>jersey-mp-config</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>io.helidon.microprofile.config</groupId>
+            <artifactId>helidon-microprofile-config</artifactId>
+            <version>${helidon.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.eclipse.microprofile.config</groupId>
+                    <artifactId>microprofile-config-api</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+<!--    Instead of helidon-microprofile-config, smallrye-config can be used -->
+<!--        <dependency>-->
+<!--            <groupId>io.smallrye</groupId>-->
+<!--            <artifactId>smallrye-config</artifactId>-->
+<!--            <version>${smallrye.config.version}</version>-->
+<!--            <exclusions>-->
+<!--                <exclusion>-->
+<!--                    <groupId>org.eclipse.microprofile.config</groupId>-->
+<!--                    <artifactId>microprofile-config-api</artifactId>-->
+<!--                </exclusion>-->
+<!--            </exclusions>-->
+<!--        </dependency>-->
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework</groupId>
+            <artifactId>jersey-test-framework-util</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.examples.configured.client.App</mainClass>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>pre-release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+</project>
diff --git a/examples/configured-client/src/main/java/org/glassfish/jersey/examples/configured/client/App.java b/examples/configured-client/src/main/java/org/glassfish/jersey/examples/configured/client/App.java
new file mode 100644
index 0000000..0f6d49b
--- /dev/null
+++ b/examples/configured-client/src/main/java/org/glassfish/jersey/examples/configured/client/App.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.configured.client;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+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;
+
+/**
+ * Hello world!
+ */
+public class App {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/base/");
+    public static final String ROOT_PATH = "helloworld";
+    /* package */ static final String ENTITY_PROPERTY = "entity.value";
+
+    public static void main(String[] args) {
+        try {
+            System.out.println("\"Hello World\" Jersey Example App");
+
+            final ResourceConfig resourceConfig = new ResourceConfig(HelloWorldResource.class);
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, resourceConfig, false);
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    server.shutdownNow();
+                }
+            }));
+            server.start();
+
+            WebTarget target = ClientBuilder.newClient().target(BASE_URI);
+            Object entity = target.getConfiguration().getProperty(ENTITY_PROPERTY);
+            Object provider = target.getConfiguration().getProperty(ClientProperties.CONNECTOR_PROVIDER);
+
+            System.out
+                    .append("  Application started.\n")
+                    .append("  Sending entity \"").append((String) entity).append("\"")
+                    .append(" using ").append((String) provider).append(" connector provider")
+                    .append(" to echo resource ").append(BASE_URI.toASCIIString()).println(ROOT_PATH);
+
+            try (Response response = target.path(ROOT_PATH).request().post(Entity.entity(entity, MediaType.TEXT_PLAIN_TYPE))) {
+                System.out.append("  Recieved: \"").append(response.readEntity(String.class)).println("\"");
+            }
+
+            server.stop();
+            System.exit(0);
+        } catch (IOException ex) {
+            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
+        }
+
+    }
+}
diff --git a/examples/configured-client/src/main/java/org/glassfish/jersey/examples/configured/client/HelloWorldResource.java b/examples/configured-client/src/main/java/org/glassfish/jersey/examples/configured/client/HelloWorldResource.java
new file mode 100644
index 0000000..3c25599
--- /dev/null
+++ b/examples/configured-client/src/main/java/org/glassfish/jersey/examples/configured/client/HelloWorldResource.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.configured.client;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.Configuration;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.MediaType;
+
+
+@Path("helloworld")
+public class HelloWorldResource {
+    @Context
+    Configuration configuration;
+
+    @POST
+    @Produces("text/plain")
+    public String postHello(String helloMsg) {
+        return helloMsg;
+    }
+
+    @GET
+    @Path("agent")
+    @Produces(MediaType.TEXT_PLAIN)
+    public String getAgent(@Context HttpHeaders headers) {
+        return headers.getHeaderString(HttpHeaders.USER_AGENT);
+    }
+
+}
diff --git a/examples/configured-client/src/main/resources/META-INF/microprofile-config.properties b/examples/configured-client/src/main/resources/META-INF/microprofile-config.properties
new file mode 100644
index 0000000..5c6cbb2
--- /dev/null
+++ b/examples/configured-client/src/main/resources/META-INF/microprofile-config.properties
@@ -0,0 +1,12 @@
+#
+# Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Distribution License v. 1.0, which is available at
+# http://www.eclipse.org/org/documents/edl-v10.php.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+jersey.config.client.connector.provider=org.glassfish.jersey.apache5.connector.Apache5ConnectorProvider
+entity.value=Hello World!
\ No newline at end of file
diff --git a/examples/configured-client/src/test/java/org/glassfish/jersey/examples/configured/client/HelloWorldTest.java b/examples/configured-client/src/test/java/org/glassfish/jersey/examples/configured/client/HelloWorldTest.java
new file mode 100644
index 0000000..7499c2b
--- /dev/null
+++ b/examples/configured-client/src/test/java/org/glassfish/jersey/examples/configured/client/HelloWorldTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.configured.client;
+
+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 org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.parallel.Execution;
+import org.junit.jupiter.api.parallel.ExecutionMode;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
+import org.junit.jupiter.api.parallel.ResourceLock;
+
+import static org.glassfish.jersey.examples.configured.client.App.ENTITY_PROPERTY;
+
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+public class HelloWorldTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        // mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.inmemory.InMemoryTestContainerFactory
+        // mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory
+        // mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.jdkhttp.JdkHttpServerTestContainerFactory
+        // mvn test -Djersey.config.test.container.factory=org.glassfish.jersey.test.simple.SimpleTestContainerFactory
+        enable(TestProperties.LOG_TRAFFIC);
+        // enable(TestProperties.DUMP_ENTITY);
+        return new ResourceConfig(HelloWorldResource.class);
+    }
+
+    @Test
+    @Execution(ExecutionMode.CONCURRENT)
+    @ResourceLock(value = "dummy", mode = ResourceAccessMode.READ)
+    public void testEntity() {
+        WebTarget target = target("helloworld");
+        Object entity = target.getConfiguration().getProperty(ENTITY_PROPERTY);
+        try (Response response = target.request().post(Entity.entity(entity, MediaType.TEXT_PLAIN_TYPE))) {
+            Assertions.assertEquals(200, response.getStatus());
+            String readEntity = response.readEntity(String.class);
+            System.out.println(entity);
+            Assertions.assertEquals(entity, readEntity);
+        }
+    }
+
+    @Test
+    @Execution(ExecutionMode.CONCURRENT)
+    @ResourceLock(value = "dummy", mode = ResourceAccessMode.READ)
+    public void testConnector() {
+        try (Response response = target("helloworld").path("agent").request().get()) {
+            Assertions.assertEquals(200, response.getStatus());
+            String entity = response.readEntity(String.class);
+            System.out.println(entity);
+            Assertions.assertTrue(entity.contains("Apache HttpClient 5"));
+        }
+    }
+}
diff --git a/examples/pom.xml b/examples/pom.xml
index de547df..48a1f51 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -60,6 +60,7 @@
         <module>cdi-webapp</module>
         <module>clipboard</module>
         <module>clipboard-programmatic</module>
+        <module>configured-client</module>
         <module>declarative-linking</module>
         <module>entity-filtering</module>
         <module>entity-filtering-selectable</module>
diff --git a/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/ProxyInvocationHandler.java b/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/ProxyInvocationHandler.java
index b8192e5..0fd557e 100644
--- a/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/ProxyInvocationHandler.java
+++ b/ext/microprofile/mp-rest-client/src/main/java/org/glassfish/jersey/microprofile/restclient/ProxyInvocationHandler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2023 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -18,6 +18,7 @@
 
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import jakarta.ws.rs.client.Client;
@@ -56,6 +57,18 @@
         if (method.getName().equals("toString") && (args == null || args.length == 0)) {
             return restClientModel.toString();
         }
+
+        if (args == null && method.getName().equals("hashCode")) {
+            return hashCode();
+        }
+
+        if (args != null && args.length == 1 && method.getName().equals("equals")) {
+            if (!(args[0] instanceof Proxy)) {
+                return false;
+            }
+            return equals(Proxy.getInvocationHandler(args[0]));
+        }
+
         if (method.getName().equals("close") && (args == null || args.length == 0)) {
             closed.set(true);
             if (null != client) {
diff --git a/pom.xml b/pom.xml
index 910f57a..229c0c6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2258,7 +2258,7 @@
 
         <guava.version>31.1-jre</guava.version>
         <hamcrest.version>2.2</hamcrest.version>
-        <!--<helidon.version>1.4.9</helidon.version>-->
+        <helidon.version>1.4.13</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>
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/internal/ParamConvertersTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/internal/ParamConvertersTest.java
new file mode 100644
index 0000000..67048f8
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/internal/ParamConvertersTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.tests.e2e.common.internal;
+
+import org.glassfish.jersey.CommonProperties;
+import org.glassfish.jersey.internal.inject.Bindings;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.inject.Injections;
+import org.glassfish.jersey.internal.inject.ParamConverterConfigurator;
+import org.glassfish.jersey.internal.inject.Providers;
+import org.glassfish.jersey.model.internal.CommonConfig;
+import org.glassfish.jersey.model.internal.ComponentBag;
+import org.glassfish.jersey.model.internal.RankedComparator;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.core.Configuration;
+import jakarta.ws.rs.ext.ParamConverter;
+import jakarta.ws.rs.ext.ParamConverterProvider;
+import java.util.Date;
+
+public class ParamConvertersTest {
+    private CommonConfig config;
+
+    @BeforeEach
+    public void setUp() throws Exception {
+        config = new CommonConfig(null, ComponentBag.INCLUDE_ALL);
+    }
+
+    @Test
+    public void paramConvertersReturnNull() {
+        ParamConverter<String> converter = getParamConverter(String.class);
+        assertNull(String.class);
+        assertNull(Date.class);
+        assertNull(Character.class);
+    }
+
+    @Test
+    public void paramConvertersThrow() {
+        config.property(CommonProperties.PARAM_CONVERTERS_THROW_IAE, true);
+
+        assertThrow(String.class);
+        assertThrow(Date.class);
+        assertThrow(Character.class);
+    }
+
+    private <T> void assertNull(Class<T> clazz) {
+        ParamConverter<T> converter = getParamConverter(clazz);
+        Assertions.assertNotNull(converter);
+        Assertions.assertNull(converter.fromString(null));
+        Assertions.assertNull(converter.toString(null));
+    }
+
+    private <T> void assertThrow(Class<T> clazz) {
+        ParamConverter<T> converter = getParamConverter(clazz);
+        Assertions.assertNotNull(converter);
+
+        try {
+            converter.fromString(null);
+            throw new RuntimeException("The IAE was not thrown");
+        } catch (IllegalArgumentException illegalArgumentException) {
+            //expected
+        }
+
+        try {
+            converter.toString(null);
+            throw new RuntimeException("The IAE was not thrown");
+        } catch (IllegalArgumentException illegalArgumentException) {
+            //expected
+        }
+    }
+
+    private <T> ParamConverter<T> getParamConverter(Class<T> clazz) {
+        InjectionManager injectionManager = Injections.createInjectionManager();
+        new ParamConverterConfigurator().init(injectionManager, null);
+        injectionManager.register(Bindings.service(config).to(Configuration.class));
+        injectionManager.completeRegistration();
+
+        final Iterable<ParamConverterProvider> allProviders =
+                Providers.getAllProviders(injectionManager, ParamConverterProvider.class, new RankedComparator<>());
+        for (ParamConverterProvider provider : allProviders) {
+            ParamConverter<T> converter = provider.getConverter(clazz, clazz, null);
+            if (converter != null) {
+                return converter;
+            }
+        }
+        return null;
+    }
+
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/CompletionStageTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/CompletionStageTest.java
index 0355f04..0d44ac2 100644
--- a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/CompletionStageTest.java
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/CompletionStageTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2023 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -158,7 +158,15 @@
 
     @Test
     public void testCompletionStageUnwrappedInGenericType() {
-        try (Response r = target("cs/databeanlist").request().get()){
+        try (Response r = target("cs/databeanlist").request().get()) {
+            assertEquals(200, r.getStatus());
+            assertTrue(r.readEntity(String.class).startsWith(ENTITY));
+        }
+    }
+
+    @Test
+    void testExtends() {
+        try (Response r = target("cs/csextends").request().get()) {
             assertEquals(200, r.getStatus());
             assertTrue(r.readEntity(String.class).startsWith(ENTITY));
         }
@@ -288,6 +296,12 @@
             return cs;
         }
 
+        @GET
+        @Path("csextends")
+        public CompletionStage<? extends CharSequence> csExtends() {
+            return CompletableFuture.completedFuture(ENTITY);
+        }
+
         private void delaySubmit(Runnable runnable) {
             EXECUTOR_SERVICE.submit(() -> {
                 try {
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/TypedVariableTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/TypedVariableTest.java
new file mode 100644
index 0000000..8c3485b
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/TypedVariableTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.tests.e2e.server;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Response;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class TypedVariableTest extends JerseyTest {
+
+    private static final String ENTITY = "entity";
+
+    @Path("/typed")
+    public static class Resource {
+        @GET
+        @Path("extends")
+        public <T extends CharSequence> T justExtends() {
+            return (T) ENTITY;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class);
+    }
+
+    @Test
+    void testExtends() {
+        try (Response r = target("typed/extends").request().get()) {
+            assertEquals(200, r.getStatus());
+            assertTrue(r.readEntity(String.class).startsWith(ENTITY));
+        }
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/ClientTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/ClientTest.java
index f8307a3..e14b461 100644
--- a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/ClientTest.java
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/ClientTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,8 +16,10 @@
 
 package org.glassfish.jersey.tests.e2e;
 
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import jakarta.ws.rs.Consumes;
 import jakarta.ws.rs.GET;
@@ -32,12 +34,14 @@
 import jakarta.ws.rs.core.Context;
 import jakarta.ws.rs.core.HttpHeaders;
 import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.MultivaluedMap;
 import jakarta.ws.rs.core.Response;
 import jakarta.ws.rs.core.Variant;
 
 import org.glassfish.jersey.server.ResourceConfig;
 import org.glassfish.jersey.test.JerseyTest;
 
+import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -80,6 +84,30 @@
 
             return sb.toString();
         }
+
+        @GET
+        @Path("/tostring")
+        public String headersGet(@Context HttpHeaders hs) {
+            StringBuilder sb = new StringBuilder();
+            List<String> myHeaders = Arrays.asList("Accept", "Content-Type");
+
+            try {
+                MultivaluedMap<String, String> rqhdrs = hs.getRequestHeaders();
+                Set<String> keys = rqhdrs.keySet();
+                sb.append("getRequestHeaders= ");
+                for (String header : myHeaders) {
+                    if (keys.contains(header)) {
+                        sb.append(
+                                "Found " + header + ": " + hs.getRequestHeader(header) + "; ");
+                    }
+                }
+            } catch (Throwable ex) {
+                sb.append("Unexpected exception thrown in getRequestHeaders: "
+                        + ex.getMessage());
+                ex.printStackTrace();
+            }
+            return sb.toString();
+        }
     }
 
     @Override
@@ -98,6 +126,26 @@
     }
 
     @Test
+    public void testHeadersToString() {
+        try (Response response = target("headers").path("tostring").request()
+                .header(HttpHeaders.ACCEPT, "text/*, text/html, text/html;level=1, */*")
+                .header(HttpHeaders.CONTENT_TYPE, "application/xml;charset=utf8")
+                .get()) {
+            String content = response.readEntity(String.class);
+            int index = -1;
+            Assertions.assertTrue((index = content.indexOf("getRequestHeaders=")) != -1);
+            Assertions.assertTrue((index = content.indexOf("Accept:")) != -1);
+            Assertions.assertTrue((index = content.indexOf("text/*")) != -1);
+            Assertions.assertTrue((index = content.indexOf("text/html")) != -1);
+            Assertions.assertTrue((index = content.indexOf("text/html")) != -1);
+            Assertions.assertTrue((index = content.indexOf("*/*")) != -1);
+            Assertions.assertTrue((index = content.indexOf("Content-Type:")) != -1);
+            Assertions.assertTrue((index = content.indexOf("application/xml")) != -1);
+            Assertions.assertTrue((index = content.indexOf("charset=utf8")) != -1);
+        }
+    }
+
+    @Test
     public void testAccesingMissingResource() {
         final WebTarget missingResource = target().path("missing");
         final Response r = missingResource.request().get();
diff --git a/tests/release-test/pom.xml b/tests/release-test/pom.xml
index 00fa896..6f7ad39 100644
--- a/tests/release-test/pom.xml
+++ b/tests/release-test/pom.xml
@@ -99,6 +99,12 @@
             <version>${maven.version}</version>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+            <version>${httpclient.version}</version>
+            <scope>test</scope>
+        </dependency>
 
         <dependency>
             <groupId>org.apache.maven.plugin-testing</groupId>
diff --git a/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/DownloadBomPomDependencies.java b/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/DownloadBomPomDependencies.java
index 01ba01e..99c94ee 100644
--- a/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/DownloadBomPomDependencies.java
+++ b/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/DownloadBomPomDependencies.java
@@ -21,6 +21,7 @@
 import org.eclipse.aether.artifact.Artifact;
 import org.eclipse.aether.impl.DefaultServiceLocator;
 import org.eclipse.aether.repository.LocalRepository;
+import org.eclipse.aether.resolution.ArtifactResolutionException;
 import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
 import org.eclipse.aether.spi.connector.transport.TransporterFactory;
 import org.eclipse.aether.transport.http.HttpTransporterFactory;
@@ -51,60 +52,97 @@
 
     @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);
+        MavenEnvironment mavenEnvironment = new MavenEnvironment();
         List<Dependency> memberDeps = MavenUtil.streamJerseyJars().collect(Collectors.toList());
         for (Dependency member : memberDeps) {
-            member.setVersion(jerseyVersion);
-            Artifact m = DependencyResolver.resolveArtifact(member, remoteRepos, repositorySystem, repoSession);
+            Artifact m = mavenEnvironment.resolveArtifact(member);
             System.out.append("Resolved ").append(member.getGroupId()).append(":").append(member.getArtifactId()).append(":")
                     .append(member.getVersion()).append(" to ").println(m.getFile().getName());
         }
     }
 
-    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);
+    @Test
+    public void testDownloadNonBomPomDependencies() throws Exception {
+        MavenEnvironment mavenEnvironment = new MavenEnvironment();
+        MavenProject project = mavenEnvironment.getMavenProjectForResourceFile("/non-bom-pom-deps.xml");
+        for (Dependency dependency : project.getDependencies()) {
+            if (dependency.getArtifactId().contains("jackson1") && mavenEnvironment.jerseyVersion.startsWith("3")) {
+                continue;
+            }
 
-        ProjectBuilder projectBuilder = lookup(ProjectBuilder.class);
-        ProjectBuildingResult projectBuildingResult = projectBuilder.build(pom, buildingRequest);
-        MavenProject project = projectBuildingResult.getProject();
-
-        List<RemoteRepository> remoteArtifactRepositories = project.getRemoteProjectRepositories();
-        return remoteArtifactRepositories;
+            Artifact m = mavenEnvironment.resolveArtifact(dependency);
+            System.out.append("Resolved ").append(dependency.getGroupId()).append(":")
+                    .append(dependency.getArtifactId()).append(":")
+                    .append(dependency.getVersion()).append(" to ").println(m.getFile().getName());
+        }
     }
 
-    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 class MavenEnvironment {
+        private final RepositorySystem repositorySystem;
+        private final RepositorySystemSession repoSession;
+        private final List<RemoteRepository> remoteRepos;
+        private final String jerseyVersion;
 
-    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!");
+        MavenEnvironment() throws Exception {
+            DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();
+            locator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class);
+            locator.addService(TransporterFactory.class, HttpTransporterFactory.class);
+
+            repositorySystem = locator.getService(RepositorySystem.class);
+            repoSession = getRepoSession();
+
+            remoteRepos = getRemoteRepositories();
+
+            Properties properties = MavenUtil.getMavenProperties();
+            jerseyVersion = MavenUtil.getJerseyVersion(properties);
         }
-        File file = new File(resource.toURI());
-        if (!file.exists()) {
-            throw new IllegalStateException("Cannot locate test pom xml file!");
+
+        Artifact resolveArtifact(Dependency dependency) throws ArtifactResolutionException {
+            dependency.setVersion(jerseyVersion);
+            return DependencyResolver.resolveArtifact(dependency, remoteRepos, repositorySystem, repoSession);
         }
-        return file;
+
+        private List<RemoteRepository> getRemoteRepositories() throws Exception {
+            MavenProject project = getMavenProjectForResourceFile("/release-test-pom.xml");
+            List<RemoteRepository> remoteArtifactRepositories = project.getRemoteProjectRepositories();
+            return remoteArtifactRepositories;
+        }
+
+        private MavenProject getMavenProjectForResourceFile(String resourceFile)
+                throws Exception {
+            File pom = lookupResourcesPom(resourceFile);
+            MavenExecutionRequest request = new DefaultMavenExecutionRequest();
+            request.setPom(pom);
+            request.addActiveProfile("staging");
+            ProjectBuildingRequest buildingRequest = request
+                    .getProjectBuildingRequest()
+                    .setRepositorySession(repoSession)
+                    .setResolveDependencies(true);
+
+            ProjectBuilder projectBuilder = lookup(ProjectBuilder.class);
+            ProjectBuildingResult projectBuildingResult = projectBuilder.build(pom, buildingRequest);
+            MavenProject project = projectBuildingResult.getProject();
+
+            return project;
+        }
+
+        private RepositorySystemSession getRepoSession() {
+            DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
+            LocalRepository localRepo = new LocalRepository(MavenUtil.getLocalMavenRepository().getAbsolutePath());
+            session.setLocalRepositoryManager(repositorySystem.newLocalRepositoryManager(session, localRepo));
+            return session;
+        }
+
+        private 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/resources/non-bom-pom-deps.xml b/tests/release-test/src/test/resources/non-bom-pom-deps.xml
new file mode 100644
index 0000000..7ced6b0
--- /dev/null
+++ b/tests/release-test/src/test/resources/non-bom-pom-deps.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    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>
+    </parent>
+
+    <groupId>org.glassfish.jersey.tests</groupId>
+    <artifactId>release-test-test</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jackson1</artifactId>
+            <version>2.27</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.security</groupId>
+            <artifactId>oauth1-client</artifactId>
+            <version>2.27</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.security</groupId>
+            <artifactId>oauth1-server</artifactId>
+            <version>2.27</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.security</groupId>
+            <artifactId>oauth1-signature</artifactId>
+            <version>2.27</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.incubator</groupId>
+            <artifactId>jersey-injectless-client</artifactId>
+            <version>2.27</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.incubator</groupId>
+            <artifactId>jersey-open-tracing</artifactId>
+            <version>2.27</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-kryo</artifactId>
+            <version>2.27</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-declarative-linking</artifactId>
+            <version>2.27</version>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/tests/release-test/src/test/resources/release-test-pom.xml b/tests/release-test/src/test/resources/release-test-pom.xml
index d8aa486..4fac652 100644
--- a/tests/release-test/src/test/resources/release-test-pom.xml
+++ b/tests/release-test/src/test/resources/release-test-pom.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
 
-    Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2022, 2023 Oracle and/or its affiliates. All rights reserved.
 
     This program and the accompanying materials are made available under the
     terms of the Eclipse Public License v. 2.0, which is available at
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.eclipse.ee4j</groupId>
         <artifactId>project</artifactId>
-        <version>1.0.7</version>
+        <version>1.0.8</version>
     </parent>
 
     <groupId>org.glassfish.jersey.tests</groupId>
diff --git a/tests/version-agnostic/pom.xml b/tests/version-agnostic/pom.xml
index 979fed4..c4c44f5 100644
--- a/tests/version-agnostic/pom.xml
+++ b/tests/version-agnostic/pom.xml
@@ -80,6 +80,17 @@
 
     <profiles>
         <profile>
+            <id>noTests</id>
+            <activation>
+                <property>
+                    <name>skipTests</name>
+                </property>
+            </activation>
+            <properties>
+                <maven.test.skip>true</maven.test.skip>
+            </properties>
+        </profile>
+        <profile>
             <id>version-agnostic</id>
             <activation>
                 <property>
diff --git a/tests/version-agnostic/src/test/java/org/glassfish/jersey/test/agnostic/JettyHeaderTest.java b/tests/version-agnostic/src/test/java/org/glassfish/jersey/test/agnostic/JettyHeaderTest.java
index 01187a2..e720080 100644
--- a/tests/version-agnostic/src/test/java/org/glassfish/jersey/test/agnostic/JettyHeaderTest.java
+++ b/tests/version-agnostic/src/test/java/org/glassfish/jersey/test/agnostic/JettyHeaderTest.java
@@ -24,14 +24,11 @@
 import jakarta.ws.rs.core.Response;
 import org.glassfish.jersey.client.ClientConfig;
 import org.glassfish.jersey.jetty.connector.JettyConnectorProvider;
-import org.glassfish.jersey.logging.LoggingFeature;
 import org.glassfish.jersey.server.ResourceConfig;
 import org.glassfish.jersey.test.JerseyTest;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
-import java.util.logging.Level;
-
 public class JettyHeaderTest extends JerseyTest {
     @Path("/")
     public static class JettyHeaderTestResource {