From 43ce466aa643b5c724b67e2921afc3d2c18581d0 Mon Sep 17 00:00:00 2001 From: mposolda Date: Wed, 23 Aug 2017 13:37:44 +0200 Subject: [PATCH] KEYCLOAK-5294 Cross-dc working on Wildfly --- ...ltInfinispanConnectionProviderFactory.java | 11 +-- .../KcRemoteStoreConfigurationBuilder.java | 39 ---------- ...oteStore.java => KeycloakRemoteStore.java} | 76 ++++++++++++++++--- ... => KeycloakRemoteStoreConfiguration.java} | 30 +++++--- ...ycloakRemoteStoreConfigurationBuilder.java | 61 +++++++++++++++ .../ConcurrencyJDGSessionsCacheTest.java | 12 +-- 6 files changed, 151 insertions(+), 78 deletions(-) delete mode 100644 model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/KcRemoteStoreConfigurationBuilder.java rename model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/{KcRemoteStore.java => KeycloakRemoteStore.java} (50%) rename model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/{KcRemoteStoreConfiguration.java => KeycloakRemoteStoreConfiguration.java} (53%) create mode 100644 model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/KeycloakRemoteStoreConfigurationBuilder.java 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 a17e7240f5..5135e90bea 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 @@ -40,12 +40,9 @@ import org.jboss.logging.Logger; import org.jgroups.JChannel; import org.keycloak.Config; import org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory; -import org.keycloak.common.util.HostUtils; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; -import org.keycloak.models.sessions.infinispan.remotestore.KcRemoteStoreConfigurationBuilder; -import org.keycloak.models.sessions.infinispan.util.InfinispanUtil; -import org.keycloak.models.utils.KeycloakModelUtils; +import org.keycloak.models.sessions.infinispan.remotestore.KeycloakRemoteStoreConfigurationBuilder; import javax.naming.InitialContext; @@ -249,7 +246,7 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon if (jdgEnabled) { sessionConfigBuilder = new ConfigurationBuilder(); sessionConfigBuilder.read(sessionCacheConfigurationBase); - configureRemoteCacheStore(sessionConfigBuilder, async, InfinispanConnectionProvider.SESSION_CACHE_NAME, KcRemoteStoreConfigurationBuilder.class); + configureRemoteCacheStore(sessionConfigBuilder, async, InfinispanConnectionProvider.SESSION_CACHE_NAME, KeycloakRemoteStoreConfigurationBuilder.class); } Configuration sessionCacheConfiguration = sessionConfigBuilder.build(); cacheManager.defineConfiguration(InfinispanConnectionProvider.SESSION_CACHE_NAME, sessionCacheConfiguration); @@ -257,7 +254,7 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon if (jdgEnabled) { sessionConfigBuilder = new ConfigurationBuilder(); sessionConfigBuilder.read(sessionCacheConfigurationBase); - configureRemoteCacheStore(sessionConfigBuilder, async, InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME, KcRemoteStoreConfigurationBuilder.class); + configureRemoteCacheStore(sessionConfigBuilder, async, InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME, KeycloakRemoteStoreConfigurationBuilder.class); } sessionCacheConfiguration = sessionConfigBuilder.build(); cacheManager.defineConfiguration(InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME, sessionCacheConfiguration); @@ -265,7 +262,7 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon if (jdgEnabled) { sessionConfigBuilder = new ConfigurationBuilder(); sessionConfigBuilder.read(sessionCacheConfigurationBase); - configureRemoteCacheStore(sessionConfigBuilder, async, InfinispanConnectionProvider.LOGIN_FAILURE_CACHE_NAME, KcRemoteStoreConfigurationBuilder.class); + configureRemoteCacheStore(sessionConfigBuilder, async, InfinispanConnectionProvider.LOGIN_FAILURE_CACHE_NAME, KeycloakRemoteStoreConfigurationBuilder.class); } sessionCacheConfiguration = sessionConfigBuilder.build(); cacheManager.defineConfiguration(InfinispanConnectionProvider.LOGIN_FAILURE_CACHE_NAME, sessionCacheConfiguration); diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/KcRemoteStoreConfigurationBuilder.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/KcRemoteStoreConfigurationBuilder.java deleted file mode 100644 index 9e99967467..0000000000 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/KcRemoteStoreConfigurationBuilder.java +++ /dev/null @@ -1,39 +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.configuration.cache.PersistenceConfigurationBuilder; -import org.infinispan.persistence.remote.configuration.RemoteStoreConfiguration; -import org.infinispan.persistence.remote.configuration.RemoteStoreConfigurationBuilder; - -/** - * @author Marek Posolda - */ -public class KcRemoteStoreConfigurationBuilder extends RemoteStoreConfigurationBuilder { - - public KcRemoteStoreConfigurationBuilder(PersistenceConfigurationBuilder builder) { - super(builder); - } - - @Override - public KcRemoteStoreConfiguration create() { - RemoteStoreConfiguration cfg = super.create(); - KcRemoteStoreConfiguration cfg2 = new KcRemoteStoreConfiguration(cfg.attributes(), cfg.async(), cfg.singletonStore(), cfg.asyncExecutorFactory(), cfg.connectionPool()); - return cfg2; - } -} diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/KcRemoteStore.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/KeycloakRemoteStore.java similarity index 50% rename from model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/KcRemoteStore.java rename to model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/KeycloakRemoteStore.java index ba413ae124..a0f5e42379 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/KcRemoteStore.java +++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/KeycloakRemoteStore.java @@ -17,14 +17,23 @@ package org.keycloak.models.sessions.infinispan.remotestore; +import java.util.Optional; import java.util.concurrent.Executor; -import org.infinispan.client.hotrod.Flag; +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.remote.configuration.RemoteStoreConfigurationBuilder; +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; @@ -33,26 +42,68 @@ import org.keycloak.models.sessions.infinispan.entities.SessionEntity; /** * @author Marek Posolda */ -@ConfiguredBy(KcRemoteStoreConfiguration.class) -public class KcRemoteStore extends RemoteStore { +@ConfiguredBy(KeycloakRemoteStoreConfiguration.class) +public class KeycloakRemoteStore extends RemoteStore { - protected static final Logger logger = Logger.getLogger(KcRemoteStore.class); + protected static final Logger logger = Logger.getLogger(KeycloakRemoteStore.class); - private String cacheName; + private String remoteCacheName; @Override public void start() throws PersistenceException { + this.remoteCacheName = getConfiguration().remoteCacheName(); + + 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); + + Optional optional = cacheManager.getCacheConfiguration(cacheTemplateName).persistence().stores().stream().filter((StoreConfiguration storeConfig) -> { + + return storeConfig instanceof RemoteStoreConfiguration; + + }).findFirst(); + + if (!optional.isPresent()) { + throw new CacheException("Unable to find remoteStore on cache '" + cacheTemplateName + "."); + } + + RemoteStoreConfiguration templateConfig = (RemoteStoreConfiguration) optional.get(); + + // We have template configuration, so create new configuration from it. Override just remoteCacheName + PersistenceConfigurationBuilder readPersistenceBuilder = new ConfigurationBuilder().read(ctx.getCache().getCacheConfiguration()).persistence(); + RemoteStoreConfigurationBuilder configBuilder = new RemoteStoreConfigurationBuilder(readPersistenceBuilder); + configBuilder.read(templateConfig); + + configBuilder.remoteCacheName(this.remoteCacheName); + + 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()); + } + super.start(); + if (getRemoteCache() == null) { String cacheName = getConfiguration().remoteCacheName(); - throw new IllegalStateException("Remote cache '" + cacheName + "' is not available."); + throw new CacheException("Remote cache '" + cacheName + "' is not available."); } - this.cacheName = getRemoteCache().getName(); } @Override public MarshalledEntry load(Object key) throws PersistenceException { - logger.debugf("Calling load: '%s' for remote cache '%s'", key, cacheName); + logger.debugf("Calling load: '%s' for remote cache '%s'", key, remoteCacheName); MarshalledEntry entry = super.load(key); if (entry == null) { @@ -74,7 +125,7 @@ public class KcRemoteStore extends RemoteStore { // 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) { - logger.debugf("Skip calling process with filter '%s' on cache '%s'", filter, cacheName); + logger.debugf("Skip calling process with filter '%s' on cache '%s'", filter, remoteCacheName); // super.process(filter, task, executor, fetchValue, fetchMetadata); } @@ -87,7 +138,7 @@ public class KcRemoteStore extends RemoteStore { @Override public boolean delete(Object key) throws PersistenceException { - logger.debugf("Calling delete for key '%s' on cache '%s'", key, cacheName); + 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 @@ -101,5 +152,8 @@ public class KcRemoteStore extends RemoteStore { } - + @Override + public KeycloakRemoteStoreConfiguration getConfiguration() { + return (KeycloakRemoteStoreConfiguration) super.getConfiguration(); + } } diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/KcRemoteStoreConfiguration.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/KeycloakRemoteStoreConfiguration.java similarity index 53% rename from model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/KcRemoteStoreConfiguration.java rename to model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/KeycloakRemoteStoreConfiguration.java index d786872e24..79788e32dc 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/KcRemoteStoreConfiguration.java +++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/KeycloakRemoteStoreConfiguration.java @@ -19,22 +19,30 @@ 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.AttributeSet; -import org.infinispan.configuration.cache.AsyncStoreConfiguration; -import org.infinispan.configuration.cache.SingletonStoreConfiguration; -import org.infinispan.persistence.remote.configuration.ConnectionPoolConfiguration; -import org.infinispan.persistence.remote.configuration.ExecutorFactoryConfiguration; +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(KcRemoteStoreConfigurationBuilder.class) -@ConfigurationFor(KcRemoteStore.class) -public class KcRemoteStoreConfiguration extends RemoteStoreConfiguration { +@BuiltBy(KeycloakRemoteStoreConfigurationBuilder.class) +@ConfigurationFor(KeycloakRemoteStore.class) +public class KeycloakRemoteStoreConfiguration extends RemoteStoreConfiguration { - public KcRemoteStoreConfiguration(AttributeSet attributes, AsyncStoreConfiguration async, SingletonStoreConfiguration singletonStore, - ExecutorFactoryConfiguration asyncExecutorFactory, ConnectionPoolConfiguration connectionPool) { - super(attributes, async, singletonStore, asyncExecutorFactory, connectionPool); + static final AttributeDefinition USE_CONFIG_TEMPLATE_FROM_CACHE = AttributeDefinition.builder("useConfigTemplateFromCache", null, String.class).immutable().build(); + + private final Attribute useConfigTemplateFromCache; + + + public KeycloakRemoteStoreConfiguration(RemoteStoreConfiguration other) { + super(other.attributes(), other.async(), other.singletonStore(), other.asyncExecutorFactory(), other.connectionPool()); + useConfigTemplateFromCache = attributes.attribute(USE_CONFIG_TEMPLATE_FROM_CACHE.name()); + } + + + + public String useConfigTemplateFromCache() { + return useConfigTemplateFromCache==null ? null : useConfigTemplateFromCache.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 new file mode 100644 index 0000000000..2a0ec19705 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/KeycloakRemoteStoreConfigurationBuilder.java @@ -0,0 +1,61 @@ +/* + * 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 { + AttributeDefinition def = KeycloakRemoteStoreConfiguration.USE_CONFIG_TEMPLATE_FROM_CACHE; + Attribute attribute = def.toAttribute(); + + Field f = Reflections.findDeclaredField(AttributeSet.class, "attributes"); + f.setAccessible(true); + Map> attributesInternal = (Map>) f.get(this.attributes); + attributesInternal.put(def.name(), attribute); + } catch (IllegalAccessException iae) { + throw new CacheConfigurationException(iae); + } + } + + + @Override + public KeycloakRemoteStoreConfiguration create() { + RemoteStoreConfiguration cfg = super.create(); + KeycloakRemoteStoreConfiguration cfg2 = new KeycloakRemoteStoreConfiguration(cfg); + return cfg2; + } +} 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 825d05a224..d95c5f9137 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 @@ -29,15 +29,8 @@ import org.infinispan.client.hotrod.annotation.ClientCacheEntryModified; import org.infinispan.client.hotrod.annotation.ClientListener; import org.infinispan.client.hotrod.event.ClientCacheEntryCreatedEvent; import org.infinispan.client.hotrod.event.ClientCacheEntryModifiedEvent; -import org.infinispan.configuration.cache.Configuration; -import org.infinispan.configuration.cache.ConfigurationBuilder; -import org.infinispan.configuration.global.GlobalConfigurationBuilder; import org.infinispan.context.Flag; -import org.infinispan.manager.DefaultCacheManager; import org.infinispan.manager.EmbeddedCacheManager; -import org.infinispan.persistence.manager.PersistenceManager; -import org.infinispan.persistence.remote.RemoteStore; -import org.infinispan.persistence.remote.configuration.ExhaustedAction; import org.jboss.logging.Logger; import org.junit.Assert; import org.keycloak.common.util.Time; @@ -46,8 +39,7 @@ 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.KcRemoteStore; -import org.keycloak.models.sessions.infinispan.remotestore.KcRemoteStoreConfigurationBuilder; +import org.keycloak.models.sessions.infinispan.remotestore.KeycloakRemoteStoreConfigurationBuilder; import org.keycloak.models.sessions.infinispan.util.InfinispanUtil; /** @@ -212,7 +204,7 @@ public class ConcurrencyJDGSessionsCacheTest { private static EmbeddedCacheManager createManager(int threadId) { - return new TestCacheManagerFactory().createManager(threadId, InfinispanConnectionProvider.SESSION_CACHE_NAME, KcRemoteStoreConfigurationBuilder.class); + return new TestCacheManagerFactory().createManager(threadId, InfinispanConnectionProvider.SESSION_CACHE_NAME, KeycloakRemoteStoreConfigurationBuilder.class); }