diff --git a/core-client/src/main/java/org/glassfish/jersey/client/ClientConfig.java b/core-client/src/main/java/org/glassfish/jersey/client/ClientConfig.java index 53cc138518b..54cf97f9bd0 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/ClientConfig.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/ClientConfig.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2026 Contributors to the Eclipse Foundation * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018 Payara Foundation and/or its affiliates. * @@ -425,12 +426,21 @@ private ClientRuntime initRuntime() { injectionManager.completeRegistration(); - bootstrapConfigurators.forEach(configurator -> configurator.postInit(injectionManager, preInit.bootstrapBag)); + bootstrapConfigurators.forEach(configurator -> { + if (!configurator.equals(preInit.clientComponentConfigurator)) { + configurator.postInit(injectionManager, preInit.bootstrapBag); + } + }); + final ClientConfig configuration = new ClientConfig(runtimeCfgState); final Connector connector = connectorProvider.getConnector(client, configuration); final ClientRuntime crt = new ClientRuntime(configuration, connector, injectionManager, preInit.bootstrapBag); + // We call postInit here to clean up thread locals, + // while other configurators need to be postInit earlier because they set up dependencies for ClientRuntime + preInit.clientComponentConfigurator.postInit(injectionManager, preInit.bootstrapBag); + client.registerShutdownHook(crt); preInit.messageBodyWorkersConfigurator.setClientRuntime(crt); @@ -479,6 +489,7 @@ public int hashCode() { private final ClientBootstrapBag bootstrapBag; private final List bootstrapConfigurators; private final ClientMessageBodyFactory.MessageBodyWorkersConfigurator messageBodyWorkersConfigurator; + private final ClientComponentConfigurator clientComponentConfigurator; /* package */ PreInitialization(InjectionManager injectionManager) { this(new State(new JerseyClient()), injectionManager, null, null); @@ -496,9 +507,9 @@ public int hashCode() { bootstrapBag.setManagedObjectsFinalizer(new ManagedObjectsFinalizer(injectionManager)); messageBodyWorkersConfigurator = new ClientMessageBodyFactory.MessageBodyWorkersConfigurator(); // 2020 + clientComponentConfigurator = new ClientComponentConfigurator(); - bootstrapConfigurators = Arrays.asList( - new RequestScope.RequestScopeConfigurator(), + bootstrapConfigurators = Arrays.asList(new RequestScope.RequestScopeConfigurator(), new ParamConverterConfigurator(), // 2010 new ParameterUpdaterConfigurator(), // 2011 new RuntimeConfigConfigurator(runtimeCfgState), // 2012 @@ -507,7 +518,7 @@ public int hashCode() { new ExceptionMapperFactory.ExceptionMappersConfigurator(), // 2015 new JaxrsProviders.ProvidersConfigurator(), // 2016 new AutoDiscoverableConfigurator(RuntimeType.CLIENT), - new ClientComponentConfigurator(), + clientComponentConfigurator, new FeatureConfigurator(RuntimeType.CLIENT)); bootstrapConfigurators.forEach(configurator -> configurator.init(injectionManager, bootstrapBag)); diff --git a/ext/cdi/jersey-cdi1x/src/main/java/org/glassfish/jersey/ext/cdi1x/internal/CdiComponentProvider.java b/ext/cdi/jersey-cdi1x/src/main/java/org/glassfish/jersey/ext/cdi1x/internal/CdiComponentProvider.java index 4e3627bc816..95d85e6c555 100644 --- a/ext/cdi/jersey-cdi1x/src/main/java/org/glassfish/jersey/ext/cdi1x/internal/CdiComponentProvider.java +++ b/ext/cdi/jersey-cdi1x/src/main/java/org/glassfish/jersey/ext/cdi1x/internal/CdiComponentProvider.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2026 Contributors to the Eclipse Foundation * Copyright (c) 2013, 2025 Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2022 Payara Foundation and/or its affiliates. All rights reserved. * @@ -145,6 +146,10 @@ public Boolean apply(final Class clazz) { private boolean initialized = false; + // Shared by multiple clients, which can be initialized in parallel, + // therefore storing to a thread local and removing after initialization done in the done() method + private ThreadLocal effectiveInjectionManager = new ThreadLocal<>(); + public CdiComponentProvider() { customHk2TypesProvider = CdiUtil.lookupService(Hk2CustomBoundTypesProvider.class); injectionManagerStore = CdiUtil.createHk2InjectionManagerStore(); @@ -259,6 +264,14 @@ private boolean bind(final Class clazz, final Set> providerContracts @Override public void done() { + + if (beanManager != null) { + final CdiComponentProvider extension = beanManager.getExtension(CdiComponentProvider.class); + if (extension != null) { + extension.effectiveInjectionManager.remove(); + } + } + if (requestScopedComponents.size() > 0) { InstanceBinding descriptor = Bindings .service((ForeignRequestScopeBridge) () -> requestScopedComponents) @@ -658,7 +671,6 @@ private StringBuilder listElements(final StringBuilder logMsgBuilder, final Coll /* package */ abstract class InjectionManagerInjectedCdiTarget implements InjectionManagerInjectedTarget { private final InjectionTarget delegate; - private volatile InjectionManager effectiveInjectionManager; public InjectionManagerInjectedCdiTarget(InjectionTarget delegate) { this.delegate = delegate; @@ -671,7 +683,7 @@ public InjectionManagerInjectedCdiTarget(InjectionTarget delegate) { public void inject(final Object t, final CreationalContext cc) { InjectionManager injectingManager = getEffectiveInjectionManager(); if (injectingManager == null || /* reload */ injectingManager.isShutdown()) { - injectingManager = effectiveInjectionManager; + injectingManager = effectiveInjectionManager.get(); threadInjectionManagers.set(injectingManager); } @@ -706,7 +718,16 @@ public void dispose(final Object t) { @Override public void setInjectionManager(final InjectionManager injectionManager) { - this.effectiveInjectionManager = injectionManager; + // The injectionManager is always the same within a single invocation. + // We set it only once, which is a bit faster than calling set with the same value + final InjectionManager manager = effectiveInjectionManager.get(); + if (manager == null) { + effectiveInjectionManager.set(injectionManager); + } else if (!manager.equals(injectionManager)) { + LOGGER.log(Level.SEVERE, "Leaking injection manager found in the current thread." + + " Very likely because it wasn't removed in a previous invocation and" + + " leaked into this invocation."); + } } }