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);
}