diff --git a/model/infinispan/src/main/java/org/keycloak/cluster/infinispan/InfinispanClusterProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/cluster/infinispan/InfinispanClusterProviderFactory.java index 73ab3fe4c3..c5417c2cbc 100644 --- a/model/infinispan/src/main/java/org/keycloak/cluster/infinispan/InfinispanClusterProviderFactory.java +++ b/model/infinispan/src/main/java/org/keycloak/cluster/infinispan/InfinispanClusterProviderFactory.java @@ -19,6 +19,7 @@ package org.keycloak.cluster.infinispan; import org.infinispan.Cache; import org.infinispan.client.hotrod.exceptions.HotRodClientException; +import org.infinispan.lifecycle.ComponentStatus; import org.infinispan.manager.EmbeddedCacheManager; import org.infinispan.notifications.Listener; import org.infinispan.notifications.cachemanagerlistener.annotation.ViewChanged; @@ -230,7 +231,11 @@ public class InfinispanClusterProviderFactory implements ClusterProviderFactory, Tracked in https://github.com/keycloak/keycloak/issues/9871 */ synchronized (DefaultInfinispanConnectionProviderFactory.class) { - workCache.entrySet().removeIf(new LockEntryPredicate(removedNodesAddresses)); + if (workCache.getStatus() == ComponentStatus.RUNNING) { + workCache.entrySet().removeIf(new LockEntryPredicate(removedNodesAddresses)); + } else { + logger.warn("work cache is not running, ignoring event"); + } } } } catch (Throwable t) { diff --git a/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java index b9f543aff0..35c78f0acd 100755 --- a/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java +++ b/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java @@ -66,17 +66,17 @@ import static org.keycloak.models.cache.infinispan.InfinispanCacheRealmProviderF */ public class DefaultInfinispanConnectionProviderFactory implements InfinispanConnectionProviderFactory, EnvironmentDependentProviderFactory { - protected static final Logger logger = Logger.getLogger(DefaultInfinispanConnectionProviderFactory.class); + private static final Logger logger = Logger.getLogger(DefaultInfinispanConnectionProviderFactory.class); - protected Config.Scope config; + private Config.Scope config; - protected EmbeddedCacheManager cacheManager; + private volatile EmbeddedCacheManager cacheManager; - protected RemoteCacheProvider remoteCacheProvider; + private volatile RemoteCacheProvider remoteCacheProvider; - protected boolean containerManaged; + protected volatile boolean containerManaged; - private TopologyInfo topologyInfo; + private volatile TopologyInfo topologyInfo; @Override public InfinispanConnectionProvider create(KeycloakSession session) { @@ -102,7 +102,6 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon if (remoteCacheProvider != null) { remoteCacheProvider.stop(); } - cacheManager = null; } } @@ -142,62 +141,67 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon managedCacheManager = provider.getCacheManager(config); } - + + // store it in a locale variable first, so it is not visible to the outside, yet + EmbeddedCacheManager localCacheManager; if (managedCacheManager == null) { if (!config.getBoolean("embedded", false)) { throw new RuntimeException("No " + ManagedCacheManagerProvider.class.getName() + " found. If running in embedded mode set the [embedded] property to this provider."); } - initEmbedded(); + localCacheManager = initEmbedded(); } else { - initContainerManaged(managedCacheManager); + localCacheManager = initContainerManaged(managedCacheManager); } logger.infof(topologyInfo.toString()); - remoteCacheProvider = new RemoteCacheProvider(config, cacheManager); + remoteCacheProvider = new RemoteCacheProvider(config, localCacheManager); + // only set the cache manager attribute at the very end to avoid passing a half-initialized entry callers + cacheManager = localCacheManager; } } } } - protected void initContainerManaged(EmbeddedCacheManager cacheManager) { - this.cacheManager = cacheManager; + protected EmbeddedCacheManager initContainerManaged(EmbeddedCacheManager cacheManager) { containerManaged = true; - long realmRevisionsMaxEntries = this.cacheManager.getCache(InfinispanConnectionProvider.REALM_CACHE_NAME).getCacheConfiguration().memory().size(); + long realmRevisionsMaxEntries = cacheManager.getCache(InfinispanConnectionProvider.REALM_CACHE_NAME).getCacheConfiguration().memory().size(); realmRevisionsMaxEntries = realmRevisionsMaxEntries > 0 ? 2 * realmRevisionsMaxEntries : InfinispanConnectionProvider.REALM_REVISIONS_CACHE_DEFAULT_MAX; - this.cacheManager.defineConfiguration(InfinispanConnectionProvider.REALM_REVISIONS_CACHE_NAME, getRevisionCacheConfig(realmRevisionsMaxEntries)); - this.cacheManager.getCache(InfinispanConnectionProvider.REALM_REVISIONS_CACHE_NAME, true); + cacheManager.defineConfiguration(InfinispanConnectionProvider.REALM_REVISIONS_CACHE_NAME, getRevisionCacheConfig(realmRevisionsMaxEntries)); + cacheManager.getCache(InfinispanConnectionProvider.REALM_REVISIONS_CACHE_NAME, true); - long userRevisionsMaxEntries = this.cacheManager.getCache(InfinispanConnectionProvider.USER_CACHE_NAME).getCacheConfiguration().memory().size(); + long userRevisionsMaxEntries = cacheManager.getCache(InfinispanConnectionProvider.USER_CACHE_NAME).getCacheConfiguration().memory().size(); userRevisionsMaxEntries = userRevisionsMaxEntries > 0 ? 2 * userRevisionsMaxEntries : InfinispanConnectionProvider.USER_REVISIONS_CACHE_DEFAULT_MAX; - this.cacheManager.defineConfiguration(InfinispanConnectionProvider.USER_REVISIONS_CACHE_NAME, getRevisionCacheConfig(userRevisionsMaxEntries)); - this.cacheManager.getCache(InfinispanConnectionProvider.USER_REVISIONS_CACHE_NAME, true); - this.cacheManager.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME, true); - this.cacheManager.getCache(InfinispanConnectionProvider.AUTHENTICATION_SESSIONS_CACHE_NAME, true); - this.cacheManager.getCache(InfinispanConnectionProvider.KEYS_CACHE_NAME, true); - this.cacheManager.getCache(InfinispanConnectionProvider.ACTION_TOKEN_CACHE, true); + cacheManager.defineConfiguration(InfinispanConnectionProvider.USER_REVISIONS_CACHE_NAME, getRevisionCacheConfig(userRevisionsMaxEntries)); + cacheManager.getCache(InfinispanConnectionProvider.USER_REVISIONS_CACHE_NAME, true); + cacheManager.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME, true); + cacheManager.getCache(InfinispanConnectionProvider.AUTHENTICATION_SESSIONS_CACHE_NAME, true); + cacheManager.getCache(InfinispanConnectionProvider.KEYS_CACHE_NAME, true); + cacheManager.getCache(InfinispanConnectionProvider.ACTION_TOKEN_CACHE, true); - long authzRevisionsMaxEntries = this.cacheManager.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME).getCacheConfiguration().memory().size(); + long authzRevisionsMaxEntries = cacheManager.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME).getCacheConfiguration().memory().size(); authzRevisionsMaxEntries = authzRevisionsMaxEntries > 0 ? 2 * authzRevisionsMaxEntries : InfinispanConnectionProvider.AUTHORIZATION_REVISIONS_CACHE_DEFAULT_MAX; - this.cacheManager.defineConfiguration(InfinispanConnectionProvider.AUTHORIZATION_REVISIONS_CACHE_NAME, getRevisionCacheConfig(authzRevisionsMaxEntries)); - this.cacheManager.getCache(InfinispanConnectionProvider.AUTHORIZATION_REVISIONS_CACHE_NAME, true); + cacheManager.defineConfiguration(InfinispanConnectionProvider.AUTHORIZATION_REVISIONS_CACHE_NAME, getRevisionCacheConfig(authzRevisionsMaxEntries)); + cacheManager.getCache(InfinispanConnectionProvider.AUTHORIZATION_REVISIONS_CACHE_NAME, true); - this.topologyInfo = new TopologyInfo(this.cacheManager, config, false); + this.topologyInfo = new TopologyInfo(cacheManager, config, false); logger.debugv("Using container managed Infinispan cache container, lookup={0}", cacheManager); + + return cacheManager; } - protected void initEmbedded() { + protected EmbeddedCacheManager initEmbedded() { GlobalConfigurationBuilder gcb = new GlobalConfigurationBuilder(); boolean clustered = config.getBoolean("clustered", false); @@ -221,7 +225,7 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon // See https://infinispan.org/docs/stable/titles/developing/developing.html#marshalling for the details gcb.serialization().marshaller(new JBossUserMarshaller()); - cacheManager = new DefaultCacheManager(gcb.build()); + EmbeddedCacheManager cacheManager = new DefaultCacheManager(gcb.build()); if (useKeycloakTimeService) { setTimeServiceToKeycloakTime(cacheManager); } @@ -374,6 +378,8 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon cacheManager.defineConfiguration(InfinispanConnectionProvider.AUTHORIZATION_REVISIONS_CACHE_NAME, getRevisionCacheConfig(authzRevisionsMaxEntries)); cacheManager.getCache(InfinispanConnectionProvider.AUTHORIZATION_REVISIONS_CACHE_NAME, true); + + return cacheManager; } private Configuration getRevisionCacheConfig(long maxEntries) { diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/infinispan/LegacyInfinispanConnectionFactory.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/infinispan/LegacyInfinispanConnectionFactory.java index c1a86a32a4..f8f0b42250 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/infinispan/LegacyInfinispanConnectionFactory.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/infinispan/LegacyInfinispanConnectionFactory.java @@ -29,11 +29,12 @@ public class LegacyInfinispanConnectionFactory extends DefaultInfinispanConnecti implements EnvironmentDependentProviderFactory { @Override - protected void initContainerManaged(EmbeddedCacheManager cacheManager) { - super.initContainerManaged(cacheManager); + protected EmbeddedCacheManager initContainerManaged(EmbeddedCacheManager cacheManager) { + EmbeddedCacheManager result = super.initContainerManaged(cacheManager); // force closing the cache manager when stopping the provider // we probably want to refactor the default impl a bit to support this use case containerManaged = false; + return result; } @Override diff --git a/testsuite/model/pom.xml b/testsuite/model/pom.xml index f866f1b541..184b69270a 100644 --- a/testsuite/model/pom.xml +++ b/testsuite/model/pom.xml @@ -148,6 +148,8 @@ @{argLine} -Xmx1536m -XX:+ExitOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED + + 1 ${keycloak.model.parameters} diff --git a/testsuite/model/src/main/java/org/keycloak/testsuite/model/Config.java b/testsuite/model/src/main/java/org/keycloak/testsuite/model/Config.java index b65713f8fe..a034cac90f 100644 --- a/testsuite/model/src/main/java/org/keycloak/testsuite/model/Config.java +++ b/testsuite/model/src/main/java/org/keycloak/testsuite/model/Config.java @@ -24,6 +24,7 @@ import org.keycloak.common.util.SystemEnvProperties; import java.util.HashMap; import java.util.Map; import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.BooleanSupplier; import java.util.stream.Collectors; @@ -35,7 +36,7 @@ public class Config implements ConfigProvider { private final Properties systemProperties = new SystemEnvProperties(); - private final Map defaultProperties = new HashMap<>(); + private final Map defaultProperties = new ConcurrentHashMap<>(); private final ThreadLocal> properties = new ThreadLocal>() { @Override protected Map initialValue() {