fix: refines the provider caching logic (#34220)

closes: #34219

Signed-off-by: Steve Hawkins <shawkins@redhat.com>
This commit is contained in:
Steven Hawkins 2024-10-23 15:00:00 -04:00 committed by GitHub
parent e520c71a1a
commit 964f6b9aac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -60,9 +60,11 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -71,7 +73,7 @@ import java.util.stream.Collectors;
public abstract class DefaultKeycloakSession implements KeycloakSession { public abstract class DefaultKeycloakSession implements KeycloakSession {
private final DefaultKeycloakSessionFactory factory; private final DefaultKeycloakSessionFactory factory;
private final Map<Integer, Provider> providers = new HashMap<>(); private final Map<List<String>, Provider> providers = new HashMap<>();
private final List<Provider> closable = new LinkedList<>(); private final List<Provider> closable = new LinkedList<>();
private final DefaultKeycloakTransactionManager transactionManager; private final DefaultKeycloakTransactionManager transactionManager;
private final Map<String, Object> attributes = new HashMap<>(); private final Map<String, Object> attributes = new HashMap<>();
@ -167,16 +169,20 @@ public abstract class DefaultKeycloakSession implements KeycloakSession {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public <T extends Provider> T getProvider(Class<T> clazz) { public <T extends Provider> T getProvider(Class<T> clazz) {
Integer hash = clazz.hashCode(); List<String> key = List.of(clazz.getName());
T provider = (T) providers.get(hash); return getOrCreateProvider(key, () -> factory.getProviderFactory(clazz));
}
private <T extends Provider> T getOrCreateProvider(List<String> key, Supplier<ProviderFactory<T>> supplier) {
T provider = (T) providers.get(key);
// KEYCLOAK-11890 - Avoid using HashMap.computeIfAbsent() to implement logic in outer if() block below, // KEYCLOAK-11890 - Avoid using HashMap.computeIfAbsent() to implement logic in outer if() block below,
// since per JDK-8071667 the remapping function should not modify the map during computation. While // since per JDK-8071667 the remapping function should not modify the map during computation. While
// allowed on JDK 1.8, attempt of such a modification throws ConcurrentModificationException with JDK 9+ // allowed on JDK 1.8, attempt of such a modification throws ConcurrentModificationException with JDK 9+
if (provider == null) { if (provider == null) {
ProviderFactory<T> providerFactory = factory.getProviderFactory(clazz); ProviderFactory<T> providerFactory = supplier.get();
if (providerFactory != null) { if (providerFactory != null) {
provider = providerFactory.create(DefaultKeycloakSession.this); provider = providerFactory.create(DefaultKeycloakSession.this);
providers.put(hash, provider); providers.put(key, provider);
} }
} }
return provider; return provider;
@ -185,19 +191,8 @@ public abstract class DefaultKeycloakSession implements KeycloakSession {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public <T extends Provider> T getProvider(Class<T> clazz, String id) { public <T extends Provider> T getProvider(Class<T> clazz, String id) {
Integer hash = clazz.hashCode() + id.hashCode(); List<String> key = List.of(clazz.getName(), id);
T provider = (T) providers.get(hash); return getOrCreateProvider(key, () -> factory.getProviderFactory(clazz, id));
// KEYCLOAK-11890 - Avoid using HashMap.computeIfAbsent() to implement logic in outer if() block below,
// since per JDK-8071667 the remapping function should not modify the map during computation. While
// allowed on JDK 1.8, attempt of such a modification throws ConcurrentModificationException with JDK 9+
if (provider == null) {
ProviderFactory<T> providerFactory = factory.getProviderFactory(clazz, id);
if (providerFactory != null) {
provider = providerFactory.create(DefaultKeycloakSession.this);
providers.put(hash, provider);
}
}
return provider;
} }
@Override @Override
@ -214,22 +209,9 @@ public abstract class DefaultKeycloakSession implements KeycloakSession {
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T extends Provider> T getComponentProvider(Class<T> clazz, String componentId, Function<KeycloakSessionFactory, ComponentModel> modelGetter) { public <T extends Provider> T getComponentProvider(Class<T> clazz, String componentId, Function<KeycloakSessionFactory, ComponentModel> modelGetter) {
Integer hash = clazz.hashCode() + componentId.hashCode(); List<String> key = List.of("component", clazz.getName(), componentId);
T provider = (T) providers.get(hash);
final RealmModel realm = getContext().getRealm(); final RealmModel realm = getContext().getRealm();
return getOrCreateProvider(key, () -> factory.getProviderFactory(clazz, Optional.ofNullable(realm.getId()).orElse(null), componentId, modelGetter));
// KEYCLOAK-11890 - Avoid using HashMap.computeIfAbsent() to implement logic in outer if() block below,
// since per JDK-8071667 the remapping function should not modify the map during computation. While
// allowed on JDK 1.8, attempt of such a modification throws ConcurrentModificationException with JDK 9+
if (provider == null) {
final String realmId = realm == null ? null : realm.getId();
ProviderFactory<T> providerFactory = factory.getProviderFactory(clazz, realmId, componentId, modelGetter);
if (providerFactory != null) {
provider = providerFactory.create(this);
providers.put(hash, provider);
}
}
return provider;
} }
@Override @Override