merge of the actual 3.0 into the 3.1

diff --git a/core-client/src/main/java/org/glassfish/jersey/client/HttpUrlConnectorProvider.java b/core-client/src/main/java/org/glassfish/jersey/client/HttpUrlConnectorProvider.java
index 306e336..dd96928 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/HttpUrlConnectorProvider.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/HttpUrlConnectorProvider.java
@@ -21,9 +21,6 @@
 import java.net.Proxy;
 import java.net.URL;
 import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
 import java.util.logging.Logger;
 
 import jakarta.ws.rs.client.Client;
@@ -290,16 +287,12 @@
          * @throws java.io.IOException in case the connection cannot be provided.
          */
         default HttpURLConnection getConnection(URL url, Proxy proxy) throws IOException {
-            synchronized (this){
-                return (proxy == null) ? getConnection(url) : (HttpURLConnection) url.openConnection(proxy);
-            }
+            return (proxy == null) ? getConnection(url) : (HttpURLConnection) url.openConnection(proxy);
         }
     }
 
     private static class DefaultConnectionFactory implements ConnectionFactory {
 
-        private final ConcurrentHashMap<URL, Lock> locks = new ConcurrentHashMap<>();
-
         @Override
         public HttpURLConnection getConnection(final URL url) throws IOException {
             return connect(url, null);
@@ -311,13 +304,7 @@
         }
 
         private HttpURLConnection connect(URL url, Proxy proxy) throws IOException {
-            Lock lock = locks.computeIfAbsent(url, u -> new ReentrantLock());
-            lock.lock();
-            try {
-                return (proxy == null) ? (HttpURLConnection) url.openConnection() : (HttpURLConnection) url.openConnection(proxy);
-            } finally {
-                lock.unlock();
-            }
+            return (proxy == null) ? (HttpURLConnection) url.openConnection() : (HttpURLConnection) url.openConnection(proxy);
         }
     }
 
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 8fb151a..09d8d88 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
@@ -17,6 +17,7 @@
 package org.glassfish.jersey.client.innate.inject;
 
 import org.glassfish.jersey.client.internal.LocalizationMessages;
+import org.glassfish.jersey.internal.inject.AbstractBinder;
 import org.glassfish.jersey.internal.inject.Binder;
 import org.glassfish.jersey.internal.inject.Binding;
 import org.glassfish.jersey.internal.inject.ClassBinding;
@@ -52,10 +53,12 @@
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Proxy;
 import java.lang.reflect.Type;
-import java.util.Collections;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.function.Supplier;
 import java.util.logging.Level;
@@ -75,8 +78,6 @@
     private final MultivaluedMap<Type, SupplierInstanceBinding<?>> supplierTypeInstanceBindings = new MultivaluedHashMap<>();
     private final MultivaluedMap<Type, SupplierClassBinding<?>> supplierTypeClassBindings = new MultivaluedHashMap<>();
 
-    private final MultivaluedMap<DisposableSupplier, Object> disposableSupplierObjects = new MultivaluedHashMap<>();
-
     private final Instances instances = new Instances();
     private final Types types = new Types();
 
@@ -89,11 +90,12 @@
      * @param <TYPE> the type for which the instance is created, either Class, or ParametrizedType (for instance
      * Provider&lt;SomeClass&gt;).
      */
