diff --git a/misc/CrossDataCenter.md b/misc/CrossDataCenter.md index 21dd586654..b5a56706b4 100644 --- a/misc/CrossDataCenter.md +++ b/misc/CrossDataCenter.md @@ -116,64 +116,62 @@ Keycloak servers setup ``` -3.3) Add the `store` under `work` cache: +3.3) Add the `remote-store` under `work` cache: ```xml - - true + + true org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory - work - false - + ``` -3.5) Add the `store` like this under `sessions` cache: +3.5) Add the `remote-store` like this under `sessions` cache: ```xml - - sessions - work - true - + + true + org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory + ``` -3.6) Same for `offlineSessions` and `loginFailures` caches (The only difference from `sessions` cache is, that `remoteCacheName` property value are different: +3.6) Same for `offlineSessions`, `loginFailures`, and `actionTokens` caches (the only difference from `sessions` cache is that `cache` property value are different): ```xml - - offlineSessions - work - true - + + true + org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory + - - loginFailures - work - true - + + true + org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory + -``` -3.7) The configuration of `actionTokens` cache have different `remoteCacheName`, `sessionCache` and the `preload` attribute: - -```xml - - actionTokens - work - false - + + true + org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory + -``` +``` + +3.7) Add outbound socket binding for the remote store into `socket-binding-group` configuration: + +```xml + + + +``` 3.8) The configuration of distributed cache `authenticationSessions` and other caches is left unchanged. diff --git a/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProvider.java b/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProvider.java index d95e4a4bb1..5513777e3c 100644 --- a/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProvider.java +++ b/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProvider.java @@ -25,7 +25,7 @@ import org.infinispan.manager.EmbeddedCacheManager; */ public class DefaultInfinispanConnectionProvider implements InfinispanConnectionProvider { - private EmbeddedCacheManager cacheManager; + private final EmbeddedCacheManager cacheManager; private final String siteName; private final String nodeName; 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 6c84385b54..c657e44267 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 @@ -41,9 +41,9 @@ import org.keycloak.Config; import org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; -import org.keycloak.models.sessions.infinispan.remotestore.KeycloakRemoteStoreConfigurationBuilder; import javax.naming.InitialContext; +import org.infinispan.persistence.remote.configuration.RemoteStoreConfigurationBuilder; /** * @author Stian Thorgersen @@ -157,7 +157,7 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon this.nodeName = generateNodeName(); } - logger.debugv("Using container managed Infinispan cache container, lookup={1}", cacheContainerLookup); + logger.debugv("Using container managed Infinispan cache container, lookup={0}", cacheContainerLookup); } catch (Exception e) { throw new RuntimeException("Failed to retrieve cache container", e); } @@ -354,8 +354,7 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon builder.persistence() .passivation(false) - .addStore(KeycloakRemoteStoreConfigurationBuilder.class) - .sessionCache(sessionCache) + .addStore(RemoteStoreConfigurationBuilder.class) .fetchPersistentState(false) .ignoreModifications(false) .purgeOnStartup(false) @@ -382,8 +381,7 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon builder.persistence() .passivation(false) - .addStore(KeycloakRemoteStoreConfigurationBuilder.class) - .sessionCache(false) + .addStore(RemoteStoreConfigurationBuilder.class) .fetchPersistentState(false) .ignoreModifications(false) .purgeOnStartup(false) diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/CacheDecorators.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/CacheDecorators.java index e9b3288103..23e5fb1b0a 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/CacheDecorators.java +++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/CacheDecorators.java @@ -26,13 +26,31 @@ import org.infinispan.context.Flag; */ public class CacheDecorators { + /** + * Adds {@link Flag#CACHE_MODE_LOCAL} flag to the cache. + * @param cache + * @return Cache with the flag applied. + */ public static AdvancedCache localCache(Cache cache) { return cache.getAdvancedCache().withFlags(Flag.CACHE_MODE_LOCAL); } + /** + * Adds {@link Flag#SKIP_CACHE_LOAD} and {@link Flag#SKIP_CACHE_STORE} flags to the cache. + * @param cache + * @return Cache with the flags applied. + */ public static AdvancedCache skipCacheLoaders(Cache cache) { return cache.getAdvancedCache().withFlags(Flag.SKIP_CACHE_LOAD, Flag.SKIP_CACHE_STORE); } + /** + * Adds {@link Flag#SKIP_CACHE_STORE} flag to the cache. + * @param cache + * @return Cache with the flags applied. + */ + public static AdvancedCache skipCacheStore(Cache cache) { + return cache.getAdvancedCache().withFlags(Flag.SKIP_CACHE_STORE); + } } diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java index 48d1965fca..f9e21e18de 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java +++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java @@ -303,8 +303,9 @@ public class InfinispanUserSessionProvider implements UserSessionProvider { RemoteCache remoteCache = InfinispanUtil.getRemoteCache(cache); if (remoteCache != null) { - UserSessionEntity remoteSessionEntity = (UserSessionEntity) remoteCache.get(id); - if (remoteSessionEntity != null) { + SessionEntityWrapper remoteSessionEntityWrapper = (SessionEntityWrapper) remoteCache.get(id); + if (remoteSessionEntityWrapper != null) { + UserSessionEntity remoteSessionEntity = remoteSessionEntityWrapper.getEntity(); log.debugf("getUserSessionWithPredicate(%s): remote cache contains session entity %s", id, remoteSessionEntity); UserSessionModel remoteSessionAdapter = wrap(realm, remoteSessionEntity, offline); @@ -399,7 +400,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider { FuturesHelper futures = new FuturesHelper(); - // Each cluster node cleanups just local sessions, which are those owned by himself (+ few more taking l1 cache into account) + // Each cluster node cleanups just local sessions, which are those owned by itself (+ few more taking l1 cache into account) Cache> localCache = CacheDecorators.localCache(sessionCache); Cache> localCacheStoreIgnore = CacheDecorators.skipCacheLoaders(localCache); diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProviderFactory.java index ea37382e10..ccc9b84943 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProviderFactory.java +++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProviderFactory.java @@ -39,6 +39,7 @@ import org.keycloak.models.sessions.infinispan.remotestore.RemoteCacheInvoker; import org.keycloak.models.sessions.infinispan.changes.SessionEntityWrapper; import org.keycloak.models.sessions.infinispan.entities.LoginFailureEntity; import org.keycloak.models.sessions.infinispan.entities.LoginFailureKey; +import org.keycloak.models.sessions.infinispan.entities.SessionEntity; import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity; import org.keycloak.models.sessions.infinispan.events.AbstractUserSessionClusterListener; import org.keycloak.models.sessions.infinispan.events.ClientRemovedSessionEvent; @@ -204,7 +205,7 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider InfinispanConnectionProvider ispn = session.getProvider(InfinispanConnectionProvider.class); - Cache sessionsCache = ispn.getCache(InfinispanConnectionProvider.SESSION_CACHE_NAME); + Cache> sessionsCache = ispn.getCache(InfinispanConnectionProvider.SESSION_CACHE_NAME); boolean sessionsRemoteCache = checkRemoteCache(session, sessionsCache, (RealmModel realm) -> { return realm.getSsoSessionIdleTimeout() * 1000; }); @@ -214,7 +215,7 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider } - Cache offlineSessionsCache = ispn.getCache(InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME); + Cache> offlineSessionsCache = ispn.getCache(InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME); boolean offlineSessionsRemoteCache = checkRemoteCache(session, offlineSessionsCache, (RealmModel realm) -> { return realm.getOfflineSessionIdleTimeout() * 1000; }); @@ -223,13 +224,13 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider offlineLastSessionRefreshStore = new LastSessionRefreshStoreFactory().createAndInit(session, offlineSessionsCache, true); } - Cache loginFailuresCache = ispn.getCache(InfinispanConnectionProvider.LOGIN_FAILURE_CACHE_NAME); + Cache> loginFailuresCache = ispn.getCache(InfinispanConnectionProvider.LOGIN_FAILURE_CACHE_NAME); boolean loginFailuresRemoteCache = checkRemoteCache(session, loginFailuresCache, (RealmModel realm) -> { return realm.getMaxDeltaTimeSeconds() * 1000; }); } - private boolean checkRemoteCache(KeycloakSession session, Cache ispnCache, RemoteCacheInvoker.MaxIdleTimeLoader maxIdleLoader) { + private boolean checkRemoteCache(KeycloakSession session, Cache> ispnCache, RemoteCacheInvoker.MaxIdleTimeLoader maxIdleLoader) { Set remoteStores = InfinispanUtil.getRemoteStores(ispnCache); if (remoteStores.isEmpty()) { @@ -238,7 +239,7 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider } else { log.infof("Remote store configured for cache '%s'", ispnCache.getName()); - RemoteCache remoteCache = remoteStores.iterator().next().getRemoteCache(); + RemoteCache> remoteCache = (RemoteCache) remoteStores.iterator().next().getRemoteCache(); remoteCacheInvoker.addRemoteCache(ispnCache.getName(), remoteCache, maxIdleLoader); diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/InfinispanChangelogBasedTransaction.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/InfinispanChangelogBasedTransaction.java index e5a7a47c78..195099258d 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/InfinispanChangelogBasedTransaction.java +++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/InfinispanChangelogBasedTransaction.java @@ -27,6 +27,7 @@ import org.jboss.logging.Logger; import org.keycloak.models.AbstractKeycloakTransaction; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; +import org.keycloak.models.sessions.infinispan.CacheDecorators; import org.keycloak.models.sessions.infinispan.entities.SessionEntity; import org.keycloak.models.sessions.infinispan.remotestore.RemoteCacheInvoker; @@ -172,17 +173,17 @@ public class InfinispanChangelogBasedTransaction ext switch (operation) { case REMOVE: // Just remove it - cache + CacheDecorators.skipCacheStore(cache) .getAdvancedCache().withFlags(Flag.IGNORE_RETURN_VALUES) .remove(key); break; case ADD: - cache + CacheDecorators.skipCacheStore(cache) .getAdvancedCache().withFlags(Flag.IGNORE_RETURN_VALUES) .put(key, sessionWrapper, task.getLifespanMs(), TimeUnit.MILLISECONDS); break; case ADD_IF_ABSENT: - SessionEntityWrapper existing = cache.putIfAbsent(key, sessionWrapper); + SessionEntityWrapper existing = CacheDecorators.skipCacheStore(cache).putIfAbsent(key, sessionWrapper); if (existing != null) { logger.debugf("Existing entity in cache for key: %s . Will update it", key); @@ -210,7 +211,7 @@ public class InfinispanChangelogBasedTransaction ext SessionEntityWrapper newVersionEntity = generateNewVersionAndWrapEntity(session, oldVersionEntity.getLocalMetadata()); // Atomic cluster-aware replace - replaced = cache.replace(key, oldVersionEntity, newVersionEntity); + replaced = CacheDecorators.skipCacheStore(cache).replace(key, oldVersionEntity, newVersionEntity); // Replace fail. Need to load latest entity from cache, apply updates again and try to replace in cache again if (!replaced) { diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/SessionEntityWrapper.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/SessionEntityWrapper.java index 254cd38b67..0a3a5bf39b 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/SessionEntityWrapper.java +++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/SessionEntityWrapper.java @@ -29,6 +29,7 @@ import org.infinispan.commons.marshall.Externalizer; import org.infinispan.commons.marshall.MarshallUtil; import org.infinispan.commons.marshall.SerializeWith; import org.keycloak.models.sessions.infinispan.entities.SessionEntity; +import org.jboss.logging.Logger; /** * @author Marek Posolda @@ -36,11 +37,12 @@ import org.keycloak.models.sessions.infinispan.entities.SessionEntity; @SerializeWith(SessionEntityWrapper.ExternalizerImpl.class) public class SessionEntityWrapper { + private static final Logger log = Logger.getLogger(SessionEntityWrapper.class); + private UUID version; private final S entity; private final Map localMetadata; - protected SessionEntityWrapper(UUID version, Map localMetadata, S entity) { if (version == null) { throw new IllegalArgumentException("Version UUID can't be null"); @@ -52,13 +54,34 @@ public class SessionEntityWrapper { } public SessionEntityWrapper(Map localMetadata, S entity) { - this(UUID.randomUUID(),localMetadata, entity); + this(UUID.randomUUID(), localMetadata, entity); } public SessionEntityWrapper(S entity) { this(new ConcurrentHashMap<>(), entity); } + private SessionEntityWrapper(S entity, boolean forTransport) { + if (! forTransport) { + throw new IllegalArgumentException("This constructor is only for transport entities"); + } + + this.version = null; + this.localMetadata = null; + this.entity = entity; + } + + public static SessionEntityWrapper forTransport(S entity) { + return new SessionEntityWrapper<>(entity, true); + } + + public SessionEntityWrapper forTransport() { + return new SessionEntityWrapper<>(this.entity, true); + } + + private boolean isForTransport() { + return this.version == null; + } public UUID getVersion() { return version; @@ -68,16 +91,21 @@ public class SessionEntityWrapper { this.version = version; } - public S getEntity() { return entity; } public String getLocalMetadataNote(String key) { + if (isForTransport()) { + throw new IllegalStateException("This entity is only intended for transport"); + } return localMetadata.get(key); } public void putLocalMetadataNote(String key, String value) { + if (isForTransport()) { + throw new IllegalStateException("This entity is only intended for transport"); + } localMetadata.put(key, value); } @@ -87,6 +115,9 @@ public class SessionEntityWrapper { } public void putLocalMetadataNoteInt(String key, int value) { + if (isForTransport()) { + throw new IllegalStateException("This entity is only intended for transport"); + } localMetadata.put(key, String.valueOf(value)); } @@ -122,31 +153,45 @@ public class SessionEntityWrapper { public static class ExternalizerImpl implements Externalizer { + private static final int VERSION_1 = 1; @Override public void writeObject(ObjectOutput output, SessionEntityWrapper obj) throws IOException { - MarshallUtil.marshallUUID(obj.version, output, false); - MarshallUtil.marshallMap(obj.localMetadata, output); - output.writeObject(obj.getEntity()); + output.write(VERSION_1); + + final boolean forTransport = obj.isForTransport(); + output.writeBoolean(forTransport); + + if (! forTransport) { + output.writeLong(obj.getVersion().getMostSignificantBits()); + output.writeLong(obj.getVersion().getLeastSignificantBits()); + MarshallUtil.marshallMap(obj.localMetadata, output); + } + + output.writeObject(obj.entity); } @Override public SessionEntityWrapper readObject(ObjectInput input) throws IOException, ClassNotFoundException { - UUID objVersion = MarshallUtil.unmarshallUUID(input, false); + byte version = input.readByte(); - Map localMetadata = MarshallUtil.unmarshallMap(input, new MarshallUtil.MapBuilder>() { + if (version != VERSION_1) { + throw new IOException("Invalid version: " + version); + } + final boolean forTransport = input.readBoolean(); - @Override - public Map build(int size) { - return new ConcurrentHashMap<>(size); - } - - }); - - SessionEntity entity = (SessionEntity) input.readObject(); - - return new SessionEntityWrapper<>(objVersion, localMetadata, entity); + if (forTransport) { + final SessionEntity entity = (SessionEntity) input.readObject(); + log.debugf("Loaded entity from remote store: %s", entity); + return new SessionEntityWrapper(entity); + } else { + UUID sessionVersion = new UUID(input.readLong(), input.readLong()); + ConcurrentHashMap map = MarshallUtil.unmarshallMap(input, (size) -> new ConcurrentHashMap<>(size)); + final SessionEntity entity = (SessionEntity) input.readObject(); + log.debugf("Found entity locally: %s", entity); + return new SessionEntityWrapper(sessionVersion, map, entity); + } } } diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/sessions/LastSessionRefreshListener.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/sessions/LastSessionRefreshListener.java index 40cbb31972..892ecfe973 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/sessions/LastSessionRefreshListener.java +++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/sessions/LastSessionRefreshListener.java @@ -28,6 +28,7 @@ import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.RealmModel; import org.keycloak.models.UserSessionModel; import org.keycloak.models.sessions.infinispan.changes.SessionEntityWrapper; +import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity; import org.keycloak.models.sessions.infinispan.util.InfinispanUtil; import org.keycloak.models.utils.KeycloakModelUtils; @@ -43,11 +44,11 @@ public class LastSessionRefreshListener implements ClusterListener { private final boolean offline; private final KeycloakSessionFactory sessionFactory; - private final Cache cache; + private final Cache> cache; private final boolean distributed; private final String myAddress; - public LastSessionRefreshListener(KeycloakSession session, Cache cache, boolean offline) { + public LastSessionRefreshListener(KeycloakSession session, Cache> cache, boolean offline) { this.sessionFactory = session.getKeycloakSessionFactory(); this.cache = cache; this.offline = offline; diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/sessions/LastSessionRefreshStoreFactory.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/sessions/LastSessionRefreshStoreFactory.java index 21ed476bf3..d7b85590f5 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/sessions/LastSessionRefreshStoreFactory.java +++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/sessions/LastSessionRefreshStoreFactory.java @@ -22,6 +22,7 @@ import org.keycloak.cluster.ClusterProvider; import org.keycloak.common.util.Time; import org.keycloak.models.KeycloakSession; import org.keycloak.models.sessions.infinispan.changes.SessionEntityWrapper; +import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity; import org.keycloak.timer.TimerProvider; /** @@ -39,12 +40,12 @@ public class LastSessionRefreshStoreFactory { public static final int DEFAULT_MAX_COUNT = 100; - public LastSessionRefreshStore createAndInit(KeycloakSession kcSession, Cache cache, boolean offline) { + public LastSessionRefreshStore createAndInit(KeycloakSession kcSession, Cache> cache, boolean offline) { return createAndInit(kcSession, cache, DEFAULT_TIMER_INTERVAL_MS, DEFAULT_MAX_INTERVAL_BETWEEN_MESSAGES_SECONDS, DEFAULT_MAX_COUNT, offline); } - public LastSessionRefreshStore createAndInit(KeycloakSession kcSession, Cache cache, long timerIntervalMs, int maxIntervalBetweenMessagesSeconds, int maxCount, boolean offline) { + public LastSessionRefreshStore createAndInit(KeycloakSession kcSession, Cache> cache, long timerIntervalMs, int maxIntervalBetweenMessagesSeconds, int maxCount, boolean offline) { String eventKey = offline ? "lastSessionRefreshes-offline" : "lastSessionRefreshes"; LastSessionRefreshStore store = createStoreInstance(maxIntervalBetweenMessagesSeconds, maxCount, eventKey); diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/KeycloakRemoteStore.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/KeycloakRemoteStore.java deleted file mode 100644 index a6f526dbe9..0000000000 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/KeycloakRemoteStore.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.keycloak.models.sessions.infinispan.remotestore; - -import java.util.concurrent.Executor; - -import org.infinispan.commons.CacheException; -import org.infinispan.commons.configuration.ConfiguredBy; -import org.infinispan.configuration.cache.ConfigurationBuilder; -import org.infinispan.configuration.cache.PersistenceConfigurationBuilder; -import org.infinispan.configuration.cache.StoreConfiguration; -import org.infinispan.filter.KeyFilter; -import org.infinispan.manager.EmbeddedCacheManager; -import org.infinispan.marshall.core.MarshalledEntry; -import org.infinispan.metadata.InternalMetadata; -import org.infinispan.persistence.InitializationContextImpl; -import org.infinispan.persistence.remote.RemoteStore; -import org.infinispan.persistence.remote.configuration.RemoteStoreConfiguration; -import org.infinispan.persistence.spi.InitializationContext; -import org.infinispan.persistence.spi.PersistenceException; -import org.jboss.logging.Logger; -import org.keycloak.models.sessions.infinispan.changes.SessionEntityWrapper; -import org.keycloak.models.sessions.infinispan.entities.SessionEntity; - -/** - * @author Marek Posolda - */ -@ConfiguredBy(KeycloakRemoteStoreConfiguration.class) -public class KeycloakRemoteStore extends RemoteStore { - - protected static final Logger logger = Logger.getLogger(KeycloakRemoteStore.class); - - private String remoteCacheName; - - @Override - public void start() throws PersistenceException { - this.remoteCacheName = getConfiguration().remoteCacheName(); - Boolean sessionCache = getConfiguration().sessionCache(); - - String cacheTemplateName = getConfiguration().useConfigTemplateFromCache(); - - if (cacheTemplateName != null) { - logger.debugf("Will override configuration of cache '%s' from template of cache '%s'", ctx.getCache().getName(), cacheTemplateName); - - // Just to ensure that dependent cache is started and it's configuration fully loaded - EmbeddedCacheManager cacheManager = ctx.getCache().getCacheManager(); - cacheManager.getCache(cacheTemplateName, true); - - KeycloakRemoteStoreConfiguration templateConfig = (KeycloakRemoteStoreConfiguration) cacheManager.getCacheConfiguration(cacheTemplateName).persistence().stores().stream() - .filter((StoreConfiguration storeConfig) -> storeConfig instanceof KeycloakRemoteStoreConfiguration) - .findFirst() - .orElseThrow(() -> new CacheException("Unable to find remoteStore on cache '" + cacheTemplateName + ".")); - - // We have template configuration, so create new configuration from it. Override just remoteCacheName and sessionsCache (not pretty, but works for now) - PersistenceConfigurationBuilder readPersistenceBuilder = new ConfigurationBuilder().read(ctx.getCache().getCacheConfiguration()).persistence(); - KeycloakRemoteStoreConfigurationBuilder configBuilder = new KeycloakRemoteStoreConfigurationBuilder(readPersistenceBuilder); - configBuilder.read(templateConfig); - - // Rather log this to clearly show in the log that this might be a configuration mistake (Note that it can be expected for some cases) - if (!this.remoteCacheName.equals(ctx.getCache().getName())) { - logger.warnf("Cache name and remoteCache name are different - maybe it's expected. Cache name '%s', remoteCache name '%s'.", ctx.getCache().getName(), this.remoteCacheName); - } - - configBuilder.remoteCacheName(this.remoteCacheName); - configBuilder.sessionCache(sessionCache); - - RemoteStoreConfiguration newCfg1 = configBuilder.create(); - KeycloakRemoteStoreConfiguration newCfg = new KeycloakRemoteStoreConfiguration(newCfg1); - - InitializationContext ctx = new InitializationContextImpl(newCfg, this.ctx.getCache(), this.ctx.getMarshaller(), this.ctx.getTimeService(), - this.ctx.getByteBufferFactory(), this.ctx.getMarshalledEntryFactory()); - - init(ctx); - - } else { - logger.debugf("Skip overriding configuration from template for cache '%s'", ctx.getCache().getName()); - } - - logger.debugf("Using configuration for remote cache '%s': %s", remoteCacheName, getConfiguration().toString()); - - super.start(); - - if (getRemoteCache() == null) { - String cacheName = getConfiguration().remoteCacheName(); - throw new CacheException("Remote cache '" + cacheName + "' is not available."); - } - } - - @Override - public MarshalledEntry load(Object key) throws PersistenceException { - if (!getConfiguration().sessionCache()) { - return super.load(key); - } - - logger.debugf("Calling load: '%s' for remote cache '%s'", key, remoteCacheName); - - MarshalledEntry entry = super.load(key); - if (entry == null) { - return null; - } - - // wrap remote entity - SessionEntity entity = (SessionEntity) entry.getValue(); - SessionEntityWrapper entityWrapper = new SessionEntityWrapper(entity); - - MarshalledEntry wrappedEntry = marshalledEntry(entry.getKey(), entityWrapper, entry.getMetadata()); - - logger.debugf("Found entry in load: %s", wrappedEntry.toString()); - - return wrappedEntry; - } - - - // Don't do anything. Iterate over remoteCache.keySet() can have big performance impact. We handle bulk load by ourselves if needed. - @Override - public void process(KeyFilter filter, CacheLoaderTask task, Executor executor, boolean fetchValue, boolean fetchMetadata) { - if (!getConfiguration().sessionCache()) { - super.process(filter, task, executor, fetchValue, fetchMetadata); - return; - } - - logger.debugf("Skip calling process with filter '%s' on cache '%s'", filter, remoteCacheName); - // super.process(filter, task, executor, fetchValue, fetchMetadata); - } - - - // Don't do anything. Writes handled by KC itself as we need more flexibility - @Override - public void write(MarshalledEntry entry) throws PersistenceException { - if (!getConfiguration().sessionCache()) { - super.write(entry); - return; - } - } - - - @Override - public boolean delete(Object key) throws PersistenceException { - if (!getConfiguration().sessionCache()) { - return super.delete(key); - } - - logger.debugf("Calling delete for key '%s' on cache '%s'", key, remoteCacheName); - - // Optimization - we don't need to know the previous value. - // TODO: For some usecases (bulk removal of user sessions), it may be better for performance to call removeAsync and wait for all futures to be finished - getRemoteCache().remove(key); - - return true; - } - - protected MarshalledEntry marshalledEntry(Object key, Object value, InternalMetadata metadata) { - return ctx.getMarshalledEntryFactory().newMarshalledEntry(key, value, metadata); - } - - - @Override - public KeycloakRemoteStoreConfiguration getConfiguration() { - return (KeycloakRemoteStoreConfiguration) super.getConfiguration(); - } -} diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/KeycloakRemoteStoreConfiguration.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/KeycloakRemoteStoreConfiguration.java deleted file mode 100644 index fda2c76a61..0000000000 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/KeycloakRemoteStoreConfiguration.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.keycloak.models.sessions.infinispan.remotestore; - -import org.infinispan.commons.configuration.BuiltBy; -import org.infinispan.commons.configuration.ConfigurationFor; -import org.infinispan.commons.configuration.attributes.Attribute; -import org.infinispan.commons.configuration.attributes.AttributeDefinition; -import org.infinispan.persistence.remote.configuration.RemoteStoreConfiguration; - -/** - * @author Marek Posolda - */ -@BuiltBy(KeycloakRemoteStoreConfigurationBuilder.class) -@ConfigurationFor(KeycloakRemoteStore.class) -public class KeycloakRemoteStoreConfiguration extends RemoteStoreConfiguration { - - static final AttributeDefinition USE_CONFIG_TEMPLATE_FROM_CACHE = AttributeDefinition.builder("useConfigTemplateFromCache", null, String.class).immutable().build(); - static final AttributeDefinition SESSION_CACHE = AttributeDefinition.builder("sessionCache", null, Boolean.class).immutable().build(); - - private final Attribute useConfigTemplateFromCache; - private final Attribute sessionCache; - - - public KeycloakRemoteStoreConfiguration(RemoteStoreConfiguration other) { - super(other.attributes(), other.async(), other.singletonStore(), other.asyncExecutorFactory(), other.connectionPool()); - useConfigTemplateFromCache = attributes.attribute(USE_CONFIG_TEMPLATE_FROM_CACHE.name()); - sessionCache = attributes.attribute(SESSION_CACHE.name()); - } - - - public String useConfigTemplateFromCache() { - return useConfigTemplateFromCache.get(); - } - - - public Boolean sessionCache() { - return sessionCache.get()==null ? false : sessionCache.get(); - } -} diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/KeycloakRemoteStoreConfigurationBuilder.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/KeycloakRemoteStoreConfigurationBuilder.java deleted file mode 100644 index 373411472b..0000000000 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/KeycloakRemoteStoreConfigurationBuilder.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.keycloak.models.sessions.infinispan.remotestore; - -import java.lang.reflect.Field; -import java.util.Map; - -import org.infinispan.commons.CacheConfigurationException; -import org.infinispan.commons.configuration.attributes.Attribute; -import org.infinispan.commons.configuration.attributes.AttributeDefinition; -import org.infinispan.commons.configuration.attributes.AttributeSet; -import org.infinispan.configuration.cache.PersistenceConfigurationBuilder; -import org.infinispan.persistence.remote.configuration.RemoteStoreConfiguration; -import org.infinispan.persistence.remote.configuration.RemoteStoreConfigurationBuilder; -import org.keycloak.common.util.reflections.Reflections; - -/** - * @author Marek Posolda - */ -public class KeycloakRemoteStoreConfigurationBuilder extends RemoteStoreConfigurationBuilder { - - public KeycloakRemoteStoreConfigurationBuilder(PersistenceConfigurationBuilder builder) { - super(builder); - - // No better way to add new attribute definition to superclass :/ - try { - Field f = Reflections.findDeclaredField(AttributeSet.class, "attributes"); - f.setAccessible(true); - Map> attributesInternal = (Map>) f.get(this.attributes); - - AttributeDefinition def = KeycloakRemoteStoreConfiguration.USE_CONFIG_TEMPLATE_FROM_CACHE; - Attribute attribute = def.toAttribute(); - attributesInternal.put(def.name(), attribute); - - AttributeDefinition defBool = KeycloakRemoteStoreConfiguration.SESSION_CACHE; - Attribute attributeBool = defBool.toAttribute(); - attributesInternal.put(defBool.name(), attributeBool); - - } catch (IllegalAccessException iae) { - throw new CacheConfigurationException(iae); - } - } - - - @Override - public KeycloakRemoteStoreConfiguration create() { - RemoteStoreConfiguration cfg = super.create(); - KeycloakRemoteStoreConfiguration cfg2 = new KeycloakRemoteStoreConfiguration(cfg); - return cfg2; - } - - - public KeycloakRemoteStoreConfigurationBuilder useConfigTemplateFromCache(String useConfigTemplateFromCache) { - attributes.attribute(KeycloakRemoteStoreConfiguration.USE_CONFIG_TEMPLATE_FROM_CACHE).set(useConfigTemplateFromCache); - return this; - } - - - public KeycloakRemoteStoreConfigurationBuilder sessionCache(Boolean sessionCache) { - attributes.attribute(KeycloakRemoteStoreConfiguration.SESSION_CACHE).set(sessionCache); - return this; - } -} diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheInvoker.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheInvoker.java index 8891469f3b..3559c82f1b 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheInvoker.java +++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheInvoker.java @@ -82,23 +82,22 @@ public class RemoteCacheInvoker { } - private void runOnRemoteCache(RemoteCache remoteCache, long maxIdleMs, K key, SessionUpdateTask task, SessionEntityWrapper sessionWrapper) { - V session = sessionWrapper.getEntity(); + private void runOnRemoteCache(RemoteCache> remoteCache, long maxIdleMs, K key, SessionUpdateTask task, SessionEntityWrapper sessionWrapper) { + final V session = sessionWrapper.getEntity(); SessionUpdateTask.CacheOperation operation = task.getOperation(session); switch (operation) { case REMOVE: - // REMOVE already handled at remote cache store level - //remoteCache.remove(key); + remoteCache.remove(key); break; case ADD: - remoteCache.put(key, session, task.getLifespanMs(), TimeUnit.MILLISECONDS, maxIdleMs, TimeUnit.MILLISECONDS); + remoteCache.put(key, sessionWrapper.forTransport(), task.getLifespanMs(), TimeUnit.MILLISECONDS, maxIdleMs, TimeUnit.MILLISECONDS); break; case ADD_IF_ABSENT: final int currentTime = Time.currentTime(); - SessionEntity existing = remoteCache + SessionEntityWrapper existing = remoteCache .withFlags(Flag.FORCE_RETURN_VALUE) - .putIfAbsent(key, session, -1, TimeUnit.MILLISECONDS, maxIdleMs, TimeUnit.MILLISECONDS); + .putIfAbsent(key, sessionWrapper.forTransport(), -1, TimeUnit.MILLISECONDS, maxIdleMs, TimeUnit.MILLISECONDS); if (existing != null) { logger.debugf("Existing entity in remote cache for key: %s . Will update it", key); @@ -116,23 +115,24 @@ public class RemoteCacheInvoker { } - private void replace(RemoteCache remoteCache, long lifespanMs, long maxIdleMs, K key, SessionUpdateTask task) { + private void replace(RemoteCache> remoteCache, long lifespanMs, long maxIdleMs, K key, SessionUpdateTask task) { boolean replaced = false; while (!replaced) { - VersionedValue versioned = remoteCache.getVersioned(key); + VersionedValue> versioned = remoteCache.getVersioned(key); if (versioned == null) { logger.warnf("Not found entity to replace for key '%s'", key); return; } - V session = versioned.getValue(); + SessionEntityWrapper sessionWrapper = versioned.getValue(); + final V session = sessionWrapper.getEntity(); // Run task on the remote session task.runUpdate(session); logger.debugf("Before replaceWithVersion. Entity to write version %d: %s", versioned.getVersion(), session); - replaced = remoteCache.replaceWithVersion(key, session, versioned.getVersion(), lifespanMs, TimeUnit.MILLISECONDS, maxIdleMs, TimeUnit.MILLISECONDS); + replaced = remoteCache.replaceWithVersion(key, SessionEntityWrapper.forTransport(session), versioned.getVersion(), lifespanMs, TimeUnit.MILLISECONDS, maxIdleMs, TimeUnit.MILLISECONDS); if (!replaced) { logger.debugf("Failed to replace entity '%s' version %d. Will retry again", key, versioned.getVersion()); diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheSessionListener.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheSessionListener.java index 04e14f5640..1639c78599 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheSessionListener.java +++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheSessionListener.java @@ -47,7 +47,7 @@ public class RemoteCacheSessionListener { protected static final Logger logger = Logger.getLogger(RemoteCacheSessionListener.class); private Cache> cache; - private RemoteCache remoteCache; + private RemoteCache> remoteCache; private boolean distributed; private String myAddress; private ClientListenerExecutorDecorator executor; @@ -57,7 +57,7 @@ public class RemoteCacheSessionListener { } - protected void init(KeycloakSession session, Cache> cache, RemoteCache remoteCache) { + protected void init(KeycloakSession session, Cache> cache, RemoteCache> remoteCache) { this.cache = cache; this.remoteCache = remoteCache; @@ -113,10 +113,10 @@ public class RemoteCacheSessionListener { replaceRetries++; SessionEntityWrapper localEntityWrapper = cache.get(key); - VersionedValue remoteSessionVersioned = remoteCache.getVersioned(key); + VersionedValue> remoteSessionVersioned = remoteCache.getVersioned(key); // Probably already removed - if (remoteSessionVersioned == null) { + if (remoteSessionVersioned == null || remoteSessionVersioned.getValue() == null) { logger.debugf("Entity '%s' not present in remoteCache. Ignoring replace", key.toString()); return; @@ -134,7 +134,7 @@ public class RemoteCacheSessionListener { sleepInterval = sleepInterval << 1; } } - SessionEntity remoteSession = remoteSessionVersioned.getValue(); + SessionEntity remoteSession = remoteSessionVersioned.getValue().getEntity(); logger.debugf("Read session entity from the remote cache: %s . replaceRetries=%d", remoteSession.toString(), replaceRetries); @@ -201,7 +201,7 @@ public class RemoteCacheSessionListener { } - public static RemoteCacheSessionListener createListener(KeycloakSession session, Cache> cache, RemoteCache remoteCache) { + public static RemoteCacheSessionListener createListener(KeycloakSession session, Cache> cache, RemoteCache> remoteCache) { /*boolean isCoordinator = InfinispanUtil.isCoordinator(cache); // Just cluster coordinator will fetch userSessions from remote cache. diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheSessionsLoader.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheSessionsLoader.java index 789fc160e8..b96b9bd7c8 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheSessionsLoader.java +++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheSessionsLoader.java @@ -29,7 +29,6 @@ import org.jboss.logging.Logger; import org.keycloak.connections.infinispan.InfinispanConnectionProvider; import org.keycloak.models.KeycloakSession; import org.keycloak.models.sessions.infinispan.changes.SessionEntityWrapper; -import org.keycloak.models.sessions.infinispan.entities.SessionEntity; import org.keycloak.models.sessions.infinispan.initializer.BaseCacheInitializer; import org.keycloak.models.sessions.infinispan.initializer.OfflinePersistentUserSessionLoader; import org.keycloak.models.sessions.infinispan.initializer.SessionLoader; @@ -116,9 +115,7 @@ public class RemoteCacheSessionsLoader implements SessionLoader { for (Map.Entry entry : remoteObjects.entrySet()) { try { Object key = marshaller.objectFromByteBuffer(entry.getKey()); - SessionEntity entity = (SessionEntity) marshaller.objectFromByteBuffer(entry.getValue()); - - SessionEntityWrapper entityWrapper = new SessionEntityWrapper(entity); + SessionEntityWrapper entityWrapper = (SessionEntityWrapper) marshaller.objectFromByteBuffer(entry.getValue()); decoratedCache.putAsync(key, entityWrapper); } catch (Exception e) { diff --git a/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGSessionsCacheTest.java b/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGSessionsCacheTest.java index a4091a151c..5e79226f1b 100644 --- a/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGSessionsCacheTest.java +++ b/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGSessionsCacheTest.java @@ -39,8 +39,8 @@ import org.keycloak.models.sessions.infinispan.changes.SessionEntityWrapper; import org.keycloak.models.sessions.infinispan.entities.AuthenticatedClientSessionEntity; import org.keycloak.models.sessions.infinispan.entities.SessionEntity; import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity; -import org.keycloak.models.sessions.infinispan.remotestore.KeycloakRemoteStoreConfigurationBuilder; import org.keycloak.models.sessions.infinispan.util.InfinispanUtil; +import org.infinispan.persistence.remote.configuration.RemoteStoreConfigurationBuilder; /** * Test concurrency for remoteStore (backed by HotRod RemoteCaches) against external JDG. Especially tests "replaceWithVersion" contract. @@ -207,7 +207,7 @@ public class ConcurrencyJDGSessionsCacheTest { private static EmbeddedCacheManager createManager(int threadId) { - return new TestCacheManagerFactory().createManager(threadId, InfinispanConnectionProvider.SESSION_CACHE_NAME, KeycloakRemoteStoreConfigurationBuilder.class); + return new TestCacheManagerFactory().createManager(threadId, InfinispanConnectionProvider.SESSION_CACHE_NAME, RemoteStoreConfigurationBuilder.class); } diff --git a/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/TestCacheManagerFactory.java b/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/TestCacheManagerFactory.java index 6b4eec11e4..0fc8d7efe0 100644 --- a/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/TestCacheManagerFactory.java +++ b/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/TestCacheManagerFactory.java @@ -19,11 +19,12 @@ package org.keycloak.cluster.infinispan; import org.infinispan.configuration.cache.Configuration; import org.infinispan.configuration.cache.ConfigurationBuilder; +import org.infinispan.configuration.cache.StoreConfigurationBuilder; import org.infinispan.configuration.global.GlobalConfigurationBuilder; import org.infinispan.manager.DefaultCacheManager; import org.infinispan.manager.EmbeddedCacheManager; import org.infinispan.persistence.remote.configuration.ExhaustedAction; -import org.infinispan.persistence.remote.configuration.RemoteStoreConfigurationBuilder; +import org.infinispan.persistence.remote.configuration.RemoteStoreConfigurationChildBuilder; /** * @author Marek Posolda @@ -31,7 +32,7 @@ import org.infinispan.persistence.remote.configuration.RemoteStoreConfigurationB class TestCacheManagerFactory { - EmbeddedCacheManager createManager(int threadId, String cacheName, Class builderClass) { + & RemoteStoreConfigurationChildBuilder> EmbeddedCacheManager createManager(int threadId, String cacheName, Class builderClass) { System.setProperty("java.net.preferIPv4Stack", "true"); System.setProperty("jgroups.tcp.port", "53715"); GlobalConfigurationBuilder gcb = new GlobalConfigurationBuilder(); @@ -57,7 +58,7 @@ class TestCacheManagerFactory { } - private Configuration getCacheBackedByRemoteStore(int threadId, String cacheName, Class builderClass) { + private & RemoteStoreConfigurationChildBuilder> Configuration getCacheBackedByRemoteStore(int threadId, String cacheName, Class builderClass) { ConfigurationBuilder cacheConfigBuilder = new ConfigurationBuilder(); String host = "localhost"; diff --git a/services/src/main/java/org/keycloak/executors/DefaultExecutorsProviderFactory.java b/services/src/main/java/org/keycloak/executors/DefaultExecutorsProviderFactory.java index aa8f83b5f9..adc761dfc5 100644 --- a/services/src/main/java/org/keycloak/executors/DefaultExecutorsProviderFactory.java +++ b/services/src/main/java/org/keycloak/executors/DefaultExecutorsProviderFactory.java @@ -44,8 +44,8 @@ public class DefaultExecutorsProviderFactory implements ExecutorsProviderFactory protected static final Logger logger = Logger.getLogger(DefaultExecutorsProviderFactory.class); - private int DEFAULT_MIN_THREADS = 4; - private int DEFAULT_MAX_THREADS = 16; + private static final int DEFAULT_MIN_THREADS = 4; + private static final int DEFAULT_MAX_THREADS = 16; private static final String MANAGED_EXECUTORS_SERVICE_JNDI_PREFIX = "java:jboss/ee/concurrency/executor/"; diff --git a/testsuite/integration-arquillian/servers/auth-server/jboss/common/crossdc/cross-dc-setup.cli b/testsuite/integration-arquillian/servers/auth-server/jboss/common/crossdc/cross-dc-setup.cli index 3a0a7b0cd5..3eee2fcc2e 100644 --- a/testsuite/integration-arquillian/servers/auth-server/jboss/common/crossdc/cross-dc-setup.cli +++ b/testsuite/integration-arquillian/servers/auth-server/jboss/common/crossdc/cross-dc-setup.cli @@ -8,105 +8,88 @@ echo *** Update jgoups subsystem *** echo *** Update infinispan subsystem *** /subsystem=infinispan/cache-container=keycloak:write-attribute(name=module, value=org.keycloak.keycloak-model-infinispan) +echo ** Add remote socket binding to infinispan server ** +/socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=remote-cache:add(host=${remote.cache.host:localhost}, port=${remote.cache.port:11222}) + echo ** Update replicated-cache work element ** -/subsystem=infinispan/cache-container=keycloak/replicated-cache=work/store=custom:add( \ - class=org.keycloak.models.sessions.infinispan.remotestore.KeycloakRemoteStoreConfigurationBuilder, \ +/subsystem=infinispan/cache-container=keycloak/replicated-cache=work/store=remote:add( \ passivation=false, \ fetch-state=false, \ purge=false, \ preload=false, \ - shared=true \ -) - -/subsystem=infinispan/cache-container=keycloak/replicated-cache=work/store=custom:write-attribute( \ - name=properties, value={ \ + shared=true, \ + remote-servers=["remote-cache"], \ + cache=work, \ + properties={ \ rawValues=true, \ - marshaller=org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory, \ - remoteCacheName=work, \ - sessionCache=false \ + marshaller=org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory \ } \ ) /subsystem=infinispan/cache-container=keycloak/replicated-cache=work:write-attribute(name=statistics-enabled,value=true) echo ** Update distributed-cache sessions element ** -/subsystem=infinispan/cache-container=keycloak/distributed-cache=sessions/store=custom:add( \ - class=org.keycloak.models.sessions.infinispan.remotestore.KeycloakRemoteStoreConfigurationBuilder, \ +/subsystem=infinispan/cache-container=keycloak/distributed-cache=sessions/store=remote:add( \ passivation=false, \ fetch-state=false, \ purge=false, \ preload=false, \ - shared=true \ -) - -/subsystem=infinispan/cache-container=keycloak/distributed-cache=sessions/store=custom:write-attribute( \ - name=properties, value={ \ - remoteCacheName=sessions, \ - useConfigTemplateFromCache=work, \ - sessionCache=true \ + shared=true, \ + remote-servers=["remote-cache"], \ + cache=sessions, \ + properties={ \ + rawValues=true, \ + marshaller=org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory \ } \ ) - /subsystem=infinispan/cache-container=keycloak/distributed-cache=sessions:write-attribute(name=statistics-enabled,value=true) echo ** Update distributed-cache offlineSessions element ** -/subsystem=infinispan/cache-container=keycloak/distributed-cache=offlineSessions/store=custom:add( \ - class=org.keycloak.models.sessions.infinispan.remotestore.KeycloakRemoteStoreConfigurationBuilder, \ +/subsystem=infinispan/cache-container=keycloak/distributed-cache=offlineSessions/store=remote:add( \ passivation=false, \ fetch-state=false, \ purge=false, \ preload=false, \ - shared=true \ -) - -/subsystem=infinispan/cache-container=keycloak/distributed-cache=offlineSessions/store=custom:write-attribute( \ - name=properties, value={ \ - remoteCacheName=offlineSessions, \ - useConfigTemplateFromCache=work, \ - sessionCache=true \ + shared=true, \ + remote-servers=["remote-cache"], \ + cache=offlineSessions, \ + properties={ \ + rawValues=true, \ + marshaller=org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory \ } \ ) - /subsystem=infinispan/cache-container=keycloak/distributed-cache=offlineSessions:write-attribute(name=statistics-enabled,value=true) echo ** Update distributed-cache loginFailures element ** -/subsystem=infinispan/cache-container=keycloak/distributed-cache=loginFailures/store=custom:add( \ - class=org.keycloak.models.sessions.infinispan.remotestore.KeycloakRemoteStoreConfigurationBuilder, \ +/subsystem=infinispan/cache-container=keycloak/distributed-cache=loginFailures/store=remote:add( \ passivation=false, \ fetch-state=false, \ purge=false, \ preload=false, \ - shared=true \ -) - -/subsystem=infinispan/cache-container=keycloak/distributed-cache=loginFailures/store=custom:write-attribute( \ - name=properties, value={ \ - remoteCacheName=loginFailures, \ - useConfigTemplateFromCache=work, \ - sessionCache=true \ + shared=true, \ + remote-servers=["remote-cache"], \ + cache=loginFailures, \ + properties={ \ + rawValues=true, \ + marshaller=org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory \ } \ ) - /subsystem=infinispan/cache-container=keycloak/distributed-cache=loginFailures:write-attribute(name=statistics-enabled,value=true) echo ** Update distributed-cache actionTokens element ** -/subsystem=infinispan/cache-container=keycloak/distributed-cache=actionTokens/store=custom:add( \ - class=org.keycloak.models.sessions.infinispan.remotestore.KeycloakRemoteStoreConfigurationBuilder, \ +/subsystem=infinispan/cache-container=keycloak/distributed-cache=actionTokens/store=remote:add( \ passivation=false, \ fetch-state=false, \ purge=false, \ - preload=true, \ - shared=true \ -) - -/subsystem=infinispan/cache-container=keycloak/distributed-cache=actionTokens/store=custom:write-attribute( \ - name=properties, value={ \ - remoteCacheName=actionTokens, \ - useConfigTemplateFromCache=work, \ - sessionCache=false \ + preload=false, \ + shared=true, \ + cache=actionTokens, \ + remote-servers=["remote-cache"], \ + properties={ \ + rawValues=true, \ + marshaller=org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory \ } \ ) - /subsystem=infinispan/cache-container=keycloak/distributed-cache=actionTokens:write-attribute(name=statistics-enabled,value=true) echo ** Update distributed-cache authenticationSessions element ** diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/resource/TestCacheResource.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/resource/TestCacheResource.java index 1954cb87df..d1e7be1f49 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/resource/TestCacheResource.java +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/resource/TestCacheResource.java @@ -35,6 +35,7 @@ import org.infinispan.remoting.transport.Transport; import org.jgroups.JChannel; import org.keycloak.connections.infinispan.InfinispanConnectionProvider; import org.keycloak.models.KeycloakSession; +import org.keycloak.models.sessions.infinispan.changes.SessionEntityWrapper; import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity; import org.keycloak.models.sessions.infinispan.util.InfinispanUtil; import org.keycloak.testsuite.rest.representation.JGroupsStats; @@ -136,11 +137,11 @@ public class TestCacheResource { if (remoteCache == null) { return -1; } else { - UserSessionEntity userSession = (UserSessionEntity) remoteCache.get(userSessionId); + SessionEntityWrapper userSession = (SessionEntityWrapper) remoteCache.get(userSessionId); if (userSession == null) { return -1; } else { - return userSession.getLastSessionRefresh(); + return userSession.getEntity().getLastSessionRefresh(); } } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/BruteForceCrossDCTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/BruteForceCrossDCTest.java index 3007cab443..e7d9026ab1 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/BruteForceCrossDCTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/BruteForceCrossDCTest.java @@ -222,10 +222,10 @@ public class BruteForceCrossDCTest extends AbstractAdminCrossDCTest { log.infof("%s: dc0User1=%d, dc0user2=%d, dc1user1=%d, dc1user2=%d, dc0CacheSize=%d, dc1CacheSize=%d", prefixMessage, dc0user1, dc0user2, dc1user1, dc1user2, dc0CacheSize, dc1CacheSize); - Assert.assertEquals(dc0user1, expectedUser1); - Assert.assertEquals(dc0user2, expectedUser2); - Assert.assertEquals(dc1user1, expectedUser1); - Assert.assertEquals(dc1user2, expectedUser2); + Assert.assertEquals(expectedUser1, dc0user1); + Assert.assertEquals(expectedUser2, dc0user2); + Assert.assertEquals(expectedUser1, dc1user1); + Assert.assertEquals(expectedUser2, dc1user2); Assert.assertEquals(expectedCacheSize, dc0CacheSize); Assert.assertEquals(expectedCacheSize, dc1CacheSize); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/session/LastSessionRefreshUnitTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/session/LastSessionRefreshUnitTest.java index 5b8c559d25..894612725a 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/session/LastSessionRefreshUnitTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/session/LastSessionRefreshUnitTest.java @@ -34,6 +34,7 @@ import org.keycloak.models.sessions.infinispan.changes.SessionEntityWrapper; import org.keycloak.models.sessions.infinispan.changes.sessions.LastSessionRefreshStore; import org.keycloak.models.sessions.infinispan.changes.sessions.LastSessionRefreshStoreFactory; import org.keycloak.models.sessions.infinispan.changes.sessions.SessionData; +import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.testsuite.AbstractKeycloakTest; import org.keycloak.testsuite.Retry; @@ -168,7 +169,7 @@ public class LastSessionRefreshUnitTest extends AbstractKeycloakTest { }; - Cache cache = session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.SESSION_CACHE_NAME); + Cache> cache = session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.SESSION_CACHE_NAME); return factory.createAndInit(session, cache, timerIntervalMs, maxIntervalBetweenMessagesSeconds, 10, false); }