Merge pull request #6111 from dmatej/classanalyzer-race-backport-31
Classanalyzer race condition fix backport for 3.1
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 b072c1d..67ff45e 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,5 +1,6 @@
/*
- * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
+ * 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.
*
* This program and the accompanying materials are made available under the
@@ -417,67 +418,34 @@
runtimeCfgState.markAsShared();
final InjectionManager injectionManager = findInjectionManager();
- injectionManager.register(new ClientBinder(runtimeCfgState.getProperties()));
-
- final ClientBootstrapBag bootstrapBag = new ClientBootstrapBag();
- bootstrapBag.setManagedObjectsFinalizer(new ManagedObjectsFinalizer(injectionManager));
-
- final ClientMessageBodyFactory.MessageBodyWorkersConfigurator messageBodyWorkersConfigurator =
- new ClientMessageBodyFactory.MessageBodyWorkersConfigurator();
-
- List<BootstrapConfigurator> bootstrapConfigurators = Arrays.asList(
- new RequestScope.RequestScopeConfigurator(),
- new ParamConverterConfigurator(),
- new ParameterUpdaterConfigurator(),
- new RuntimeConfigConfigurator(runtimeCfgState),
- new ContextResolverFactory.ContextResolversConfigurator(),
- messageBodyWorkersConfigurator,
- new ExceptionMapperFactory.ExceptionMappersConfigurator(),
- new JaxrsProviders.ProvidersConfigurator(),
- new AutoDiscoverableConfigurator(RuntimeType.CLIENT),
- new ClientComponentConfigurator(),
- new FeatureConfigurator(RuntimeType.CLIENT));
- bootstrapConfigurators.forEach(configurator -> configurator.init(injectionManager, bootstrapBag));
-
- // AutoDiscoverable.
- if (!CommonProperties.getValue(runtimeCfgState.getProperties(), RuntimeType.CLIENT,
- CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE, Boolean.FALSE, Boolean.class)) {
- runtimeCfgState.configureAutoDiscoverableProviders(injectionManager, bootstrapBag.getAutoDiscoverables());
- } else {
- runtimeCfgState.configureForcedAutoDiscoverableProviders(injectionManager);
- }
-
- // Configure binders and features.
- runtimeCfgState.configureMetaProviders(injectionManager, bootstrapBag.getManagedObjectsFinalizer());
-
- // Bind providers.
- final Collection<ComponentProvider> componentProviders = bootstrapBag.getComponentProviders().get();
- ProviderBinder.bindProviders(
- runtimeCfgState.getComponentBag(), RuntimeType.CLIENT, null, injectionManager, componentProviders
- );
-
- ClientExecutorProvidersConfigurator executorProvidersConfigurator =
- new ClientExecutorProvidersConfigurator(runtimeCfgState.getComponentBag(),
- runtimeCfgState.client,
- this.executorService,
- this.scheduledExecutorService);
- executorProvidersConfigurator.init(injectionManager, bootstrapBag);
+ final PreInitialization preInit =
+ new PreInitialization(runtimeCfgState, injectionManager, this.executorService, this.scheduledExecutorService);
+ List<BootstrapConfigurator> bootstrapConfigurators = preInit.bootstrapConfigurators;
injectionManager.completeRegistration();
- bootstrapConfigurators.forEach(configurator -> configurator.postInit(injectionManager, 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, bootstrapBag);
+ 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);
- messageBodyWorkersConfigurator.setClientRuntime(crt);
+ preInit.messageBodyWorkersConfigurator.setClientRuntime(crt);
return crt;
}
- private final InjectionManager findInjectionManager() {
+ private InjectionManager findInjectionManager() {
try {
return Injections.createInjectionManager(RuntimeType.CLIENT);
} catch (IllegalStateException ise) {
@@ -515,6 +483,69 @@
}
}
+ /* package */ static class PreInitialization {
+ private final ClientBootstrapBag bootstrapBag;
+ private final List<BootstrapConfigurator> bootstrapConfigurators;
+ private final ClientMessageBodyFactory.MessageBodyWorkersConfigurator messageBodyWorkersConfigurator;
+ private final ClientComponentConfigurator clientComponentConfigurator;
+
+ /* package */ PreInitialization(InjectionManager injectionManager) {
+ this(new State(new JerseyClient()), injectionManager, null, null);
+ }
+
+
+ /* package */ PreInitialization(
+ State runtimeCfgState,
+ InjectionManager injectionManager,
+ ExecutorService executorService,
+ ScheduledExecutorService scheduledExecutorService) {
+ injectionManager.register(new ClientBinder(runtimeCfgState.getProperties()));
+
+ bootstrapBag = new ClientBootstrapBag();
+ bootstrapBag.setManagedObjectsFinalizer(new ManagedObjectsFinalizer(injectionManager));
+
+ messageBodyWorkersConfigurator = new ClientMessageBodyFactory.MessageBodyWorkersConfigurator(); // 2020
+ clientComponentConfigurator = new ClientComponentConfigurator();
+
+ bootstrapConfigurators = Arrays.asList(new RequestScope.RequestScopeConfigurator(),
+ new ParamConverterConfigurator(), // 2010
+ new ParameterUpdaterConfigurator(), // 2011
+ new RuntimeConfigConfigurator(runtimeCfgState), // 2012
+ new ContextResolverFactory.ContextResolversConfigurator(), // 2014
+ messageBodyWorkersConfigurator,
+ new ExceptionMapperFactory.ExceptionMappersConfigurator(), // 2015
+ new JaxrsProviders.ProvidersConfigurator(), // 2016
+ new AutoDiscoverableConfigurator(RuntimeType.CLIENT),
+ clientComponentConfigurator,
+ new FeatureConfigurator(RuntimeType.CLIENT));
+ bootstrapConfigurators.forEach(configurator -> configurator.init(injectionManager, bootstrapBag));
+
+ // AutoDiscoverable.
+ if (!CommonProperties.getValue(runtimeCfgState.getProperties(), RuntimeType.CLIENT,
+ CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE, Boolean.FALSE, Boolean.class)) {
+ runtimeCfgState.configureAutoDiscoverableProviders(injectionManager, bootstrapBag.getAutoDiscoverables());
+ } else {
+ runtimeCfgState.configureForcedAutoDiscoverableProviders(injectionManager);
+ }
+
+ // Configure binders and features.
+ runtimeCfgState.configureMetaProviders(injectionManager, bootstrapBag.getManagedObjectsFinalizer());
+
+ // Bind providers.
+ final Collection<ComponentProvider> componentProviders = bootstrapBag.getComponentProviders().get();
+ ProviderBinder.bindProviders(
+ runtimeCfgState.getComponentBag(), RuntimeType.CLIENT, null, injectionManager, componentProviders
+ );
+
+ ClientExecutorProvidersConfigurator executorProvidersConfigurator =
+ new ClientExecutorProvidersConfigurator(runtimeCfgState.getComponentBag(),
+ runtimeCfgState.client,
+ executorService,
+ scheduledExecutorService);
+ executorProvidersConfigurator.init(injectionManager, bootstrapBag);
+ }
+ }
+
/**
* Construct a new Jersey configuration instance with the default features
* and property values.
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 45fc11b..642a83a 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.
*
@@ -144,6 +145,10 @@
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<InjectionManager> effectiveInjectionManager = new ThreadLocal<>();
+
public CdiComponentProvider() {
customHk2TypesProvider = CdiUtil.lookupService(Hk2CustomBoundTypesProvider.class);
injectionManagerStore = CdiUtil.createHk2InjectionManagerStore();
@@ -253,6 +258,14 @@
@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<ForeignRequestScopeBridge> descriptor = Bindings
.service((ForeignRequestScopeBridge) () -> requestScopedComponents)
@@ -656,7 +669,6 @@
/* package */ abstract class InjectionManagerInjectedCdiTarget implements InjectionManagerInjectedTarget {
private final InjectionTarget delegate;
- private volatile InjectionManager effectiveInjectionManager;
public InjectionManagerInjectedCdiTarget(InjectionTarget delegate) {
this.delegate = delegate;
@@ -669,17 +681,20 @@
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);
}
- delegate.inject(t, cc); // here the injection manager is used in HK2Bean
+ try {
+ delegate.inject(t, cc); // here the injection manager is used in HK2Bean
- if (injectingManager != null) {
- injectingManager.inject(t, CdiComponentProvider.CDI_CLASS_ANALYZER);
+ if (injectingManager != null) {
+ injectingManager.inject(t, CdiComponentProvider.CDI_CLASS_ANALYZER);
+ }
+ } finally {
+ threadInjectionManagers.remove();
+ effectiveInjectionManager.remove();
}
-
- threadInjectionManagers.remove();
}
@Override
@@ -704,7 +719,16 @@
@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.");
+ }
}
}