diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/content/standalone/configuration/keycloak-server.json b/distribution/feature-packs/server-feature-pack/src/main/resources/content/standalone/configuration/keycloak-server.json index 407f0c003a..c4139a1ac7 100755 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/content/standalone/configuration/keycloak-server.json +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/content/standalone/configuration/keycloak-server.json @@ -23,7 +23,7 @@ }, "userCache": { - "infinispan" : { + "default" : { "enabled": true } }, @@ -61,15 +61,15 @@ }, "realmCache": { - "provider": "infinispan-locking", - "infinispan-locking" : { + "provider": "default", + "default" : { "enabled": true } }, "connectionsInfinispan": { - "provider": "locking", - "locking": { + "provider": "default", + "default": { "cacheContainer" : "java:comp/env/infinispan/Keycloak" } } 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 01d229fd54..ded6c3c917 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 @@ -24,6 +24,7 @@ import org.infinispan.configuration.global.GlobalConfigurationBuilder; import org.infinispan.manager.DefaultCacheManager; import org.infinispan.manager.EmbeddedCacheManager; import org.infinispan.transaction.LockingMode; +import org.infinispan.transaction.TransactionMode; import org.infinispan.transaction.lookup.DummyTransactionManagerLookup; import org.jboss.logging.Logger; import org.keycloak.Config; @@ -154,6 +155,15 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon } Configuration replicationCacheConfiguration = replicationConfigBuilder.build(); cacheManager.defineConfiguration(InfinispanConnectionProvider.WORK_CACHE_NAME, replicationCacheConfiguration); + + ConfigurationBuilder counterConfigBuilder = new ConfigurationBuilder(); + counterConfigBuilder.invocationBatching().enable() + .transaction().transactionMode(TransactionMode.TRANSACTIONAL); + counterConfigBuilder.transaction().transactionManagerLookup(new DummyTransactionManagerLookup()); + counterConfigBuilder.transaction().lockingMode(LockingMode.PESSIMISTIC); + Configuration counterCacheConfiguration = counterConfigBuilder.build(); + + cacheManager.defineConfiguration(InfinispanConnectionProvider.VERSION_CACHE_NAME, counterCacheConfiguration); } } diff --git a/model/infinispan/src/main/java/org/keycloak/connections/infinispan/InfinispanConnectionProvider.java b/model/infinispan/src/main/java/org/keycloak/connections/infinispan/InfinispanConnectionProvider.java old mode 100644 new mode 100755 index b37beb9c0c..8a21def7ec --- a/model/infinispan/src/main/java/org/keycloak/connections/infinispan/InfinispanConnectionProvider.java +++ b/model/infinispan/src/main/java/org/keycloak/connections/infinispan/InfinispanConnectionProvider.java @@ -25,6 +25,7 @@ import org.keycloak.provider.Provider; */ public interface InfinispanConnectionProvider extends Provider { + public static final String VERSION_CACHE_NAME = "realmVersions"; static final String REALM_CACHE_NAME = "realms"; static final String USER_CACHE_NAME = "users"; static final String SESSION_CACHE_NAME = "sessions"; diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java index a9145b2bd1..cdaa2e7537 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java @@ -45,7 +45,7 @@ public class ClientAdapter implements ClientModel { private void getDelegateForUpdate() { if (updated == null) { - cacheSession.registerApplicationInvalidation(getId()); + cacheSession.registerClientInvalidation(getId()); updated = cacheSession.getDelegate().getClientById(getId(), cachedRealm); if (updated == null) throw new IllegalStateException("Not found in database"); } @@ -377,7 +377,6 @@ public class ClientAdapter implements ClientModel { public void setClientId(String clientId) { getDelegateForUpdate(); updated.setClientId(clientId); - cacheSession.registerRealmInvalidation(cachedRealm.getId()); } @Override diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/DefaultCacheRealmProvider.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/DefaultCacheRealmProvider.java deleted file mode 100755 index b998b8c02b..0000000000 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/DefaultCacheRealmProvider.java +++ /dev/null @@ -1,396 +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.cache.infinispan; - -import org.keycloak.migration.MigrationModel; -import org.keycloak.models.ClientModel; -import org.keycloak.models.ClientTemplateModel; -import org.keycloak.models.GroupModel; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakTransaction; -import org.keycloak.models.RealmModel; -import org.keycloak.models.RealmProvider; -import org.keycloak.models.RoleModel; -import org.keycloak.models.cache.CacheRealmProvider; -import org.keycloak.models.cache.RealmCache; -import org.keycloak.models.cache.entities.CachedClient; -import org.keycloak.models.cache.entities.CachedClientRole; -import org.keycloak.models.cache.entities.CachedClientTemplate; -import org.keycloak.models.cache.entities.CachedGroup; -import org.keycloak.models.cache.entities.CachedRealm; -import org.keycloak.models.cache.entities.CachedRealmRole; -import org.keycloak.models.cache.entities.CachedRole; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - - -/** - * @author Bill Burke - * @version $Revision: 1 $ - */ -public class DefaultCacheRealmProvider implements CacheRealmProvider { - protected RealmCache cache; - protected KeycloakSession session; - protected RealmProvider delegate; - protected boolean transactionActive; - protected boolean setRollbackOnly; - - protected Set realmInvalidations = new HashSet<>(); - protected Set appInvalidations = new HashSet<>(); - protected Set clientTemplateInvalidations = new HashSet<>(); - protected Set roleInvalidations = new HashSet<>(); - protected Set groupInvalidations = new HashSet<>(); - protected Map managedRealms = new HashMap<>(); - protected Map managedApplications = new HashMap<>(); - protected Map managedClientTemplates = new HashMap<>(); - protected Map managedRoles = new HashMap<>(); - protected Map managedGroups = new HashMap<>(); - - protected boolean clearAll; - - public DefaultCacheRealmProvider(RealmCache cache, KeycloakSession session) { - this.cache = cache; - this.session = session; - - session.getTransaction().enlistAfterCompletion(getTransaction()); - } - - @Override - public void clear() { - cache.clear(); - } - - @Override - public MigrationModel getMigrationModel() { - return getDelegate().getMigrationModel(); - } - - @Override - public RealmProvider getDelegate() { - if (!transactionActive) throw new IllegalStateException("Cannot access delegate without a transaction"); - if (delegate != null) return delegate; - delegate = session.getProvider(RealmProvider.class); - return delegate; - } - - @Override - public void registerRealmInvalidation(String id) { - realmInvalidations.add(id); - } - - @Override - public void registerApplicationInvalidation(String id) { - appInvalidations.add(id); - } - @Override - public void registerClientTemplateInvalidation(String id) { - clientTemplateInvalidations.add(id); - } - - @Override - public void registerRoleInvalidation(String id) { - roleInvalidations.add(id); - } - - @Override - public void registerGroupInvalidation(String id) { - groupInvalidations.add(id); - - } - - protected void runInvalidations() { - for (String id : realmInvalidations) { - cache.invalidateRealmById(id); - } - for (String id : roleInvalidations) { - cache.invalidateRoleById(id); - } - for (String id : groupInvalidations) { - cache.invalidateGroupById(id); - } - for (String id : appInvalidations) { - cache.invalidateClientById(id); - } - for (String id : clientTemplateInvalidations) { - cache.invalidateClientTemplateById(id); - } - } - - private KeycloakTransaction getTransaction() { - return new KeycloakTransaction() { - @Override - public void begin() { - transactionActive = true; - } - - @Override - public void commit() { - if (delegate == null) return; - if (clearAll) { - cache.clear(); - } - runInvalidations(); - transactionActive = false; - } - - @Override - public void rollback() { - setRollbackOnly = true; - runInvalidations(); - transactionActive = false; - } - - @Override - public void setRollbackOnly() { - setRollbackOnly = true; - } - - @Override - public boolean getRollbackOnly() { - return setRollbackOnly; - } - - @Override - public boolean isActive() { - return transactionActive; - } - }; - } - - @Override - public RealmModel createRealm(String name) { - RealmModel realm = getDelegate().createRealm(name); - registerRealmInvalidation(realm.getId()); - return realm; - } - - @Override - public RealmModel createRealm(String id, String name) { - RealmModel realm = getDelegate().createRealm(id, name); - registerRealmInvalidation(realm.getId()); - return realm; - } - - @Override - public RealmModel getRealm(String id) { - CachedRealm cached = cache.getRealm(id); - if (cached == null) { - RealmModel model = getDelegate().getRealm(id); - if (model == null) return null; - if (realmInvalidations.contains(id)) return model; - cached = new CachedRealm(cache, this, model); - cache.addRealm(cached); - } else if (realmInvalidations.contains(id)) { - return getDelegate().getRealm(id); - } else if (managedRealms.containsKey(id)) { - return managedRealms.get(id); - } - RealmAdapter adapter = new RealmAdapter(cached, this); - managedRealms.put(id, adapter); - return adapter; - } - - @Override - public RealmModel getRealmByName(String name) { - CachedRealm cached = cache.getRealmByName(name); - if (cached == null) { - RealmModel model = getDelegate().getRealmByName(name); - if (model == null) return null; - if (realmInvalidations.contains(model.getId())) return model; - cached = new CachedRealm(cache, this, model); - cache.addRealm(cached); - } else if (realmInvalidations.contains(cached.getId())) { - return getDelegate().getRealmByName(name); - } else if (managedRealms.containsKey(cached.getId())) { - return managedRealms.get(cached.getId()); - } - RealmAdapter adapter = new RealmAdapter(cached, this); - managedRealms.put(cached.getId(), adapter); - return adapter; - } - - @Override - public List getRealms() { - // Retrieve realms from backend - List backendRealms = getDelegate().getRealms(); - - // Return cache delegates to ensure cache invalidated during write operations - List cachedRealms = new LinkedList(); - for (RealmModel realm : backendRealms) { - RealmModel cached = getRealm(realm.getId()); - cachedRealms.add(cached); - } - return cachedRealms; - } - - @Override - public boolean removeRealm(String id) { - cache.invalidateRealmById(id); - - RealmModel realm = getDelegate().getRealm(id); - Set realmRoles = null; - if (realm != null) { - realmRoles = realm.getRoles(); - } - - boolean didIt = getDelegate().removeRealm(id); - realmInvalidations.add(id); - - // TODO: Temporary workaround to invalidate cached realm roles - if (didIt && realmRoles != null) { - for (RoleModel role : realmRoles) { - roleInvalidations.add(role.getId()); - } - } - - return didIt; - } - - @Override - public void close() { - if (delegate != null) delegate.close(); - } - - @Override - public RoleModel getRoleById(String id, RealmModel realm) { - CachedRole cached = cache.getRole(id); - if (cached != null && !cached.getRealm().equals(realm.getId())) { - cached = null; - } - - if (cached == null) { - RoleModel model = getDelegate().getRoleById(id, realm); - if (model == null) return null; - if (roleInvalidations.contains(id)) return model; - if (model.getContainer() instanceof ClientModel) { - cached = new CachedClientRole(((ClientModel) model.getContainer()).getId(), model, realm); - } else { - cached = new CachedRealmRole(model, realm); - } - cache.addRole(cached); - - } else if (roleInvalidations.contains(id)) { - return getDelegate().getRoleById(id, realm); - } else if (managedRoles.containsKey(id)) { - return managedRoles.get(id); - } - RoleAdapter adapter = new RoleAdapter(cached, cache, this, realm); - managedRoles.put(id, adapter); - return adapter; - } - - @Override - public GroupModel getGroupById(String id, RealmModel realm) { - CachedGroup cached = cache.getGroup(id); - if (cached != null && !cached.getRealm().equals(realm.getId())) { - cached = null; - } - - if (cached == null) { - GroupModel model = getDelegate().getGroupById(id, realm); - if (model == null) return null; - if (groupInvalidations.contains(id)) return model; - cached = new CachedGroup(realm, model); - cache.addGroup(cached); - - } else if (groupInvalidations.contains(id)) { - return getDelegate().getGroupById(id, realm); - } else if (managedGroups.containsKey(id)) { - return managedGroups.get(id); - } - GroupAdapter adapter = new GroupAdapter(cached, this, session, realm); - managedGroups.put(id, adapter); - return adapter; - } - - @Override - public ClientModel getClientById(String id, RealmModel realm) { - CachedClient cached = cache.getClient(id); - if (cached != null && !cached.getRealm().equals(realm.getId())) { - cached = null; - } - - if (cached == null) { - ClientModel model = getDelegate().getClientById(id, realm); - if (model == null) return null; - if (appInvalidations.contains(id)) return model; - cached = new CachedClient(cache, getDelegate(), realm, model); - cache.addClient(cached); - } else if (appInvalidations.contains(id)) { - return getDelegate().getClientById(id, realm); - } else if (managedApplications.containsKey(id)) { - return managedApplications.get(id); - } - ClientAdapter adapter = new ClientAdapter(realm, cached, this, cache); - managedApplications.put(id, adapter); - return adapter; - } - - @Override - public ClientModel getClientByClientId(String clientId, RealmModel realm) { - return getDelegate().getClientByClientId(clientId, realm); - } - - @Override - public boolean removeClient(String id, RealmModel realm) { - ClientModel client = getClientById(id, realm); - if (client == null) return false; - registerApplicationInvalidation(id); - registerRealmInvalidation(realm.getId()); - cache.invalidateClientById(id); - cache.invalidateRealmById(realm.getId()); - - - - Set roles = client.getRoles(); - for (RoleModel role : roles) { - registerRoleInvalidation(role.getId()); - } - return getDelegate().removeClient(id, realm); - } - - - @Override - public ClientTemplateModel getClientTemplateById(String id, RealmModel realm) { - CachedClientTemplate cached = cache.getClientTemplate(id); - if (cached != null && !cached.getRealm().equals(realm.getId())) { - cached = null; - } - - if (cached == null) { - ClientTemplateModel model = getDelegate().getClientTemplateById(id, realm); - if (model == null) return null; - if (clientTemplateInvalidations.contains(id)) return model; - cached = new CachedClientTemplate(cache, getDelegate(), realm, model); - cache.addClientTemplate(cached); - } else if (clientTemplateInvalidations.contains(id)) { - return getDelegate().getClientTemplateById(id, realm); - } else if (managedClientTemplates.containsKey(id)) { - return managedClientTemplates.get(id); - } - ClientTemplateModel adapter = new ClientTemplateAdapter(realm, cached, this, cache); - managedClientTemplates.put(id, adapter); - return adapter; - } - -} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanCacheRealmProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanCacheRealmProviderFactory.java index 34aa5ced85..39bc4938e4 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanCacheRealmProviderFactory.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanCacheRealmProviderFactory.java @@ -36,6 +36,8 @@ import org.keycloak.models.cache.CacheRealmProvider; import org.keycloak.models.cache.CacheRealmProviderFactory; import org.keycloak.models.cache.entities.CachedClient; import org.keycloak.models.cache.entities.CachedRealm; +import org.keycloak.models.cache.infinispan.stream.StreamCacheRealmProvider; +import org.keycloak.models.cache.infinispan.stream.StreamRealmCache; import java.util.concurrent.ConcurrentHashMap; @@ -47,14 +49,12 @@ public class InfinispanCacheRealmProviderFactory implements CacheRealmProviderFa private static final Logger log = Logger.getLogger(InfinispanCacheRealmProviderFactory.class); - protected volatile InfinispanRealmCache realmCache; - - protected final ConcurrentHashMap realmLookup = new ConcurrentHashMap<>(); + protected volatile StreamRealmCache realmCache; @Override public CacheRealmProvider create(KeycloakSession session) { lazyInit(session); - return new DefaultCacheRealmProvider(realmCache, session); + return new StreamCacheRealmProvider(realmCache, session); } private void lazyInit(KeycloakSession session) { @@ -62,8 +62,8 @@ public class InfinispanCacheRealmProviderFactory implements CacheRealmProviderFa synchronized (this) { if (realmCache == null) { Cache cache = session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.REALM_CACHE_NAME); - cache.addListener(new CacheListener()); - realmCache = new InfinispanRealmCache(cache, realmLookup); + Cache revisions = session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.VERSION_CACHE_NAME); + realmCache = new StreamRealmCache(cache, revisions); } } } @@ -84,77 +84,7 @@ public class InfinispanCacheRealmProviderFactory implements CacheRealmProviderFa @Override public String getId() { - return "infinispan"; + return "default"; } - @Listener - public class CacheListener { - - @CacheEntryCreated - public void created(CacheEntryCreatedEvent event) { - if (!event.isPre()) { - Object object = event.getValue(); - if (object != null) { - if (object instanceof CachedRealm) { - CachedRealm realm = (CachedRealm) object; - realmLookup.put(realm.getName(), realm.getId()); - log.tracev("Realm added realm={0}", realm.getName()); - } - } - } - } - - @CacheEntryRemoved - public void removed(CacheEntryRemovedEvent event) { - if (event.isPre()) { - Object object = event.getValue(); - if (object != null) { - remove(object); - } - } - } - - @CacheEntryInvalidated - public void removed(CacheEntryInvalidatedEvent event) { - if (event.isPre()) { - Object object = event.getValue(); - if (object != null) { - remove(object); - } - } - } - - @CacheEntriesEvicted - public void userEvicted(CacheEntriesEvictedEvent event) { - for (Object object : event.getEntries().values()) { - remove(object); - } - } - - private void remove(Object object) { - if (object instanceof CachedRealm) { - CachedRealm realm = (CachedRealm) object; - - realmLookup.remove(realm.getName()); - - for (String r : realm.getRealmRoles().values()) { - realmCache.evictRoleById(r); - } - - for (String c : realm.getClients()) { - realmCache.evictClientById(c); - } - - log.tracev("Realm removed realm={0}", realm.getName()); - } else if (object instanceof CachedClient) { - CachedClient client = (CachedClient) object; - - for (String r : client.getRoles().values()) { - realmCache.evictRoleById(r); - } - - log.tracev("Client removed client={0}", client.getId()); - } - } - } } diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanCacheUserProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanCacheUserProviderFactory.java index b5f62e22fb..b473d60e9f 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanCacheUserProviderFactory.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanCacheUserProviderFactory.java @@ -78,7 +78,7 @@ public class InfinispanCacheUserProviderFactory implements CacheUserProviderFact @Override public String getId() { - return "infinispan"; + return "default"; } @Listener diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanRealmCache.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanRealmCache.java deleted file mode 100755 index 8dfe923c28..0000000000 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanRealmCache.java +++ /dev/null @@ -1,202 +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.cache.infinispan; - -import org.infinispan.Cache; -import org.jboss.logging.Logger; -import org.keycloak.models.cache.RealmCache; -import org.keycloak.models.cache.entities.CachedClient; -import org.keycloak.models.cache.entities.CachedClientTemplate; -import org.keycloak.models.cache.entities.CachedGroup; -import org.keycloak.models.cache.entities.CachedRealm; -import org.keycloak.models.cache.entities.CachedRole; - -import java.util.concurrent.ConcurrentHashMap; - -/** - * @author Stian Thorgersen - */ -public class InfinispanRealmCache implements RealmCache { - - protected static final Logger logger = Logger.getLogger(InfinispanRealmCache.class); - - protected final Cache cache; - protected final ConcurrentHashMap realmLookup; - - public InfinispanRealmCache(Cache cache, ConcurrentHashMap realmLookup) { - this.cache = cache; - this.realmLookup = realmLookup; - } - - public Cache getCache() { - return cache; - } - - @Override - public void clear() { - cache.clear(); - } - - @Override - public CachedRealm getRealm(String id) { - return get(id, CachedRealm.class); - } - - @Override - public void invalidateRealm(CachedRealm realm) { - logger.tracev("Invalidating realm {0}", realm.getId()); - cache.remove(realm.getId()); - realmLookup.remove(realm.getName()); - } - - @Override - public void invalidateRealmById(String id) { - CachedRealm cached = (CachedRealm) cache.remove(id); - if (cached != null) realmLookup.remove(cached.getName()); - } - - @Override - public void addRealm(CachedRealm realm) { - logger.tracev("Adding realm {0}", realm.getId()); - cache.putForExternalRead(realm.getId(), realm); - realmLookup.put(realm.getName(), realm.getId()); - } - - @Override - public CachedRealm getRealmByName(String name) { - String id = realmLookup.get(name); - return id != null ? getRealm(id) : null; - } - - @Override - public CachedClient getClient(String id) { - return get(id, CachedClient.class); - } - - @Override - public void invalidateClient(CachedClient app) { - logger.tracev("Removing application {0}", app.getId()); - cache.remove(app.getId()); - } - - @Override - public void addClient(CachedClient app) { - logger.tracev("Adding application {0}", app.getId()); - cache.putForExternalRead(app.getId(), app); - } - - @Override - public void invalidateClientById(String id) { - logger.tracev("Removing application {0}", id); - cache.remove(id); - } - - @Override - public void evictClientById(String id) { - logger.tracev("Evicting application {0}", id); - cache.evict(id); - } - - @Override - public CachedGroup getGroup(String id) { - return get(id, CachedGroup.class); - } - - @Override - public void invalidateGroup(CachedGroup role) { - logger.tracev("Removing group {0}", role.getId()); - cache.remove(role.getId()); - } - - @Override - public void addGroup(CachedGroup role) { - logger.tracev("Adding group {0}", role.getId()); - cache.putForExternalRead(role.getId(), role); - } - - @Override - public void invalidateGroupById(String id) { - logger.tracev("Removing group {0}", id); - cache.remove(id); - } - - @Override - public CachedRole getRole(String id) { - return get(id, CachedRole.class); - } - - @Override - public void invalidateRole(CachedRole role) { - logger.tracev("Removing role {0}", role.getId()); - cache.remove(role.getId()); - } - - @Override - public void invalidateRoleById(String id) { - logger.tracev("Removing role {0}", id); - cache.remove(id); - } - - @Override - public void evictRoleById(String id) { - logger.tracev("Evicting role {0}", id); - cache.evict(id); - } - - @Override - public void addRole(CachedRole role) { - logger.tracev("Adding role {0}", role.getId()); - cache.putForExternalRead(role.getId(), role); - } - - private T get(String id, Class type) { - Object o = cache.get(id); - return o != null && type.isInstance(o) ? type.cast(o) : null; - } - - @Override - public CachedClientTemplate getClientTemplate(String id) { - return get(id, CachedClientTemplate.class); - } - - @Override - public void invalidateClientTemplate(CachedClientTemplate app) { - logger.tracev("Removing client template {0}", app.getId()); - cache.remove(app.getId()); - } - - @Override - public void addClientTemplate(CachedClientTemplate app) { - logger.tracev("Adding client template {0}", app.getId()); - cache.putForExternalRead(app.getId(), app); - } - - @Override - public void invalidateClientTemplateById(String id) { - logger.tracev("Removing client template {0}", id); - cache.remove(id); - } - - @Override - public void evictClientTemplateById(String id) { - logger.tracev("Evicting client template {0}", id); - cache.evict(id); - } - - -} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanUserCache.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanUserCache.java index 52edabcb7d..de83e6ac71 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanUserCache.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanUserCache.java @@ -27,7 +27,7 @@ import org.keycloak.models.cache.entities.CachedUser; */ public class InfinispanUserCache implements UserCache { - protected static final Logger logger = Logger.getLogger(InfinispanRealmCache.class); + protected static final Logger logger = Logger.getLogger(InfinispanUserCache.class); protected volatile boolean enabled = true; diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java index 2f3048ad31..39564ef434 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java @@ -589,40 +589,23 @@ public class RealmAdapter implements RealmModel { @Override public List getClients() { - if (updated != null) return updated.getClients(); - List apps = new LinkedList<>(); - for (String id : cached.getClients()) { - ClientModel model = cacheSession.getClientById(id, this); - if (model == null) { - throw new IllegalStateException("Cached application not found: " + id); - } - apps.add(model); - } - return Collections.unmodifiableList(apps); + return cacheSession.getClients(this); } @Override public ClientModel addClient(String name) { - getDelegateForUpdate(); - ClientModel app = updated.addClient(name); - cacheSession.registerApplicationInvalidation(app.getId()); - return app; + return cacheSession.addClient(this, name); } @Override public ClientModel addClient(String id, String clientId) { - getDelegateForUpdate(); - ClientModel app = updated.addClient(id, clientId); - cacheSession.registerApplicationInvalidation(app.getId()); - return app; + return cacheSession.addClient(this, id, clientId); } @Override public boolean removeClient(String id) { - cacheSession.registerApplicationInvalidation(id); - getDelegateForUpdate(); - return updated.removeClient(id); + return cacheSession.removeClient(id, this); } @Override diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RoleAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RoleAdapter.java index 6458c1ae1e..c7ebc44cfb 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RoleAdapter.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RoleAdapter.java @@ -125,7 +125,7 @@ public class RoleAdapter implements RoleModel { for (String id : cached.getComposites()) { RoleModel role = realm.getRoleById(id); if (role == null) { - throw new IllegalStateException("Could not find composite: " + id); + throw new IllegalStateException("Could not find composite in role " + getName() + ": " + id); } set.add(role); } @@ -138,7 +138,7 @@ public class RoleAdapter implements RoleModel { return realm; } else { CachedClientRole appRole = (CachedClientRole)cached; - return realm.getClientById(appRole.getIdClient()); + return realm.getClientById(appRole.getClientId()); } } diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/LockingCacheRealmProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/LockingCacheRealmProviderFactory.java deleted file mode 100755 index d2bfbf4389..0000000000 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/LockingCacheRealmProviderFactory.java +++ /dev/null @@ -1,159 +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.cache.infinispan.locking; - -import org.infinispan.Cache; -import org.infinispan.notifications.Listener; -import org.infinispan.notifications.cachelistener.annotation.CacheEntriesEvicted; -import org.infinispan.notifications.cachelistener.annotation.CacheEntryCreated; -import org.infinispan.notifications.cachelistener.annotation.CacheEntryInvalidated; -import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved; -import org.infinispan.notifications.cachelistener.event.CacheEntriesEvictedEvent; -import org.infinispan.notifications.cachelistener.event.CacheEntryCreatedEvent; -import org.infinispan.notifications.cachelistener.event.CacheEntryInvalidatedEvent; -import org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent; -import org.jboss.logging.Logger; -import org.keycloak.Config; -import org.keycloak.connections.infinispan.InfinispanConnectionProvider; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; -import org.keycloak.models.cache.CacheRealmProvider; -import org.keycloak.models.cache.CacheRealmProviderFactory; -import org.keycloak.models.cache.entities.CachedClient; -import org.keycloak.models.cache.entities.CachedRealm; - -/** - * @author Bill Burke - * @author Stian Thorgersen - */ -public class LockingCacheRealmProviderFactory implements CacheRealmProviderFactory { - - private static final Logger log = Logger.getLogger(LockingCacheRealmProviderFactory.class); - - protected volatile LockingRealmCache realmCache; - - @Override - public CacheRealmProvider create(KeycloakSession session) { - lazyInit(session); - return new LockingCacheRealmProvider(realmCache, session); - } - - private void lazyInit(KeycloakSession session) { - if (realmCache == null) { - synchronized (this) { - if (realmCache == null) { - Cache cache = session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.REALM_CACHE_NAME); - Cache counterCache = session.getProvider(InfinispanConnectionProvider.class).getCache(LockingConnectionProviderFactory.VERSION_CACHE_NAME); - cache.addListener(new CacheListener()); - realmCache = new LockingRealmCache(cache, counterCache); - } - } - } - } - - @Override - public void init(Config.Scope config) { - } - - @Override - public void postInit(KeycloakSessionFactory factory) { - - } - - @Override - public void close() { - } - - @Override - public String getId() { - return "infinispan-locking"; - } - - @Listener - public class CacheListener { - - @CacheEntryCreated - public void created(CacheEntryCreatedEvent event) { - if (!event.isPre()) { - Object object = event.getValue(); - if (object != null) { - if (object instanceof CachedRealm) { - CachedRealm realm = (CachedRealm) object; - realmCache.getRealmLookup().put(realm.getName(), realm.getId()); - log.tracev("Realm added realm={0}", realm.getName()); - } - } - } - } - - @CacheEntryRemoved - public void removed(CacheEntryRemovedEvent event) { - if (event.isPre()) { - Object object = event.getValue(); - if (object != null) { - remove(object); - } - } - } - - @CacheEntryInvalidated - public void removed(CacheEntryInvalidatedEvent event) { - if (event.isPre()) { - Object object = event.getValue(); - if (object != null) { - remove(object); - } - } - } - - @CacheEntriesEvicted - public void userEvicted(CacheEntriesEvictedEvent event) { - for (Object object : event.getEntries().values()) { - remove(object); - } - } - - private void remove(Object object) { - if (object instanceof CachedRealm) { - CachedRealm realm = (CachedRealm) object; - - realmCache.getRealmLookup().remove(realm.getName()); - - for (String r : realm.getRealmRoles().values()) { - realmCache.evictRoleById(r); - } - - for (String c : realm.getClients()) { - realmCache.evictClientById(c); - } - - log.tracev("Realm removed realm={0}", realm.getName()); - } else if (object instanceof CachedClient) { - CachedClient client = (CachedClient) object; - - realmCache.getClientLookup().remove(client.getRealm() + "." + client.getClientId()); - - for (String r : client.getRoles().values()) { - realmCache.evictRoleById(r); - } - - log.tracev("Client removed client={0}", client.getId()); - } - } - } -} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/LockingConnectionProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/LockingConnectionProviderFactory.java deleted file mode 100755 index 68da34dac2..0000000000 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/LockingConnectionProviderFactory.java +++ /dev/null @@ -1,54 +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.cache.infinispan.locking; - -import org.infinispan.configuration.cache.Configuration; -import org.infinispan.configuration.cache.ConfigurationBuilder; -import org.infinispan.transaction.LockingMode; -import org.infinispan.transaction.TransactionMode; -import org.infinispan.transaction.lookup.DummyTransactionManagerLookup; -import org.jboss.logging.Logger; -import org.keycloak.connections.infinispan.DefaultInfinispanConnectionProviderFactory; - -/** - * @author Stian Thorgersen - */ -public class LockingConnectionProviderFactory extends DefaultInfinispanConnectionProviderFactory { - public static final String VERSION_CACHE_NAME = "realmVersions"; - - protected static final Logger logger = Logger.getLogger(LockingConnectionProviderFactory.class); - - @Override - public String getId() { - return "locking"; - } - - - protected void initEmbedded() { - super.initEmbedded(); - ConfigurationBuilder counterConfigBuilder = new ConfigurationBuilder(); - counterConfigBuilder.invocationBatching().enable() - .transaction().transactionMode(TransactionMode.TRANSACTIONAL); - counterConfigBuilder.transaction().transactionManagerLookup(new DummyTransactionManagerLookup()); - counterConfigBuilder.transaction().lockingMode(LockingMode.PESSIMISTIC); - Configuration counterCacheConfiguration = counterConfigBuilder.build(); - - cacheManager.defineConfiguration(VERSION_CACHE_NAME, counterCacheConfiguration); - } - -} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/LockingRealmCache.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/LockingRealmCache.java deleted file mode 100755 index a77c59b8e2..0000000000 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/LockingRealmCache.java +++ /dev/null @@ -1,300 +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.cache.infinispan.locking; - -import org.infinispan.Cache; -import org.jboss.logging.Logger; -import org.keycloak.models.RealmModel; -import org.keycloak.models.cache.RealmCache; -import org.keycloak.models.cache.entities.CachedClient; -import org.keycloak.models.cache.entities.CachedClientTemplate; -import org.keycloak.models.cache.entities.CachedGroup; -import org.keycloak.models.cache.entities.CachedRealm; -import org.keycloak.models.cache.entities.CachedRole; - -import java.util.concurrent.ConcurrentHashMap; - -/** - * @author Stian Thorgersen - */ -public class LockingRealmCache implements RealmCache { - - protected static final Logger logger = Logger.getLogger(LockingRealmCache.class); - - protected final Cache revisions; - protected final Cache cache; - - protected final ConcurrentHashMap realmLookup = new ConcurrentHashMap<>(); - protected final ConcurrentHashMap clientLookup = new ConcurrentHashMap<>(); - - public LockingRealmCache(Cache cache, Cache revisions) { - this.cache = cache; - this.revisions = revisions; - } - - public Cache getCache() { - return cache; - } - - public Cache getRevisions() { - return revisions; - } - - public ConcurrentHashMap getRealmLookup() { - return realmLookup; - } - - public ConcurrentHashMap getClientLookup() { - return clientLookup; - } - - public Long getCurrentRevision(String id) { - return revisions.get(id); - } - - public void endRevisionBatch() { - try { - revisions.endBatch(true); - } catch (Exception e) { - } - - } - - private T get(String id, Class type) { - Revisioned o = (Revisioned)cache.get(id); - if (o == null) { - return null; - } - Long rev = revisions.get(id); - if (rev == null) { - logger.tracev("get() missing rev"); - return null; - } - long oRev = o.getRevision() == null ? -1L : o.getRevision().longValue(); - if (rev > oRev) { - logger.tracev("get() rev: {0} o.rev: {1}", rev.longValue(), oRev); - return null; - } - return o != null && type.isInstance(o) ? type.cast(o) : null; - } - - protected Object invalidateObject(String id) { - Object removed = cache.remove(id); - revisions.put(id, UpdateCounter.next()); - return removed; - } - - protected void addRevisioned(String id, Revisioned object) { - //startRevisionBatch(); - try { - //revisions.getAdvancedCache().lock(id); - Long rev = revisions.get(id); - if (rev == null) { - rev = UpdateCounter.current(); - revisions.put(id, rev); - } - revisions.startBatch(); - if (!revisions.getAdvancedCache().lock(id)) { - logger.trace("Could not obtain version lock"); - } - rev = revisions.get(id); - if (rev == null) { - return; - } - if (rev.equals(object.getRevision())) { - cache.putForExternalRead(id, object); - return; - } - if (rev > object.getRevision()) { // revision is ahead, don't cache - return; - } - // revisions cache has a lower value than the object.revision, so update revision and add it to cache - revisions.put(id, object.getRevision()); - cache.putForExternalRead(id, object); - } finally { - endRevisionBatch(); - } - - } - - - - - @Override - public void clear() { - cache.clear(); - } - - @Override - public CachedRealm getRealm(String id) { - return get(id, CachedRealm.class); - } - - @Override - public void invalidateRealm(CachedRealm realm) { - logger.tracev("Invalidating realm {0}", realm.getId()); - invalidateObject(realm.getId()); - realmLookup.remove(realm.getName()); - } - - @Override - public void invalidateRealmById(String id) { - CachedRealm cached = (CachedRealm) invalidateObject(id); - if (cached != null) realmLookup.remove(cached.getName()); - } - - @Override - public void addRealm(CachedRealm realm) { - logger.tracev("Adding realm {0}", realm.getId()); - addRevisioned(realm.getId(), (Revisioned) realm); - realmLookup.put(realm.getName(), realm.getId()); - } - - - @Override - public CachedRealm getRealmByName(String name) { - String id = realmLookup.get(name); - return id != null ? getRealm(id) : null; - } - - @Override - public CachedClient getClient(String id) { - return get(id, CachedClient.class); - } - - public CachedClient getClientByClientId(RealmModel realm, String clientId) { - String id = clientLookup.get(realm.getId() + "." + clientId); - return id != null ? getClient(id) : null; - } - - @Override - public void invalidateClient(CachedClient app) { - logger.tracev("Removing application {0}", app.getId()); - invalidateObject(app.getId()); - clientLookup.remove(getClientIdKey(app)); - } - - @Override - public void addClient(CachedClient app) { - logger.tracev("Adding application {0}", app.getId()); - addRevisioned(app.getId(), (Revisioned) app); - clientLookup.put(getClientIdKey(app), app.getId()); - } - - @Override - public void invalidateClientById(String id) { - CachedClient client = (CachedClient)invalidateObject(id); - if (client != null) { - logger.tracev("Removing application {0}", client.getClientId()); - clientLookup.remove(getClientIdKey(client)); - } - } - - protected String getClientIdKey(CachedClient client) { - return client.getRealm() + "." + client.getClientId(); - } - - @Override - public void evictClientById(String id) { - logger.tracev("Evicting application {0}", id); - cache.evict(id); - } - - @Override - public CachedGroup getGroup(String id) { - return get(id, CachedGroup.class); - } - - @Override - public void invalidateGroup(CachedGroup role) { - logger.tracev("Removing group {0}", role.getId()); - invalidateObject(role.getId()); - } - - @Override - public void addGroup(CachedGroup role) { - logger.tracev("Adding group {0}", role.getId()); - addRevisioned(role.getId(), (Revisioned) role); - } - - @Override - public void invalidateGroupById(String id) { - logger.tracev("Removing group {0}", id); - invalidateObject(id); - } - - @Override - public CachedRole getRole(String id) { - return get(id, CachedRole.class); - } - - @Override - public void invalidateRole(CachedRole role) { - logger.tracev("Removing role {0}", role.getId()); - invalidateObject(role.getId()); - } - - @Override - public void invalidateRoleById(String id) { - logger.tracev("Removing role {0}", id); - invalidateObject(id); - } - - @Override - public void evictRoleById(String id) { - logger.tracev("Evicting role {0}", id); - cache.evict(id); - } - - @Override - public void addRole(CachedRole role) { - logger.tracev("Adding role {0}", role.getId()); - addRevisioned(role.getId(), (Revisioned) role); - } - - @Override - public CachedClientTemplate getClientTemplate(String id) { - return get(id, CachedClientTemplate.class); - } - - @Override - public void invalidateClientTemplate(CachedClientTemplate app) { - logger.tracev("Removing client template {0}", app.getId()); - invalidateObject(app.getId()); - } - - @Override - public void addClientTemplate(CachedClientTemplate app) { - logger.tracev("Adding client template {0}", app.getId()); - addRevisioned(app.getId(), (Revisioned) app); - } - - @Override - public void invalidateClientTemplateById(String id) { - logger.tracev("Removing client template {0}", id); - invalidateObject(id); - } - - @Override - public void evictClientTemplateById(String id) { - logger.tracev("Evicting client template {0}", id); - cache.evict(id); - } - - -} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/ClientListQuery.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/ClientListQuery.java new file mode 100755 index 0000000000..1a7d5e2d05 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/ClientListQuery.java @@ -0,0 +1,50 @@ +package org.keycloak.models.cache.infinispan.stream; + +import org.keycloak.models.RealmModel; +import org.keycloak.models.cache.infinispan.stream.entities.AbstractRevisioned; + +import java.util.HashSet; +import java.util.Set; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class ClientListQuery extends AbstractRevisioned implements ClientQuery { + private final Set clients; + private final String realm; + private final String realmName; + + public ClientListQuery(Long revisioned, String id, RealmModel realm, Set clients) { + super(revisioned, id); + this.realm = realm.getId(); + this.realmName = realm.getName(); + this.clients = clients; + } + + public ClientListQuery(Long revisioned, String id, RealmModel realm, String client) { + super(revisioned, id); + this.realm = realm.getId(); + this.realmName = realm.getName(); + this.clients = new HashSet<>(); + this.clients.add(client); + } + + @Override + public Set getClients() { + return clients; + } + + @Override + public String getRealm() { + return realm; + } + + @Override + public String toString() { + return "ClientListQuery{" + + "id='" + getId() + "'" + + "realmName='" + realmName + '\'' + + '}'; + } +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/ClientQuery.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/ClientQuery.java new file mode 100755 index 0000000000..399ec693ed --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/ClientQuery.java @@ -0,0 +1,14 @@ +package org.keycloak.models.cache.infinispan.stream; + +import org.keycloak.models.cache.infinispan.stream.entities.InRealm; + +import java.util.List; +import java.util.Set; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public interface ClientQuery extends InRealm { + Set getClients(); +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/ClientQueryPredicate.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/ClientQueryPredicate.java new file mode 100755 index 0000000000..1e0c555519 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/ClientQueryPredicate.java @@ -0,0 +1,46 @@ +package org.keycloak.models.cache.infinispan.stream; + +import org.jboss.logging.Logger; + +import java.io.Serializable; +import java.util.Map; +import java.util.function.Predicate; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class ClientQueryPredicate implements Predicate>, Serializable { + protected static final Logger logger = Logger.getLogger(ClientQueryPredicate.class); + private String client; + private String inRealm; + + public static ClientQueryPredicate create() { + return new ClientQueryPredicate(); + } + + public ClientQueryPredicate client(String client) { + this.client = client; + return this; + } + + public ClientQueryPredicate inRealm(String inRealm) { + this.inRealm = inRealm; + return this; + } + + + + + + @Override + public boolean test(Map.Entry entry) { + Object value = entry.getValue(); + if (value == null) return false; + if (!(value instanceof ClientQuery)) return false; + ClientQuery query = (ClientQuery)value; + if (client != null && !query.getClients().contains(client)) return false; + if (inRealm != null && !query.getRealm().equals(inRealm)) return false; + return true; + } +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/ClientTemplateQuery.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/ClientTemplateQuery.java new file mode 100755 index 0000000000..e63cb67de4 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/ClientTemplateQuery.java @@ -0,0 +1,13 @@ +package org.keycloak.models.cache.infinispan.stream; + +import org.keycloak.models.cache.infinispan.stream.entities.InRealm; + +import java.util.Set; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public interface ClientTemplateQuery extends InRealm { + Set getTemplates(); +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/ClientTemplateQueryPredicate.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/ClientTemplateQueryPredicate.java new file mode 100755 index 0000000000..ebf4b6ce63 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/ClientTemplateQueryPredicate.java @@ -0,0 +1,37 @@ +package org.keycloak.models.cache.infinispan.stream; + +import java.io.Serializable; +import java.util.Map; +import java.util.function.Predicate; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class ClientTemplateQueryPredicate implements Predicate>, Serializable { + private String template; + + public static ClientTemplateQueryPredicate create() { + return new ClientTemplateQueryPredicate(); + } + + public ClientTemplateQueryPredicate template(String template) { + this.template = template; + return this; + } + + + + + + @Override + public boolean test(Map.Entry entry) { + Object value = entry.getValue(); + if (value == null) return false; + if (!(value instanceof ClientTemplateQuery)) return false; + ClientTemplateQuery query = (ClientTemplateQuery)value; + + + return query.getTemplates().contains(template); + } +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/GroupQuery.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/GroupQuery.java new file mode 100755 index 0000000000..b3d86c0c93 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/GroupQuery.java @@ -0,0 +1,13 @@ +package org.keycloak.models.cache.infinispan.stream; + +import org.keycloak.models.cache.infinispan.stream.entities.InRealm; + +import java.util.Set; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public interface GroupQuery extends InRealm { + Set getGroups(); +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/GroupQueryPredicate.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/GroupQueryPredicate.java new file mode 100755 index 0000000000..6e4a662969 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/GroupQueryPredicate.java @@ -0,0 +1,37 @@ +package org.keycloak.models.cache.infinispan.stream; + +import java.io.Serializable; +import java.util.Map; +import java.util.function.Predicate; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class GroupQueryPredicate implements Predicate>, Serializable { + private String group; + + public static GroupQueryPredicate create() { + return new GroupQueryPredicate(); + } + + public GroupQueryPredicate group(String group) { + this.group = group; + return this; + } + + + + + + @Override + public boolean test(Map.Entry entry) { + Object value = entry.getValue(); + if (value == null) return false; + if (!(value instanceof GroupQuery)) return false; + GroupQuery query = (GroupQuery)value; + + + return query.getGroups().contains(group); + } +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/HasRolePredicate.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/HasRolePredicate.java new file mode 100755 index 0000000000..5946726ae7 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/HasRolePredicate.java @@ -0,0 +1,40 @@ +package org.keycloak.models.cache.infinispan.stream; + +import org.keycloak.models.cache.entities.CachedGroup; +import org.keycloak.models.cache.entities.CachedRole; + +import java.io.Serializable; +import java.util.Map; +import java.util.function.Predicate; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class HasRolePredicate implements Predicate>, Serializable { + private String role; + + public static HasRolePredicate create() { + return new HasRolePredicate(); + } + + public HasRolePredicate role(String role) { + this.role = role; + return this; + } + + @Override + public boolean test(Map.Entry entry) { + Object value = entry.getValue(); + if (value == null) return false; + if (value instanceof CachedRole) { + CachedRole cachedRole = (CachedRole)value; + if (cachedRole.getComposites().contains(role)) return true; + } + if (value instanceof CachedGroup) { + CachedGroup cachedRole = (CachedGroup)value; + if (cachedRole.getRoleMappings().contains(role)) return true; + } + return false; + } +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/InClientPredicate.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/InClientPredicate.java new file mode 100755 index 0000000000..ee5343f841 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/InClientPredicate.java @@ -0,0 +1,34 @@ +package org.keycloak.models.cache.infinispan.stream; + +import org.keycloak.models.cache.infinispan.stream.entities.InClient; +import org.keycloak.models.cache.infinispan.stream.entities.InRealm; + +import java.io.Serializable; +import java.util.Map; +import java.util.function.Predicate; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class InClientPredicate implements Predicate>, Serializable { + private String clientId; + + public static InClientPredicate create() { + return new InClientPredicate(); + } + + public InClientPredicate client(String id) { + clientId = id; + return this; + } + + @Override + public boolean test(Map.Entry entry) { + Object value = entry.getValue(); + if (value == null) return false; + if (!(value instanceof InClient)) return false; + + return clientId.equals(((InClient)value).getClientId()); + } +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/InRealmPredicate.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/InRealmPredicate.java new file mode 100755 index 0000000000..265e4d2655 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/InRealmPredicate.java @@ -0,0 +1,33 @@ +package org.keycloak.models.cache.infinispan.stream; + +import org.keycloak.models.cache.infinispan.stream.entities.InRealm; + +import java.io.Serializable; +import java.util.Map; +import java.util.function.Predicate; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class InRealmPredicate implements Predicate>, Serializable { + private String realm; + + public static InRealmPredicate create() { + return new InRealmPredicate(); + } + + public InRealmPredicate realm(String id) { + realm = id; + return this; + } + + @Override + public boolean test(Map.Entry entry) { + Object value = entry.getValue(); + if (value == null) return false; + if (!(value instanceof InRealm)) return false; + + return realm.equals(((InRealm)value).getRealm()); + } +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/RealmListQuery.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/RealmListQuery.java new file mode 100755 index 0000000000..22a615caf2 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/RealmListQuery.java @@ -0,0 +1,29 @@ +package org.keycloak.models.cache.infinispan.stream; + +import org.keycloak.models.cache.infinispan.stream.entities.AbstractRevisioned; + +import java.util.HashSet; +import java.util.Set; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class RealmListQuery extends AbstractRevisioned implements RealmQuery { + private final Set realms; + + public RealmListQuery(Long revision, String id, String realm) { + super(revision, id); + realms = new HashSet<>(); + realms.add(realm); + } + public RealmListQuery(Long revision, String id, Set realms) { + super(revision, id); + this.realms = realms; + } + + @Override + public Set getRealms() { + return realms; + } +} \ No newline at end of file diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/RealmQuery.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/RealmQuery.java new file mode 100755 index 0000000000..210978fb15 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/RealmQuery.java @@ -0,0 +1,13 @@ +package org.keycloak.models.cache.infinispan.stream; + +import org.keycloak.models.cache.infinispan.stream.entities.InRealm; + +import java.util.Set; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public interface RealmQuery { + Set getRealms(); +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/RealmQueryPredicate.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/RealmQueryPredicate.java new file mode 100755 index 0000000000..179c7da043 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/RealmQueryPredicate.java @@ -0,0 +1,37 @@ +package org.keycloak.models.cache.infinispan.stream; + +import java.io.Serializable; +import java.util.Map; +import java.util.function.Predicate; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class RealmQueryPredicate implements Predicate>, Serializable { + private String realm; + + public static RealmQueryPredicate create() { + return new RealmQueryPredicate(); + } + + public RealmQueryPredicate realm(String realm) { + this.realm = realm; + return this; + } + + + + + + @Override + public boolean test(Map.Entry entry) { + Object value = entry.getValue(); + if (value == null) return false; + if (!(value instanceof RealmQuery)) return false; + RealmQuery query = (RealmQuery)value; + + + return query.getRealms().contains(realm); + } +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/RoleQuery.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/RoleQuery.java new file mode 100755 index 0000000000..0fcb78a98b --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/RoleQuery.java @@ -0,0 +1,13 @@ +package org.keycloak.models.cache.infinispan.stream; + +import org.keycloak.models.cache.infinispan.stream.entities.InRealm; + +import java.util.Set; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public interface RoleQuery extends InRealm { + Set getRoles(); +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/RoleQueryPredicate.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/RoleQueryPredicate.java new file mode 100755 index 0000000000..3320963d30 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/RoleQueryPredicate.java @@ -0,0 +1,37 @@ +package org.keycloak.models.cache.infinispan.stream; + +import java.io.Serializable; +import java.util.Map; +import java.util.function.Predicate; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class RoleQueryPredicate implements Predicate>, Serializable { + private String role; + + public static RoleQueryPredicate create() { + return new RoleQueryPredicate(); + } + + public RoleQueryPredicate role(String role) { + this.role = role; + return this; + } + + + + + + @Override + public boolean test(Map.Entry entry) { + Object value = entry.getValue(); + if (value == null) return false; + if (!(value instanceof RoleQuery)) return false; + RoleQuery query = (RoleQuery)value; + + + return query.getRoles().contains(role); + } +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/LockingCacheRealmProvider.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/StreamCacheRealmProvider.java similarity index 57% rename from model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/LockingCacheRealmProvider.java rename to model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/StreamCacheRealmProvider.java index 4edb046af5..d68f50c125 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/LockingCacheRealmProvider.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/StreamCacheRealmProvider.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.models.cache.infinispan.locking; +package org.keycloak.models.cache.infinispan.stream; import org.jboss.logging.Logger; import org.keycloak.migration.MigrationModel; @@ -28,22 +28,19 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.RealmProvider; import org.keycloak.models.RoleModel; import org.keycloak.models.cache.CacheRealmProvider; -import org.keycloak.models.cache.entities.CachedClient; -import org.keycloak.models.cache.entities.CachedClientTemplate; -import org.keycloak.models.cache.entities.CachedGroup; -import org.keycloak.models.cache.entities.CachedRealm; import org.keycloak.models.cache.entities.CachedRole; import org.keycloak.models.cache.infinispan.ClientAdapter; import org.keycloak.models.cache.infinispan.ClientTemplateAdapter; import org.keycloak.models.cache.infinispan.GroupAdapter; import org.keycloak.models.cache.infinispan.RealmAdapter; import org.keycloak.models.cache.infinispan.RoleAdapter; -import org.keycloak.models.cache.infinispan.locking.entities.RevisionedCachedClient; -import org.keycloak.models.cache.infinispan.locking.entities.RevisionedCachedClientRole; -import org.keycloak.models.cache.infinispan.locking.entities.RevisionedCachedClientTemplate; -import org.keycloak.models.cache.infinispan.locking.entities.RevisionedCachedGroup; -import org.keycloak.models.cache.infinispan.locking.entities.RevisionedCachedRealm; -import org.keycloak.models.cache.infinispan.locking.entities.RevisionedCachedRealmRole; +import org.keycloak.models.cache.infinispan.stream.entities.Revisioned; +import org.keycloak.models.cache.infinispan.stream.entities.RevisionedCachedClient; +import org.keycloak.models.cache.infinispan.stream.entities.RevisionedCachedClientRole; +import org.keycloak.models.cache.infinispan.stream.entities.RevisionedCachedClientTemplate; +import org.keycloak.models.cache.infinispan.stream.entities.RevisionedCachedGroup; +import org.keycloak.models.cache.infinispan.stream.entities.RevisionedCachedRealm; +import org.keycloak.models.cache.infinispan.stream.entities.RevisionedCachedRealmRole; import java.util.Collections; import java.util.HashMap; @@ -55,34 +52,36 @@ import java.util.Set; /** + * + * - any relationship should be resolved from session.realms(). For example if JPA.getClientByClientId() is invoked, + * JPA should find the id of the client and then call session.realms().getClientById(). THis is to ensure that the cached + * object is invoked and all proper invalidation are being invoked. + * * @author Bill Burke * @version $Revision: 1 $ */ -public class LockingCacheRealmProvider implements CacheRealmProvider { - protected static final Logger logger = Logger.getLogger(LockingCacheRealmProvider.class); - protected LockingRealmCache cache; +public class StreamCacheRealmProvider implements CacheRealmProvider { + protected static final Logger logger = Logger.getLogger(StreamCacheRealmProvider.class); + public static final String REALM_CLIENTS_QUERY_SUFFIX = ".realm.clients"; + protected StreamRealmCache cache; protected KeycloakSession session; protected RealmProvider delegate; protected boolean transactionActive; protected boolean setRollbackOnly; - protected Set realmInvalidations = new HashSet<>(); - protected Set appInvalidations = new HashSet<>(); - protected Set clientTemplateInvalidations = new HashSet<>(); - protected Set roleInvalidations = new HashSet<>(); - protected Set groupInvalidations = new HashSet<>(); protected Map managedRealms = new HashMap<>(); protected Map managedApplications = new HashMap<>(); protected Map managedClientTemplates = new HashMap<>(); protected Map managedRoles = new HashMap<>(); protected Map managedGroups = new HashMap<>(); + protected Set clientListInvalidations = new HashSet<>(); + protected Set invalidations = new HashSet<>(); protected boolean clearAll; - public LockingCacheRealmProvider(LockingRealmCache cache, KeycloakSession session) { + public StreamCacheRealmProvider(StreamRealmCache cache, KeycloakSession session) { this.cache = cache; this.session = session; - session.getTransaction().enlistPrepare(getPrepareTransaction()); session.getTransaction().enlistAfterCompletion(getAfterTransaction()); } @@ -105,50 +104,38 @@ public class LockingCacheRealmProvider implements CacheRealmProvider { return delegate; } - public LockingRealmCache getCache() { - return cache; - } - @Override public void registerRealmInvalidation(String id) { - realmInvalidations.add(id); + invalidations.add(id); + cache.realmInvalidation(id, invalidations); } @Override - public void registerApplicationInvalidation(String id) { - appInvalidations.add(id); + public void registerClientInvalidation(String id) { + invalidations.add(id); + cache.clientInvalidation(id, invalidations); } @Override public void registerClientTemplateInvalidation(String id) { - clientTemplateInvalidations.add(id); + invalidations.add(id); + cache.clientTemplateInvalidation(id, invalidations); } @Override public void registerRoleInvalidation(String id) { - roleInvalidations.add(id); + invalidations.add(id); + cache.roleInvalidation(id, invalidations); } @Override public void registerGroupInvalidation(String id) { - groupInvalidations.add(id); - + invalidations.add(id); + cache.groupInvalidation(id, invalidations); } protected void runInvalidations() { - for (String id : realmInvalidations) { - cache.invalidateRealmById(id); - } - for (String id : roleInvalidations) { - cache.invalidateRoleById(id); - } - for (String id : groupInvalidations) { - cache.invalidateGroupById(id); - } - for (String id : appInvalidations) { - cache.invalidateClientById(id); - } - for (String id : clientTemplateInvalidations) { - cache.invalidateClientTemplateById(id); + for (String id : invalidations) { + cache.invalidateObject(id); } } @@ -162,27 +149,14 @@ public class LockingCacheRealmProvider implements CacheRealmProvider { @Override public void commit() { if (delegate == null) return; - List invalidates = new LinkedList<>(); - for (String id : realmInvalidations) { - invalidates.add(id); - } - for (String id : roleInvalidations) { - invalidates.add(id); - } - for (String id : groupInvalidations) { - invalidates.add(id); - } - for (String id : appInvalidations) { - invalidates.add(id); - } - for (String id : clientTemplateInvalidations) { - invalidates.add(id); - } + List locks = new LinkedList<>(); + locks.addAll(invalidations); - Collections.sort(invalidates); // lock ordering + Collections.sort(locks); // lock ordering cache.getRevisions().startBatch(); - for (String id : invalidates) { - cache.getRevisions().getAdvancedCache().lock(id); + //if (!invalidates.isEmpty()) cache.getRevisions().getAdvancedCache().lock(invalidates); + for (String lock : locks) { + boolean success = cache.getRevisions().getAdvancedCache().lock(lock); } } @@ -275,19 +249,18 @@ public class LockingCacheRealmProvider implements CacheRealmProvider { @Override public RealmModel getRealm(String id) { - CachedRealm cached = cache.getRealm(id); + RevisionedCachedRealm cached = cache.get(id, RevisionedCachedRealm.class); if (cached != null) { logger.tracev("by id cache hit: {0}", cached.getName()); } if (cached == null) { Long loaded = cache.getCurrentRevision(id); - if (loaded == null) loaded = UpdateCounter.current(); RealmModel model = getDelegate().getRealm(id); if (model == null) return null; - if (realmInvalidations.contains(id)) return model; - cached = new RevisionedCachedRealm(loaded, cache, this, model); - cache.addRealm(cached); - } else if (realmInvalidations.contains(id)) { + if (invalidations.contains(id)) return model; + cached = new RevisionedCachedRealm(loaded, null, this, model); + cache.addRevisioned(cached); + } else if (invalidations.contains(id)) { return getDelegate().getRealm(id); } else if (managedRealms.containsKey(id)) { return managedRealms.get(id); @@ -299,25 +272,28 @@ public class LockingCacheRealmProvider implements CacheRealmProvider { @Override public RealmModel getRealmByName(String name) { - CachedRealm cached = cache.getRealmByName(name); - if (cached != null) { - logger.tracev("by name cache hit: {0}", cached.getName()); + String cacheKey = "realm.query.by.name." + name; + RealmListQuery query = cache.get(cacheKey, RealmListQuery.class); + if (query != null) { + logger.tracev("realm by name cache hit: {0}", name); } - if (cached == null) { - Long loaded = UpdateCounter.current(); + if (query == null) { + Long loaded = cache.getCurrentRevision(cacheKey); RealmModel model = getDelegate().getRealmByName(name); if (model == null) return null; - if (realmInvalidations.contains(model.getId())) return model; - cached = new RevisionedCachedRealm(loaded, cache, this, model); - cache.addRealm(cached); - } else if (realmInvalidations.contains(cached.getId())) { + if (invalidations.contains(model.getId())) return model; + query = new RealmListQuery(loaded, cacheKey, model.getId()); + cache.addRevisioned(query); + return model; + } else if (invalidations.contains(cacheKey)) { return getDelegate().getRealmByName(name); - } else if (managedRealms.containsKey(cached.getId())) { - return managedRealms.get(cached.getId()); + } else { + String realmId = query.getRealms().iterator().next(); + if (invalidations.contains(realmId)) { + return getDelegate().getRealmByName(name); + } + return getRealm(realmId); } - RealmAdapter adapter = new RealmAdapter(cached, this); - managedRealms.put(cached.getId(), adapter); - return adapter; } @Override @@ -336,42 +312,87 @@ public class LockingCacheRealmProvider implements CacheRealmProvider { @Override public boolean removeRealm(String id) { - cache.invalidateRealmById(id); + if (getRealm(id) == null) return false; - RealmModel realm = getDelegate().getRealm(id); - Set realmRoles = null; - if (realm != null) { - realmRoles = realm.getRoles(); + invalidations.add(getRealmClientsQueryCacheKey(id)); + cache.invalidateObject(id); + cache.realmRemoval(id, invalidations); + return getDelegate().removeRealm(id); + } + + @Override + public ClientModel addClient(RealmModel realm, String clientId) { + ClientModel client = getDelegate().addClient(realm, clientId); + return addedClient(realm, client); + } + + @Override + public ClientModel addClient(RealmModel realm, String id, String clientId) { + ClientModel client = getDelegate().addClient(realm, id, clientId); + return addedClient(realm, client); + } + + private ClientModel addedClient(RealmModel realm, ClientModel client) { + logger.trace("added Client....."); + // need to invalidate realm client query cache every time as it may not be loaded on this node, but loaded on another + invalidations.add(getRealmClientsQueryCacheKey(realm.getId())); + invalidations.add(client.getId()); + cache.clientAdded(realm.getId(), client.getId(), invalidations); + clientListInvalidations.add(realm.getId()); + return client; + } + + private String getRealmClientsQueryCacheKey(String realm) { + return realm + REALM_CLIENTS_QUERY_SUFFIX; + } + + @Override + public List getClients(RealmModel realm) { + String cacheKey = getRealmClientsQueryCacheKey(realm.getId()); + boolean queryDB = invalidations.contains(cacheKey) || clientListInvalidations.contains(realm.getId()); + if (queryDB) { + return getDelegate().getClients(realm); } - boolean didIt = getDelegate().removeRealm(id); - realmInvalidations.add(id); + ClientListQuery query = cache.get(cacheKey, ClientListQuery.class); + if (query != null) { + logger.tracev("getClients cache hit: {0}", realm.getName()); + } - // TODO: Temporary workaround to invalidate cached realm roles - if (didIt && realmRoles != null) { - for (RoleModel role : realmRoles) { - roleInvalidations.add(role.getId()); + if (query == null) { + Long loaded = cache.getCurrentRevision(cacheKey); + List model = getDelegate().getClients(realm); + if (model == null) return null; + Set ids = new HashSet<>(); + for (ClientModel client : model) ids.add(client.getId()); + query = new ClientListQuery(loaded, cacheKey, realm, ids); + logger.tracev("adding realm clients cache miss: realm {0} key {1}", realm.getName(), cacheKey); + cache.addRevisioned(query); + return model; + } + List list = new LinkedList<>(); + for (String id : query.getClients()) { + ClientModel client = session.realms().getClientById(id, realm); + if (client == null) { + invalidations.add(cacheKey); + return getDelegate().getClients(realm); } + list.add(client); } - - return didIt; + return list; } @Override public boolean removeClient(String id, RealmModel realm) { ClientModel client = getClientById(id, realm); if (client == null) return false; - - registerApplicationInvalidation(id); - registerRealmInvalidation(realm.getId()); - cache.invalidateClientById(id); - cache.invalidateRealmById(realm.getId()); - - - - Set roles = client.getRoles(); - for (RoleModel role : roles) { - registerRoleInvalidation(role.getId()); + // need to invalidate realm client query cache every time client list is changed + invalidations.add(getRealmClientsQueryCacheKey(realm.getId())); + clientListInvalidations.add(realm.getId()); + registerClientInvalidation(id); + cache.clientRemoval(realm.getId(), id, invalidations); + for (RoleModel role : client.getRoles()) { + cache.roleInvalidation(role.getId(), invalidations); } return getDelegate().removeClient(id, realm); } @@ -383,51 +404,49 @@ public class LockingCacheRealmProvider implements CacheRealmProvider { @Override public RoleModel getRoleById(String id, RealmModel realm) { - CachedRole cached = cache.getRole(id); + CachedRole cached = cache.get(id, CachedRole.class); if (cached != null && !cached.getRealm().equals(realm.getId())) { cached = null; } if (cached == null) { Long loaded = cache.getCurrentRevision(id); - if (loaded == null) loaded = UpdateCounter.current(); RoleModel model = getDelegate().getRoleById(id, realm); if (model == null) return null; - if (roleInvalidations.contains(id)) return model; + if (invalidations.contains(id)) return model; if (model.getContainer() instanceof ClientModel) { cached = new RevisionedCachedClientRole(loaded, ((ClientModel) model.getContainer()).getId(), model, realm); } else { cached = new RevisionedCachedRealmRole(loaded, model, realm); } - cache.addRole(cached); + cache.addRevisioned((Revisioned)cached); - } else if (roleInvalidations.contains(id)) { + } else if (invalidations.contains(id)) { return getDelegate().getRoleById(id, realm); } else if (managedRoles.containsKey(id)) { return managedRoles.get(id); } - RoleAdapter adapter = new RoleAdapter(cached, cache, this, realm); + RoleAdapter adapter = new RoleAdapter(cached, null, this, realm); managedRoles.put(id, adapter); return adapter; } @Override public GroupModel getGroupById(String id, RealmModel realm) { - CachedGroup cached = cache.getGroup(id); + RevisionedCachedGroup cached = cache.get(id, RevisionedCachedGroup.class); if (cached != null && !cached.getRealm().equals(realm.getId())) { cached = null; } if (cached == null) { Long loaded = cache.getCurrentRevision(id); - if (loaded == null) loaded = UpdateCounter.current(); GroupModel model = getDelegate().getGroupById(id, realm); if (model == null) return null; - if (groupInvalidations.contains(id)) return model; + if (invalidations.contains(id)) return model; cached = new RevisionedCachedGroup(loaded, realm, model); - cache.addGroup(cached); + cache.addRevisioned(cached); - } else if (groupInvalidations.contains(id)) { + } else if (invalidations.contains(id)) { return getDelegate().getGroupById(id, realm); } else if (managedGroups.containsKey(id)) { return managedGroups.get(id); @@ -439,7 +458,7 @@ public class LockingCacheRealmProvider implements CacheRealmProvider { @Override public ClientModel getClientById(String id, RealmModel realm) { - CachedClient cached = cache.getClient(id); + RevisionedCachedClient cached = cache.get(id, RevisionedCachedClient.class); if (cached != null && !cached.getRealm().equals(realm.getId())) { cached = null; } @@ -449,73 +468,72 @@ public class LockingCacheRealmProvider implements CacheRealmProvider { if (cached == null) { Long loaded = cache.getCurrentRevision(id); - if (loaded == null) loaded = UpdateCounter.current(); ClientModel model = getDelegate().getClientById(id, realm); if (model == null) return null; - if (appInvalidations.contains(id)) return model; - cached = new RevisionedCachedClient(loaded, cache, getDelegate(), realm, model); + if (invalidations.contains(id)) return model; + cached = new RevisionedCachedClient(loaded, null, getDelegate(), realm, model); logger.tracev("adding client by id cache miss: {0}", cached.getClientId()); - cache.addClient(cached); - } else if (appInvalidations.contains(id)) { + cache.addRevisioned(cached); + } else if (invalidations.contains(id)) { return getDelegate().getClientById(id, realm); } else if (managedApplications.containsKey(id)) { return managedApplications.get(id); } - ClientAdapter adapter = new ClientAdapter(realm, cached, this, cache); + ClientAdapter adapter = new ClientAdapter(realm, cached, this, null); managedApplications.put(id, adapter); return adapter; } @Override public ClientModel getClientByClientId(String clientId, RealmModel realm) { - CachedClient cached = cache.getClientByClientId(realm, clientId); - if (cached != null && !cached.getRealm().equals(realm.getId())) { - cached = null; - } - if (cached != null) { - logger.tracev("client by name cache hit: {0}", cached.getClientId()); + String cacheKey = realm.getId() + ".client.query.by.clientId." + clientId; + ClientListQuery query = cache.get(cacheKey, ClientListQuery.class); + String id = null; + + if (query != null) { + logger.tracev("client by name cache hit: {0}", clientId); } - if (cached == null) { - Long loaded = UpdateCounter.current(); - if (loaded == null) loaded = UpdateCounter.current(); + if (query == null) { + Long loaded = cache.getCurrentRevision(cacheKey); ClientModel model = getDelegate().getClientByClientId(clientId, realm); if (model == null) return null; - if (appInvalidations.contains(model.getId())) return model; - cached = new RevisionedCachedClient(loaded, cache, getDelegate(), realm, model); - logger.tracev("adding client by name cache miss: {0}", cached.getClientId()); - cache.addClient(cached); - } else if (appInvalidations.contains(cached.getId())) { - return getDelegate().getClientById(cached.getId(), realm); - } else if (managedApplications.containsKey(cached.getId())) { - return managedApplications.get(cached.getId()); + if (invalidations.contains(model.getId())) return model; + id = model.getId(); + query = new ClientListQuery(loaded, cacheKey, realm, id); + logger.tracev("adding client by name cache miss: {0}", clientId); + cache.addRevisioned(query); + } else if (invalidations.contains(cacheKey)) { + return getDelegate().getClientByClientId(clientId, realm); + } else { + id = query.getClients().iterator().next(); + if (invalidations.contains(id)) { + return getDelegate().getClientByClientId(clientId, realm); + } } - ClientAdapter adapter = new ClientAdapter(realm, cached, this, cache); - managedApplications.put(cached.getId(), adapter); - return adapter; + return getClientById(id, realm); } @Override public ClientTemplateModel getClientTemplateById(String id, RealmModel realm) { - CachedClientTemplate cached = cache.getClientTemplate(id); + RevisionedCachedClientTemplate cached = cache.get(id, RevisionedCachedClientTemplate.class); if (cached != null && !cached.getRealm().equals(realm.getId())) { cached = null; } if (cached == null) { Long loaded = cache.getCurrentRevision(id); - if (loaded == null) loaded = UpdateCounter.current(); ClientTemplateModel model = getDelegate().getClientTemplateById(id, realm); if (model == null) return null; - if (clientTemplateInvalidations.contains(id)) return model; - cached = new RevisionedCachedClientTemplate(loaded, cache, getDelegate(), realm, model); - cache.addClientTemplate(cached); - } else if (clientTemplateInvalidations.contains(id)) { + if (invalidations.contains(id)) return model; + cached = new RevisionedCachedClientTemplate(loaded, null, getDelegate(), realm, model); + cache.addRevisioned(cached); + } else if (invalidations.contains(id)) { return getDelegate().getClientTemplateById(id, realm); } else if (managedClientTemplates.containsKey(id)) { return managedClientTemplates.get(id); } - ClientTemplateModel adapter = new ClientTemplateAdapter(realm, cached, this, cache); + ClientTemplateModel adapter = new ClientTemplateAdapter(realm, cached, this, null); managedClientTemplates.put(id, adapter); return adapter; } diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/StreamRealmCache.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/StreamRealmCache.java new file mode 100755 index 0000000000..9dd821bdff --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/StreamRealmCache.java @@ -0,0 +1,314 @@ +/* + * 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.cache.infinispan.stream; + +import org.infinispan.Cache; +import org.infinispan.notifications.Listener; +import org.infinispan.notifications.cachelistener.annotation.CacheEntriesEvicted; +import org.infinispan.notifications.cachelistener.annotation.CacheEntryInvalidated; +import org.infinispan.notifications.cachelistener.event.CacheEntriesEvictedEvent; +import org.infinispan.notifications.cachelistener.event.CacheEntryInvalidatedEvent; +import org.jboss.logging.Logger; +import org.keycloak.models.cache.entities.CachedClient; +import org.keycloak.models.cache.entities.CachedClientTemplate; +import org.keycloak.models.cache.entities.CachedGroup; +import org.keycloak.models.cache.entities.CachedRealm; +import org.keycloak.models.cache.entities.CachedRole; +import org.keycloak.models.cache.infinispan.stream.entities.Revisioned; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.function.Predicate; + +/** + * @author Stian Thorgersen + */ +@Listener +public class StreamRealmCache { + + protected static final Logger logger = Logger.getLogger(StreamRealmCache.class); + + protected final Cache revisions; + protected final Cache cache; + + public StreamRealmCache(Cache cache, Cache revisions) { + this.cache = cache; + this.cache.addListener(this); + this.revisions = revisions; + } + + public Cache getCache() { + return cache; + } + + public Cache getRevisions() { + return revisions; + } + + public Long getCurrentRevision(String id) { + Long revision = revisions.get(id); + if (revision == null) return UpdateCounter.current(); + return revision; + } + + public void endRevisionBatch() { + try { + revisions.endBatch(true); + } catch (Exception e) { + } + + } + + public T get(String id, Class type) { + Revisioned o = (Revisioned)cache.get(id); + if (o == null) { + return null; + } + Long rev = revisions.get(id); + if (rev == null) { + logger.tracev("get() missing rev"); + return null; + } + long oRev = o.getRevision() == null ? -1L : o.getRevision().longValue(); + if (rev > oRev) { + logger.tracev("get() rev: {0} o.rev: {1}", rev.longValue(), oRev); + return null; + } + return o != null && type.isInstance(o) ? type.cast(o) : null; + } + + public Object invalidateObject(String id) { + Revisioned removed = (Revisioned)cache.remove(id); + long next = UpdateCounter.next(); + Object rev = revisions.put(id, next); + return removed; + } + + public void addRevisioned(Revisioned object) { + //startRevisionBatch(); + String id = object.getId(); + try { + //revisions.getAdvancedCache().lock(id); + Long rev = revisions.get(id); + if (rev == null) { + if (id.endsWith("realm.clients")) logger.trace("addRevisioned rev == null realm.clients"); + rev = UpdateCounter.current(); + revisions.put(id, rev); + } + revisions.startBatch(); + if (!revisions.getAdvancedCache().lock(id)) { + logger.trace("Could not obtain version lock"); + } + rev = revisions.get(id); + if (rev == null) { + if (id.endsWith("realm.clients")) logger.trace("addRevisioned rev2 == null realm.clients"); + return; + } + if (rev.equals(object.getRevision())) { + if (id.endsWith("realm.clients")) logger.tracev("adding Object.revision {0} rev {1}", object.getRevision(), rev); + cache.putForExternalRead(id, object); + return; + } + if (rev > object.getRevision()) { // revision is ahead, don't cache + if (id.endsWith("realm.clients")) logger.trace("addRevisioned revision is ahead realm.clients"); + return; + } + // revisions cache has a lower value than the object.revision, so update revision and add it to cache + if (id.endsWith("realm.clients")) logger.tracev("adding Object.revision {0} rev {1}", object.getRevision(), rev); + revisions.put(id, object.getRevision()); + cache.putForExternalRead(id, object); + } finally { + endRevisionBatch(); + } + + } + public void clear() { + cache.clear(); + } + + public void realmInvalidation(String id, Set invalidations) { + Predicate> predicate = getRealmInvalidationPredicate(id); + addInvalidations(predicate, invalidations); + } + + public Predicate> getRealmInvalidationPredicate(String id) { + return RealmQueryPredicate.create().realm(id); + } + + public void clientInvalidation(String id, Set invalidations) { + addInvalidations(getClientInvalidationPredicate(id), invalidations); + } + + public Predicate> getClientInvalidationPredicate(String id) { + return ClientQueryPredicate.create().client(id); + } + + public void roleInvalidation(String id, Set invalidations) { + addInvalidations(getRoleInvalidationPredicate(id), invalidations); + + } + + public Predicate> getRoleInvalidationPredicate(String id) { + return HasRolePredicate.create().role(id); + } + + public void groupInvalidation(String id, Set invalidations) { + addInvalidations(getGroupInvalidationPredicate(id), invalidations); + + } + + public Predicate> getGroupInvalidationPredicate(String id) { + return GroupQueryPredicate.create().group(id); + } + + public void clientTemplateInvalidation(String id, Set invalidations) { + addInvalidations(getClientTemplateInvalidationPredicate(id), invalidations); + + } + + public Predicate> getClientTemplateInvalidationPredicate(String id) { + return ClientTemplateQueryPredicate.create().template(id); + } + + public void realmRemoval(String id, Set invalidations) { + Predicate> predicate = getRealmRemovalPredicate(id); + addInvalidations(predicate, invalidations); + } + + public Predicate> getRealmRemovalPredicate(String id) { + Predicate> predicate = null; + predicate = RealmQueryPredicate.create().realm(id) + .or(InRealmPredicate.create().realm(id)); + return predicate; + } + + public void clientAdded(String realmId, String id, Set invalidations) { + addInvalidations(getClientAddedPredicate(realmId), invalidations); + } + + public Predicate> getClientAddedPredicate(String realmId) { + return ClientQueryPredicate.create().inRealm(realmId); + } + + public void clientRemoval(String realmId, String id, Set invalidations) { + Predicate> predicate = null; + predicate = getClientRemovalPredicate(realmId, id); + addInvalidations(predicate, invalidations); + } + + public Predicate> getClientRemovalPredicate(String realmId, String id) { + Predicate> predicate; + predicate = ClientQueryPredicate.create().inRealm(realmId) + .or(ClientQueryPredicate.create().client(id)) + .or(InClientPredicate.create().client(id)); + return predicate; + } + + public void roleRemoval(String id, Set invalidations) { + addInvalidations(getRoleRemovalPredicate(id), invalidations); + + } + + public Predicate> getRoleRemovalPredicate(String id) { + return getRoleInvalidationPredicate(id); + } + + public void addInvalidations(Predicate> predicate, Set invalidations) { + Iterator> it = getEntryIterator(predicate); + while (it.hasNext()) { + invalidations.add(it.next().getKey()); + } + } + + private Iterator> getEntryIterator(Predicate> predicate) { + return cache + .entrySet() + .stream() + .filter(predicate).iterator(); + } + + @CacheEntryInvalidated + public void cacheInvalidated(CacheEntryInvalidatedEvent event) { + if (event.isPre()) { + Object object = event.getValue(); + if (object != null) { + Predicate> predicate = getInvalidationPredicate(object); + if (predicate != null) runEvictions(predicate); + } + } + } + + @CacheEntriesEvicted + public void cacheEvicted(CacheEntriesEvictedEvent event) { + for (Object object : event.getEntries().values()) { + Predicate> predicate = getEvictionPredicate(object); + if (predicate != null) runEvictions(predicate); + } + } + + public void runEvictions(Predicate> current) { + Set evictions = new HashSet<>(); + addInvalidations(current, evictions); + for (String key : evictions) cache.evict(key); + } + + protected Predicate> getEvictionPredicate(Object object) { + if (object instanceof CachedRealm) { + CachedRealm cached = (CachedRealm)object; + return getRealmInvalidationPredicate(cached.getId()); + } else if (object instanceof CachedClient) { + CachedClient cached = (CachedClient)object; + return getClientInvalidationPredicate(cached.getId()); + } else if (object instanceof CachedRole) { + CachedRole cached = (CachedRole)object; + return getRoleInvalidationPredicate(cached.getId()); + } else if (object instanceof CachedGroup) { + CachedGroup cached = (CachedGroup)object; + return getGroupInvalidationPredicate(cached.getId()); + } else if (object instanceof CachedClientTemplate) { + CachedClientTemplate cached = (CachedClientTemplate)object; + return getClientTemplateInvalidationPredicate(cached.getId()); + } + return null; + } + protected Predicate> getInvalidationPredicate(Object object) { + if (object instanceof CachedRealm) { + CachedRealm cached = (CachedRealm)object; + return getRealmRemovalPredicate(cached.getId()); + } else if (object instanceof CachedClient) { + CachedClient cached = (CachedClient)object; + Predicate> predicate = getClientRemovalPredicate(cached.getRealm(), cached.getId()); + for (String roleId : cached.getRoles().values()) { + predicate.or(getRoleRemovalPredicate(roleId)); + } + return predicate; + } else if (object instanceof CachedRole) { + CachedRole cached = (CachedRole)object; + return getRoleRemovalPredicate(cached.getId()); + } else if (object instanceof CachedGroup) { + CachedGroup cached = (CachedGroup)object; + return getGroupInvalidationPredicate(cached.getId()); + } else if (object instanceof CachedClientTemplate) { + CachedClientTemplate cached = (CachedClientTemplate)object; + return getClientTemplateInvalidationPredicate(cached.getId()); + } + return null; + } +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/UpdateCounter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/UpdateCounter.java similarity index 87% rename from model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/UpdateCounter.java rename to model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/UpdateCounter.java index 8e8b5c5ba8..ea086415c9 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/UpdateCounter.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/UpdateCounter.java @@ -1,4 +1,4 @@ -package org.keycloak.models.cache.infinispan.locking; +package org.keycloak.models.cache.infinispan.stream; import java.util.concurrent.atomic.AtomicLong; diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/AbstractRevisioned.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/AbstractRevisioned.java new file mode 100755 index 0000000000..dfea429fe9 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/AbstractRevisioned.java @@ -0,0 +1,31 @@ +package org.keycloak.models.cache.infinispan.stream.entities; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class AbstractRevisioned implements Revisioned { + private String id; + private Long revision; + + public AbstractRevisioned(Long revision, String id) { + this.revision = revision; + this.id = id; + } + + @Override + public String getId() { + return id; + } + + @Override + public Long getRevision() { + return revision; + } + + @Override + public void setRevision(Long revision) { + this.revision = revision; + } + +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/InClient.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/InClient.java new file mode 100755 index 0000000000..3951c7f5c2 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/InClient.java @@ -0,0 +1,9 @@ +package org.keycloak.models.cache.infinispan.stream.entities; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public interface InClient extends InRealm { + String getClientId(); +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/InRealm.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/InRealm.java new file mode 100755 index 0000000000..6b550911aa --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/InRealm.java @@ -0,0 +1,9 @@ +package org.keycloak.models.cache.infinispan.stream.entities; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public interface InRealm extends Revisioned { + String getRealm(); +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/Revisioned.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/Revisioned.java similarity index 70% rename from model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/Revisioned.java rename to model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/Revisioned.java index 7af8d0f073..c0b136f78e 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/Revisioned.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/Revisioned.java @@ -1,10 +1,11 @@ -package org.keycloak.models.cache.infinispan.locking; +package org.keycloak.models.cache.infinispan.stream.entities; /** * @author Bill Burke * @version $Revision: 1 $ */ public interface Revisioned { + String getId(); Long getRevision(); void setRevision(Long revision); } diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedClient.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/RevisionedCachedClient.java similarity index 88% rename from model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedClient.java rename to model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/RevisionedCachedClient.java index 4feeffae2f..f2ee892809 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedClient.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/RevisionedCachedClient.java @@ -1,4 +1,4 @@ -package org.keycloak.models.cache.infinispan.locking.entities; +package org.keycloak.models.cache.infinispan.stream.entities; import org.keycloak.models.ClientModel; import org.keycloak.models.RealmModel; @@ -6,13 +6,12 @@ import org.keycloak.models.RealmProvider; import org.keycloak.models.RoleModel; import org.keycloak.models.cache.RealmCache; import org.keycloak.models.cache.entities.CachedClient; -import org.keycloak.models.cache.infinispan.locking.Revisioned; /** * @author Bill Burke * @version $Revision: 1 $ */ -public class RevisionedCachedClient extends CachedClient implements Revisioned { +public class RevisionedCachedClient extends CachedClient implements Revisioned, InRealm { public RevisionedCachedClient(Long revision, RealmCache cache, RealmProvider delegate, RealmModel realm, ClientModel model) { super(cache, delegate, realm, model); diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedClientRole.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/RevisionedCachedClientRole.java similarity index 82% rename from model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedClientRole.java rename to model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/RevisionedCachedClientRole.java index c3554f64ae..bad2ffec37 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedClientRole.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/RevisionedCachedClientRole.java @@ -1,15 +1,14 @@ -package org.keycloak.models.cache.infinispan.locking.entities; +package org.keycloak.models.cache.infinispan.stream.entities; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; import org.keycloak.models.cache.entities.CachedClientRole; -import org.keycloak.models.cache.infinispan.locking.Revisioned; /** * @author Bill Burke * @version $Revision: 1 $ */ -public class RevisionedCachedClientRole extends CachedClientRole implements Revisioned { +public class RevisionedCachedClientRole extends CachedClientRole implements Revisioned, InClient { public RevisionedCachedClientRole(Long revision, String idClient, RoleModel model, RealmModel realm) { super(idClient, model, realm); diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedClientTemplate.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/RevisionedCachedClientTemplate.java similarity index 84% rename from model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedClientTemplate.java rename to model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/RevisionedCachedClientTemplate.java index 44720d3385..ad3ac44e1d 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedClientTemplate.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/RevisionedCachedClientTemplate.java @@ -1,17 +1,16 @@ -package org.keycloak.models.cache.infinispan.locking.entities; +package org.keycloak.models.cache.infinispan.stream.entities; import org.keycloak.models.ClientTemplateModel; import org.keycloak.models.RealmModel; import org.keycloak.models.RealmProvider; import org.keycloak.models.cache.RealmCache; import org.keycloak.models.cache.entities.CachedClientTemplate; -import org.keycloak.models.cache.infinispan.locking.Revisioned; /** * @author Bill Burke * @version $Revision: 1 $ */ -public class RevisionedCachedClientTemplate extends CachedClientTemplate implements Revisioned { +public class RevisionedCachedClientTemplate extends CachedClientTemplate implements Revisioned, InRealm { public RevisionedCachedClientTemplate(Long revision, RealmCache cache, RealmProvider delegate, RealmModel realm, ClientTemplateModel model) { super(cache, delegate, realm, model); diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedGroup.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/RevisionedCachedGroup.java similarity index 82% rename from model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedGroup.java rename to model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/RevisionedCachedGroup.java index 09c7f0f659..98ba32f20e 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedGroup.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/RevisionedCachedGroup.java @@ -1,15 +1,14 @@ -package org.keycloak.models.cache.infinispan.locking.entities; +package org.keycloak.models.cache.infinispan.stream.entities; import org.keycloak.models.GroupModel; import org.keycloak.models.RealmModel; import org.keycloak.models.cache.entities.CachedGroup; -import org.keycloak.models.cache.infinispan.locking.Revisioned; /** * @author Bill Burke * @version $Revision: 1 $ */ -public class RevisionedCachedGroup extends CachedGroup implements Revisioned { +public class RevisionedCachedGroup extends CachedGroup implements Revisioned, InRealm { public RevisionedCachedGroup(Long revision, RealmModel realm, GroupModel group) { super(realm, group); this.revision = revision; diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedRealm.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/RevisionedCachedRealm.java similarity index 92% rename from model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedRealm.java rename to model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/RevisionedCachedRealm.java index c4fd4870dd..c1f91ac1f5 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedRealm.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/RevisionedCachedRealm.java @@ -1,4 +1,4 @@ -package org.keycloak.models.cache.infinispan.locking.entities; +package org.keycloak.models.cache.infinispan.stream.entities; import org.keycloak.models.ClientModel; import org.keycloak.models.ClientTemplateModel; @@ -7,7 +7,6 @@ import org.keycloak.models.RealmProvider; import org.keycloak.models.RoleModel; import org.keycloak.models.cache.RealmCache; import org.keycloak.models.cache.entities.CachedRealm; -import org.keycloak.models.cache.infinispan.locking.Revisioned; /** * @author Bill Burke diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedRealmRole.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/RevisionedCachedRealmRole.java similarity index 81% rename from model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedRealmRole.java rename to model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/RevisionedCachedRealmRole.java index 3899bd8b58..6685a3ea74 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedRealmRole.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/RevisionedCachedRealmRole.java @@ -1,15 +1,14 @@ -package org.keycloak.models.cache.infinispan.locking.entities; +package org.keycloak.models.cache.infinispan.stream.entities; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; import org.keycloak.models.cache.entities.CachedRealmRole; -import org.keycloak.models.cache.infinispan.locking.Revisioned; /** * @author Bill Burke * @version $Revision: 1 $ */ -public class RevisionedCachedRealmRole extends CachedRealmRole implements Revisioned { +public class RevisionedCachedRealmRole extends CachedRealmRole implements Revisioned, InRealm { public RevisionedCachedRealmRole(Long revision, RoleModel model, RealmModel realm) { super(model, realm); diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedUser.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/RevisionedCachedUser.java similarity index 82% rename from model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedUser.java rename to model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/RevisionedCachedUser.java index c276a371e6..e6135e9bd7 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedUser.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/entities/RevisionedCachedUser.java @@ -1,15 +1,14 @@ -package org.keycloak.models.cache.infinispan.locking.entities; +package org.keycloak.models.cache.infinispan.stream.entities; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.models.cache.entities.CachedUser; -import org.keycloak.models.cache.infinispan.locking.Revisioned; /** * @author Bill Burke * @version $Revision: 1 $ */ -public class RevisionedCachedUser extends CachedUser implements Revisioned { +public class RevisionedCachedUser extends CachedUser implements Revisioned, InRealm { public RevisionedCachedUser(Long revision, RealmModel realm, UserModel user) { super(realm, user); this.revision = revision; diff --git a/model/infinispan/src/main/resources/META-INF/services/org.keycloak.connections.infinispan.InfinispanConnectionProviderFactory b/model/infinispan/src/main/resources/META-INF/services/org.keycloak.connections.infinispan.InfinispanConnectionProviderFactory index 299da00ff3..1fe494543e 100755 --- a/model/infinispan/src/main/resources/META-INF/services/org.keycloak.connections.infinispan.InfinispanConnectionProviderFactory +++ b/model/infinispan/src/main/resources/META-INF/services/org.keycloak.connections.infinispan.InfinispanConnectionProviderFactory @@ -16,4 +16,3 @@ # org.keycloak.connections.infinispan.DefaultInfinispanConnectionProviderFactory -org.keycloak.models.cache.infinispan.locking.LockingConnectionProviderFactory \ No newline at end of file diff --git a/model/infinispan/src/main/resources/META-INF/services/org.keycloak.models.cache.CacheRealmProviderFactory b/model/infinispan/src/main/resources/META-INF/services/org.keycloak.models.cache.CacheRealmProviderFactory index 40ced18500..a2b04e7002 100755 --- a/model/infinispan/src/main/resources/META-INF/services/org.keycloak.models.cache.CacheRealmProviderFactory +++ b/model/infinispan/src/main/resources/META-INF/services/org.keycloak.models.cache.CacheRealmProviderFactory @@ -16,4 +16,3 @@ # org.keycloak.models.cache.infinispan.InfinispanCacheRealmProviderFactory -org.keycloak.models.cache.infinispan.locking.LockingCacheRealmProviderFactory \ No newline at end of file diff --git a/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/ClusteredCacheBehaviorTest.java b/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/ClusteredCacheBehaviorTest.java new file mode 100755 index 0000000000..e83a8b1116 --- /dev/null +++ b/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/ClusteredCacheBehaviorTest.java @@ -0,0 +1,117 @@ +package org.keycloak.models.sessions.infinispan.initializer; + +import org.infinispan.Cache; +import org.infinispan.configuration.cache.CacheMode; +import org.infinispan.configuration.cache.Configuration; +import org.infinispan.configuration.cache.ConfigurationBuilder; +import org.infinispan.configuration.global.GlobalConfigurationBuilder; +import org.infinispan.manager.DefaultCacheManager; +import org.infinispan.manager.EmbeddedCacheManager; +import org.infinispan.notifications.Listener; +import org.infinispan.notifications.cachelistener.annotation.CacheEntriesEvicted; +import org.infinispan.notifications.cachelistener.annotation.CacheEntryInvalidated; +import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved; +import org.infinispan.notifications.cachelistener.event.CacheEntriesEvictedEvent; +import org.infinispan.notifications.cachelistener.event.CacheEntryInvalidatedEvent; +import org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent; +import org.junit.Ignore; +import org.junit.Test; +import org.keycloak.connections.infinispan.InfinispanConnectionProvider; + +/** + * Just a simple test to see how distributed caches work + * + * @author Bill Burke + * @version $Revision: 1 $ + */ +@Ignore +public class ClusteredCacheBehaviorTest { + public EmbeddedCacheManager createManager() { + GlobalConfigurationBuilder gcb = new GlobalConfigurationBuilder(); + + boolean clustered = true; + boolean async = false; + boolean allowDuplicateJMXDomains = true; + + if (clustered) { + gcb.transport().defaultTransport(); + } + gcb.globalJmxStatistics().allowDuplicateDomains(allowDuplicateJMXDomains); + + EmbeddedCacheManager cacheManager = new DefaultCacheManager(gcb.build()); + + + ConfigurationBuilder invalidationConfigBuilder = new ConfigurationBuilder(); + if (clustered) { + invalidationConfigBuilder.clustering().cacheMode(async ? CacheMode.INVALIDATION_ASYNC : CacheMode.INVALIDATION_SYNC); + } + Configuration invalidationCacheConfiguration = invalidationConfigBuilder.build(); + + cacheManager.defineConfiguration(InfinispanConnectionProvider.REALM_CACHE_NAME, invalidationCacheConfiguration); + return cacheManager; + + } + + @Listener + public static class CacheListener { + String name; + + public CacheListener(String name) { + this.name = name; + } + + + + @CacheEntryRemoved + public void removed(CacheEntryRemovedEvent event) { + if (event.isPre()) { + System.out.println("Listener '" + name + "' entry removed"); + } + } + + @CacheEntryInvalidated + public void removed(CacheEntryInvalidatedEvent event) { + if (event.isPre()) { + System.out.println("Listener '" + name + "' entry invalidated"); + + } + } + + @CacheEntriesEvicted + public void evicted(CacheEntriesEvictedEvent event) { + System.out.println("Listener '" + name + "' entry evicted"); + + } + + } + + @Test + public void testListener() throws Exception { + EmbeddedCacheManager node1 = createManager(); + EmbeddedCacheManager node2 = createManager(); + Cache node1Cache = node1.getCache(InfinispanConnectionProvider.REALM_CACHE_NAME); + node1Cache.addListener(new CacheListener("node1")); + Cache node2Cache = node2.getCache(InfinispanConnectionProvider.REALM_CACHE_NAME); + node2Cache.addListener(new CacheListener("node2")); + + System.out.println("node1 create entry"); + node1Cache.put("key", "node1"); + System.out.println("node2 create entry"); + node2Cache.put("key", "node2"); + System.out.println("node1 remove entry"); + node1Cache.remove("key"); + System.out.println("node2 remove entry"); + node2Cache.remove("key"); + System.out.println("node2 put entry"); + node2Cache.put("key", "node2"); + System.out.println("node2 evict entry"); + node2Cache.evict("key"); + System.out.println("node1/node2 putExternal entry"); + node1Cache.putForExternalRead("key", "common"); + node2Cache.putForExternalRead("key", "common"); + System.out.println("node2 remove entry"); + node2Cache.remove("key"); + + + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java index 311540b5c9..8affd11629 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java @@ -612,12 +612,12 @@ public class ClientAdapter implements ClientModel { @Override public RoleModel getRole(String name) { - TypedQuery query = em.createNamedQuery("getClientRoleByName", RoleEntity.class); + TypedQuery query = em.createNamedQuery("getClientRoleIdByName", String.class); query.setParameter("name", name); - query.setParameter("client", entity); - List roles = query.getResultList(); + query.setParameter("client", entity.getId()); + List roles = query.getResultList(); if (roles.size() == 0) return null; - return new RoleAdapter(realm, em, roles.get(0)); + return session.realms().getRoleById(roles.get(0), realm); } @Override @@ -636,7 +636,7 @@ public class ClientAdapter implements ClientModel { entity.getRoles().add(roleEntity); em.persist(roleEntity); em.flush(); - return new RoleAdapter(realm, em, roleEntity); + return new RoleAdapter(session, realm, em, roleEntity); } @Override @@ -667,19 +667,21 @@ public class ClientAdapter implements ClientModel { @Override public Set getRoles() { Set list = new HashSet(); - /* - Collection roles = entity.getRoles(); - if (roles == null) return list; - for (RoleEntity entity : roles) { - list.add(new RoleAdapter(realm, em, entity)); - } - */ TypedQuery query = em.createNamedQuery("getClientRoles", RoleEntity.class); query.setParameter("client", entity); List roles = query.getResultList(); for (RoleEntity roleEntity : roles) { - list.add(new RoleAdapter(realm, em, roleEntity)); + list.add(new RoleAdapter(session, realm, em, roleEntity)); } + + /* + TypedQuery query = em.createNamedQuery("getClientRoleIds", String.class); + query.setParameter("client", entity.getId()); + List roles = query.getResultList(); + for (String id : roles) { + list.add(session.realms().getRoleById(id, realm)); + } + */ return list; } diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java index ce245de661..6c2ae6b84c 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java @@ -36,6 +36,7 @@ import org.keycloak.models.utils.KeycloakModelUtils; import javax.persistence.EntityManager; import javax.persistence.TypedQuery; import java.util.ArrayList; +import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -90,25 +91,27 @@ public class JpaRealmProvider implements RealmProvider { @Override public List getRealms() { - TypedQuery query = em.createNamedQuery("getAllRealms", RealmEntity.class); - List entities = query.getResultList(); + TypedQuery query = em.createNamedQuery("getAllRealmIds", String.class); + List entities = query.getResultList(); List realms = new ArrayList(); - for (RealmEntity entity : entities) { - realms.add(new RealmAdapter(session, em, entity)); + for (String id : entities) { + RealmModel realm = session.realms().getRealm(id); + if (realm != null) realms.add(realm); + } return realms; } @Override public RealmModel getRealmByName(String name) { - TypedQuery query = em.createNamedQuery("getRealmByName", RealmEntity.class); + TypedQuery query = em.createNamedQuery("getRealmIdByName", String.class); query.setParameter("name", name); - List entities = query.getResultList(); + List entities = query.getResultList(); if (entities.size() == 0) return null; if (entities.size() > 1) throw new IllegalStateException("Should not be more than one realm with same name"); - RealmEntity realm = query.getResultList().get(0); - if (realm == null) return null; - return new RealmAdapter(session, em, realm); + String id = query.getResultList().get(0); + + return session.realms().getRealm(id); } @Override @@ -126,11 +129,11 @@ public class JpaRealmProvider implements RealmProvider { num = em.createNamedQuery("deleteGroupsByRealm") .setParameter("realm", realm).executeUpdate(); - TypedQuery query = em.createNamedQuery("getClientsByRealm", ClientEntity.class); - query.setParameter("realm", realm); - List clients = query.getResultList(); - for (ClientEntity a : clients) { - adapter.removeClient(a.getId()); + TypedQuery query = em.createNamedQuery("getClientIdsByRealm", String.class); + query.setParameter("realm", realm.getId()); + List clients = query.getResultList(); + for (String client : clients) { + session.realms().removeClient(client, adapter); } /* for (ClientEntity a : new LinkedList<>(realm.getClients())) { @@ -145,11 +148,6 @@ public class JpaRealmProvider implements RealmProvider { em.flush(); em.clear(); - realm = em.find(RealmEntity.class, id); - if (realm != null) { - logger.error("WTF is the realm still there after a removal????????"); - } - return true; } @@ -162,7 +160,7 @@ public class JpaRealmProvider implements RealmProvider { RoleEntity entity = em.find(RoleEntity.class, id); if (entity == null) return null; if (!realm.getId().equals(entity.getRealmId())) return null; - return new RoleAdapter(realm, em, entity); + return new RoleAdapter(session, realm, em, entity); } @Override @@ -173,6 +171,52 @@ public class JpaRealmProvider implements RealmProvider { return new GroupAdapter(realm, em, groupEntity); } + @Override + public ClientModel addClient(RealmModel realm, String clientId) { + return addClient(realm, KeycloakModelUtils.generateId(), clientId); + } + + @Override + public ClientModel addClient(RealmModel realm, String id, String clientId) { + if (clientId == null) { + clientId = id; + } + ClientEntity entity = new ClientEntity(); + entity.setId(id); + entity.setClientId(clientId); + entity.setEnabled(true); + entity.setStandardFlowEnabled(true); + RealmEntity realmRef = em.getReference(RealmEntity.class, realm.getId()); + entity.setRealm(realmRef); + em.persist(entity); + em.flush(); + final ClientModel resource = new ClientAdapter(realm, em, session, entity); + em.flush(); + session.getKeycloakSessionFactory().publish(new RealmModel.ClientCreationEvent() { + @Override + public ClientModel getCreatedClient() { + return resource; + } + }); + return resource; + } + + @Override + public List getClients(RealmModel realm) { + TypedQuery query = em.createNamedQuery("getClientIdsByRealm", String.class); + query.setParameter("realm", realm.getId()); + List clients = query.getResultList(); + if (clients.isEmpty()) return Collections.EMPTY_LIST; + List list = new LinkedList<>(); + for (String id : clients) { + ClientModel client = session.realms().getClientById(id, realm); + if (client != null) list.add(client); + } + return Collections.unmodifiableList(list); + + } + + @Override public ClientModel getClientById(String id, RealmModel realm) { ClientEntity app = em.find(ClientEntity.class, id); @@ -184,13 +228,13 @@ public class JpaRealmProvider implements RealmProvider { @Override public ClientModel getClientByClientId(String clientId, RealmModel realm) { - TypedQuery query = em.createNamedQuery("findClientByClientId", ClientEntity.class); + TypedQuery query = em.createNamedQuery("findClientIdByClientId", String.class); query.setParameter("clientId", clientId); query.setParameter("realm", realm.getId()); - List results = query.getResultList(); + List results = query.getResultList(); if (results.isEmpty()) return null; - ClientEntity entity = results.get(0); - return new ClientAdapter(realm, em, session, entity); + String id = results.get(0); + return session.realms().getClientById(id, realm); } @Override diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java index 65d3b1b854..ad2024b940 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java @@ -723,45 +723,17 @@ public class RealmAdapter implements RealmModel { @Override public List getClients() { - TypedQuery query = em.createNamedQuery("getClientsByRealm", ClientEntity.class); - query.setParameter("realm", realm); - List clients = query.getResultList(); - if (clients.isEmpty()) return Collections.EMPTY_LIST; - List list = new LinkedList<>(); - for (ClientEntity entity : clients) { - list.add(new ClientAdapter(this, em, session, entity)); - } - return Collections.unmodifiableList(list); + return session.realms().getClients(this); } @Override public ClientModel addClient(String name) { - return this.addClient(KeycloakModelUtils.generateId(), name); + return session.realms().addClient(this, name); } @Override public ClientModel addClient(String id, String clientId) { - if (clientId == null) { - clientId = id; - } - ClientEntity entity = new ClientEntity(); - entity.setId(id); - entity.setClientId(clientId); - entity.setEnabled(true); - entity.setStandardFlowEnabled(true); - entity.setRealm(realm); - realm.getClients().add(entity); - em.persist(entity); - em.flush(); - final ClientModel resource = new ClientAdapter(this, em, session, entity); - em.flush(); - session.getKeycloakSessionFactory().publish(new ClientCreationEvent() { - @Override - public ClientModel getCreatedClient() { - return resource; - } - }); - return resource; + return session.realms().addClient(this, id, clientId); } @Override @@ -956,7 +928,7 @@ public class RealmAdapter implements RealmModel { em.remove(entity); } - List add = new LinkedList(); + List add = new LinkedList<>(); for (UserFederationProviderModel model : providers) { boolean found = false; for (UserFederationProviderEntity entity : realm.getUserFederationProviders()) { @@ -1008,12 +980,12 @@ public class RealmAdapter implements RealmModel { @Override public RoleModel getRole(String name) { - TypedQuery query = em.createNamedQuery("getRealmRoleByName", RoleEntity.class); + TypedQuery query = em.createNamedQuery("getRealmRoleIdByName", String.class); query.setParameter("name", name); - query.setParameter("realm", realm); - List roles = query.getResultList(); + query.setParameter("realm", realm.getId()); + List roles = query.getResultList(); if (roles.size() == 0) return null; - return new RoleAdapter(this, em, roles.get(0)); + return session.realms().getRoleById(roles.get(0), this); } @Override @@ -1031,7 +1003,7 @@ public class RealmAdapter implements RealmModel { realm.getRoles().add(entity); em.persist(entity); em.flush(); - return new RoleAdapter(this, em, entity); + return new RoleAdapter(session, this, em, entity); } @Override @@ -1063,7 +1035,9 @@ public class RealmAdapter implements RealmModel { if (roles == null) return Collections.EMPTY_SET; Set list = new HashSet(); for (RoleEntity entity : roles) { - list.add(new RoleAdapter(this, em, entity)); + list.add(new RoleAdapter(session, this, em, entity)); + // can't get it from cache cuz of stack overflow + // list.add(session.realms().getRoleById(entity.getId(), this)); } return Collections.unmodifiableSet(list); } @@ -1259,9 +1233,14 @@ public class RealmAdapter implements RealmModel { if (masterAdminClient == null) { return null; } - - RealmAdapter masterRealm = new RealmAdapter(session, em, masterAdminClient.getRealm()); - return new ClientAdapter(masterRealm, em, session, masterAdminClient); + RealmModel masterRealm = null; + String masterAdminClientRealmId = masterAdminClient.getRealm().getId(); + if (masterAdminClientRealmId.equals(getId())) { + masterRealm = this; + } else { + masterRealm = session.realms().getRealm(masterAdminClientRealmId); + } + return session.realms().getClientById(masterAdminClient.getId(), masterRealm); } @Override @@ -2052,11 +2031,12 @@ public class RealmAdapter implements RealmModel { @Override public List getGroups() { - List groups = em.createNamedQuery("getAllGroupsByRealm").setParameter("realm", realm).getResultList(); + List groups = em.createNamedQuery("getAllGroupIdsByRealm", String.class) + .setParameter("realm", realm.getId()).getResultList(); if (groups == null) return Collections.EMPTY_LIST; List list = new LinkedList<>(); - for (GroupEntity entity : groups) { - list.add(new GroupAdapter(this, em, entity)); + for (String id : groups) { + list.add(session.realms().getGroupById(id, this)); } return Collections.unmodifiableList(list); } @@ -2128,7 +2108,7 @@ public class RealmAdapter implements RealmModel { if (entities == null || entities.isEmpty()) return Collections.EMPTY_LIST; List list = new LinkedList<>(); for (ClientTemplateEntity entity : entities) { - list.add(new ClientTemplateAdapter(this, em, session, entity)); + list.add(session.realms().getClientTemplateById(entity.getId(), this)); } return Collections.unmodifiableList(list); } diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java index 21103d7a37..0ac1be9e8d 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java @@ -17,6 +17,7 @@ package org.keycloak.models.jpa; +import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleContainerModel; import org.keycloak.models.RoleModel; @@ -36,11 +37,13 @@ public class RoleAdapter implements RoleModel { protected RoleEntity role; protected EntityManager em; protected RealmModel realm; + protected KeycloakSession session; - public RoleAdapter(RealmModel realm, EntityManager em, RoleEntity role) { + public RoleAdapter(KeycloakSession session, RealmModel realm, EntityManager em, RoleEntity role) { this.em = em; this.realm = realm; this.role = role; + this.session = session; } public RoleEntity getRole() { @@ -115,7 +118,10 @@ public class RoleAdapter implements RoleModel { Set set = new HashSet(); for (RoleEntity composite : getRole().getCompositeRoles()) { - set.add(new RoleAdapter(realm, em, composite)); + set.add(new RoleAdapter(session, realm, em, composite)); + + // todo I want to do this, but can't as you get stack overflow + // set.add(session.realms().getRoleById(composite.getId(), realm)); } return set; } diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java index 5466a7773f..fd4e0a5323 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java @@ -50,6 +50,7 @@ import java.util.Set; @Table(name="CLIENT", uniqueConstraints = {@UniqueConstraint(columnNames = {"REALM_ID", "CLIENT_ID"})}) @NamedQueries({ @NamedQuery(name="getClientsByRealm", query="select client from ClientEntity client where client.realm = :realm"), + @NamedQuery(name="getClientIdsByRealm", query="select client.id from ClientEntity client where client.realm.id = :realm"), @NamedQuery(name="findClientIdByClientId", query="select client.id from ClientEntity client where client.clientId = :clientId and client.realm.id = :realm"), @NamedQuery(name="findClientByClientId", query="select client from ClientEntity client where client.clientId = :clientId and client.realm.id = :realm"), }) diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/GroupEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/GroupEntity.java index 9eadbc8990..0b89c87496 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/GroupEntity.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/GroupEntity.java @@ -39,6 +39,7 @@ import java.util.Collection; */ @NamedQueries({ @NamedQuery(name="getAllGroupsByRealm", query="select u from GroupEntity u where u.realm = :realm order by u.name"), + @NamedQuery(name="getAllGroupIdsByRealm", query="select u.id from GroupEntity u where u.realm.id = :realm order by u.name"), @NamedQuery(name="getGroupById", query="select u from GroupEntity u where u.id = :id and u.realm = :realm"), @NamedQuery(name="getGroupIdsByParent", query="select u.id from GroupEntity u where u.parent = :parent"), @NamedQuery(name="getGroupCount", query="select count(u) from GroupEntity u where u.realm = :realm"), diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java index 684d102b2b..f33aa211ce 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java @@ -49,8 +49,8 @@ import java.util.Set; @Table(name="REALM") @Entity @NamedQueries({ - @NamedQuery(name="getAllRealms", query="select realm from RealmEntity realm"), - @NamedQuery(name="getRealmByName", query="select realm from RealmEntity realm where realm.name = :name"), + @NamedQuery(name="getAllRealmIds", query="select realm.id from RealmEntity realm"), + @NamedQuery(name="getRealmIdByName", query="select realm.id from RealmEntity realm where realm.name = :name"), }) public class RealmEntity { @Id @@ -147,9 +147,6 @@ public class RealmEntity { @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm") Collection userFederationMappers = new ArrayList(); - @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy="realm") - Collection clients = new ArrayList<>(); - @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm") Collection clientTemplates = new ArrayList<>(); @@ -421,14 +418,6 @@ public class RealmEntity { public void setRequiredCredentials(Collection requiredCredentials) { this.requiredCredentials = requiredCredentials; } - public Collection getClients() { - return clients; - } - - public void setClients(Collection clients) { - this.clients = clients; - } - public Collection getRoles() { return roles; } diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java index b94a7439aa..5c02336621 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java @@ -49,8 +49,11 @@ import java.util.Collection; }) @NamedQueries({ @NamedQuery(name="getClientRoles", query="select role from RoleEntity role where role.client = :client"), + @NamedQuery(name="getClientRoleIds", query="select role.id from RoleEntity role where role.client.id = :client"), @NamedQuery(name="getClientRoleByName", query="select role from RoleEntity role where role.name = :name and role.client = :client"), - @NamedQuery(name="getRealmRoleByName", query="select role from RoleEntity role where role.clientRole = false and role.name = :name and role.realm = :realm") + @NamedQuery(name="getClientRoleIdByName", query="select role.id from RoleEntity role where role.name = :name and role.client.id = :client"), + @NamedQuery(name="getRealmRoleByName", query="select role from RoleEntity role where role.clientRole = false and role.name = :name and role.realm = :realm"), + @NamedQuery(name="getRealmRoleIdByName", query="select role.id from RoleEntity role where role.clientRole = false and role.name = :name and role.realm.id = :realm") }) public class RoleEntity { diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoRealmProvider.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoRealmProvider.java index c5d70fb8b7..1c4036b3c7 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoRealmProvider.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoRealmProvider.java @@ -39,6 +39,7 @@ import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity; import org.keycloak.models.utils.KeycloakModelUtils; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -105,7 +106,8 @@ public class MongoRealmProvider implements RealmProvider { List results = new ArrayList(); for (MongoRealmEntity realmEntity : realms) { - results.add(new RealmAdapter(session, realmEntity, invocationContext)); + RealmModel realm = session.realms().getRealm(realmEntity.getId()); + if (realm != null) results.add(realm); } return results; } @@ -118,7 +120,7 @@ public class MongoRealmProvider implements RealmProvider { MongoRealmEntity realm = getMongoStore().loadSingleEntity(MongoRealmEntity.class, query, invocationContext); if (realm == null) return null; - return new RealmAdapter(session, realm, invocationContext); + return session.realms().getRealm(realm.getId()); } @Override @@ -162,6 +164,51 @@ public class MongoRealmProvider implements RealmProvider { return new ClientAdapter(session, realm, appData, invocationContext); } + @Override + public ClientModel addClient(RealmModel realm, String clientId) { + return addClient(realm, KeycloakModelUtils.generateId(), clientId); + } + + @Override + public ClientModel addClient(RealmModel realm, String id, String clientId) { + MongoClientEntity clientEntity = new MongoClientEntity(); + clientEntity.setId(id); + clientEntity.setClientId(clientId); + clientEntity.setRealmId(realm.getId()); + clientEntity.setEnabled(true); + clientEntity.setStandardFlowEnabled(true); + getMongoStore().insertEntity(clientEntity, invocationContext); + + if (clientId == null) { + clientEntity.setClientId(clientEntity.getId()); + getMongoStore().updateEntity(clientEntity, invocationContext); + } + + final ClientModel model = new ClientAdapter(session, realm, clientEntity, invocationContext); + session.getKeycloakSessionFactory().publish(new RealmModel.ClientCreationEvent() { + @Override + public ClientModel getCreatedClient() { + return model; + } + }); + return model; + } + + @Override + public List getClients(RealmModel realm) { + DBObject query = new QueryBuilder() + .and("realmId").is(realm.getId()) + .get(); + List clientEntities = getMongoStore().loadEntities(MongoClientEntity.class, query, invocationContext); + + if (clientEntities.isEmpty()) return Collections.EMPTY_LIST; + List result = new ArrayList(); + for (MongoClientEntity clientEntity : clientEntities) { + result.add(session.realms().getClientById(clientEntity.getId(), realm)); + } + return Collections.unmodifiableList(result); + } + @Override public boolean removeClient(String id, RealmModel realm) { if (id == null) return false; @@ -180,7 +227,8 @@ public class MongoRealmProvider implements RealmProvider { .and("clientId").is(clientId) .get(); MongoClientEntity appEntity = getMongoStore().loadSingleEntity(MongoClientEntity.class, query, invocationContext); - return appEntity == null ? null : new ClientAdapter(session, realm, appEntity, invocationContext); + if (appEntity == null) return null; + return session.realms().getClientById(appEntity.getId(), realm); } diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java index 48978ffe12..edb57d2156 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java @@ -815,47 +815,18 @@ public class RealmAdapter extends AbstractMongoAdapter impleme @Override public List getClients() { - DBObject query = new QueryBuilder() - .and("realmId").is(getId()) - .get(); - List clientEntities = getMongoStore().loadEntities(MongoClientEntity.class, query, invocationContext); - - if (clientEntities.isEmpty()) return Collections.EMPTY_LIST; - List result = new ArrayList(); - for (MongoClientEntity clientEntity : clientEntities) { - result.add(new ClientAdapter(session, this, clientEntity, invocationContext)); - } - return Collections.unmodifiableList(result); + return session.realms().getClients(this); } @Override public ClientModel addClient(String name) { - return this.addClient(null, name); + return session.realms().addClient(this, name); } @Override public ClientModel addClient(String id, String clientId) { - MongoClientEntity clientEntity = new MongoClientEntity(); - clientEntity.setId(id); - clientEntity.setClientId(clientId); - clientEntity.setRealmId(getId()); - clientEntity.setEnabled(true); - clientEntity.setStandardFlowEnabled(true); - getMongoStore().insertEntity(clientEntity, invocationContext); + return session.realms().addClient(this, id, clientId); - if (clientId == null) { - clientEntity.setClientId(clientEntity.getId()); - getMongoStore().updateEntity(clientEntity, invocationContext); - } - - final ClientModel model = new ClientAdapter(session, this, clientEntity, invocationContext); - session.getKeycloakSessionFactory().publish(new ClientCreationEvent() { - @Override - public ClientModel getCreatedClient() { - return model; - } - }); - return model; } @Override diff --git a/server-spi/src/main/java/org/keycloak/models/RealmProvider.java b/server-spi/src/main/java/org/keycloak/models/RealmProvider.java index 2b3f5fab3c..a101ce4919 100755 --- a/server-spi/src/main/java/org/keycloak/models/RealmProvider.java +++ b/server-spi/src/main/java/org/keycloak/models/RealmProvider.java @@ -35,6 +35,12 @@ public interface RealmProvider extends Provider { RealmModel getRealm(String id); RealmModel getRealmByName(String name); + ClientModel addClient(RealmModel realm, String clientId); + + ClientModel addClient(RealmModel realm, String id, String clientId); + + List getClients(RealmModel realm); + ClientModel getClientById(String id, RealmModel realm); ClientModel getClientByClientId(String clientId, RealmModel realm); diff --git a/server-spi/src/main/java/org/keycloak/models/cache/CacheRealmProvider.java b/server-spi/src/main/java/org/keycloak/models/cache/CacheRealmProvider.java index 0f90b1d363..7bc1299764 100755 --- a/server-spi/src/main/java/org/keycloak/models/cache/CacheRealmProvider.java +++ b/server-spi/src/main/java/org/keycloak/models/cache/CacheRealmProvider.java @@ -29,7 +29,7 @@ public interface CacheRealmProvider extends RealmProvider { void registerRealmInvalidation(String id); - void registerApplicationInvalidation(String id); + void registerClientInvalidation(String id); void registerClientTemplateInvalidation(String id); void registerRoleInvalidation(String id); diff --git a/server-spi/src/main/java/org/keycloak/models/cache/entities/CachedClientRole.java b/server-spi/src/main/java/org/keycloak/models/cache/entities/CachedClientRole.java index 0109d6a1d5..3cbfa304e0 100755 --- a/server-spi/src/main/java/org/keycloak/models/cache/entities/CachedClientRole.java +++ b/server-spi/src/main/java/org/keycloak/models/cache/entities/CachedClientRole.java @@ -26,15 +26,15 @@ import org.keycloak.models.RoleModel; */ public class CachedClientRole extends CachedRole { - private final String idClient; + private final String clientId; - public CachedClientRole(String idClient, RoleModel model, RealmModel realm) { + public CachedClientRole(String clientId, RoleModel model, RealmModel realm) { super(model, realm); - this.idClient = idClient; + this.clientId = clientId; } - public String getIdClient() { - return idClient; + public String getClientId() { + return clientId; } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json index e26f6107dc..ae0f5f9a07 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json @@ -27,7 +27,10 @@ }, "userCache": { - "provider": "${keycloak.user.cache.provider:infinispan}", + "provider": "${keycloak.user.cache.provider:default}", + "default" : { + "enabled": true + }, "mem": { "maxSize": 20000 } @@ -95,15 +98,14 @@ }, "realmCache": { - "provider": "infinispan-locking", - "infinispan-locking" : { + "provider": "${keycloak.realm.cache.provider:default}", + "default" : { "enabled": true } }, "connectionsInfinispan": { - "provider": "locking", - "locking": { + "default": { "clustered": "${keycloak.connectionsInfinispan.clustered:false}", "async": "${keycloak.connectionsInfinispan.async:true}", "sessionsOwners": "${keycloak.connectionsInfinispan.sessionsOwners:2}" diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/ConcurrencyTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/ConcurrencyTest.java index 7984fae9d3..9adebd3fc2 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/ConcurrencyTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/ConcurrencyTest.java @@ -27,6 +27,7 @@ import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.RoleRepresentation; +import javax.ws.rs.NotFoundException; import javax.ws.rs.core.Response; import java.util.LinkedList; import java.util.List; @@ -90,6 +91,7 @@ public class ConcurrencyTest extends AbstractClientTest { @Test public void createClient() throws Throwable { + System.out.println("***************************"); long start = System.currentTimeMillis(); run(new KeycloakRunnable() { @Override @@ -101,7 +103,8 @@ public class ConcurrencyTest extends AbstractClientTest { String id = ApiUtil.getCreatedId(response); response.close(); - assertNotNull(realm.clients().get(id)); + c = realm.clients().get(id).toRepresentation(); + assertNotNull(c); boolean found = false; for (ClientRepresentation r : realm.clients().findAll()) { if (r.getClientId().equals(name)) { @@ -119,6 +122,58 @@ public class ConcurrencyTest extends AbstractClientTest { } + @Test + @Ignore + public void createRemoveClient() throws Throwable { + // FYI< this will fail as HSQL seems to be trying to perform table locks. + System.out.println("***************************"); + long start = System.currentTimeMillis(); + run(new KeycloakRunnable() { + @Override + public void run(Keycloak keycloak, RealmResource realm, int threadNum, int iterationNum) { + String name = "c-" + threadNum + "-" + iterationNum; + ClientRepresentation c = new ClientRepresentation(); + c.setClientId(name); + Response response = realm.clients().create(c); + String id = ApiUtil.getCreatedId(response); + response.close(); + + c = realm.clients().get(id).toRepresentation(); + assertNotNull(c); + boolean found = false; + for (ClientRepresentation r : realm.clients().findAll()) { + if (r.getClientId().equals(name)) { + found = true; + break; + } + } + if (!found) { + fail("Client " + name + " not found in client list"); + } + realm.clients().get(id).remove(); + try { + c = realm.clients().get(id).toRepresentation(); + fail("Client " + name + " should not be found. Should throw a 404"); + } catch (NotFoundException e) { + + } + found = false; + for (ClientRepresentation r : realm.clients().findAll()) { + if (r.getClientId().equals(name)) { + found = true; + break; + } + } + Assert.assertFalse("Client " + name + " should not be in client list", found); + + } + }); + long end = System.currentTimeMillis() - start; + System.out.println("createClient took " + end); + + } + + @Test public void createRole() throws Throwable { long start = System.currentTimeMillis(); diff --git a/testsuite/integration/src/test/resources/META-INF/keycloak-server.json b/testsuite/integration/src/test/resources/META-INF/keycloak-server.json index 9329cde8d0..950fd224ad 100755 --- a/testsuite/integration/src/test/resources/META-INF/keycloak-server.json +++ b/testsuite/integration/src/test/resources/META-INF/keycloak-server.json @@ -27,7 +27,7 @@ }, "userCache": { - "infinispan" : { + "default" : { "enabled": true } }, @@ -78,15 +78,13 @@ }, "realmCache": { - "provider": "infinispan-locking", - "infinispan-locking" : { + "default" : { "enabled": true } }, "connectionsInfinispan": { - "provider": "locking", - "locking": { + "default": { "clustered": "${keycloak.connectionsInfinispan.clustered:false}", "async": "${keycloak.connectionsInfinispan.async:true}", "sessionsOwners": "${keycloak.connectionsInfinispan.sessionsOwners:2}" diff --git a/testsuite/integration/src/test/resources/log4j.properties b/testsuite/integration/src/test/resources/log4j.properties index 91c9646a80..a199a4174d 100755 --- a/testsuite/integration/src/test/resources/log4j.properties +++ b/testsuite/integration/src/test/resources/log4j.properties @@ -19,7 +19,7 @@ log4j.rootLogger=info, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%c] %m%n +log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p %t [%c] %m%n log4j.logger.org.keycloak=info