-    private class TypedInstances<TYPE> {
+    private class TypedInstances<TYPE extends Type> {
         private final MultivaluedMap<TYPE, InstanceContext<?>> singletonInstances = new MultivaluedHashMap<>();
         private ThreadLocal<MultivaluedMap<TYPE, InstanceContext<?>>> threadInstances = new ThreadLocal<>();
-        private final List<Object> threadPredestroyables = Collections.synchronizedList(new LinkedList<>());
         private final ReentrantLock singletonInstancesLock = new ReentrantLock();
+        private ThreadLocal<MultivaluedMap<DisposableSupplier, Object>> disposableSupplierObjects =
+                ThreadLocal.withInitial(() -> new MultivaluedHashMap<>());
 
         private <T> List<InstanceContext<?>> _getSingletons(TYPE clazz) {
             List<InstanceContext<?>> si;
@@ -107,7 +109,7 @@
         }
 
         @SuppressWarnings("unchecked")
-        <T> T _addSingleton(TYPE clazz, T instance, Binding<?, ?> binding, Annotation[] qualifiers) {
+        <T> T _addSingleton(TYPE clazz, T instance, Binding<?, ?> binding, Annotation[] qualifiers, boolean destroy) {
             singletonInstancesLock.lock();
             try {
                 // check existing singleton with a qualifier already created by another thread io a meantime
@@ -121,8 +123,8 @@
                         return (T) qualified.get(0).instance;
                     }
                 }
-                singletonInstances.add(clazz, new InstanceContext<>(instance, binding, qualifiers));
-                threadPredestroyables.add(instance);
+                InstanceContext<?> instanceContext = new InstanceContext<>(instance, binding, qualifiers, !destroy);
+                singletonInstances.add(clazz, instanceContext);
                 return instance;
             } finally {
                 singletonInstancesLock.unlock();
@@ -131,11 +133,11 @@
 
         @SuppressWarnings("unchecked")
         <T> T addSingleton(TYPE clazz, T t, Binding<?, ?> binding, Annotation[] instanceQualifiers) {
-            T t2  = _addSingleton(clazz, t, binding, instanceQualifiers);
+            T t2 = _addSingleton(clazz, t, binding, instanceQualifiers, true);
             if (t2 == t) {
                 for (Type contract : binding.getContracts()) {
                     if (!clazz.equals(contract) && isClass(contract)) {
-                        _addSingleton((TYPE) contract, t, binding, instanceQualifiers);
+                        _addSingleton((TYPE) contract, t, binding, instanceQualifiers, false);
                     }
                 }
             }
@@ -151,21 +153,22 @@
             return list;
         }
 
-        private <T> void _addThreadInstance(TYPE clazz, T instance, Binding<T, ?> binding, Annotation[] qualifiers) {
+        private <T> void _addThreadInstance(
+                TYPE clazz, T instance, Binding<T, ?> binding, Annotation[] qualifiers, boolean destroy) {
             MultivaluedMap<TYPE, InstanceContext<?>> map = threadInstances.get();
             if (map == null) {
                 map = new MultivaluedHashMap<>();
                 threadInstances.set(map);
             }
-            map.add(clazz, new InstanceContext<>(instance, binding, qualifiers));
-            threadPredestroyables.add(instance);
+            InstanceContext<?> instanceContext = new InstanceContext<>(instance, binding, qualifiers, !destroy);
+            map.add(clazz, instanceContext);
         }
 
         <T> void addThreadInstance(TYPE clazz, T t, Binding<T, ?> binding, Annotation[] instanceQualifiers) {
-            _addThreadInstance(clazz, t, binding, instanceQualifiers);
+            _addThreadInstance(clazz, t, binding, instanceQualifiers, true);
             for (Type contract : binding.getContracts()) {
                 if (!clazz.equals(contract) && isClass(contract)) {
-                    _addThreadInstance((TYPE) contract, t, binding, instanceQualifiers);
+                    _addThreadInstance((TYPE) contract, t, binding, instanceQualifiers, false);
                 }
             }
         }
@@ -183,28 +186,78 @@
         private <T> List<InstanceContext<?>> _getContexts(TYPE clazz) {
             List<InstanceContext<?>> si = _getSingletons(clazz);
             List<InstanceContext<?>> ti = _getThreadInstances(clazz);
-            if (si == null && ti != null) {
-                si = ti;
-            } else if (ti != null) {
-                si.addAll(ti);
-            }
-            return si;
+            return InstanceContext.merge(si, ti);
         }
 
         <T> T getInstance(TYPE clazz, Annotation[] annotations) {
             List<T> i = getInstances(clazz, annotations);
             if (i != null) {
                 checkUnique(i);
-                return i.get(0);
+                return instanceOrSupply(clazz, i.get(0));
             }
             return null;
         }
 
+        private <T> T instanceOrSupply(TYPE clazz, T t) {
+            if (!Class.class.isInstance(clazz) || ((Class) clazz).isInstance(t)) {
+                return t;
+            } else if (Supplier.class.isInstance(t)) {
+                return (T) registerDisposableSupplierAndGet((Supplier) t, this);
+            } else if (Provider.class.isInstance(t)) {
+                return (T) ((Provider) t).get();
+            } else {
+                return t;
+            }
+        }
+
         void dispose() {
             singletonInstances.forEach((clazz, instances) -> instances.forEach(instance -> preDestroy(instance.getInstance())));
-            threadPredestroyables.forEach(NonInjectionManager.this::preDestroy);
+            disposeThreadInstances(true);
             /* The java.lang.ThreadLocal$ThreadLocalMap$Entry[] keeps references to this NonInjectionManager */
             threadInstances = null;
+            disposableSupplierObjects = null;
+        }
+
+        void disposeThreadInstances(boolean allThreadInstances) {
+            MultivaluedMap<TYPE, InstanceContext<?>> ti = threadInstances.get();
+            if (ti == null) {
+                return;
+            }
+            Set<Map.Entry<TYPE, List<InstanceContext<?>>>> tiSet = ti.entrySet();
+            Iterator<Map.Entry<TYPE, List<InstanceContext<?>>>> tiSetIt = tiSet.iterator();
+            while (tiSetIt.hasNext()) {
+                Map.Entry<TYPE, List<InstanceContext<?>>> entry = tiSetIt.next();
+                Iterator<InstanceContext<?>> listIt = entry.getValue().iterator();
+                while (listIt.hasNext()) {
+                    InstanceContext<?> instanceContext = listIt.next();
+                    if (allThreadInstances || instanceContext.getBinding().getScope() != PerThread.class) {
+                        listIt.remove();
+                        if (DisposableSupplier.class.isInstance(instanceContext.getInstance())) {
+                            MultivaluedMap<DisposableSupplier, Object> disposeMap = disposableSupplierObjects.get();
+                            Iterator<Map.Entry<DisposableSupplier, List<Object>>> disposeMapIt = disposeMap.entrySet().iterator();
+                            while (disposeMapIt.hasNext()) {
+                                Map.Entry<DisposableSupplier, List<Object>> disposeMapEntry = disposeMapIt.next();
+                                if (disposeMapEntry.getKey() == /* identity */ instanceContext.getInstance()) {
+                                    Iterator<Object> disposeMapEntryIt = disposeMapEntry.getValue().iterator();
+                                    while (disposeMapEntryIt.hasNext()) {
+                                        Object disposeInstance = disposeMapEntryIt.next();
+                                        ((DisposableSupplier) instanceContext.getInstance()).dispose(disposeInstance);
+                                        disposeMapEntryIt.remove();
+                                    }
+                                }
+                                if (disposeMapEntry.getValue().isEmpty()) {
+                                    disposeMapIt.remove();
+                                }
+                            }
+                        }
+                        instanceContext.destroy(NonInjectionManager.this);
+                    }
+                    if (entry.getValue().isEmpty()) {
+                        tiSetIt.remove();
+                    }
+                }
+            }
+            disposableSupplierObjects.remove();
         }
     }
 
@@ -215,9 +268,19 @@
     }
 
     public NonInjectionManager() {
+        Binding binding = new AbstractBinder() {
+            @Override
+            protected void configure() {
+                bind(NonInjectionRequestScope.class).to(RequestScope.class).in(Singleton.class);
+            }
+        }.getBindings().iterator().next();
+        RequestScope scope = new NonInjectionRequestScope(this);
+        instances.addSingleton(RequestScope.class, scope, binding, null);
+        types.addSingleton(RequestScope.class, scope, binding, null);
     }
 
     public NonInjectionManager(boolean warning) {
+        this();
         if (warning) {
             logger.warning(LocalizationMessages.NONINJECT_FALLBACK());
         } else {
@@ -227,20 +290,21 @@
 
     @Override
     public void completeRegistration() {
-        instances._addSingleton(InjectionManager.class, this, new InjectionManagerBinding(), null);
+        instances._addSingleton(InjectionManager.class, this, new InjectionManagerBinding(), null, false);
     }
 
     @Override
     public void shutdown() {
         shutdown = true;
-
-        disposableSupplierObjects.forEach((supplier, objects) -> objects.forEach(supplier::dispose));
-        disposableSupplierObjects.clear();
-
         instances.dispose();
         types.dispose();
     }
 
+    void disposeRequestScopedInstances() {
+        instances.disposeThreadInstances(false);
+        types.disposeThreadInstances(false);
+    }
+
     @Override
     public boolean isShutdown() {
         return shutdown;
@@ -419,12 +483,7 @@
             return (T) this;
         }
         if (RequestScope.class.equals(createMe)) {
-            if (!isRequestScope) {
-                isRequestScope = true;
-                return (T) new NonInjectionRequestScope();
-            } else {
-                throw new IllegalStateException(LocalizationMessages.NONINJECT_REQUESTSCOPE_CREATED());
-            }
+            throw new IllegalStateException(LocalizationMessages.NONINJECT_REQUESTSCOPE_CREATED());
         }
 
         ClassBindings<T> classBindings = classBindings(createMe);
@@ -439,12 +498,7 @@
             return (T) this;
         }
         if (RequestScope.class.equals(createMe)) {
-            if (!isRequestScope) {
-                isRequestScope = true;
-                return (T) new NonInjectionRequestScope();
-            } else {
-                throw new IllegalStateException(LocalizationMessages.NONINJECT_REQUESTSCOPE_CREATED());
-            }
+            throw new IllegalStateException(LocalizationMessages.NONINJECT_REQUESTSCOPE_CREATED());
         }
 
         ClassBindings<T> classBindings = classBindings(createMe);
@@ -543,7 +597,9 @@
 
     @Override
     public void preDestroy(Object preDestroyMe) {
-        Method preDestroy = getAnnotatedMethod(preDestroyMe, PreDestroy.class);
+        Method preDestroy = Method.class.isInstance(preDestroyMe)
+                ? (Method) preDestroyMe
+                : getAnnotatedMethod(preDestroyMe, PreDestroy.class);
         if (preDestroy != null) {
             ensureAccessible(preDestroy);
             try {
@@ -575,20 +631,27 @@
      * @return The proxy for the instance supplied by a supplier or the instance if not required to be proxied.
      */
     @SuppressWarnings("unchecked")
-    private <T> T createSupplierProxyIfNeeded(Boolean createProxy, Class<T> iface, Supplier<T> supplier) {
+    private <T> T createSupplierProxyIfNeeded(
+            Boolean createProxy, Class<T> iface, Supplier<Supplier<T>> supplier, TypedInstances<?> typedInstances) {
         if (createProxy != null && createProxy && iface.isInterface()) {
             T proxy = (T) Proxy.newProxyInstance(iface.getClassLoader(), new Class[]{iface}, new InvocationHandler() {
-                final SingleRegisterSupplier<T> singleSupplierRegister = new SingleRegisterSupplier<>(supplier);
+                final Set<Object> instances = new HashSet<>();
+
                 @Override
                 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
-                    T t = singleSupplierRegister.get();
+                    Supplier<T> supplierT = supplier.get();
+                    T t = supplierT.get();
+                    if (DisposableSupplier.class.isInstance(supplierT) && !instances.contains(t)) {
+                        MultivaluedMap<DisposableSupplier, Object> map = typedInstances.disposableSupplierObjects.get();
+                        map.add((DisposableSupplier) supplierT, t);
+                    }
                     Object ret = method.invoke(t, args);
                     return ret;
                 }
             });
             return proxy;
         } else {
-            return registerDisposableSupplierAndGet(supplier);
+            return registerDisposableSupplierAndGet(supplier.get(), typedInstances);
         }
     }
 
@@ -600,8 +663,8 @@
     private class SingleRegisterSupplier<T> {
         private final LazyValue<T> once;
 
-        private SingleRegisterSupplier(Supplier<T> supplier) {
-            once = Values.lazy((Value<T>) () -> registerDisposableSupplierAndGet(supplier));
+        private SingleRegisterSupplier(Supplier<T> supplier, TypedInstances<?> instances) {
+            once = Values.lazy((Value<T>) () -> registerDisposableSupplierAndGet(supplier, instances));
         }
 
         T get() {
@@ -609,10 +672,10 @@
         }
     }
 
-    private <T> T registerDisposableSupplierAndGet(Supplier<T> supplier) {
+    private <T> T registerDisposableSupplierAndGet(Supplier<T> supplier, TypedInstances<?> typedInstances) {
         T instance = supplier.get();
         if (DisposableSupplier.class.isInstance(supplier)) {
-            disposableSupplierObjects.add((DisposableSupplier<T>) supplier, instance);
+            typedInstances.disposableSupplierObjects.get().add((DisposableSupplier<T>) supplier, instance);
         }
         return instance;
     }
@@ -686,7 +749,7 @@
      * @param <X> The expected return type for the TYPE.
      * @param <TYPE> The Type for which a {@link Binding} has been created.
      */
-    private abstract class XBindings<X, TYPE> {
+    private abstract class XBindings<X, TYPE extends Type> {
 
         protected final List<InstanceBinding<X>> instanceBindings = new LinkedList<>();
         protected final List<SupplierInstanceBinding<X>> supplierInstanceBindings = new LinkedList<>();
@@ -767,12 +830,8 @@
 
         private X _create(SupplierInstanceBinding<X> binding) {
             Supplier<X> supplier = binding.getSupplier();
-            X t = registerDisposableSupplierAndGet(supplier);
-            if (Singleton.class.equals(binding.getScope())) {
-                _addInstance(t, binding);
-            } else if (_isPerThread(binding.getScope())) {
-                _addThreadInstance(t, binding);
-            }
+            X t = registerDisposableSupplierAndGet(supplier, instances);
+            t = addInstance(type, t, binding);
             return t;
         }
 
@@ -836,20 +895,17 @@
 
         protected abstract X _createAndStore(ClassBinding<X> binding);
 
-        protected <T> T _addInstance(TYPE type, T instance, Binding<?, ?> binding) {
+        protected <T> T _addSingletonInstance(TYPE type, T instance, Binding<?, ?> binding) {
             return instances.addSingleton(type, instance, binding, instancesQualifiers);
         }
 
-        protected void _addThreadInstance(TYPE type, Object instance, Binding binding) {
-            instances.addThreadInstance(type, instance, binding, instancesQualifiers);
-        }
-
-        protected <T> T _addInstance(T instance, Binding<?, ?> binding) {
-            return instances.addSingleton(type, instance, binding, instancesQualifiers);
-        }
-
-        protected void _addThreadInstance(Object instance, Binding binding) {
-            instances.addThreadInstance(type, instance, binding, instancesQualifiers);
+        protected <T> T addInstance(TYPE type, T instance, Binding binding) {
+            if (Singleton.class.equals(binding.getScope())) {
+                instance = instances.addSingleton(type, instance, binding, instancesQualifiers);
+            } else if (_isPerThread(binding.getScope())) {
+                instances.addThreadInstance(type, instance, binding, instancesQualifiers);
+            }
+            return instance;
         }
     }
 
@@ -909,28 +965,27 @@
         }
 
         protected T _create(SupplierClassBinding<T> binding) {
-            Supplier<T> supplier = instances.getInstance(binding.getSupplierClass(), null);
-            if (supplier == null) {
-                supplier = justCreate(binding.getSupplierClass());
-                if (Singleton.class.equals(binding.getSupplierScope())) {
-                    supplier = instances.addSingleton(binding.getSupplierClass(), supplier, binding, null);
-                } else if (_isPerThread(binding.getSupplierScope())) {
-                    instances.addThreadInstance(binding.getSupplierClass(), supplier, binding, null);
+            Supplier<Supplier<T>> supplierSupplier = () -> {
+                Supplier<T> supplier = instances.getInstance(binding.getSupplierClass(), null);
+                if (supplier == null) {
+                    supplier = justCreate(binding.getSupplierClass());
+                    if (Singleton.class.equals(binding.getSupplierScope())) {
+                        supplier = instances.addSingleton(binding.getSupplierClass(), supplier, binding, null);
+                    } else if (_isPerThread(binding.getSupplierScope()) || binding.getSupplierScope() == null) {
+                        instances.addThreadInstance(binding.getSupplierClass(), supplier, binding, null);
+                    }
                 }
-            }
+                return supplier;
+            };
 
-            T t = createSupplierProxyIfNeeded(binding.isProxiable(), (Class<T>) type, supplier);
-            if (Singleton.class.equals(binding.getScope())) {
-                t = _addInstance(type, t, binding);
-            } else if (_isPerThread(binding.getScope())) {
-                _addThreadInstance(type, t, binding);
-            }
+            T t = createSupplierProxyIfNeeded(binding.isProxiable(), (Class<T>) type, supplierSupplier, instances);
+//            t = addInstance(type, t, binding); The supplier here creates instances that ought not to be registered as beans
             return t;
         }
 
         protected T _createAndStore(ClassBinding<T> binding) {
             T result = justCreate(binding.getService());
-            result = _addInstance(binding.getService(), result, binding);
+            result = addInstance(binding.getService(), result, binding);
             return result;
         }
     }
@@ -943,19 +998,15 @@
         protected T _create(SupplierClassBinding<T> binding) {
             Supplier<T> supplier = justCreate(binding.getSupplierClass());
 
-            T t = registerDisposableSupplierAndGet(supplier);
-            if (Singleton.class.equals(binding.getScope())) {
-                t = _addInstance(type, t, binding);
-            } else if (_isPerThread(binding.getScope())) {
-                _addThreadInstance(type, t, binding);
-            }
+            T t = registerDisposableSupplierAndGet(supplier, instances);
+            t = addInstance(type, t, binding);
             return t;
         }
 
         @Override
         protected T _createAndStore(ClassBinding<T> binding) {
             T result = justCreate(binding.getService());
-            result = _addInstance(type, result, binding);
+            result = addInstance(type, result, binding);
             return result;
         }
 
@@ -976,7 +1027,7 @@
                                     return NonInjectionManager.this.getInstance(actualTypeArgument);
                                 }
                             }
-                        });
+                        }, instances);
 
                         @Override
                         public Object get() {
@@ -999,13 +1050,16 @@
         private final T instance;
         private final Binding<?, ?> binding;
         private final Annotation[] createdWithQualifiers;
+        private boolean destroyed = false;
 
-        private InstanceContext(T instance, Binding<?, ?> binding, Annotation[] qualifiers) {
+        private InstanceContext(T instance, Binding<?, ?> binding, Annotation[] qualifiers, boolean destroyed) {
             this.instance = instance;
             this.binding = binding;
             this.createdWithQualifiers = qualifiers;
+            this.destroyed = destroyed;
         }
 
+
         public Binding<?, ?> getBinding() {
             return binding;
         }
@@ -1014,6 +1068,13 @@
             return instance;
         }
 
+        public void destroy(NonInjectionManager nonInjectionManager) {
+            if (!destroyed) {
+                destroyed = true;
+                nonInjectionManager.preDestroy(instance);
+            }
+        }
+
         @SuppressWarnings("unchecked")
         static <T> List<T> toInstances(List<InstanceContext<?>> instances, Annotation[] qualifiers) {
             return instances != null
@@ -1032,6 +1093,15 @@
                     : null;
         }
 
+        private static List<InstanceContext<?>> merge(List<InstanceContext<?>> i1, List<InstanceContext<?>> i2) {
+            if (i1 == null) {
+                i1 = i2;
+            } else if (i2 != null) {
+                i1.addAll(i2);
+            }
+            return i1;
+        }
+
         private boolean hasQualifiers(Annotation[] requested) {
             if (requested != null) {
                 classLoop:
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/innate/inject/NonInjectionRequestScope.java b/core-client/src/main/java/org/glassfish/jersey/client/innate/inject/NonInjectionRequestScope.java
index 258780d..b8b23b2 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/innate/inject/NonInjectionRequestScope.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/innate/inject/NonInjectionRequestScope.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2024 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -20,14 +20,20 @@
 import org.glassfish.jersey.internal.util.LazyUid;
 import org.glassfish.jersey.process.internal.RequestScope;
 
-import java.util.concurrent.atomic.AtomicInteger;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
 public class NonInjectionRequestScope extends RequestScope {
+
+    private final NonInjectionManager nonInjectionManager;
+
+    public NonInjectionRequestScope(NonInjectionManager nonInjectionManager) {
+        this.nonInjectionManager = nonInjectionManager;
+    }
+
     @Override
     public org.glassfish.jersey.process.internal.RequestContext createContext() {
-        return new Instance();
+        return new Instance(nonInjectionManager);
     }
 
     /**
@@ -35,6 +41,8 @@
      */
     public static final class Instance implements org.glassfish.jersey.process.internal.RequestContext {
 
+        private final NonInjectionManager injectionManager;
+
         private static final ExtendedLogger logger = new ExtendedLogger(Logger.getLogger(Instance.class.getName()), Level.FINEST);
 
         /*
@@ -48,10 +56,11 @@
         /**
          * Holds the number of snapshots of this scope.
          */
-        private final AtomicInteger referenceCounter;
+        private int referenceCounter;
 
-        private Instance() {
-            this.referenceCounter = new AtomicInteger(1);
+        private Instance(NonInjectionManager injectionManager) {
+            this.injectionManager = injectionManager;
+            this.referenceCounter = 1;
         }
 
         /**
@@ -65,7 +74,7 @@
         @Override
         public NonInjectionRequestScope.Instance getReference() {
             // TODO: replace counter with a phantom reference + reference queue-based solution
-            referenceCounter.incrementAndGet();
+            referenceCounter++;
             return this;
         }
 
@@ -77,7 +86,9 @@
          */
         @Override
         public void release() {
-            referenceCounter.decrementAndGet();
+            if (0 == --referenceCounter) {
+                injectionManager.disposeRequestScopedInstances();
+            }
         }
 
         @Override
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 ca73f4c..630cb96 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
@@ -84,7 +84,7 @@
     private static final String ALLOW_RESTRICTED_HEADERS_SYSTEM_PROPERTY = "sun.net.http.allowRestrictedHeaders";
     // Avoid multi-thread uses of HttpsURLConnection.getDefaultSSLSocketFactory() because it does not implement a
     // proper lazy-initialization. See https://github.com/jersey/jersey/issues/3293
-    private static final Value<SSLSocketFactory> DEFAULT_SSL_SOCKET_FACTORY =
+    private static final LazyValue<SSLSocketFactory> DEFAULT_SSL_SOCKET_FACTORY =
             Values.lazy((Value<SSLSocketFactory>) () -> HttpsURLConnection.getDefaultSSLSocketFactory());
     // The list of restricted headers is extracted from sun.net.www.protocol.http.HttpURLConnection
     private static final String[] restrictedHeaders = {
@@ -387,6 +387,10 @@
             sniUri = request.getUri();
         }
 
+        if (!DEFAULT_SSL_SOCKET_FACTORY.isInitialized() && "HTTPS".equalsIgnoreCase(sniUri.getScheme())) {
+            DEFAULT_SSL_SOCKET_FACTORY.get();
+        }
+
         proxy.ifPresent(clientProxy -> ClientProxy.setBasicAuthorizationHeader(request.getHeaders(), proxy.get()));
         uc = this.connectionFactory.getConnection(sniUri.toURL(), proxy.isPresent() ? proxy.get().proxy() : null);
         uc.setDoInput(true);
diff --git a/core-common/pom.xml b/core-common/pom.xml
index 2c32a3f..e927538 100644
--- a/core-common/pom.xml
+++ b/core-common/pom.xml
@@ -418,6 +418,9 @@
 
         <profile>
             <id>securityOff</id>
+            <activation>
+                <jdk>[24,)</jdk>
+            </activation>
             <properties>
                 <surefire.security.argline />
             </properties>
@@ -426,12 +429,17 @@
                     <plugin>
                         <groupId>org.apache.maven.plugins</groupId>
                         <artifactId>maven-surefire-plugin</artifactId>
-                        <configuration>
-                            <excludes>
-                                <exclude>**/SecurityManagerConfiguredTest.java</exclude>
-                                <exclude>**/ReflectionHelperTest.java</exclude>
-                            </excludes>
-                        </configuration>
+                        <executions>
+                            <execution>
+                                <id>default-test</id>
+                                <configuration>
+                                    <excludes>
+                                        <exclude>**/SecurityManagerConfiguredTest.java</exclude>
+                                        <exclude>**/ReflectionHelperTest.java</exclude>
+                                    </excludes>
+                                </configuration>
+                            </execution>
+                        </executions>
                     </plugin>
                 </plugins>
             </build>
diff --git a/core-common/src/test/java/org/glassfish/jersey/internal/config/ExternalPropertiesConfigurationFactoryTest.java b/core-common/src/test/java/org/glassfish/jersey/internal/config/ExternalPropertiesConfigurationFactoryTest.java
index 6f1f7b1..ff3e9ef 100644
--- a/core-common/src/test/java/org/glassfish/jersey/internal/config/ExternalPropertiesConfigurationFactoryTest.java
+++ b/core-common/src/test/java/org/glassfish/jersey/internal/config/ExternalPropertiesConfigurationFactoryTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2024 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -30,11 +30,14 @@
 
 public class ExternalPropertiesConfigurationFactoryTest {
 
+    private static boolean isSecurityManager;
+
     /**
      * Predefine some properties to be read from config
      */
     @BeforeAll
     public static void setUp() {
+        isSecurityManager = System.getSecurityManager() != null;
         System.setProperty(CommonProperties.ALLOW_SYSTEM_PROPERTIES_PROVIDER, Boolean.TRUE.toString());
 
         System.setProperty("jersey.config.server.provider.scanning.recursive", "PASSED");
@@ -53,7 +56,11 @@
     public void readSystemPropertiesTest() {
         final Object result =
                 readExternalPropertiesMap().get("jersey.config.server.provider.scanning.recursive");
-        Assertions.assertNull(result);
+        if (isSecurityManager) {
+            Assertions.assertNull(result);
+        } else {
+            Assertions.assertEquals("PASSED", result);
+        }
         Assertions.assertEquals(Boolean.TRUE,
                 getConfig().isProperty(CommonProperties.JSON_PROCESSING_FEATURE_DISABLE));
         Assertions.assertEquals(Boolean.TRUE,
@@ -81,8 +88,11 @@
         inputProperties.put("org.jersey.microprofile.config.added", "ADDED");
         getConfig().mergeProperties(inputProperties);
         final Object result = readExternalPropertiesMap().get("jersey.config.server.provider.scanning.recursive");
-        Assertions.assertNull(result);
-        Assertions.assertNull(readExternalPropertiesMap().get("org.jersey.microprofile.config.added"));
+        final Object resultAdded = readExternalPropertiesMap().get("org.jersey.microprofile.config.added");
+        if (isSecurityManager) {
+            Assertions.assertNull(result);
+            Assertions.assertNull(resultAdded);
+        }
     }
 
 }
diff --git a/core-server/pom.xml b/core-server/pom.xml
index 7a09453..a99a0d1 100644
--- a/core-server/pom.xml
+++ b/core-server/pom.xml
@@ -260,6 +260,9 @@
     <profiles>
         <profile>
             <id>securityOff</id>
+            <activation>
+                <jdk>[24,)</jdk>
+            </activation>
             <properties>
                 <surefire.security.argline />
             </properties>
diff --git a/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/ParamConverterDateTest.java b/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/ParamConverterDateTest.java
index 1f14c63..91acc2a 100644
--- a/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/ParamConverterDateTest.java
+++ b/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/ParamConverterDateTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -40,6 +40,8 @@
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
 public class ParamConverterDateTest extends AbstractTest {
+    private final String format = "EEE MMM dd HH:mm:ss Z yyyy";
+    private final SimpleDateFormat formatter = new SimpleDateFormat(format, new Locale("US"));
 
     @Path("/")
     public static class DateResource {
@@ -55,7 +57,7 @@
     public void testDateResource() throws ExecutionException, InterruptedException {
         initiateWebApplication(getBinder(), ParamConverterDateTest.DateResource.class);
         final ContainerResponse responseContext = getResponseContext(UriBuilder.fromPath("/")
-                .queryParam("d", new Date()).build().toString());
+                .queryParam("d", formatter.format(new Date())).build().toString());
 
         assertEquals(200, responseContext.getStatus());
     }
@@ -80,8 +82,6 @@
                                     );
                                 }
                                 try {
-                                    final String format = "EEE MMM dd HH:mm:ss Z yyyy";
-                                    final SimpleDateFormat formatter = new SimpleDateFormat(format, new Locale("US"));
                                     return rawType.cast(formatter.parse(value));
                                 } catch (final ParseException ex) {
                                     throw new ExtractorException(ex);
diff --git a/examples/groovy/pom.xml b/examples/groovy/pom.xml
index 77d2821..0766899 100644
--- a/examples/groovy/pom.xml
+++ b/examples/groovy/pom.xml
@@ -43,6 +43,14 @@
                     <artifactId>junit-jupiter-api</artifactId>
                 </exclusion>
                 <exclusion>
+                    <groupId>org.junit.jupiter</groupId>
+                    <artifactId>junit-jupiter-engine</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.junit.platform</groupId>
+                    <artifactId>junit-platform-commons</artifactId>
+                </exclusion>
+                <exclusion>
                     <groupId>org.ow2.asm</groupId>
                     <artifactId>asm</artifactId>
                 </exclusion>
@@ -117,10 +125,12 @@
                             <goal>removeTestStubs</goal>
                             <goal>groovydoc</goal>
                         </goals>
+                        <configuration>
+                            <targetBytecode>11</targetBytecode>
+                        </configuration>
                     </execution>
                 </executions>
             </plugin>
-
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-antrun-plugin</artifactId>
diff --git a/examples/osgi-helloworld-webapp/pom.xml b/examples/osgi-helloworld-webapp/pom.xml
index f25be4e..531d0b0 100644
--- a/examples/osgi-helloworld-webapp/pom.xml
+++ b/examples/osgi-helloworld-webapp/pom.xml
@@ -25,16 +25,21 @@
     <name>jersey-examples-osgi-helloworld-webapp</name>
     <packaging>pom</packaging>
 
-    <modules>
-        <module>war-bundle</module>
-        <module>functional-test</module>
-        <module>lib-bundle</module>
-        <module>additional-bundle</module>
-        <module>alternate-version-bundle</module>
-    </modules>
-
     <profiles>
         <profile>
+            <id>securityOn</id>
+            <activation>
+                <jdk>[11,24)</jdk>
+            </activation>
+            <modules>
+                <module>war-bundle</module>
+                <module>functional-test</module>
+                <module>lib-bundle</module>
+                <module>additional-bundle</module>
+                <module>alternate-version-bundle</module>
+            </modules>
+        </profile>
+        <profile>
             <id>pre-release</id>
             <build>
                 <plugins>
diff --git a/ext/micrometer/src/main/java/org/glassfish/jersey/micrometer/server/ObservationRequestEventListener.java b/ext/micrometer/src/main/java/org/glassfish/jersey/micrometer/server/ObservationRequestEventListener.java
index 953944b..2db4cd1 100644
--- a/ext/micrometer/src/main/java/org/glassfish/jersey/micrometer/server/ObservationRequestEventListener.java
+++ b/ext/micrometer/src/main/java/org/glassfish/jersey/micrometer/server/ObservationRequestEventListener.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -65,7 +65,7 @@
 
         switch (event.getType()) {
             case ON_EXCEPTION:
-                if (!isNotFoundException(event)) {
+                if (!isClientError(event) || observations.get(containerRequest) != null) {
                     break;
                 }
                 startObservation(event);
@@ -102,13 +102,14 @@
         observations.put(event.getContainerRequest(), new ObservationScopeAndContext(scope, jerseyContext));
     }
 
-    private boolean isNotFoundException(RequestEvent event) {
+    private boolean isClientError(RequestEvent event) {
         Throwable t = event.getException();
         if (t == null) {
             return false;
         }
-        String className = t.getClass().getCanonicalName();
-        return className.equals("jakarta.ws.rs.NotFoundException") || className.equals("jakarta.ws.rs.NotFoundException");
+        String className = t.getClass().getSuperclass().getCanonicalName();
+        return className.equals("jakarta.ws.rs.ClientErrorException")
+                || className.equals("javax.ws.rs.ClientErrorException");
     }
 
     private static class ObservationScopeAndContext {
diff --git a/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/observation/AbstractObservationRequestEventListenerTest.java b/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/observation/AbstractObservationRequestEventListenerTest.java
index 0ae7ab6..d46e855 100644
--- a/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/observation/AbstractObservationRequestEventListenerTest.java
+++ b/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/observation/AbstractObservationRequestEventListenerTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -20,6 +20,7 @@
 import java.util.logging.Logger;
 
 import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.MediaType;
 
 import io.micrometer.core.instrument.MeterRegistry;
 import io.micrometer.core.instrument.Tag;
@@ -38,6 +39,7 @@
 import io.micrometer.tracing.test.simple.SpansAssert;
 import org.glassfish.jersey.micrometer.server.ObservationApplicationEventListener;
 import org.glassfish.jersey.micrometer.server.ObservationRequestEventListener;
+import org.glassfish.jersey.micrometer.server.mapper.ResourceGoneExceptionMapper;
 import org.glassfish.jersey.micrometer.server.resources.TestResource;
 import org.glassfish.jersey.server.ResourceConfig;
 import org.glassfish.jersey.test.JerseyTest;
@@ -85,6 +87,7 @@
         final ResourceConfig config = new ResourceConfig();
         config.register(listener);
         config.register(TestResource.class);
+        config.register(ResourceGoneExceptionMapper.class);
 
         return config;
     }
@@ -131,6 +134,53 @@
                 .hasTag("outcome", "SUCCESS")
                 .hasTag("status", "200")
                 .hasTag("uri", "/sub-resource/sub-hello/{name}");
+        assertThat(observationRegistry.getCurrentObservation()).isNull();
+    }
+
+    @Test
+    void errorResourcesAreTimed() {
+        try {
+            target("throws-exception").request().get();
+        }
+        catch (Exception ignored) {
+        }
+        try {
+            target("throws-webapplication-exception").request().get();
+        }
+        catch (Exception ignored) {
+        }
+        try {
+            target("throws-mappable-exception").request().get();
+        }
+        catch (Exception ignored) {
+        }
+        try {
+            target("produces-text-plain").request(MediaType.APPLICATION_JSON).get();
+        }
+        catch (Exception ignored) {
+        }
+
+        assertThat(registry.get(METRIC_NAME)
+                       .tags(tagsFrom("/throws-exception", "500", "SERVER_ERROR", "IllegalArgumentException"))
+                       .timer()
+                       .count()).isEqualTo(1);
+
+        assertThat(registry.get(METRIC_NAME)
+                       .tags(tagsFrom("/throws-webapplication-exception", "401", "CLIENT_ERROR", "NotAuthorizedException"))
+                       .timer()
+                       .count()).isEqualTo(1);
+
+        assertThat(registry.get(METRIC_NAME)
+                       .tags(tagsFrom("/throws-mappable-exception", "410", "CLIENT_ERROR", "ResourceGoneException"))
+                       .timer()
+                       .count()).isEqualTo(1);
+
+        assertThat(registry.get(METRIC_NAME)
+                       .tags(tagsFrom("UNKNOWN", "406", "CLIENT_ERROR", "NotAcceptableException"))
+                       .timer()
+                       .count()).isEqualTo(1);
+
+        assertThat(observationRegistry.getCurrentObservation()).isNull();
     }
 
     private static Iterable<Tag> tagsFrom(String uri, String status, String outcome, String exception) {
diff --git a/incubator/pom.xml b/incubator/pom.xml
index 609fd98..528eb08 100644
--- a/incubator/pom.xml
+++ b/incubator/pom.xml
@@ -39,7 +39,6 @@
         <module>cdi-inject-weld</module>
         <module>declarative-linking</module>
         <module>gae-integration</module>
-        <module>html-json</module>
         <module>injectless-client</module>
         <module>kryo</module>
         <module>open-tracing</module>
@@ -53,4 +52,16 @@
             <scope>test</scope>
         </dependency>
     </dependencies>
+
+    <profiles>
+        <profile>
+            <id>HTML-JSON-FOR-PRE-JDK24</id>
+            <activation>
+                <jdk>[11, 24)</jdk>
+            </activation>
+            <modules>
+                <module>html-json</module>
+            </modules>
+        </profile>
+    </profiles>
 </project>
diff --git a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/JacksonFeature.java b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/JacksonFeature.java
index 5411720..9391bc6 100644
--- a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/JacksonFeature.java
+++ b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/JacksonFeature.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -29,8 +29,10 @@
 import org.glassfish.jersey.jackson.internal.DefaultJacksonJaxbJsonProvider;
 import org.glassfish.jersey.jackson.internal.FilteringJacksonJaxbJsonProvider;
 import org.glassfish.jersey.jackson.internal.JacksonFilteringFeature;
+import org.glassfish.jersey.jackson.internal.JaxrsFeatureBag;
 import org.glassfish.jersey.jackson.internal.jackson.jaxrs.base.JsonMappingExceptionMapper;
 import org.glassfish.jersey.jackson.internal.jackson.jaxrs.base.JsonParseExceptionMapper;
+import org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.JaxRSFeature;
 import org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.JacksonJaxbJsonProvider;
 import org.glassfish.jersey.message.MessageProperties;
 import org.glassfish.jersey.message.filtering.EntityFilteringFeature;
@@ -41,7 +43,7 @@
  * @author Stepan Kopriva
  * @author Michal Gajdos
  */
-public class JacksonFeature implements Feature {
+public class JacksonFeature extends JaxrsFeatureBag<JacksonFeature> implements Feature {
 
     /**
      * Define whether to use Jackson's exception mappers ore not
@@ -100,6 +102,16 @@
         return this;
     }
 
+    /**
+     * Register {@link JaxRSFeature} with the Jackson providers.
+     * @param feature the {@link JaxRSFeature} to be enabled or disabled.
+     * @param state {@code true} for enabling the feature, {@code false} for disabling.
+     * @return JacksonFeature with {@link JaxRSFeature} registered to be set on a created Jackson provider.
+     */
+    public JacksonFeature jaxrsFeature(JaxRSFeature feature, boolean state) {
+        return super.jaxrsFeature(feature, state);
+    }
+
     private static final String JSON_FEATURE = JacksonFeature.class.getSimpleName();
 
     @Override
@@ -138,6 +150,10 @@
             context.property(MessageProperties.JSON_MAX_STRING_LENGTH, maxStringLength);
         }
 
+        if (hasJaxrsFeature()) {
+            context.property(JaxrsFeatureBag.JAXRS_FEATURE, this);
+        }
+
         return true;
     }
 }
diff --git a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/JaxRSFeatureObjectMapper.java b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/JaxRSFeatureObjectMapper.java
new file mode 100644
index 0000000..d759e56
--- /dev/null
+++ b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/JaxRSFeatureObjectMapper.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.jackson;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.glassfish.jersey.jackson.internal.AbstractObjectMapper;
+import org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.JaxRSFeature;
+
+
+/**
+ * The Jackson {@link ObjectMapper} supporting {@link JaxRSFeature}s.
+ */
+public class JaxRSFeatureObjectMapper extends AbstractObjectMapper {
+
+    public JaxRSFeatureObjectMapper() {
+        super();
+    }
+
+    /**
+     * Method for changing state of an on/off {@link org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.JaxRSFeature}
+     * features.
+     */
+    public ObjectMapper configure(JaxRSFeature f, boolean state) {
+        jaxrsFeatureBag.jaxrsFeature(f, state);
+        return this;
+    }
+
+    /**
+     * Method for enabling specified {@link org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.JaxRSFeature}s
+     * for parser instances this object mapper creates.
+     */
+    public ObjectMapper enable(JaxRSFeature... features) {
+        if (features != null) {
+            for (JaxRSFeature f : features) {
+                jaxrsFeatureBag.jaxrsFeature(f, true);
+            }
+        }
+        return this;
+    }
+
+    /**
+     * Method for disabling specified {@link org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.JaxRSFeature}s
+     * for parser instances this object mapper creates.
+     */
+    public ObjectMapper disable(JaxRSFeature... features) {
+        if (features != null) {
+            for (JaxRSFeature f : features) {
+                jaxrsFeatureBag.jaxrsFeature(f, false);
+            }
+        }
+        return this;
+    }
+}
diff --git a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/AbstractObjectMapper.java b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/AbstractObjectMapper.java
new file mode 100644
index 0000000..2f331cf
--- /dev/null
+++ b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/AbstractObjectMapper.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.jackson.internal;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * Internal ObjectMapper with {@link JaxrsFeatureBag}.
+ */
+public abstract class AbstractObjectMapper extends ObjectMapper {
+    protected AbstractObjectMapper() {
+
+    }
+    protected JaxrsFeatureBag jaxrsFeatureBag = new JaxrsFeatureBag();
+}
diff --git a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/DefaultJacksonJaxbJsonProvider.java b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/DefaultJacksonJaxbJsonProvider.java
index ac0b5c9..0b9222c 100644
--- a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/DefaultJacksonJaxbJsonProvider.java
+++ b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/DefaultJacksonJaxbJsonProvider.java
@@ -18,7 +18,6 @@
 
 import com.fasterxml.jackson.core.JsonFactory;
 import com.fasterxml.jackson.core.StreamReadConstraints;
-import com.fasterxml.jackson.core.Version;
 import com.fasterxml.jackson.core.json.PackageVersion;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.Module;
@@ -41,6 +40,7 @@
 import jakarta.inject.Singleton;
 import jakarta.ws.rs.core.Configuration;
 import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.ext.Providers;
 
 /**
@@ -53,9 +53,7 @@
 
     @Inject
     public DefaultJacksonJaxbJsonProvider(@Context Providers providers, @Context Configuration config) {
-        super(new JacksonMapperConfigurator(null, DEFAULT_ANNOTATIONS));
-        this.commonConfig = config;
-        _providers = providers;
+        this(providers, config, DEFAULT_ANNOTATIONS);
     }
 
     //do not register JaxbAnnotationModule because it brakes default annotations processing
@@ -65,6 +63,20 @@
         super(new JacksonMapperConfigurator(null, annotationsToUse));
         this.commonConfig = config;
         _providers = providers;
+
+        Object jaxrsFeatureBag = config.getProperty(JaxrsFeatureBag.JAXRS_FEATURE);
+        if (jaxrsFeatureBag != null && (JaxrsFeatureBag.class.isInstance(jaxrsFeatureBag))) {
+            ((JaxrsFeatureBag) jaxrsFeatureBag).configureJaxrsFeatures(this);
+        }
+    }
+
+    @Override
+    protected ObjectMapper _locateMapperViaProvider(Class<?> type, MediaType mediaType) {
+        ObjectMapper mapper = super._locateMapperViaProvider(type, mediaType);
+        if (AbstractObjectMapper.class.isInstance(mapper)) {
+            ((AbstractObjectMapper) mapper).jaxrsFeatureBag.configureJaxrsFeatures(this);
+        }
+        return mapper;
     }
 
     @Override
diff --git a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/JaxrsFeatureBag.java b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/JaxrsFeatureBag.java
new file mode 100644
index 0000000..4711b56
--- /dev/null
+++ b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/JaxrsFeatureBag.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.jackson.internal;
+
+import org.glassfish.jersey.jackson.internal.jackson.jaxrs.base.ProviderBase;
+import org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.JaxRSFeature;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Internal holder class for {@link JaxRSFeature} settings and their values.
+ */
+public class JaxrsFeatureBag<T extends JaxrsFeatureBag> {
+    protected static final String JAXRS_FEATURE = "jersey.config.jackson.jaxrs.feature";
+
+    private static class JaxRSFeatureState {
+        /* package */ final JaxRSFeature feature;
+        /* package */ final boolean state;
+        public JaxRSFeatureState(JaxRSFeature feature, boolean state) {
+            this.feature = feature;
+            this.state = state;
+        }
+    }
+
+    private Optional<List<JaxRSFeatureState>> jaxRSFeature = Optional.empty();
+
+    public T jaxrsFeature(JaxRSFeature feature, boolean state) {
+        if (!jaxRSFeature.isPresent()) {
+            jaxRSFeature = Optional.of(new ArrayList<>());
+        }
+        jaxRSFeature.ifPresent(list -> list.add(new JaxrsFeatureBag.JaxRSFeatureState(feature, state)));
+        return (T) this;
+    }
+
+    protected boolean hasJaxrsFeature() {
+        return jaxRSFeature.isPresent();
+    }
+
+    /* package */ void configureJaxrsFeatures(ProviderBase providerBase) {
+        jaxRSFeature.ifPresent(list -> list.stream().forEach(state -> providerBase.configure(state.feature, state.state)));
+    }
+}
diff --git a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/json/JsonMapperConfigurator.java b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/json/JsonMapperConfigurator.java
index f4bec3e..fd5d9aa 100644
--- a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/json/JsonMapperConfigurator.java
+++ b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/jackson/jaxrs/json/JsonMapperConfigurator.java
@@ -129,7 +129,7 @@
                     }
                     return _jaxbIntrospectorClass.newInstance();
                 } catch (Exception e) {
-                    throw new IllegalStateException("Failed to instantiate JaxbAnnotationIntrospector: "+e.getMessage(), e);
+                    throw new IllegalStateException("Failed to instantiate JakartaXmlBindAnnotationIntrospector: "+e.getMessage(), e);
                 }
             default:
                 throw new IllegalStateException();
diff --git a/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/JaxRSFeatureTest.java b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/JaxRSFeatureTest.java
new file mode 100644
index 0000000..86b6200
--- /dev/null
+++ b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/JaxRSFeatureTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.jackson.internal;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.jackson.JaxRSFeatureObjectMapper;
+import org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.JaxRSFeature;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.Test;
+
+import jakarta.inject.Inject;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.ClientRequestContext;
+import jakarta.ws.rs.client.ClientRequestFilter;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.ext.ContextResolver;
+import jakarta.ws.rs.ext.Providers;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+public class JaxRSFeatureTest {
+    @Test
+    public void testJaxrsFeatureOnJacksonFeature() {
+        Client client = ClientBuilder.newClient()
+                .register(new JacksonFeature().jaxrsFeature(JaxRSFeature.READ_FULL_STREAM, false))
+                .register(JaxrsFeatureFilter.class);
+
+        try (Response r = client.target("http://xxx.yyy").request().get()) {
+            MatcherAssert.assertThat(r.getStatus(), Matchers.is(200));
+        }
+    }
+
+    @Test
+    public void testJaxrsFeatureOnContextResolver() {
+        Client client = ClientBuilder.newClient()
+                .register(JacksonFeature.class)
+                .register(JaxrsFetureContextResolver.class)
+                .register(JaxrsFeatureFilter.class);
+
+        try (Response r = client.target("http://xxx.yyy").request().get()) {
+            MatcherAssert.assertThat(r.getStatus(), Matchers.is(200));
+        }
+    }
+
+
+    public static class JaxrsFeatureFilter implements ClientRequestFilter {
+        private final DefaultJacksonJaxbJsonProvider jacksonProvider;
+        @Inject
+        public JaxrsFeatureFilter(Providers allProviders) {
+            jacksonProvider = (DefaultJacksonJaxbJsonProvider)
+                    allProviders.getMessageBodyReader(Object.class, Object.class, null, MediaType.APPLICATION_JSON_TYPE);
+            try {
+                jacksonProvider.readFrom(Object.class, Object.class, null, MediaType.APPLICATION_JSON_TYPE, null,
+                        new ByteArrayInputStream("{}".getBytes()));
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        };
+
+        @Override
+        public void filter(ClientRequestContext requestContext) throws IOException {
+            Response.Status status = jacksonProvider.isEnabled(JaxRSFeature.READ_FULL_STREAM)
+                    ? Response.Status.FORBIDDEN
+                    : Response.Status.OK;
+            requestContext.abortWith(Response.status(status).build());
+        }
+    }
+
+    public static class JaxrsFetureContextResolver implements ContextResolver<ObjectMapper> {
+
+        @Override
+        public ObjectMapper getContext(Class<?> type) {
+            JaxRSFeatureObjectMapper objectMapper = new JaxRSFeatureObjectMapper();
+            objectMapper.disable(JaxRSFeature.READ_FULL_STREAM);
+            return objectMapper;
+        }
+    }
+}
diff --git a/pom.xml b/pom.xml
index 3f62b1d..f1ab454 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2106,7 +2106,7 @@
         <resources.mvn.plugin.version>3.3.1</resources.mvn.plugin.version>
         <shade.mvn.plugin.version>3.6.0</shade.mvn.plugin.version>
         <source.mvn.plugin.version>3.3.1</source.mvn.plugin.version>
-        <surefire.mvn.plugin.version>3.3.1</surefire.mvn.plugin.version>
+        <surefire.mvn.plugin.version>3.5.2</surefire.mvn.plugin.version>
         <war.mvn.plugin.version>3.4.0</war.mvn.plugin.version>
         <wiremock.mvn.plugin.version>2.11.0</wiremock.mvn.plugin.version>
         <xml.mvn.plugin.version>1.1.0</xml.mvn.plugin.version>
@@ -2135,7 +2135,7 @@
         <findbugs.glassfish.version>1.7</findbugs.glassfish.version>
         <freemarker.version>2.3.33</freemarker.version>
         <gae.version>2.0.29</gae.version>
-        <groovy.version>4.0.23</groovy.version>
+        <groovy.version>5.0.0-alpha-11</groovy.version>
         <gson.version>2.11.0</gson.version>
 
         <!--versions, extracted here due to maven-enforcer-plugin -->
@@ -2169,7 +2169,7 @@
         <jmh.version>1.37</jmh.version>
         <jmockit.version>1.49</jmockit.version>
         <junit4.version>4.13.2</junit4.version>
-        <junit5.version>5.11.0</junit5.version>
+        <junit5.version>5.11.4</junit5.version>
         <junit-platform-suite.version>1.11.0</junit-platform-suite.version>
         <junit-platform-suite.legacy.version>1.10.0</junit-platform-suite.legacy.version>
         <kryo.version>4.0.3</kryo.version>
diff --git a/test-framework/maven/container-runner-maven-plugin/pom.xml b/test-framework/maven/container-runner-maven-plugin/pom.xml
index b706320..6acb575 100644
--- a/test-framework/maven/container-runner-maven-plugin/pom.xml
+++ b/test-framework/maven/container-runner-maven-plugin/pom.xml
@@ -36,7 +36,6 @@
     </description>
 
     <properties>
-<!--        <groovy.version>3.0.21</groovy.version>-->
         <groovy-eclipse-compiler.version>3.7.0</groovy-eclipse-compiler.version>
         <groovy-eclipse-batch.version>3.0.8-01</groovy-eclipse-batch.version>
         <maven.version>3.9.2</maven.version>
diff --git a/tests/e2e-inject/non-inject/pom.xml b/tests/e2e-inject/non-inject/pom.xml
new file mode 100644
index 0000000..76a53e2
--- /dev/null
+++ b/tests/e2e-inject/non-inject/pom.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>e2e-inject</artifactId>
+        <groupId>org.glassfish.jersey.tests</groupId>
+        <version>3.1.99-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>e2e-inject-noninject</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.incubator</groupId>
+            <artifactId>jersey-injectless-client</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-client</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+
+</project>
diff --git a/tests/e2e-inject/non-inject/src/test/org/glassfish/jersey/tests/e2e/inject/noninject/DisposableSuplierTest.java b/tests/e2e-inject/non-inject/src/test/org/glassfish/jersey/tests/e2e/inject/noninject/DisposableSuplierTest.java
new file mode 100644
index 0000000..0d36f34
--- /dev/null
+++ b/tests/e2e-inject/non-inject/src/test/org/glassfish/jersey/tests/e2e/inject/noninject/DisposableSuplierTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.tests.e2e.inject.noninject;
+
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.internal.inject.DisposableSupplier;
+import org.glassfish.jersey.process.internal.RequestScoped;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.Test;
+
+import jakarta.inject.Inject;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.ClientRequestContext;
+import jakarta.ws.rs.client.ClientRequestFilter;
+import jakarta.ws.rs.core.Response;
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class DisposableSuplierTest {
+    private static final AtomicInteger disposeCounter = new AtomicInteger(0);
+    private static final String HOST = "http://somewhere.anywhere";
+
+    public interface ResponseObject {
+        Response getResponse();
+    }
+
+    private static class TestDisposableSupplier implements DisposableSupplier<ResponseObject> {
+        AtomicInteger counter = new AtomicInteger(300);
+
+        @Override
+        public void dispose(ResponseObject instance) {
+            disposeCounter.incrementAndGet();
+        }
+
+        @Override
+        public ResponseObject get() {
+            return new ResponseObject() {
+
+                @Override
+                public Response getResponse() {
+                    return Response.ok().build();
+                }
+            };
+        }
+    }
+
+    private static class DisposableSupplierInjectingFilter implements ClientRequestFilter {
+        private final ResponseObject responseSupplier;
+
+        @Inject
+        private DisposableSupplierInjectingFilter(ResponseObject responseSupplier) {
+            this.responseSupplier = responseSupplier;
+        }
+
+        @Override
+        public void filter(ClientRequestContext requestContext) throws IOException {
+            requestContext.abortWith(responseSupplier.getResponse());
+        }
+    }
+
+    @Test
+    public void testDisposeCount() {
+        disposeCounter.set(0);
+        int CNT = 4;
+        Client client = ClientBuilder.newClient()
+                .register(new AbstractBinder() {
+                    @Override
+                    protected void configure() {
+                        bindFactory(TestDisposableSupplier.class).to(ResponseObject.class)
+                                .proxy(true).proxyForSameScope(false).in(RequestScoped.class);
+                    }
+                }).register(DisposableSupplierInjectingFilter.class);
+
+        for (int i = 0; i != CNT; i++) {
+            try (Response response = client.target(HOST).request().get()) {
+                MatcherAssert.assertThat(response.getStatus(), Matchers.is(200));
+            }
+        }
+
+        MatcherAssert.assertThat(disposeCounter.get(), Matchers.is(CNT));
+    }
+}
diff --git a/tests/e2e-inject/non-inject/src/test/org/glassfish/jersey/tests/e2e/inject/noninject/InstanceListSizeTest.java b/tests/e2e-inject/non-inject/src/test/org/glassfish/jersey/tests/e2e/inject/noninject/InstanceListSizeTest.java
new file mode 100644
index 0000000..2acb538
--- /dev/null
+++ b/tests/e2e-inject/non-inject/src/test/org/glassfish/jersey/tests/e2e/inject/noninject/InstanceListSizeTest.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.tests.e2e.inject.noninject;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientRequest;
+import org.glassfish.jersey.client.innate.inject.NonInjectionManager;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.Test;
+
+import jakarta.inject.Inject;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.ClientRequestContext;
+import jakarta.ws.rs.client.ClientRequestFilter;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.MultivaluedMap;
+import jakarta.ws.rs.core.Response;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+
+public class InstanceListSizeTest {
+
+    private static final String TEST_HEADER = "TEST";
+    private static final String HOST = "https://anywhere.any";
+
+    @Test
+    public void leakTest() throws ExecutionException, InterruptedException {
+        int CNT = 100;
+        Client client = ClientBuilder.newClient();
+        client.register(InjectionManagerGrowChecker.class);
+        Response response = client.target(HOST).request().header(TEST_HEADER, "0").get();
+        int status = response.getStatus();
+        response.close();
+        //Create instance in NonInjectionManager$TypedInstances.threadPredestroyables
+
+        for (int i = 0; i <= CNT; i++) {
+            final String header = String.valueOf(i + 1);
+            try (Response r = client.target(HOST).request()
+                    .header(TEST_HEADER, header)
+                    .async()
+                    .post(Entity.text("text")).get()) {
+                int stat = r.getStatus();
+                MatcherAssert.assertThat(
+                        "NonInjectionManager#Types#disposableSupplierObjects is increasing", stat, Matchers.is(202));
+            }
+        }
+        //Create 10 instance in NonInjectionManager$TypedInstances.threadPredestroyables
+
+        for (int i = 0; i <= CNT; i++) {
+            final String header = String.valueOf(i + CNT + 2);
+            final Object text = CompletableFuture.supplyAsync(() -> {
+                Response test = client.target(HOST).request()
+                        .header("TEST", header)
+                        .post(Entity.text("text"));
+                int stat = test.getStatus();
+                test.close();
+                MatcherAssert.assertThat(
+                        "NonInjectionManager#Types#disposableSupplierObjects is increasing", stat, Matchers.is(202));
+
+                return null;
+            }).join();
+        }
+        //Create 10 instance in NonInjectionManager$TypedInstances.threadPredestroyables
+
+        response = client.target(HOST).request().header(TEST_HEADER,  2 * CNT + 3).get();
+        status = response.getStatus();
+        MatcherAssert.assertThat(status, Matchers.is(202));
+        response.close();
+    }
+
+    private static class InjectionManagerGrowChecker implements ClientRequestFilter {
+        private boolean first = true;
+        private int disposableSize = 0;
+        private int threadInstancesSize = 0;
+        private HttpHeaders headers;
+        private int headerCnt = 0;
+
+        @Inject
+        public InjectionManagerGrowChecker(HttpHeaders headers) {
+            this.headers = headers;
+        }
+
+        @Override
+        public void filter(ClientRequestContext requestContext) throws IOException {
+            Response.Status status = Response.Status.ACCEPTED;
+            if (headerCnt++ != Integer.parseInt(headers.getHeaderString("TEST"))) {
+                status = Response.Status.BAD_REQUEST;
+            }
+
+            NonInjectionManager nonInjectionManager = getInjectionManager(requestContext);
+            Object types = getDeclaredField(nonInjectionManager, "types");
+            Object instances = getDeclaredField(nonInjectionManager, "instances");
+            if (first) {
+                first = false;
+                disposableSize = getThreadInstances(types, "disposableSupplierObjects")
+                        + getThreadInstances(instances, "disposableSupplierObjects");
+                threadInstancesSize = getThreadInstances(types, "threadInstances")
+                        + getThreadInstances(instances, "threadInstances");
+            } else {
+                int newPredestroyableSize = getThreadInstances(types, "disposableSupplierObjects")
+                        + getThreadInstances(instances, "disposableSupplierObjects");
+                if (newPredestroyableSize > disposableSize + 1 /* a new service to get disposed */) {
+                    status = Response.Status.EXPECTATION_FAILED;
+                }
+                int newThreadInstances = getThreadInstances(types, "threadInstances")
+                        + getThreadInstances(instances, "threadInstances");
+                if (newThreadInstances > threadInstancesSize) {
+                    status = Response.Status.PRECONDITION_FAILED;
+                }
+            }
+
+            requestContext.abortWith(Response.status(status).build());
+        }
+    }
+
+    private static NonInjectionManager getInjectionManager(ClientRequestContext context) {
+        ClientRequest request = ((ClientRequest) context);
+        try {
+            Method clientConfigMethod = ClientRequest.class.getDeclaredMethod("getClientConfig");
+            clientConfigMethod.setAccessible(true);
+            ClientConfig clientConfig = (ClientConfig) clientConfigMethod.invoke(request);
+
+            Method runtimeMethod = ClientConfig.class.getDeclaredMethod("getRuntime");
+            runtimeMethod.setAccessible(true);
+            Object clientRuntime = runtimeMethod.invoke(clientConfig);
+            Class<?> clientRuntimeClass = clientRuntime.getClass();
+
+            Method injectionManagerMethod = clientRuntimeClass.getDeclaredMethod("getInjectionManager");
+            injectionManagerMethod.setAccessible(true);
+            InjectionManager injectionManager = (InjectionManager) injectionManagerMethod.invoke(clientRuntime);
+            return (NonInjectionManager) injectionManager;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static Object getDeclaredField(NonInjectionManager nonInjectionManager, String name) {
+        try {
+            Field typesField = NonInjectionManager.class.getDeclaredField(name);
+            typesField.setAccessible(true);
+            return typesField.get(nonInjectionManager);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static int getThreadInstances(Object typedInstances, String threadLocalName) {
+        try {
+            Field threadLocalField =
+                    typedInstances.getClass().getSuperclass().getDeclaredField(threadLocalName);
+            threadLocalField.setAccessible(true);
+            ThreadLocal<MultivaluedMap<?,?>> threadLocal =
+                    (ThreadLocal<MultivaluedMap<?, ?>>) threadLocalField.get(typedInstances);
+            MultivaluedMap<?, ?> map = threadLocal.get();
+            if (map == null) {
+                return 0;
+            } else {
+                int cnt = 0;
+                Set<? extends Map.Entry<?, ? extends List<?>>> set = map.entrySet();
+                for (Map.Entry<?, ? extends List<?>> entry : map.entrySet()) {
+                    cnt += entry.getValue().size();
+                }
+                return cnt;
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+
+}
diff --git a/tests/e2e-inject/non-inject/src/test/org/glassfish/jersey/tests/e2e/inject/noninject/PreDestroyTest.java b/tests/e2e-inject/non-inject/src/test/org/glassfish/jersey/tests/e2e/inject/noninject/PreDestroyTest.java
new file mode 100644
index 0000000..0f43b44
--- /dev/null
+++ b/tests/e2e-inject/non-inject/src/test/org/glassfish/jersey/tests/e2e/inject/noninject/PreDestroyTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.tests.e2e.inject.noninject;
+
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.internal.inject.DisposableSupplier;
+import org.glassfish.jersey.process.internal.RequestScoped;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.Test;
+
+import jakarta.annotation.PreDestroy;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.ClientRequestContext;
+import jakarta.ws.rs.client.ClientRequestFilter;
+import jakarta.ws.rs.core.Response;
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class PreDestroyTest {
+    private static final AtomicInteger disposeCounter = new AtomicInteger(0);
+    private static final String HOST = "http://somewhere.anywhere";
+
+    public interface ResponseObject {
+        Response getResponse();
+    }
+
+    public static class ResponseObjectImpl implements ResponseObject {
+
+        public ResponseObjectImpl() {
+
+        }
+
+        @PreDestroy
+        public void preDestroy() {
+            disposeCounter.incrementAndGet();
+        }
+
+        @Override
+        public Response getResponse() {
+            return Response.ok().build();
+        }
+    }
+
+    private static class PreDestroyInjectingFilter implements ClientRequestFilter {
+        private final ResponseObject responseSupplier;
+
+        @Inject
+        private PreDestroyInjectingFilter(ResponseObject responseSupplier) {
+            this.responseSupplier = responseSupplier;
+        }
+
+        @Override
+        public void filter(ClientRequestContext requestContext) throws IOException {
+            requestContext.abortWith(responseSupplier.getResponse());
+        }
+    }
+
+    @Test
+    public void testPreDestroyCount() {
+        disposeCounter.set(0);
+        int CNT = 4;
+        Client client = ClientBuilder.newClient()
+                .register(new AbstractBinder() {
+                    @Override
+                    protected void configure() {
+                        bind(ResponseObjectImpl.class).to(ResponseObject.class)
+                                .proxy(true).proxyForSameScope(false).in(RequestScoped.class);
+                    }
+                }).register(PreDestroyInjectingFilter.class);
+
+        for (int i = 0; i != CNT; i++) {
+            try (Response response = client.target(HOST).request().get()) {
+                MatcherAssert.assertThat(response.getStatus(), Matchers.is(200));
+            }
+        }
+
+        MatcherAssert.assertThat(disposeCounter.get(), Matchers.is(1));
+    }
+}
diff --git a/tests/e2e-inject/pom.xml b/tests/e2e-inject/pom.xml
index f7744f5..2c9d093 100644
--- a/tests/e2e-inject/pom.xml
+++ b/tests/e2e-inject/pom.xml
@@ -36,5 +36,6 @@
         <module>cdi2-se</module>
         <module>cdi-inject-weld</module>
         <module>hk2</module>
+        <module>non-inject</module>
     </modules>
 </project>
diff --git a/tests/e2e-tls/pom.xml b/tests/e2e-tls/pom.xml
index 188f4f0..2ac849e 100644
--- a/tests/e2e-tls/pom.xml
+++ b/tests/e2e-tls/pom.xml
@@ -170,6 +170,24 @@
                 </http.patch.addopens>
             </properties>
         </profile>
+        <profile>
+            <id>JDK_17-</id>
+            <activation>
+                <jdk>[11,17)</jdk>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <!-- The certificate is not working with JDK 11 -->
+                            <excludes><exclude>**/ConcurrentHttpsUrlConnectionTest*</exclude></excludes>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
     </profiles>
 
 </project>
diff --git a/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/connector/ConcurrentHttpsUrlConnectionTest.java b/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/connector/ConcurrentHttpsUrlConnectionTest.java
new file mode 100644
index 0000000..444cee6
--- /dev/null
+++ b/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/connector/ConcurrentHttpsUrlConnectionTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.tests.e2e.tls.connector;
+
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.core.GenericType;
+import jakarta.ws.rs.core.MediaType;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.security.KeyStore;
+import java.util.Random;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManagerFactory;
+
+/**
+ * Jersey client seems to be not thread-safe:
+ * When the first GET request is in progress,
+ * all parallel requests from other Jersey client instances fail
+ * with SSLHandshakeException: PKIX path building failed.
+ * <p>
+ * Once the first GET request is completed,
+ * all subsequent requests work without error.
+ * <p>
+ * BUG 5749
+ */
+public class ConcurrentHttpsUrlConnectionTest {
+    private static int THREAD_NUMBER = 5;
+
+    private static volatile int responseCounter = 0;
+
+    private static SSLContext createContext() throws Exception {
+        URL url = ConcurrentHttpsUrlConnectionTest.class.getResource("keystore.jks");
+        KeyStore keyStore = KeyStore.getInstance("JKS");
+        try (InputStream is = url.openStream()) {
+            keyStore.load(is, "password".toCharArray());
+        }
+        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+        kmf.init(keyStore, "password".toCharArray());
+        TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
+        tmf.init(keyStore);
+        SSLContext context = SSLContext.getInstance("TLS");
+        context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+        return context;
+    }
+
+    @Test
+    public void testSSLConnections() throws Exception {
+        if (THREAD_NUMBER == 1) {
+            System.out.println("\nThis is the working case (THREAD_NUMBER==1). Set THREAD_NUMBER > 1 to reproduce the error! \n");
+        }
+
+        final HttpsServer server = new HttpsServer(createContext());
+        Executors.newFixedThreadPool(1).submit(server);
+
+        // set THREAD_NUMBER > 1 to reproduce an issue
+        ExecutorService executorService2clients = Executors.newFixedThreadPool(THREAD_NUMBER);
+
+        final ClientBuilder builder = ClientBuilder.newBuilder().sslContext(createContext())
+                .hostnameVerifier(new HostnameVerifier() {
+                    public boolean verify(String arg0, SSLSession arg1) {
+                        return true;
+                    }
+                });
+
+        AtomicInteger counter = new AtomicInteger(0);
+
+        for (int i = 0; i < THREAD_NUMBER; i++) {
+            executorService2clients.submit(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        Client client = builder.build();
+                        String ret = client.target("https://127.0.0.1:" + server.getPort() + "/" + new Random().nextInt())
+                                .request(MediaType.TEXT_HTML)
+                                .get(new GenericType<String>() {
+                                });
+                        System.out.print(++responseCounter + ". Server returned: " + ret);
+                    } catch (Exception e) {
+                        //get an exception here, if jersey lib is buggy and THREAD_NUMBER > 1:
+                        //jakarta.ws.rs.ProcessingException: jakarta.net.ssl.SSLHandshakeException: PKIX path building failed:
+                        e.printStackTrace();
+                    } finally {
+                        System.out.println(counter.incrementAndGet());
+                    }
+                }
+            });
+        }
+
+        while (counter.get() != THREAD_NUMBER) {
+            Thread.sleep(100L);
+        }
+        server.stop();
+    }
+}
diff --git a/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/connector/HttpsServer.java b/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/connector/HttpsServer.java
new file mode 100644
index 0000000..31214f1
--- /dev/null
+++ b/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/connector/HttpsServer.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.tests.e2e.tls.connector;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLSocket;
+
+class HttpsServer implements Runnable {
+    private final SSLServerSocket sslServerSocket;
+    private boolean closed = false;
+
+    public HttpsServer(SSLContext context) throws Exception {
+        sslServerSocket = (SSLServerSocket) context.getServerSocketFactory().createServerSocket(0);
+    }
+
+    public int getPort() {
+        return sslServerSocket.getLocalPort();
+    }
+
+    @Override
+    public void run() {
+        System.out.printf("Server started on port %d%n", getPort());
+        while (!closed) {
+            SSLSocket s;
+            try {
+                s = (SSLSocket) sslServerSocket.accept();
+            } catch (IOException e2) {
+                s = null;
+            }
+            final SSLSocket socket = s;
+            new Thread(new Runnable() {
+                public void run() {
+                    try {
+                        if (socket != null) {
+                            InputStream is = new BufferedInputStream(socket.getInputStream());
+                            byte[] data = new byte[2048];
+                            int len = is.read(data);
+                            if (len <= 0) {
+                                throw new IOException("no data received");
+                            }
+                            //System.out.printf("Server received: %s\n", new String(data, 0, len));
+                            PrintWriter writer = new PrintWriter(socket.getOutputStream());
+                            writer.println("HTTP/1.1 200 OK");
+                            writer.println("Content-Type: text/html");
+                            writer.println();
+                            writer.println("Hello from server!");
+                            writer.flush();
+                            writer.close();
+                            socket.close();
+                        }
+                    } catch (Exception e1) {
+                        e1.printStackTrace();
+                    }
+                }
+            }).start();
+        }
+    }
+
+    void stop() {
+        try {
+            closed = true;
+            sslServerSocket.close();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/tests/e2e-tls/src/test/resources/org/glassfish/jersey/tests/e2e/tls/connector/keystore.jks b/tests/e2e-tls/src/test/resources/org/glassfish/jersey/tests/e2e/tls/connector/keystore.jks
new file mode 100644
index 0000000..130aa71
--- /dev/null
+++ b/tests/e2e-tls/src/test/resources/org/glassfish/jersey/tests/e2e/tls/connector/keystore.jks
Binary files differ
diff --git a/tests/integration/microprofile/rest-client/pom.xml b/tests/integration/microprofile/rest-client/pom.xml
index 09204f9..2b98db3 100644
--- a/tests/integration/microprofile/rest-client/pom.xml
+++ b/tests/integration/microprofile/rest-client/pom.xml
@@ -107,6 +107,15 @@
                 </dependency>
             </dependencies>
         </profile>
+        <profile>
+            <id>securityOff</id>
+            <activation>
+                <jdk>[24,)</jdk>
+            </activation>
+            <properties>
+                <surefire.security.argline />
+            </properties>
+        </profile>
     </profiles>
 
     <properties>
diff --git a/tests/pom.xml b/tests/pom.xml
index 8611c67..24f7a82 100644
--- a/tests/pom.xml
+++ b/tests/pom.xml
@@ -47,7 +47,6 @@
         <module>e2e-testng</module>
         <module>e2e-tls</module>
         <module>integration</module>
-        <module>jmockit</module>
         <module>mem-leaks</module>
         <module>osgi</module>
         <module>stress</module>
@@ -133,5 +132,14 @@
                 <module>version-agnostic</module>
             </modules>
         </profile>
+        <profile>
+            <id>JMOCKIT-MODULE-FOR-PRE-JDK24</id>
+            <activation>
+                <jdk>[11,24)</jdk>
+            </activation>
+            <modules>
+                <module>jmockit</module>
+            </modules>
+        </profile>
     </profiles>
 </project>