From d79a7146ba896aaf2f11f08c78c68ed7719754cb Mon Sep 17 00:00:00 2001 From: Bill Burke Date: Thu, 11 Feb 2016 22:01:54 -0500 Subject: [PATCH 1/4] fix cache --- .../counter/RevisionedCacheRealmProvider.java | 401 ------------ .../RevisionedCacheRealmProviderFactory.java | 161 ----- .../RevisionedConnectionProviderFactory.java | 47 -- .../counter/RevisionedRealmCache.java | 255 -------- .../locking/LockingCacheRealmProvider.java | 20 +- .../infinispan/locking/LockingRealmCache.java | 69 +-- .../{counter => locking}/Revisioned.java | 2 +- .../{counter => locking}/UpdateCounter.java | 2 +- .../entities/RevisionedCachedClient.java | 5 +- .../entities/RevisionedCachedClientRole.java | 4 +- .../RevisionedCachedClientTemplate.java | 4 +- .../entities/RevisionedCachedGroup.java | 4 +- .../entities/RevisionedCachedRealm.java | 8 +- .../entities/RevisionedCachedRealmRole.java | 4 +- .../entities/RevisionedCachedUser.java | 4 +- ...eadWriteSkewCacheRealmProviderFactory.java | 160 ----- ...eadWriteSkewConnectionProviderFactory.java | 164 ----- .../RepeatableReadWriteSkewRealmCache.java | 269 --------- ...atableReadWriteSkewRealmCacheProvider.java | 472 --------------- ...nispan.InfinispanConnectionProviderFactory | 1 - ...oak.models.cache.CacheRealmProviderFactory | 1 - .../testsuite/admin/ConcurrencyTest.java | 4 +- testsuite/pom.xml | 1 + testsuite/stress/pom.xml | 571 ++++++++++++++++++ .../keycloak/test/stress/MaxRateExecutor.java | 137 +++++ .../keycloak/test/stress/StressExecutor.java | 65 ++ .../keycloak/test/stress/StressResult.java | 62 ++ .../org/keycloak/test/stress/StressTest.java | 37 ++ .../java/org/keycloak/test/stress/Test.java | 11 + .../org/keycloak/test/stress/TestFactory.java | 9 + .../test/stress/tests/LoginLogout.java | 104 ++++ .../test/CustomerDatabaseServlet.java | 51 ++ .../java/org/keycloak/test/FrameworkTest.java | 82 +++ .../adapter-test/cust-app-keycloak.json | 11 + .../stress/src/test/resources/testrealm.json | 185 ++++++ 35 files changed, 1390 insertions(+), 1997 deletions(-) delete mode 100755 model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/RevisionedCacheRealmProvider.java delete mode 100755 model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/RevisionedCacheRealmProviderFactory.java delete mode 100755 model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/RevisionedConnectionProviderFactory.java delete mode 100755 model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/RevisionedRealmCache.java rename model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/{counter => locking}/Revisioned.java (78%) rename model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/{counter => locking}/UpdateCounter.java (87%) rename model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/{counter => locking}/entities/RevisionedCachedClient.java (85%) rename model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/{counter => locking}/entities/RevisionedCachedClientRole.java (85%) rename model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/{counter => locking}/entities/RevisionedCachedClientTemplate.java (87%) rename model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/{counter => locking}/entities/RevisionedCachedGroup.java (84%) rename model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/{counter => locking}/entities/RevisionedCachedRealm.java (81%) rename model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/{counter => locking}/entities/RevisionedCachedRealmRole.java (84%) rename model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/{counter => locking}/entities/RevisionedCachedUser.java (84%) delete mode 100755 model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/skewed/RepeatableReadWriteSkewCacheRealmProviderFactory.java delete mode 100755 model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/skewed/RepeatableReadWriteSkewConnectionProviderFactory.java delete mode 100755 model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/skewed/RepeatableReadWriteSkewRealmCache.java delete mode 100755 model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/skewed/RepeatableReadWriteSkewRealmCacheProvider.java create mode 100755 testsuite/stress/pom.xml create mode 100755 testsuite/stress/src/main/java/org/keycloak/test/stress/MaxRateExecutor.java create mode 100755 testsuite/stress/src/main/java/org/keycloak/test/stress/StressExecutor.java create mode 100755 testsuite/stress/src/main/java/org/keycloak/test/stress/StressResult.java create mode 100755 testsuite/stress/src/main/java/org/keycloak/test/stress/StressTest.java create mode 100755 testsuite/stress/src/main/java/org/keycloak/test/stress/Test.java create mode 100755 testsuite/stress/src/main/java/org/keycloak/test/stress/TestFactory.java create mode 100755 testsuite/stress/src/main/java/org/keycloak/test/stress/tests/LoginLogout.java create mode 100755 testsuite/stress/src/test/java/org/keycloak/test/CustomerDatabaseServlet.java create mode 100755 testsuite/stress/src/test/java/org/keycloak/test/FrameworkTest.java create mode 100755 testsuite/stress/src/test/resources/adapter-test/cust-app-keycloak.json create mode 100755 testsuite/stress/src/test/resources/testrealm.json diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/RevisionedCacheRealmProvider.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/RevisionedCacheRealmProvider.java deleted file mode 100755 index d241519aef..0000000000 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/RevisionedCacheRealmProvider.java +++ /dev/null @@ -1,401 +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.counter; - -import org.jboss.logging.Logger; -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 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.counter.entities.RevisionedCachedClient; -import org.keycloak.models.cache.infinispan.counter.entities.RevisionedCachedClientRole; -import org.keycloak.models.cache.infinispan.counter.entities.RevisionedCachedClientTemplate; -import org.keycloak.models.cache.infinispan.counter.entities.RevisionedCachedGroup; -import org.keycloak.models.cache.infinispan.counter.entities.RevisionedCachedRealm; -import org.keycloak.models.cache.infinispan.counter.entities.RevisionedCachedRealmRole; - -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 RevisionedCacheRealmProvider implements CacheRealmProvider { - protected static final Logger logger = Logger.getLogger(RevisionedCacheRealmProvider.class); - protected RevisionedRealmCache 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 RevisionedCacheRealmProvider(RevisionedRealmCache 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.invalidateCachedRealmById(id); - } - for (String id : roleInvalidations) { - cache.invalidateRoleById(id); - } - for (String id : groupInvalidations) { - cache.invalidateGroupById(id); - } - for (String id : appInvalidations) { - cache.invalidateCachedApplicationById(id); - } - for (String id : clientTemplateInvalidations) { - cache.invalidateCachedClientTemplateById(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.getCachedRealm(id); - if (cached != null) { - logger.tracev("by id cache hit: {0}", cached.getName()); - } - if (cached == null) { - Long 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); - logger.tracev("try caching realm: {0} {1}", cached.getName(), loaded); - cache.addCachedRealm(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.getCachedRealmByName(name); - if (cached != null) { - logger.tracev("by name cache hit: {0}", cached.getName()); - } - if (cached == null) { - Long loaded = UpdateCounter.current(); - RealmModel model = getDelegate().getRealmByName(name); - if (model == null) return null; - if (realmInvalidations.contains(model.getId())) return model; - cached = new RevisionedCachedRealm(loaded, cache, this, model); - logger.tracev("try caching realm: {0}", cached.getName()); - cache.addCachedRealm(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.invalidateCachedRealmById(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) { - Long loaded = UpdateCounter.current(); - RoleModel model = getDelegate().getRoleById(id, realm); - if (model == null) return null; - if (roleInvalidations.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.addCachedRole(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) { - Long loaded = UpdateCounter.current(); - GroupModel model = getDelegate().getGroupById(id, realm); - if (model == null) return null; - if (groupInvalidations.contains(id)) return model; - cached = new RevisionedCachedGroup(loaded, realm, model); - cache.addCachedGroup(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.getApplication(id); - if (cached != null && !cached.getRealm().equals(realm.getId())) { - cached = null; - } - if (cached != null) { - logger.tracev("client by id cache hit: {0}", cached.getClientId()); - } - - if (cached == null) { - Long 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); - cache.addCachedClient(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 ClientTemplateModel getClientTemplateById(String id, RealmModel realm) { - CachedClientTemplate cached = cache.getClientTemplate(id); - if (cached != null && !cached.getRealm().equals(realm.getId())) { - cached = null; - } - - if (cached == null) { - Long 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.addCachedClientTemplate(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/counter/RevisionedCacheRealmProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/RevisionedCacheRealmProviderFactory.java deleted file mode 100755 index d50eb0b885..0000000000 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/RevisionedCacheRealmProviderFactory.java +++ /dev/null @@ -1,161 +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.counter; - -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; - -import java.util.concurrent.ConcurrentHashMap; - -/** - * @author Bill Burke - * @author Stian Thorgersen - */ -public class RevisionedCacheRealmProviderFactory implements CacheRealmProviderFactory { - - private static final Logger log = Logger.getLogger(RevisionedCacheRealmProviderFactory.class); - - protected volatile RevisionedRealmCache realmCache; - - protected final ConcurrentHashMap realmLookup = new ConcurrentHashMap<>(); - - @Override - public CacheRealmProvider create(KeycloakSession session) { - lazyInit(session); - return new RevisionedCacheRealmProvider(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(RevisionedConnectionProviderFactory.VERSION_CACHE_NAME); - cache.addListener(new CacheListener()); - realmCache = new RevisionedRealmCache(cache, counterCache, realmLookup); - } - } - } - } - - @Override - public void init(Config.Scope config) { - } - - @Override - public void postInit(KeycloakSessionFactory factory) { - - } - - @Override - public void close() { - } - - @Override - public String getId() { - return "infinispan-revisioned"; - } - - @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.evictCachedRoleById(r); - } - - for (String c : realm.getClients().values()) { - realmCache.evictCachedApplicationById(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.evictCachedRoleById(r); - } - - log.tracev("Client removed client={0}", client.getId()); - } - } - } -} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/RevisionedConnectionProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/RevisionedConnectionProviderFactory.java deleted file mode 100755 index 845a907461..0000000000 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/RevisionedConnectionProviderFactory.java +++ /dev/null @@ -1,47 +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.counter; - -import org.infinispan.configuration.cache.Configuration; -import org.infinispan.configuration.cache.ConfigurationBuilder; -import org.jboss.logging.Logger; -import org.keycloak.connections.infinispan.DefaultInfinispanConnectionProviderFactory; - -/** - * @author Stian Thorgersen - */ -public class RevisionedConnectionProviderFactory extends DefaultInfinispanConnectionProviderFactory { - public static final String VERSION_CACHE_NAME = "realmVersions"; - - protected static final Logger logger = Logger.getLogger(RevisionedConnectionProviderFactory.class); - - @Override - public String getId() { - return "revisioned"; - } - - - protected void initEmbedded() { - super.initEmbedded(); - ConfigurationBuilder counterConfigBuilder = new ConfigurationBuilder(); - Configuration counterCacheConfiguration = counterConfigBuilder.build(); - - cacheManager.defineConfiguration(VERSION_CACHE_NAME, counterCacheConfiguration); - } - -} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/RevisionedRealmCache.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/RevisionedRealmCache.java deleted file mode 100755 index f32836ee20..0000000000 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/RevisionedRealmCache.java +++ /dev/null @@ -1,255 +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.counter; - -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; -import java.util.concurrent.atomic.AtomicLong; - -/** - * @author Stian Thorgersen - */ -public class RevisionedRealmCache implements RealmCache { - - protected static final Logger logger = Logger.getLogger(RevisionedRealmCache.class); - - protected final Cache revisions; - protected final Cache cache; - - protected final ConcurrentHashMap realmLookup; - - public RevisionedRealmCache(Cache cache, Cache revisions, ConcurrentHashMap realmLookup) { - this.cache = cache; - this.realmLookup = realmLookup; - this.revisions = revisions; - } - - public Cache getCache() { - return cache; - } - - 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("stale 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) { - Long rev = revisions.get(id); - if (rev == null) { - logger.tracev("rev was null in addRevisioned, adding one"); - rev = UpdateCounter.next(); - revisions.put(id, rev); - return; - } - cache.putForExternalRead(id, object); - } - - - - - - - - @Override - public void clear() { - cache.clear(); - } - - @Override - public CachedRealm getCachedRealm(String id) { - return get(id, CachedRealm.class); - } - - @Override - public void invalidateCachedRealm(CachedRealm realm) { - logger.tracev("Invalidating realm {0}", realm.getId()); - invalidateObject(realm.getId()); - realmLookup.remove(realm.getName()); - } - - @Override - public void invalidateCachedRealmById(String id) { - CachedRealm cached = (CachedRealm) invalidateObject(id); - if (cached != null) realmLookup.remove(cached.getName()); - } - - @Override - public void addCachedRealm(CachedRealm realm) { - logger.tracev("Adding realm {0}", realm.getId()); - addRevisioned(realm.getId(), (Revisioned) realm); - realmLookup.put(realm.getName(), realm.getId()); - } - - @Override - public CachedRealm getCachedRealmByName(String name) { - String id = realmLookup.get(name); - return id != null ? getCachedRealm(id) : null; - } - - @Override - public CachedClient getApplication(String id) { - return get(id, CachedClient.class); - } - - @Override - public void invalidateApplication(CachedClient app) { - logger.tracev("Removing application {0}", app.getId()); - invalidateObject(app.getId()); - } - - @Override - public void addCachedClient(CachedClient app) { - logger.tracev("Adding application {0}", app.getId()); - addRevisioned(app.getId(), (Revisioned) app); - } - - @Override - public void invalidateCachedApplicationById(String id) { - CachedClient client = (CachedClient)invalidateObject(id); - if (client != null) logger.tracev("Removing application {0}", client.getClientId()); - } - - @Override - public void evictCachedApplicationById(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 addCachedGroup(CachedGroup role) { - logger.tracev("Adding group {0}", role.getId()); - addRevisioned(role.getId(), (Revisioned) role); - } - - @Override - public void invalidateCachedGroupById(String id) { - logger.tracev("Removing group {0}", id); - invalidateObject(id); - - } - - @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 evictCachedRoleById(String id) { - logger.tracev("Evicting role {0}", id); - cache.evict(id); - } - - @Override - public void addCachedRole(CachedRole role) { - logger.tracev("Adding role {0}", role.getId()); - addRevisioned(role.getId(), (Revisioned) role); - } - - @Override - public void invalidateCachedRoleById(String id) { - logger.tracev("Removing role {0}", id); - invalidateObject(id); - } - - @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 addCachedClientTemplate(CachedClientTemplate app) { - logger.tracev("Adding client template {0}", app.getId()); - addRevisioned(app.getId(), (Revisioned) app); - } - - @Override - public void invalidateCachedClientTemplateById(String id) { - logger.tracev("Removing client template {0}", id); - invalidateObject(id); - } - - @Override - public void evictCachedClientTemplateById(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/locking/LockingCacheRealmProvider.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/LockingCacheRealmProvider.java index ea451b0876..162a36a737 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/locking/LockingCacheRealmProvider.java @@ -38,12 +38,12 @@ 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.counter.entities.RevisionedCachedClient; -import org.keycloak.models.cache.infinispan.counter.entities.RevisionedCachedClientRole; -import org.keycloak.models.cache.infinispan.counter.entities.RevisionedCachedClientTemplate; -import org.keycloak.models.cache.infinispan.counter.entities.RevisionedCachedGroup; -import org.keycloak.models.cache.infinispan.counter.entities.RevisionedCachedRealm; -import org.keycloak.models.cache.infinispan.counter.entities.RevisionedCachedRealmRole; +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 java.util.Collections; import java.util.HashMap; @@ -277,6 +277,7 @@ public class LockingCacheRealmProvider implements CacheRealmProvider { } 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; @@ -299,10 +300,11 @@ public class LockingCacheRealmProvider implements CacheRealmProvider { logger.tracev("by name cache hit: {0}", cached.getName()); } if (cached == null) { + Long loaded = UpdateCounter.current(); RealmModel model = getDelegate().getRealmByName(name); if (model == null) return null; if (realmInvalidations.contains(model.getId())) return model; - cached = new RevisionedCachedRealm(null, cache, this, model); + cached = new RevisionedCachedRealm(loaded, cache, this, model); cache.addCachedRealm(cached); } else if (realmInvalidations.contains(cached.getId())) { return getDelegate().getRealmByName(name); @@ -365,6 +367,7 @@ public class LockingCacheRealmProvider implements CacheRealmProvider { 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; @@ -394,6 +397,7 @@ public class LockingCacheRealmProvider implements CacheRealmProvider { 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; @@ -422,6 +426,7 @@ 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; @@ -445,6 +450,7 @@ public class LockingCacheRealmProvider implements CacheRealmProvider { 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; 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 index 9c8e784aff..2460502d91 100755 --- 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 @@ -25,7 +25,6 @@ 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.counter.Revisioned; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; @@ -39,11 +38,6 @@ public class LockingRealmCache implements RealmCache { protected final Cache revisions; protected final Cache cache; - final AtomicLong realmCounter = new AtomicLong(); - final AtomicLong clientCounter = new AtomicLong(); - final AtomicLong clientTemplateCounter = new AtomicLong(); - final AtomicLong roleCounter = new AtomicLong(); - final AtomicLong groupCounter = new AtomicLong(); protected final ConcurrentHashMap realmLookup; @@ -61,8 +55,8 @@ public class LockingRealmCache implements RealmCache { return revisions; } - public void startRevisionBatch() { - revisions.startBatch(); + public Long getCurrentRevision(String id) { + return revisions.get(id); } public void endRevisionBatch() { @@ -91,33 +85,39 @@ public class LockingRealmCache implements RealmCache { return o != null && type.isInstance(o) ? type.cast(o) : null; } - protected Object invalidateObject(String id, AtomicLong counter) { + protected Object invalidateObject(String id) { Object removed = cache.remove(id); - revisions.put(id, counter.incrementAndGet()); + revisions.put(id, UpdateCounter.next()); return removed; } - protected void addRevisioned(String id, Revisioned object, AtomicLong counter) { + protected void addRevisioned(String id, Revisioned object) { //startRevisionBatch(); try { //revisions.getAdvancedCache().lock(id); Long rev = revisions.get(id); if (rev == null) { - rev = counter.incrementAndGet(); + rev = UpdateCounter.current(); revisions.put(id, rev); - return; } revisions.startBatch(); - revisions.getAdvancedCache().lock(id); + if (!revisions.getAdvancedCache().lock(id)) { + logger.trace("Could not obtain version lock"); + } rev = revisions.get(id); if (rev == null) { - rev = counter.incrementAndGet(); - revisions.put(id, rev); 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(); } @@ -127,9 +127,6 @@ public class LockingRealmCache implements RealmCache { - public Long getCurrentRevision(String id) { - return revisions.get(id); - } @Override public void clear() { cache.clear(); @@ -143,20 +140,20 @@ public class LockingRealmCache implements RealmCache { @Override public void invalidateCachedRealm(CachedRealm realm) { logger.tracev("Invalidating realm {0}", realm.getId()); - invalidateObject(realm.getId(), realmCounter); + invalidateObject(realm.getId()); realmLookup.remove(realm.getName()); } @Override public void invalidateCachedRealmById(String id) { - CachedRealm cached = (CachedRealm) invalidateObject(id, realmCounter); + CachedRealm cached = (CachedRealm) invalidateObject(id); if (cached != null) realmLookup.remove(cached.getName()); } @Override public void addCachedRealm(CachedRealm realm) { logger.tracev("Adding realm {0}", realm.getId()); - addRevisioned(realm.getId(), (Revisioned) realm, realmCounter); + addRevisioned(realm.getId(), (Revisioned) realm); realmLookup.put(realm.getName(), realm.getId()); } @@ -175,18 +172,18 @@ public class LockingRealmCache implements RealmCache { @Override public void invalidateApplication(CachedClient app) { logger.tracev("Removing application {0}", app.getId()); - invalidateObject(app.getId(), clientCounter); + invalidateObject(app.getId()); } @Override public void addCachedClient(CachedClient app) { logger.tracev("Adding application {0}", app.getId()); - addRevisioned(app.getId(), (Revisioned) app, clientCounter); + addRevisioned(app.getId(), (Revisioned) app); } @Override public void invalidateCachedApplicationById(String id) { - CachedClient client = (CachedClient)invalidateObject(id, clientCounter); + CachedClient client = (CachedClient)invalidateObject(id); if (client != null) logger.tracev("Removing application {0}", client.getClientId()); } @@ -204,26 +201,26 @@ public class LockingRealmCache implements RealmCache { @Override public void invalidateGroup(CachedGroup role) { logger.tracev("Removing group {0}", role.getId()); - invalidateObject(role.getId(), groupCounter); + invalidateObject(role.getId()); } @Override public void addCachedGroup(CachedGroup role) { logger.tracev("Adding group {0}", role.getId()); - addRevisioned(role.getId(), (Revisioned) role, groupCounter); + addRevisioned(role.getId(), (Revisioned) role); } @Override public void invalidateCachedGroupById(String id) { logger.tracev("Removing group {0}", id); - invalidateObject(id, groupCounter); + invalidateObject(id); } @Override public void invalidateGroupById(String id) { logger.tracev("Removing group {0}", id); - invalidateObject(id, groupCounter); + invalidateObject(id); } @Override @@ -234,13 +231,13 @@ public class LockingRealmCache implements RealmCache { @Override public void invalidateRole(CachedRole role) { logger.tracev("Removing role {0}", role.getId()); - invalidateObject(role.getId(), roleCounter); + invalidateObject(role.getId()); } @Override public void invalidateRoleById(String id) { logger.tracev("Removing role {0}", id); - invalidateObject(id, roleCounter); + invalidateObject(id); } @Override @@ -252,13 +249,13 @@ public class LockingRealmCache implements RealmCache { @Override public void addCachedRole(CachedRole role) { logger.tracev("Adding role {0}", role.getId()); - addRevisioned(role.getId(), (Revisioned) role, roleCounter); + addRevisioned(role.getId(), (Revisioned) role); } @Override public void invalidateCachedRoleById(String id) { logger.tracev("Removing role {0}", id); - invalidateObject(id, roleCounter); + invalidateObject(id); } @Override @@ -269,19 +266,19 @@ public class LockingRealmCache implements RealmCache { @Override public void invalidateClientTemplate(CachedClientTemplate app) { logger.tracev("Removing client template {0}", app.getId()); - invalidateObject(app.getId(), clientTemplateCounter); + invalidateObject(app.getId()); } @Override public void addCachedClientTemplate(CachedClientTemplate app) { logger.tracev("Adding client template {0}", app.getId()); - addRevisioned(app.getId(), (Revisioned) app, clientTemplateCounter); + addRevisioned(app.getId(), (Revisioned) app); } @Override public void invalidateCachedClientTemplateById(String id) { logger.tracev("Removing client template {0}", id); - invalidateObject(id, clientTemplateCounter); + invalidateObject(id); } @Override diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/Revisioned.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/Revisioned.java similarity index 78% rename from model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/Revisioned.java rename to model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/Revisioned.java index f4d4c7ae8c..7af8d0f073 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/Revisioned.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/Revisioned.java @@ -1,4 +1,4 @@ -package org.keycloak.models.cache.infinispan.counter; +package org.keycloak.models.cache.infinispan.locking; /** * @author Bill Burke diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/UpdateCounter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/UpdateCounter.java similarity index 87% rename from model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/UpdateCounter.java rename to model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/UpdateCounter.java index 88d598b3d8..8e8b5c5ba8 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/UpdateCounter.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/UpdateCounter.java @@ -1,4 +1,4 @@ -package org.keycloak.models.cache.infinispan.counter; +package org.keycloak.models.cache.infinispan.locking; import java.util.concurrent.atomic.AtomicLong; diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/entities/RevisionedCachedClient.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedClient.java similarity index 85% rename from model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/entities/RevisionedCachedClient.java rename to model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedClient.java index c07725ed4f..4feeffae2f 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/entities/RevisionedCachedClient.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedClient.java @@ -1,4 +1,4 @@ -package org.keycloak.models.cache.infinispan.counter.entities; +package org.keycloak.models.cache.infinispan.locking.entities; import org.keycloak.models.ClientModel; import org.keycloak.models.RealmModel; @@ -6,8 +6,7 @@ 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.entities.CachedClientRole; -import org.keycloak.models.cache.infinispan.counter.Revisioned; +import org.keycloak.models.cache.infinispan.locking.Revisioned; /** * @author Bill Burke diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/entities/RevisionedCachedClientRole.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedClientRole.java similarity index 85% rename from model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/entities/RevisionedCachedClientRole.java rename to model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedClientRole.java index 2bdb1763c5..c3554f64ae 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/entities/RevisionedCachedClientRole.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedClientRole.java @@ -1,9 +1,9 @@ -package org.keycloak.models.cache.infinispan.counter.entities; +package org.keycloak.models.cache.infinispan.locking.entities; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; import org.keycloak.models.cache.entities.CachedClientRole; -import org.keycloak.models.cache.infinispan.counter.Revisioned; +import org.keycloak.models.cache.infinispan.locking.Revisioned; /** * @author Bill Burke diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/entities/RevisionedCachedClientTemplate.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedClientTemplate.java similarity index 87% rename from model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/entities/RevisionedCachedClientTemplate.java rename to model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedClientTemplate.java index eeca1f6b78..44720d3385 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/entities/RevisionedCachedClientTemplate.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedClientTemplate.java @@ -1,11 +1,11 @@ -package org.keycloak.models.cache.infinispan.counter.entities; +package org.keycloak.models.cache.infinispan.locking.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.counter.Revisioned; +import org.keycloak.models.cache.infinispan.locking.Revisioned; /** * @author Bill Burke diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/entities/RevisionedCachedGroup.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedGroup.java similarity index 84% rename from model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/entities/RevisionedCachedGroup.java rename to model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedGroup.java index 32529ea5b1..09c7f0f659 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/entities/RevisionedCachedGroup.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedGroup.java @@ -1,9 +1,9 @@ -package org.keycloak.models.cache.infinispan.counter.entities; +package org.keycloak.models.cache.infinispan.locking.entities; import org.keycloak.models.GroupModel; import org.keycloak.models.RealmModel; import org.keycloak.models.cache.entities.CachedGroup; -import org.keycloak.models.cache.infinispan.counter.Revisioned; +import org.keycloak.models.cache.infinispan.locking.Revisioned; /** * @author Bill Burke diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/entities/RevisionedCachedRealm.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedRealm.java similarity index 81% rename from model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/entities/RevisionedCachedRealm.java rename to model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedRealm.java index c527ea5c7a..2b3a524386 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/entities/RevisionedCachedRealm.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedRealm.java @@ -1,4 +1,4 @@ -package org.keycloak.models.cache.infinispan.counter.entities; +package org.keycloak.models.cache.infinispan.locking.entities; import org.keycloak.models.ClientModel; import org.keycloak.models.ClientTemplateModel; @@ -6,12 +6,8 @@ import org.keycloak.models.RealmModel; 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.entities.CachedClientTemplate; import org.keycloak.models.cache.entities.CachedRealm; -import org.keycloak.models.cache.entities.CachedRealmRole; -import org.keycloak.models.cache.entities.CachedRole; -import org.keycloak.models.cache.infinispan.counter.Revisioned; +import org.keycloak.models.cache.infinispan.locking.Revisioned; /** * @author Bill Burke diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/entities/RevisionedCachedRealmRole.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedRealmRole.java similarity index 84% rename from model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/entities/RevisionedCachedRealmRole.java rename to model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedRealmRole.java index 776b885b72..3899bd8b58 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/entities/RevisionedCachedRealmRole.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedRealmRole.java @@ -1,9 +1,9 @@ -package org.keycloak.models.cache.infinispan.counter.entities; +package org.keycloak.models.cache.infinispan.locking.entities; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; import org.keycloak.models.cache.entities.CachedRealmRole; -import org.keycloak.models.cache.infinispan.counter.Revisioned; +import org.keycloak.models.cache.infinispan.locking.Revisioned; /** * @author Bill Burke diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/entities/RevisionedCachedUser.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedUser.java similarity index 84% rename from model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/entities/RevisionedCachedUser.java rename to model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedUser.java index f281bef1b8..c276a371e6 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/counter/entities/RevisionedCachedUser.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/entities/RevisionedCachedUser.java @@ -1,9 +1,9 @@ -package org.keycloak.models.cache.infinispan.counter.entities; +package org.keycloak.models.cache.infinispan.locking.entities; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.models.cache.entities.CachedUser; -import org.keycloak.models.cache.infinispan.counter.Revisioned; +import org.keycloak.models.cache.infinispan.locking.Revisioned; /** * @author Bill Burke diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/skewed/RepeatableReadWriteSkewCacheRealmProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/skewed/RepeatableReadWriteSkewCacheRealmProviderFactory.java deleted file mode 100755 index 7dd49c563c..0000000000 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/skewed/RepeatableReadWriteSkewCacheRealmProviderFactory.java +++ /dev/null @@ -1,160 +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.skewed; - -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; - -import java.util.concurrent.ConcurrentHashMap; - -/** - * @author Bill Burke - * @author Stian Thorgersen - */ -public class RepeatableReadWriteSkewCacheRealmProviderFactory implements CacheRealmProviderFactory { - - private static final Logger log = Logger.getLogger(RepeatableReadWriteSkewCacheRealmProviderFactory.class); - - protected volatile RepeatableReadWriteSkewRealmCache realmCache; - - protected final ConcurrentHashMap realmLookup = new ConcurrentHashMap<>(); - - @Override - public CacheRealmProvider create(KeycloakSession session) { - lazyInit(session); - return new RepeatableReadWriteSkewRealmCacheProvider(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.addListener(new CacheListener()); - realmCache = new RepeatableReadWriteSkewRealmCache(cache, realmLookup); - } - } - } - } - - @Override - public void init(Config.Scope config) { - } - - @Override - public void postInit(KeycloakSessionFactory factory) { - - } - - @Override - public void close() { - } - - @Override - public String getId() { - return "infinispan-versioned"; - } - - @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.evictCachedRoleById(r); - } - - for (String c : realm.getClients().values()) { - realmCache.evictCachedApplicationById(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.evictCachedRoleById(r); - } - - log.tracev("Client removed client={0}", client.getId()); - } - } - } -} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/skewed/RepeatableReadWriteSkewConnectionProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/skewed/RepeatableReadWriteSkewConnectionProviderFactory.java deleted file mode 100755 index b389347c37..0000000000 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/skewed/RepeatableReadWriteSkewConnectionProviderFactory.java +++ /dev/null @@ -1,164 +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.skewed; - -import org.infinispan.configuration.cache.CacheMode; -import org.infinispan.configuration.cache.Configuration; -import org.infinispan.configuration.cache.ConfigurationBuilder; -import org.infinispan.configuration.cache.VersioningScheme; -import org.infinispan.configuration.global.GlobalConfigurationBuilder; -import org.infinispan.manager.DefaultCacheManager; -import org.infinispan.manager.EmbeddedCacheManager; -import org.infinispan.transaction.lookup.DummyTransactionManagerLookup; -import org.infinispan.util.concurrent.IsolationLevel; -import org.jboss.logging.Logger; -import org.keycloak.Config; -import org.keycloak.connections.infinispan.DefaultInfinispanConnectionProvider; -import org.keycloak.connections.infinispan.InfinispanConnectionProvider; -import org.keycloak.connections.infinispan.InfinispanConnectionProviderFactory; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; - -import javax.naming.InitialContext; - -/** - * @author Stian Thorgersen - */ -public class RepeatableReadWriteSkewConnectionProviderFactory implements InfinispanConnectionProviderFactory { - - protected static final Logger logger = Logger.getLogger(RepeatableReadWriteSkewConnectionProviderFactory.class); - - private Config.Scope config; - - private EmbeddedCacheManager cacheManager; - - private boolean containerManaged; - - @Override - public InfinispanConnectionProvider create(KeycloakSession session) { - lazyInit(); - - return new DefaultInfinispanConnectionProvider(cacheManager); - } - - @Override - public void close() { - if (cacheManager != null && !containerManaged) { - cacheManager.stop(); - } - cacheManager = null; - } - - @Override - public String getId() { - return "versioned"; - } - - @Override - public void init(Config.Scope config) { - this.config = config; - } - - @Override - public void postInit(KeycloakSessionFactory factory) { - - } - - private void lazyInit() { - if (cacheManager == null) { - synchronized (this) { - if (cacheManager == null) { - String cacheContainer = config.get("cacheContainer"); - if (cacheContainer != null) { - initContainerManaged(cacheContainer); - } else { - initEmbedded(); - } - } - } - } - } - - private void initContainerManaged(String cacheContainerLookup) { - try { - cacheManager = (EmbeddedCacheManager) new InitialContext().lookup(cacheContainerLookup); - containerManaged = true; - - logger.debugv("Using container managed Infinispan cache container, lookup={1}", cacheContainerLookup); - } catch (Exception e) { - throw new RuntimeException("Failed to retrieve cache container", e); - } - } - - private void initEmbedded() { - GlobalConfigurationBuilder gcb = new GlobalConfigurationBuilder(); - - boolean clustered = config.getBoolean("clustered", false); - boolean async = config.getBoolean("async", true); - boolean allowDuplicateJMXDomains = config.getBoolean("allowDuplicateJMXDomains", true); - - if (clustered) { - gcb.transport().defaultTransport(); - } - gcb.globalJmxStatistics().allowDuplicateDomains(allowDuplicateJMXDomains); - - cacheManager = new DefaultCacheManager(gcb.build()); - containerManaged = false; - - logger.debug("Started embedded Infinispan cache container"); - - ConfigurationBuilder invalidationConfigBuilder = new ConfigurationBuilder(); - if (clustered) { - invalidationConfigBuilder.clustering().cacheMode(async ? CacheMode.INVALIDATION_ASYNC : CacheMode.INVALIDATION_SYNC); - } - - invalidationConfigBuilder.transaction().transactionManagerLookup(new DummyTransactionManagerLookup()); - invalidationConfigBuilder.locking().isolationLevel(IsolationLevel.REPEATABLE_READ).writeSkewCheck(true).versioning().enable().scheme(VersioningScheme.SIMPLE); - cacheManager.defineConfiguration(InfinispanConnectionProvider.REALM_CACHE_NAME, invalidationConfigBuilder.build()); - - ConfigurationBuilder userConfigBuilder = new ConfigurationBuilder(); - if (clustered) { - userConfigBuilder.clustering().cacheMode(async ? CacheMode.INVALIDATION_ASYNC : CacheMode.INVALIDATION_SYNC); - } - Configuration userCacheConfiguration = userConfigBuilder.build(); - - cacheManager.defineConfiguration(InfinispanConnectionProvider.USER_CACHE_NAME, userCacheConfiguration); - - ConfigurationBuilder sessionConfigBuilder = new ConfigurationBuilder(); - if (clustered) { - String sessionsMode = config.get("sessionsMode", "distributed"); - if (sessionsMode.equalsIgnoreCase("replicated")) { - sessionConfigBuilder.clustering().cacheMode(async ? CacheMode.REPL_ASYNC : CacheMode.REPL_SYNC); - } else if (sessionsMode.equalsIgnoreCase("distributed")) { - sessionConfigBuilder.clustering().cacheMode(async ? CacheMode.DIST_ASYNC : CacheMode.DIST_SYNC); - } else { - throw new RuntimeException("Invalid value for sessionsMode"); - } - - sessionConfigBuilder.clustering().hash() - .numOwners(config.getInt("sessionsOwners", 2)) - .numSegments(config.getInt("sessionsSegments", 60)).build(); - } - - Configuration sessionCacheConfiguration = sessionConfigBuilder.build(); - cacheManager.defineConfiguration(InfinispanConnectionProvider.SESSION_CACHE_NAME, sessionCacheConfiguration); - cacheManager.defineConfiguration(InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME, sessionCacheConfiguration); - cacheManager.defineConfiguration(InfinispanConnectionProvider.LOGIN_FAILURE_CACHE_NAME, sessionCacheConfiguration); - } - -} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/skewed/RepeatableReadWriteSkewRealmCache.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/skewed/RepeatableReadWriteSkewRealmCache.java deleted file mode 100755 index 18e5d866ef..0000000000 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/skewed/RepeatableReadWriteSkewRealmCache.java +++ /dev/null @@ -1,269 +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.skewed; - -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 javax.transaction.NotSupportedException; -import javax.transaction.Status; -import javax.transaction.SystemException; -import java.util.concurrent.ConcurrentHashMap; - -/** - * @author Stian Thorgersen - */ -public class RepeatableReadWriteSkewRealmCache implements RealmCache { - - protected static final Logger logger = Logger.getLogger(RepeatableReadWriteSkewRealmCache.class); - - protected final Cache cache; - protected final ConcurrentHashMap realmLookup; - - public RepeatableReadWriteSkewRealmCache(Cache cache, ConcurrentHashMap realmLookup) { - this.cache = cache; - this.realmLookup = realmLookup; - } - - public Cache getCache() { - return cache; - } - - public void startBatch() { - logger.trace("*** START BATCH ***"); - try { - if (cache.getAdvancedCache().getTransactionManager().getStatus() == Status.STATUS_NO_TRANSACTION) { - cache.getAdvancedCache().getTransactionManager().begin(); - } - } catch (NotSupportedException e) { - throw new RuntimeException(e); - } catch (SystemException e) { - throw new RuntimeException(e); - } - - } - - public void endBatch(boolean commit) { - logger.trace("*** END BATCH ***"); - try { - if (cache.getAdvancedCache().getTransactionManager().getStatus() == Status.STATUS_ACTIVE) { - if (commit) { - cache.getAdvancedCache().getTransactionManager().commit(); - - } else { - cache.getAdvancedCache().getTransactionManager().rollback(); - - } - } - } catch (Exception e) { - //throw new RuntimeException(e); - } - } - - @Override - public void clear() { - cache.clear(); - } - - @Override - public CachedRealm getCachedRealm(String id) { - return get(id, CachedRealm.class); - } - - @Override - public void invalidateCachedRealm(CachedRealm realm) { - logger.tracev("Invalidating realm {0}", realm.getId()); - invalidate(realm.getId()); - realmLookup.remove(realm.getName()); - } - - protected Object invalidate(String id) { - startBatch(); - Object rtn = cache.remove(id); - logger.trace("*** END BATCH ***"); - try { - if (cache.getAdvancedCache().getTransactionManager().getStatus() == Status.STATUS_ACTIVE) { - if (true) { - cache.getAdvancedCache().getTransactionManager().commit(); - - } else { - cache.getAdvancedCache().getTransactionManager().rollback(); - - } - } - } catch (Exception e) { - logger.trace("Failed to commit invalidate"); - } - return rtn; - } - - @Override - public void invalidateCachedRealmById(String id) { - CachedRealm cached = (CachedRealm) invalidate(id); - if (cached != null) realmLookup.remove(cached.getName()); - } - - @Override - public void addCachedRealm(CachedRealm realm) { - logger.tracev("Adding realm {0}", realm.getId()); - cache.putForExternalRead(realm.getId(), realm); - realmLookup.put(realm.getName(), realm.getId()); - } - - @Override - public CachedRealm getCachedRealmByName(String name) { - String id = realmLookup.get(name); - return id != null ? getCachedRealm(id) : null; - } - - @Override - public CachedClient getApplication(String id) { - return get(id, CachedClient.class); - } - - @Override - public void invalidateApplication(CachedClient app) { - logger.tracev("Removing application {0}", app.getId()); - invalidate(app.getId()); - } - - @Override - public void addCachedClient(CachedClient app) { - logger.tracev("Adding application {0}", app.getId()); - cache.putForExternalRead(app.getId(), app); - } - - @Override - public void invalidateCachedApplicationById(String id) { - logger.tracev("Removing application {0}", id); - invalidate(id); - } - - @Override - public void evictCachedApplicationById(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()); - invalidate(role.getId()); - } - - @Override - public void addCachedGroup(CachedGroup role) { - logger.tracev("Adding group {0}", role.getId()); - cache.putForExternalRead(role.getId(), role); - } - - @Override - public void invalidateCachedGroupById(String id) { - logger.tracev("Removing group {0}", id); - invalidate(id); - - } - - @Override - public void invalidateGroupById(String id) { - logger.tracev("Removing group {0}", id); - invalidate(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()); - invalidate(role.getId()); - } - - @Override - public void invalidateRoleById(String id) { - logger.tracev("Removing role {0}", id); - invalidate(id); - } - - @Override - public void evictCachedRoleById(String id) { - logger.tracev("Evicting role {0}", id); - cache.evict(id); - } - - @Override - public void addCachedRole(CachedRole role) { - logger.tracev("Adding role {0}", role.getId()); - cache.putForExternalRead(role.getId(), role); - } - - @Override - public void invalidateCachedRoleById(String id) { - logger.tracev("Removing role {0}", id); - invalidate(id); - } - - 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()); - invalidate(app.getId()); - } - - @Override - public void addCachedClientTemplate(CachedClientTemplate app) { - logger.tracev("Adding client template {0}", app.getId()); - cache.putForExternalRead(app.getId(), app); - } - - @Override - public void invalidateCachedClientTemplateById(String id) { - logger.tracev("Removing client template {0}", id); - invalidate(id); - } - - @Override - public void evictCachedClientTemplateById(String id) { - logger.tracev("Evicting client template {0}", id); - invalidate(id); - } - - -} diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/skewed/RepeatableReadWriteSkewRealmCacheProvider.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/skewed/RepeatableReadWriteSkewRealmCacheProvider.java deleted file mode 100755 index 5ff2ff8f93..0000000000 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/skewed/RepeatableReadWriteSkewRealmCacheProvider.java +++ /dev/null @@ -1,472 +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.skewed; - -import org.jboss.logging.Logger; -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.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 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 java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - - -/** - * DO NOT USE THIS!! - * - * Tries unsuccessfully to use Infinispan with REPEATABLE_READ, write-skew-checking - * - * @author Bill Burke - * @version $Revision: 1 $ - */ -public class RepeatableReadWriteSkewRealmCacheProvider implements CacheRealmProvider { - protected static final Logger logger = Logger.getLogger(RepeatableReadWriteSkewRealmCacheProvider.class); - - protected RepeatableReadWriteSkewRealmCache 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 RepeatableReadWriteSkewRealmCacheProvider(RepeatableReadWriteSkewRealmCache 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.invalidateCachedRealmById(id); - } - for (String id : roleInvalidations) { - cache.invalidateRoleById(id); - } - for (String id : groupInvalidations) { - cache.invalidateGroupById(id); - } - for (String id : appInvalidations) { - cache.invalidateCachedApplicationById(id); - } - for (String id : clientTemplateInvalidations) { - cache.invalidateCachedClientTemplateById(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) { - //cache.startBatch(); - cache.startBatch(); - boolean batchEnded = false; - try { - CachedRealm cached = cache.getCachedRealm(id); - boolean wasNull = cached == null; - 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.addCachedRealm(cached); - try { - batchEnded = true; - cache.endBatch(true); - logger.trace("returning new cached realm"); - } catch (Exception exception) { - logger.trace("failed to add to cache", exception); - return model; - } - } else if (realmInvalidations.contains(id)) { - return getDelegate().getRealm(id); - } else if (managedRealms.containsKey(id)) { - return managedRealms.get(id); - } - if (!wasNull) logger.trace("returning cached realm: " + cached.getName()); - RealmAdapter adapter = new RealmAdapter(cached, this); - managedRealms.put(id, adapter); - return adapter; - } finally { - if (!batchEnded) cache.endBatch(true); - } - } - - @Override - public RealmModel getRealmByName(String name) { - cache.startBatch(); - boolean batchEnded = false; - try { - CachedRealm cached = cache.getCachedRealmByName(name); - boolean wasNull = cached == null; - 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.addCachedRealm(cached); - try { - batchEnded = true; - cache.endBatch(true); - logger.trace("returning new cached realm: " + cached.getName()); - } catch (Exception exception) { - logger.trace("failed to add to cache", exception); - return model; - } - } else if (realmInvalidations.contains(cached.getId())) { - return getDelegate().getRealmByName(name); - } else if (managedRealms.containsKey(cached.getId())) { - return managedRealms.get(cached.getId()); - } - if (!wasNull) logger.trace("returning cached realm: " + cached.getName()); - RealmAdapter adapter = new RealmAdapter(cached, this); - managedRealms.put(cached.getId(), adapter); - return adapter; - } finally { - if (!batchEnded) cache.endBatch(true); - - } - } - - @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.invalidateCachedRealmById(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) { - cache.startBatch(); - boolean batchEnded = false; - try { - 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.addCachedRole(cached); - try { - batchEnded = true; - cache.endBatch(true); - } catch (Exception exception) { - logger.trace("failed to add to cache", exception); - return model; - } - - } 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; - } finally { - if (!batchEnded) cache.endBatch(true); - - } - } - - @Override - public GroupModel getGroupById(String id, RealmModel realm) { - cache.startBatch(); - boolean batchEnded = false; - try { - 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.addCachedGroup(cached); - try { - batchEnded = true; - cache.endBatch(true); - } catch (Exception exception) { - logger.trace("failed to add to cache", exception); - return model; - } - - } 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; - } finally { - if (!batchEnded) cache.endBatch(true); - - } - } - - @Override - public ClientModel getClientById(String id, RealmModel realm) { - cache.startBatch(); - boolean batchEnded = false; - CachedClient cached = cache.getApplication(id); - try { - 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.addCachedClient(cached); - try { - batchEnded = true; - cache.endBatch(true); - } catch (Exception exception) { - logger.trace("failed to add to cache", exception); - return model; - } - } 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; - } finally { - if (!batchEnded) cache.endBatch(true); - - } - } - @Override - public ClientTemplateModel getClientTemplateById(String id, RealmModel realm) { - cache.startBatch(); - boolean batchEnded = false; - try { - 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.addCachedClientTemplate(cached); - try { - batchEnded = true; - cache.endBatch(true); - } catch (Exception exception) { - logger.trace("failed to add to cache", exception); - return model; - } - } 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; - } finally { - if (!batchEnded) cache.endBatch(true); - - } - } - -} 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 ef880ece25..299da00ff3 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,5 +16,4 @@ # org.keycloak.connections.infinispan.DefaultInfinispanConnectionProviderFactory -org.keycloak.models.cache.infinispan.counter.RevisionedConnectionProviderFactory 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 655476558d..40ced18500 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,5 +16,4 @@ # org.keycloak.models.cache.infinispan.InfinispanCacheRealmProviderFactory -org.keycloak.models.cache.infinispan.counter.RevisionedCacheRealmProviderFactory org.keycloak.models.cache.infinispan.locking.LockingCacheRealmProviderFactory \ No newline at end of file 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 f510f04dfd..7984fae9d3 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 @@ -46,8 +46,8 @@ public class ConcurrencyTest extends AbstractClientTest { private static final Logger log = Logger.getLogger(ConcurrencyTest.class); - private static final int DEFAULT_THREADS = 1; - private static final int DEFAULT_ITERATIONS = 5; + private static final int DEFAULT_THREADS = 5; + private static final int DEFAULT_ITERATIONS = 20; // If enabled only one request is allowed at the time. Useful for checking that test is working. private static final boolean SYNCHRONIZED = false; diff --git a/testsuite/pom.xml b/testsuite/pom.xml index 6f8079e55e..bc054ceb10 100755 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -57,6 +57,7 @@ tomcat8 jetty performance + stress integration-arquillian diff --git a/testsuite/stress/pom.xml b/testsuite/stress/pom.xml new file mode 100755 index 0000000000..f7ea8248bc --- /dev/null +++ b/testsuite/stress/pom.xml @@ -0,0 +1,571 @@ + + + + + + keycloak-testsuite-pom + org.keycloak + 1.9.0.Final-SNAPSHOT + ../pom.xml + + 4.0.0 + + keycloak-stress-tester + Keycloak Stress TestSuite + + + + + org.bouncycastle + bcprov-jdk15on + + + org.bouncycastle + bcpkix-jdk15on + + + org.keycloak + keycloak-dependencies-server-all + pom + + + org.keycloak + keycloak-admin-client + + + org.keycloak + keycloak-wildfly-adduser + + + log4j + log4j + + + org.jboss.spec.javax.servlet + jboss-servlet-api_3.0_spec + + + org.jboss.spec.javax.ws.rs + jboss-jaxrs-api_2.0_spec + + + org.jboss.resteasy + async-http-servlet-3.0 + + + org.jboss.resteasy + resteasy-jaxrs + + + log4j + log4j + + + org.slf4j + slf4j-api + + + org.slf4j + slf4j-simple + + + + + org.jboss.resteasy + resteasy-client + + + org.jboss.resteasy + resteasy-undertow + + + org.jboss.resteasy + resteasy-multipart-provider + + + org.jboss.resteasy + resteasy-jackson2-provider + + + com.google.zxing + javase + + + org.apache.httpcomponents + httpclient + + + org.keycloak + keycloak-server-spi + + + org.keycloak + keycloak-ldap-federation + + + org.keycloak + keycloak-kerberos-federation + + + org.keycloak + keycloak-undertow-adapter + + + org.keycloak + keycloak-saml-adapter-core + + + org.keycloak + keycloak-saml-servlet-filter-adapter + + + org.keycloak + keycloak-servlet-filter-adapter + + + org.keycloak + keycloak-saml-undertow-adapter + + + org.keycloak + keycloak-jaxrs-oauth-client + + + org.keycloak + federation-properties-example + + + org.jboss.logging + jboss-logging + + + io.undertow + undertow-servlet + + + io.undertow + undertow-core + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.core + jackson-annotations + + + junit + junit + provided + + + org.hamcrest + hamcrest-all + + + org.hibernate.javax.persistence + hibernate-jpa-2.1-api + + + com.h2database + h2 + + + org.hibernate + hibernate-entitymanager + + + com.icegreen + greenmail + + + org.slf4j + slf4j-api + + + + + org.infinispan + infinispan-core + + + org.seleniumhq.selenium + selenium-java + provided + + + xml-apis + xml-apis + + + org.seleniumhq.selenium + selenium-chrome-driver + provided + + + + + org.keycloak + keycloak-util-embedded-ldap + + + + org.wildfly + wildfly-undertow + test + + + org.keycloak + keycloak-testsuite-integration + test-jar + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.2 + + + + test-jar + + + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${maven.compiler.source} + ${maven.compiler.target} + + + + org.codehaus.mojo + exec-maven-plugin + + ${project.basedir} + + + + org.apache.felix + maven-bundle-plugin + true + true + + + org.apache.maven.plugins + maven-javadoc-plugin + + true + + + + + + + + keycloak-server + + + + org.codehaus.mojo + exec-maven-plugin + + org.keycloak.testsuite.KeycloakServer + test + + + + + + + mail-server + + + + org.codehaus.mojo + exec-maven-plugin + + org.keycloak.testsuite.MailServer + test + + + + + + + totp + + + + org.codehaus.mojo + exec-maven-plugin + + org.keycloak.testsuite.TotpGenerator + test + + + + + + + ldap + + + + org.codehaus.mojo + exec-maven-plugin + + org.keycloak.util.ldap.LDAPEmbeddedServer + test + + + + + + + kerberos + + + + org.codehaus.mojo + exec-maven-plugin + + org.keycloak.util.ldap.KerberosEmbeddedServer + test + + + + + + + + jpa + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + jpa + jpa + jpa + jpa + + debug + + + + + + + + + mongo + + + localhost + 27018 + keycloak + 127.0.0.1 + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + test + integration-test + + test + + + + mongo + mongo + mongo + mongo + ${keycloak.connectionsMongo.host} + ${keycloak.connectionsMongo.port} + ${keycloak.connectionsMongo.db} + ${keycloak.connectionsMongo.bindIp} + + + + + default-test + + true + + + + + + + + com.github.joelittlejohn.embedmongo + embedmongo-maven-plugin + + + start-mongodb + pre-integration-test + + start + + + ${keycloak.connectionsMongo.port} + file + ${project.build.directory}/mongodb.log + ${keycloak.connectionsMongo.bindIp} + + + + stop-mongodb + post-integration-test + + stop + + + + + + + + + + + + + + keycloak.connectionsJpa.driver + com.mysql.jdbc.Driver + + + mysql + + + mysql + mysql-connector-java + + + + + + + + + keycloak.connectionsJpa.driver + org.postgresql.Driver + + + postgresql + + + org.postgresql + postgresql + ${postgresql.version} + + + + + + clean-jpa + + + + org.liquibase + liquibase-maven-plugin + + META-INF/jpa-changelog-master.xml + + ${keycloak.connectionsJpa.url} + ${keycloak.connectionsJpa.driver} + ${keycloak.connectionsJpa.user} + ${keycloak.connectionsJpa.password} + + false + ${keycloak.connectionsJpa.liquibaseDatabaseClass} + + + + clean-jpa + clean + + dropAll + + + + + + + + + + + + + ldap.vendor + msad + + + msad + + + + org.apache.maven.plugins + maven-surefire-plugin + + + org/keycloak/testsuite/federation/ldap/base/** + + + **/LDAPMultipleAttributesTest.java + + + + + + + + + diff --git a/testsuite/stress/src/main/java/org/keycloak/test/stress/MaxRateExecutor.java b/testsuite/stress/src/main/java/org/keycloak/test/stress/MaxRateExecutor.java new file mode 100755 index 0000000000..c1f0a6b02f --- /dev/null +++ b/testsuite/stress/src/main/java/org/keycloak/test/stress/MaxRateExecutor.java @@ -0,0 +1,137 @@ +package org.keycloak.test.stress; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorCompletionService; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +/** + * Executes all test threads until completion. + * + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class MaxRateExecutor { + + public static class RateResult { + StressResult result; + int threads; + long time; + + public RateResult(StressResult result, int threads, long time) { + this.result = result; + this.threads = threads; + this.time = time; + } + + public StressResult getResult() { + return result; + } + + public int getThreads() { + return threads; + } + + public long getTime() { + return time; + } + } + + List allResults = new LinkedList<>(); + RateResult fastest = null; + RateResult last = null; + + + + public void best(TestFactory factory, int jobs) { + fastest = last = null; + int threads = 2; + do { + fastest = last; + try { + last = execute(factory, threads, jobs); + allResults.add(last); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } + threads++; + } while (fastest == null || fastest.time > last.time); + } + + public RateResult getFastest() { + return fastest; + } + + public RateResult getLast() { + return last; + } + + public RateResult execute(TestFactory factory, int threads, int jobs) throws InterruptedException, ExecutionException { + List tests = new LinkedList<>(); + ExecutorService executor = Executors.newFixedThreadPool(threads); + ExecutorCompletionService completionService = new ExecutorCompletionService<>(executor); + StressResult result = new StressResult("num threads:" + threads); + addTest(factory, result, tests, threads + 5); + long start = System.currentTimeMillis(); + for (StressTest stressTest : tests) { + completionService.submit(stressTest); + } + for (int i = 0; i < jobs; i++) { + Future future = completionService.take(); + StressTest stressTest = future.get(); + if (i < jobs - threads - 5) completionService.submit(stressTest); + } + executor.shutdown(); + executor.awaitTermination(10, TimeUnit.SECONDS); + long end = System.currentTimeMillis() - start; + RateResult rate = new RateResult(result, threads, end); + return rate; + } + + private void addTest(TestFactory factory, StressResult result, List tests, int num) { + int add = num - tests.size(); + for (int i = 0; i < add; i++) { + Test test = factory.create(); + test.init(); + StressTest stress = new StressTest(result, test, 1); + tests.add(stress); + } + } + + public void printResults() { + System.out.println("*******************"); + System.out.println("* Best Result *"); + System.out.println("*******************"); + printResult(fastest); + } + + + public void printResult(RateResult result) { + System.out.println("Threads: " + result.getThreads()); + System.out.println("Total Time: " + result.getTime()); + System.out.println("Successes: " + result.getResult().getSuccess()); + System.out.println("Iterations: " + result.getResult().getIterations()); + System.out.println("Average time: " + result.getResult().getAverageTime()); + System.out.println("Rate: " + result.getResult().getRate()); + + } + + public void printSummary() { + + for (RateResult result : allResults) { + System.out.println("*******************"); + printSummary(); + } + } + public void printSummary(RateResult result) { + System.out.println("Threads: " + result.getThreads()); + System.out.println("Total Time: " + result.getTime()); + } +} diff --git a/testsuite/stress/src/main/java/org/keycloak/test/stress/StressExecutor.java b/testsuite/stress/src/main/java/org/keycloak/test/stress/StressExecutor.java new file mode 100755 index 0000000000..ebf6ef117d --- /dev/null +++ b/testsuite/stress/src/main/java/org/keycloak/test/stress/StressExecutor.java @@ -0,0 +1,65 @@ +package org.keycloak.test.stress; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletionService; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorCompletionService; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +/** + * Executes all test threads until completion. + * + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class StressExecutor { + protected List tests = new LinkedList<>(); + protected List results = new LinkedList<>(); + + public void addTest(Class test, int threads, int iterations) { + StressResult result = new StressResult(test.getName()); + results.add(result); + for (int i = 0; i < threads; i++) { + try { + Test t = test.newInstance(); + t.init(); + StressTest stress = new StressTest(result, t, iterations); + tests.add(stress); + } catch (InstantiationException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + } + + public void addTest(Test test, StressResult result, int iterations) { + tests.add(new StressTest(result, test, iterations)); + } + + public void addTest(Test test, int iterations) { + StressResult result = new StressResult(test.getClass().getName()); + tests.add(new StressTest(result, test, iterations)); + } + + public long execute() throws InterruptedException, ExecutionException { + ExecutorService executor = Executors.newFixedThreadPool(tests.size()); + Collections.shuffle(tests); + long start = System.currentTimeMillis(); + for (StressTest test : tests) { + executor.submit(test); + } + executor.shutdown(); + boolean done = executor.awaitTermination(100, TimeUnit.HOURS); + long end = System.currentTimeMillis() - start; + return end; + + } +} diff --git a/testsuite/stress/src/main/java/org/keycloak/test/stress/StressResult.java b/testsuite/stress/src/main/java/org/keycloak/test/stress/StressResult.java new file mode 100755 index 0000000000..080c5532d0 --- /dev/null +++ b/testsuite/stress/src/main/java/org/keycloak/test/stress/StressResult.java @@ -0,0 +1,62 @@ +package org.keycloak.test.stress; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class StressResult { + ThreadLocal start = new ThreadLocal<>(); + AtomicLong iterations = new AtomicLong(); + AtomicLong totalTime = new AtomicLong(); + String name; + AtomicInteger success = new AtomicInteger(); + + public StressResult(String name) { + this.name = name; + } + + public void start() { + start.set(System.currentTimeMillis()); + } + + public void success() { + success.incrementAndGet(); + } + + public void end() { + long end = System.currentTimeMillis() - start.get(); + totalTime.addAndGet(end); + iterations.incrementAndGet(); + } + + public int getSuccess() { + return success.get(); + } + + public String getName() { + return name; + } + + public long getTotalTime() { + return totalTime.longValue(); + } + public long getIterations() { + return iterations.get(); + } + + public double getAverageTime() { + return (double)(double)totalTime.get() / (double)iterations.get(); + } + public double getRate() { + return (double)(double)iterations.get() / (double)totalTime.get(); + } + + public void clear() { + iterations.set(0); + totalTime.set(0); + success.set(0); + } +} diff --git a/testsuite/stress/src/main/java/org/keycloak/test/stress/StressTest.java b/testsuite/stress/src/main/java/org/keycloak/test/stress/StressTest.java new file mode 100755 index 0000000000..363c5e2d8b --- /dev/null +++ b/testsuite/stress/src/main/java/org/keycloak/test/stress/StressTest.java @@ -0,0 +1,37 @@ +package org.keycloak.test.stress; + +import jdk.nashorn.internal.codegen.CompilerConstants; + +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class StressTest implements Callable { + protected StressResult result; + protected Callable test; + protected int iterations; + + public StressTest(StressResult result, Callable test, int iterations) { + this.result = result; + this.test = test; + this.iterations = iterations; + } + + @Override + public StressTest call() throws Exception { + for (int i = 0; i < iterations; i++) { + result.start(); + try { + if (test.call()) { + result.success(); + } + } catch (Throwable throwable) { + } + result.end(); + } + return this; + } +} diff --git a/testsuite/stress/src/main/java/org/keycloak/test/stress/Test.java b/testsuite/stress/src/main/java/org/keycloak/test/stress/Test.java new file mode 100755 index 0000000000..04b7233ad1 --- /dev/null +++ b/testsuite/stress/src/main/java/org/keycloak/test/stress/Test.java @@ -0,0 +1,11 @@ +package org.keycloak.test.stress; + +import java.util.concurrent.Callable; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public interface Test extends Callable { + void init(); +} diff --git a/testsuite/stress/src/main/java/org/keycloak/test/stress/TestFactory.java b/testsuite/stress/src/main/java/org/keycloak/test/stress/TestFactory.java new file mode 100755 index 0000000000..aee1f7b463 --- /dev/null +++ b/testsuite/stress/src/main/java/org/keycloak/test/stress/TestFactory.java @@ -0,0 +1,9 @@ +package org.keycloak.test.stress; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public interface TestFactory { + Test create(); +} diff --git a/testsuite/stress/src/main/java/org/keycloak/test/stress/tests/LoginLogout.java b/testsuite/stress/src/main/java/org/keycloak/test/stress/tests/LoginLogout.java new file mode 100755 index 0000000000..859bc45367 --- /dev/null +++ b/testsuite/stress/src/main/java/org/keycloak/test/stress/tests/LoginLogout.java @@ -0,0 +1,104 @@ +package org.keycloak.test.stress.tests; + +import org.junit.Assert; +import org.keycloak.OAuth2Constants; +import org.keycloak.admin.client.Keycloak; +import org.keycloak.models.Constants; +import org.keycloak.protocol.oidc.OIDCLoginProtocolService; +import org.keycloak.test.stress.Test; +import org.keycloak.testsuite.OAuthClient; +import org.keycloak.testsuite.pages.LoginPage; +import org.keycloak.testsuite.rule.WebResource; +import org.keycloak.testsuite.rule.WebRule; +import org.openqa.selenium.WebDriver; + +import javax.ws.rs.core.UriBuilder; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class LoginLogout implements Test { + public WebRule webRule = new WebRule(this); + + protected String securedResourceUrl; + protected List containsInPage = new LinkedList<>(); + protected String realm; + protected String authServerUrl; + protected String username; + protected String password; + + protected String loginUrl; + protected String logoutUrl; + + @WebResource + protected WebDriver driver; + + @WebResource + protected LoginPage loginPage; + + public LoginLogout securedResourceUrl(String securedResourceUrl) { + this.securedResourceUrl = securedResourceUrl; + return this; + } + + public LoginLogout addPageContains(String contains) { + containsInPage.add(contains); + return this; + } + + public LoginLogout realm(String realm) { + this.realm = realm; + return this; + } + + public LoginLogout authServerUrl(String authServerUrl) { + this.authServerUrl = authServerUrl; + return this; + } + + public LoginLogout username(String username) { + this.username = username; + return this; + } + + public LoginLogout password(String password) { + this.password = password; + return this; + } + + @Override + public void init() { + + loginUrl = OIDCLoginProtocolService.authUrl(UriBuilder.fromUri(authServerUrl)).build(realm).toString(); + logoutUrl = OIDCLoginProtocolService.logoutUrl(UriBuilder.fromUri(authServerUrl)) + .queryParam(OAuth2Constants.REDIRECT_URI, securedResourceUrl).build(realm).toString(); + try { + webRule.before(); + } catch (Throwable e) { + throw new RuntimeException(e); + } + + } + + @Override + public Boolean call() throws Exception { + driver.navigate().to(securedResourceUrl); + Assert.assertTrue(driver.getCurrentUrl().startsWith(loginUrl)); + loginPage.login(username, password); + Assert.assertTrue(driver.getCurrentUrl().startsWith(securedResourceUrl)); + String pageSource = driver.getPageSource(); + for (String contains : containsInPage) { + Assert.assertTrue(pageSource.contains(contains)); + + } + + // test logout + driver.navigate().to(logoutUrl); + Assert.assertTrue(driver.getCurrentUrl().startsWith(loginUrl)); + return true; + } +} diff --git a/testsuite/stress/src/test/java/org/keycloak/test/CustomerDatabaseServlet.java b/testsuite/stress/src/test/java/org/keycloak/test/CustomerDatabaseServlet.java new file mode 100755 index 0000000000..0115ef19e0 --- /dev/null +++ b/testsuite/stress/src/test/java/org/keycloak/test/CustomerDatabaseServlet.java @@ -0,0 +1,51 @@ +/* + * 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.test; + +import org.junit.Assert; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.security.Principal; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class CustomerDatabaseServlet extends HttpServlet { + private static final String LINK = "%s"; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + resp.setContentType("text/html"); + PrintWriter pw = resp.getWriter(); + Principal principal = req.getUserPrincipal(); + Assert.assertNotNull(principal); + pw.printf("%s", "Customer Portal"); + pw.println("Stian Thorgersen"); + pw.println("Bill Burke"); + pw.print(""); + pw.flush(); + + + } +} diff --git a/testsuite/stress/src/test/java/org/keycloak/test/FrameworkTest.java b/testsuite/stress/src/test/java/org/keycloak/test/FrameworkTest.java new file mode 100755 index 0000000000..7668ac3fa6 --- /dev/null +++ b/testsuite/stress/src/test/java/org/keycloak/test/FrameworkTest.java @@ -0,0 +1,82 @@ +package org.keycloak.test; + +import org.junit.ClassRule; +import org.junit.Test; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.services.managers.RealmManager; +import org.keycloak.test.stress.MaxRateExecutor; +import org.keycloak.test.stress.StressExecutor; +import org.keycloak.test.stress.TestFactory; +import org.keycloak.test.stress.tests.LoginLogout; +import org.keycloak.testsuite.adapter.AdapterTestStrategy; +import org.keycloak.testsuite.adapter.CallAuthenticatedServlet; +import org.keycloak.testsuite.adapter.CustomerDatabaseServlet; +import org.keycloak.testsuite.adapter.CustomerServlet; +import org.keycloak.testsuite.adapter.InputServlet; +import org.keycloak.testsuite.adapter.ProductServlet; +import org.keycloak.testsuite.adapter.SessionServlet; +import org.keycloak.testsuite.rule.AbstractKeycloakRule; + +import java.net.URL; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class FrameworkTest { + @ClassRule + public static AbstractKeycloakRule keycloakRule = new AbstractKeycloakRule() { + @Override + protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) { + RealmModel realm = AdapterTestStrategy.baseAdapterTestInitialization(session, manager, adminRealm, getClass()); + + URL url = getClass().getResource("/adapter-test/cust-app-keycloak.json"); + createApplicationDeployment() + .name("customer-portal").contextPath("/customer-portal") + .servletClass(org.keycloak.test.CustomerDatabaseServlet.class).adapterConfigPath(url.getPath()) + .role("user").deployApplication(); + + } + }; + + @Test + public void testStressExecutor() throws Exception { + System.out.println("*************************"); + System.out.println(); + System.out.println(); + StressExecutor executor = new StressExecutor(); + LoginLogout test = new LoginLogout(); + test.authServerUrl("http://localhost:8081/auth") + .realm("demo") + .username("bburke@redhat.com") + .password("password") + .securedResourceUrl("http://localhost:8081/customer-portal"); + test.init(); + executor.addTest(test, 5); + long time = executor.execute(); + System.out.println("Took: " + time ); + } + @Test + public void testRate() throws Exception { + System.out.println("*************************"); + System.out.println(); + System.out.println(); + TestFactory factory = new TestFactory() { + @Override + public org.keycloak.test.stress.Test create() { + LoginLogout test = new LoginLogout(); + test.authServerUrl("http://localhost:8081/auth") + .realm("demo") + .username("bburke@redhat.com") + .password("password") + .securedResourceUrl("http://localhost:8081/customer-portal"); + return test; + } + }; + MaxRateExecutor executor = new MaxRateExecutor(); + executor.best(factory, 4); + executor.printResults(); + } + +} diff --git a/testsuite/stress/src/test/resources/adapter-test/cust-app-keycloak.json b/testsuite/stress/src/test/resources/adapter-test/cust-app-keycloak.json new file mode 100755 index 0000000000..e9ad98722c --- /dev/null +++ b/testsuite/stress/src/test/resources/adapter-test/cust-app-keycloak.json @@ -0,0 +1,11 @@ +{ + "realm": "demo", + "resource": "customer-portal", + "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", + "auth-server-url": "http://localhost:8081/auth", + "ssl-required" : "external", + "expose-token": true, + "credentials": { + "secret": "password" + } +} diff --git a/testsuite/stress/src/test/resources/testrealm.json b/testsuite/stress/src/test/resources/testrealm.json new file mode 100755 index 0000000000..b4718dde46 --- /dev/null +++ b/testsuite/stress/src/test/resources/testrealm.json @@ -0,0 +1,185 @@ +{ + "id": "test", + "realm": "test", + "enabled": true, + "sslRequired": "external", + "registrationAllowed": true, + "resetPasswordAllowed": true, + "editUsernameAllowed" : true, + "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=", + "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", + "requiredCredentials": [ "password" ], + "defaultRoles": [ "user" ], + "smtpServer": { + "from": "auto@keycloak.org", + "host": "localhost", + "port":"3025" + }, + "users" : [ + { + "username" : "test-user@localhost", + "enabled": true, + "email" : "test-user@localhost", + "firstName": "Tom", + "lastName": "Brady", + "credentials" : [ + { "type" : "password", + "value" : "password" } + ], + "realmRoles": ["user", "offline_access"], + "clientRoles": { + "test-app": [ "customer-user" ], + "account": [ "view-profile", "manage-account" ] + } + }, + { + "username" : "john-doh@localhost", + "enabled": true, + "email" : "john-doh@localhost", + "firstName": "John", + "lastName": "Doh", + "credentials" : [ + { "type" : "password", + "value" : "password" } + ], + "realmRoles": ["user"], + "clientRoles": { + "test-app": [ "customer-user" ], + "account": [ "view-profile", "manage-account" ] + } + }, + { + "username" : "keycloak-user@localhost", + "enabled": true, + "email" : "keycloak-user@localhost", + "credentials" : [ + { "type" : "password", + "value" : "password" } + ], + "realmRoles": ["user"], + "clientRoles": { + "test-app": [ "customer-user" ], + "account": [ "view-profile", "manage-account" ] + } + }, + { + "username" : "topGroupUser", + "enabled": true, + "email" : "top@redhat.com", + "credentials" : [ + { "type" : "password", + "value" : "password" } + ], + "groups": [ + "/topGroup" + ] + }, + { + "username" : "level2GroupUser", + "enabled": true, + "email" : "level2@redhat.com", + "credentials" : [ + { "type" : "password", + "value" : "password" } + ], + "groups": [ + "/topGroup/level2group" + ] + } + ], + "scopeMappings": [ + { + "client": "third-party", + "roles": ["user"] + }, + { + "client": "test-app", + "roles": ["user"] + } + ], + "clients": [ + { + "clientId": "test-app", + "enabled": true, + "baseUrl": "http://localhost:8081/app", + "redirectUris": [ + "http://localhost:8081/app/*" + ], + "adminUrl": "http://localhost:8081/app/logout", + "secret": "password" + }, + { + "clientId" : "third-party", + "enabled": true, + "consentRequired": true, + + "redirectUris": [ + "http://localhost:8081/app/*" + ], + "secret": "password" + } + ], + "roles" : { + "realm" : [ + { + "name": "user", + "description": "Have User privileges" + }, + { + "name": "admin", + "description": "Have Administrator privileges" + } + ], + "client" : { + "test-app" : [ + { + "name": "customer-user", + "description": "Have Customer User privileges" + }, + { + "name": "customer-admin", + "description": "Have Customer Admin privileges" + } + ] + } + + }, + "groups" : [ + { + "name": "topGroup", + "attributes": { + "topAttribute": ["true"] + + }, + "realmRoles": ["user"], + + "subGroups": [ + { + "name": "level2group", + "realmRoles": ["admin"], + "clientRoles": { + "test-app": ["customer-user"] + }, + "attributes": { + "level2Attribute": ["true"] + + } + } + ] + } + ], + + + "clientScopeMappings": { + "test-app": [ + { + "client": "third-party", + "roles": ["customer-user"] + } + ] + }, + + "internationalizationEnabled": true, + "supportedLocales": ["en", "de"], + "defaultLocale": "en" +} From bddf214bc0f4039d16a3418d0b958d6164e9b451 Mon Sep 17 00:00:00 2001 From: Bill Burke Date: Thu, 11 Feb 2016 22:09:58 -0500 Subject: [PATCH 2/4] test --- .../{FrameworkTest.java => LoginLogoutTest.java} | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) rename testsuite/stress/src/test/java/org/keycloak/test/{FrameworkTest.java => LoginLogoutTest.java} (93%) diff --git a/testsuite/stress/src/test/java/org/keycloak/test/FrameworkTest.java b/testsuite/stress/src/test/java/org/keycloak/test/LoginLogoutTest.java similarity index 93% rename from testsuite/stress/src/test/java/org/keycloak/test/FrameworkTest.java rename to testsuite/stress/src/test/java/org/keycloak/test/LoginLogoutTest.java index 7668ac3fa6..1348c644d4 100755 --- a/testsuite/stress/src/test/java/org/keycloak/test/FrameworkTest.java +++ b/testsuite/stress/src/test/java/org/keycloak/test/LoginLogoutTest.java @@ -24,7 +24,7 @@ import java.net.URL; * @author Bill Burke * @version $Revision: 1 $ */ -public class FrameworkTest { +public class LoginLogoutTest { @ClassRule public static AbstractKeycloakRule keycloakRule = new AbstractKeycloakRule() { @Override @@ -57,6 +57,19 @@ public class FrameworkTest { long time = executor.execute(); System.out.println("Took: " + time ); } + + /* +************************** +* Bill's Best Result * +************************** +Threads: 13 +Total Time: 1018 +Successes: 400 +Iterations: 400 +Average time: 32.8075 +Rate: 0.030480835174883793 + */ + @Test public void testRate() throws Exception { System.out.println("*************************"); From 860a1048287509fd4fe8c42fd3269652d90109a5 Mon Sep 17 00:00:00 2001 From: Bill Burke Date: Fri, 12 Feb 2016 00:30:36 -0500 Subject: [PATCH 3/4] stress testing --- .../infinispan/DefaultCacheRealmProvider.java | 32 ++++---- .../InfinispanCacheRealmProviderFactory.java | 6 +- .../infinispan/InfinispanRealmCache.java | 47 +++++------- .../models/cache/infinispan/RealmAdapter.java | 20 +++-- .../locking/LockingCacheRealmProvider.java | 59 +++++++++++---- .../LockingCacheRealmProviderFactory.java | 16 ++-- .../infinispan/locking/LockingRealmCache.java | 74 ++++++++++--------- .../keycloak/models/jpa/JpaRealmProvider.java | 11 +++ .../org/keycloak/models/jpa/RealmAdapter.java | 2 +- .../models/jpa/entities/ClientEntity.java | 2 + .../keycloak/adapters/MongoRealmProvider.java | 11 +++ .../mongo/keycloak/adapters/RealmAdapter.java | 7 +- .../org/keycloak/models/RealmProvider.java | 5 +- .../org/keycloak/models/cache/RealmCache.java | 36 ++++----- .../models/cache/entities/CachedClient.java | 2 +- .../models/cache/entities/CachedRealm.java | 28 ++++++- .../keycloak/test/stress/MaxRateExecutor.java | 8 +- .../org/keycloak/test/LoginLogoutTest.java | 3 +- 18 files changed, 222 insertions(+), 147 deletions(-) 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 index d80b8175df..a177b95f0d 100755 --- 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 @@ -120,7 +120,7 @@ public class DefaultCacheRealmProvider implements CacheRealmProvider { protected void runInvalidations() { for (String id : realmInvalidations) { - cache.invalidateCachedRealmById(id); + cache.invalidateRealmById(id); } for (String id : roleInvalidations) { cache.invalidateRoleById(id); @@ -129,10 +129,10 @@ public class DefaultCacheRealmProvider implements CacheRealmProvider { cache.invalidateGroupById(id); } for (String id : appInvalidations) { - cache.invalidateCachedApplicationById(id); + cache.invalidateClientById(id); } for (String id : clientTemplateInvalidations) { - cache.invalidateCachedClientTemplateById(id); + cache.invalidateClientTemplateById(id); } } @@ -193,13 +193,13 @@ public class DefaultCacheRealmProvider implements CacheRealmProvider { @Override public RealmModel getRealm(String id) { - CachedRealm cached = cache.getCachedRealm(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.addCachedRealm(cached); + cache.addRealm(cached); } else if (realmInvalidations.contains(id)) { return getDelegate().getRealm(id); } else if (managedRealms.containsKey(id)) { @@ -212,13 +212,13 @@ public class DefaultCacheRealmProvider implements CacheRealmProvider { @Override public RealmModel getRealmByName(String name) { - CachedRealm cached = cache.getCachedRealmByName(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.addCachedRealm(cached); + cache.addRealm(cached); } else if (realmInvalidations.contains(cached.getId())) { return getDelegate().getRealmByName(name); } else if (managedRealms.containsKey(cached.getId())) { @@ -245,7 +245,7 @@ public class DefaultCacheRealmProvider implements CacheRealmProvider { @Override public boolean removeRealm(String id) { - cache.invalidateCachedRealmById(id); + cache.invalidateRealmById(id); RealmModel realm = getDelegate().getRealm(id); Set realmRoles = null; @@ -287,7 +287,7 @@ public class DefaultCacheRealmProvider implements CacheRealmProvider { } else { cached = new CachedRealmRole(model, realm); } - cache.addCachedRole(cached); + cache.addRole(cached); } else if (roleInvalidations.contains(id)) { return getDelegate().getRoleById(id, realm); @@ -311,7 +311,7 @@ public class DefaultCacheRealmProvider implements CacheRealmProvider { if (model == null) return null; if (groupInvalidations.contains(id)) return model; cached = new CachedGroup(realm, model); - cache.addCachedGroup(cached); + cache.addGroup(cached); } else if (groupInvalidations.contains(id)) { return getDelegate().getGroupById(id, realm); @@ -325,7 +325,7 @@ public class DefaultCacheRealmProvider implements CacheRealmProvider { @Override public ClientModel getClientById(String id, RealmModel realm) { - CachedClient cached = cache.getApplication(id); + CachedClient cached = cache.getClient(id); if (cached != null && !cached.getRealm().equals(realm.getId())) { cached = null; } @@ -335,7 +335,7 @@ public class DefaultCacheRealmProvider implements CacheRealmProvider { if (model == null) return null; if (appInvalidations.contains(id)) return model; cached = new CachedClient(cache, getDelegate(), realm, model); - cache.addCachedClient(cached); + cache.addClient(cached); } else if (appInvalidations.contains(id)) { return getDelegate().getClientById(id, realm); } else if (managedApplications.containsKey(id)) { @@ -345,6 +345,12 @@ public class DefaultCacheRealmProvider implements CacheRealmProvider { managedApplications.put(id, adapter); return adapter; } + + @Override + public ClientModel getClientByClientId(String clientId, RealmModel realm) { + return getDelegate().getClientByClientId(clientId, realm); + } + @Override public ClientTemplateModel getClientTemplateById(String id, RealmModel realm) { CachedClientTemplate cached = cache.getClientTemplate(id); @@ -357,7 +363,7 @@ public class DefaultCacheRealmProvider implements CacheRealmProvider { if (model == null) return null; if (clientTemplateInvalidations.contains(id)) return model; cached = new CachedClientTemplate(cache, getDelegate(), realm, model); - cache.addCachedClientTemplate(cached); + cache.addClientTemplate(cached); } else if (clientTemplateInvalidations.contains(id)) { return getDelegate().getClientTemplateById(id, realm); } else if (managedClientTemplates.containsKey(id)) { 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 3c6573341b..ac8f3734fd 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 @@ -138,11 +138,11 @@ public class InfinispanCacheRealmProviderFactory implements CacheRealmProviderFa realmLookup.remove(realm.getName()); for (String r : realm.getRealmRoles().values()) { - realmCache.evictCachedRoleById(r); + realmCache.evictRoleById(r); } for (String c : realm.getClients().values()) { - realmCache.evictCachedApplicationById(c); + realmCache.evictClientById(c); } log.tracev("Realm removed realm={0}", realm.getName()); @@ -150,7 +150,7 @@ public class InfinispanCacheRealmProviderFactory implements CacheRealmProviderFa CachedClient client = (CachedClient) object; for (String r : client.getRoles().values()) { - realmCache.evictCachedRoleById(r); + realmCache.evictRoleById(r); } log.tracev("Client removed client={0}", client.getId()); 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 index 4f89237922..8dfe923c28 100755 --- 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 @@ -53,61 +53,61 @@ public class InfinispanRealmCache implements RealmCache { } @Override - public CachedRealm getCachedRealm(String id) { + public CachedRealm getRealm(String id) { return get(id, CachedRealm.class); } @Override - public void invalidateCachedRealm(CachedRealm realm) { + public void invalidateRealm(CachedRealm realm) { logger.tracev("Invalidating realm {0}", realm.getId()); cache.remove(realm.getId()); realmLookup.remove(realm.getName()); } @Override - public void invalidateCachedRealmById(String id) { + public void invalidateRealmById(String id) { CachedRealm cached = (CachedRealm) cache.remove(id); if (cached != null) realmLookup.remove(cached.getName()); } @Override - public void addCachedRealm(CachedRealm realm) { + 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 getCachedRealmByName(String name) { + public CachedRealm getRealmByName(String name) { String id = realmLookup.get(name); - return id != null ? getCachedRealm(id) : null; + return id != null ? getRealm(id) : null; } @Override - public CachedClient getApplication(String id) { + public CachedClient getClient(String id) { return get(id, CachedClient.class); } @Override - public void invalidateApplication(CachedClient app) { + public void invalidateClient(CachedClient app) { logger.tracev("Removing application {0}", app.getId()); cache.remove(app.getId()); } @Override - public void addCachedClient(CachedClient app) { + public void addClient(CachedClient app) { logger.tracev("Adding application {0}", app.getId()); cache.putForExternalRead(app.getId(), app); } @Override - public void invalidateCachedApplicationById(String id) { + public void invalidateClientById(String id) { logger.tracev("Removing application {0}", id); cache.remove(id); } @Override - public void evictCachedApplicationById(String id) { + public void evictClientById(String id) { logger.tracev("Evicting application {0}", id); cache.evict(id); } @@ -124,18 +124,11 @@ public class InfinispanRealmCache implements RealmCache { } @Override - public void addCachedGroup(CachedGroup role) { + public void addGroup(CachedGroup role) { logger.tracev("Adding group {0}", role.getId()); cache.putForExternalRead(role.getId(), role); } - @Override - public void invalidateCachedGroupById(String id) { - logger.tracev("Removing group {0}", id); - cache.remove(id); - - } - @Override public void invalidateGroupById(String id) { logger.tracev("Removing group {0}", id); @@ -160,23 +153,17 @@ public class InfinispanRealmCache implements RealmCache { } @Override - public void evictCachedRoleById(String id) { + public void evictRoleById(String id) { logger.tracev("Evicting role {0}", id); cache.evict(id); } @Override - public void addCachedRole(CachedRole role) { + public void addRole(CachedRole role) { logger.tracev("Adding role {0}", role.getId()); cache.putForExternalRead(role.getId(), role); } - @Override - public void invalidateCachedRoleById(String id) { - logger.tracev("Removing role {0}", id); - cache.remove(id); - } - private T get(String id, Class type) { Object o = cache.get(id); return o != null && type.isInstance(o) ? type.cast(o) : null; @@ -194,19 +181,19 @@ public class InfinispanRealmCache implements RealmCache { } @Override - public void addCachedClientTemplate(CachedClientTemplate app) { + public void addClientTemplate(CachedClientTemplate app) { logger.tracev("Adding client template {0}", app.getId()); cache.putForExternalRead(app.getId(), app); } @Override - public void invalidateCachedClientTemplateById(String id) { + public void invalidateClientTemplateById(String id) { logger.tracev("Removing client template {0}", id); cache.remove(id); } @Override - public void evictCachedClientTemplateById(String id) { + 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/RealmAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java index 41dc8d830a..14e2850752 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 @@ -415,6 +415,9 @@ public class RealmAdapter implements RealmModel { @Override public PublicKey getPublicKey() { + if (updated != null) return updated.getPublicKey(); + if (publicKey != null) return publicKey; + publicKey = cached.getPublicKey(); if (publicKey != null) return publicKey; publicKey = KeycloakModelUtils.getPublicKey(getPublicKeyPem()); return publicKey; @@ -429,6 +432,9 @@ public class RealmAdapter implements RealmModel { @Override public X509Certificate getCertificate() { + if (updated != null) return updated.getCertificate(); + if (certificate != null) return certificate; + certificate = cached.getCertificate(); if (certificate != null) return certificate; certificate = KeycloakModelUtils.getCertificate(getCertificatePem()); return certificate; @@ -456,7 +462,14 @@ public class RealmAdapter implements RealmModel { @Override public PrivateKey getPrivateKey() { - if (privateKey != null) return privateKey; + if (updated != null) return updated.getPrivateKey(); + if (privateKey != null) { + return privateKey; + } + privateKey = cached.getPrivateKey(); + if (privateKey != null) { + return privateKey; + } privateKey = KeycloakModelUtils.getPrivateKey(getPrivateKeyPem()); return privateKey; } @@ -635,10 +648,7 @@ public class RealmAdapter implements RealmModel { @Override public ClientModel getClientByClientId(String clientId) { - if (updated != null) return updated.getClientByClientId(clientId); - String id = cached.getClients().get(clientId); - if (id == null) return null; - return getClientById(id); + return cacheSession.getClientByClientId(clientId, this); } @Override 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/locking/LockingCacheRealmProvider.java index 162a36a737..a15f4f1f4b 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/locking/LockingCacheRealmProvider.java @@ -132,7 +132,7 @@ public class LockingCacheRealmProvider implements CacheRealmProvider { protected void runInvalidations() { for (String id : realmInvalidations) { - cache.invalidateCachedRealmById(id); + cache.invalidateRealmById(id); } for (String id : roleInvalidations) { cache.invalidateRoleById(id); @@ -141,10 +141,10 @@ public class LockingCacheRealmProvider implements CacheRealmProvider { cache.invalidateGroupById(id); } for (String id : appInvalidations) { - cache.invalidateCachedApplicationById(id); + cache.invalidateClientById(id); } for (String id : clientTemplateInvalidations) { - cache.invalidateCachedClientTemplateById(id); + cache.invalidateClientTemplateById(id); } } @@ -271,7 +271,7 @@ public class LockingCacheRealmProvider implements CacheRealmProvider { @Override public RealmModel getRealm(String id) { - CachedRealm cached = cache.getCachedRealm(id); + CachedRealm cached = cache.getRealm(id); if (cached != null) { logger.tracev("by id cache hit: {0}", cached.getName()); } @@ -282,7 +282,7 @@ public class LockingCacheRealmProvider implements CacheRealmProvider { if (model == null) return null; if (realmInvalidations.contains(id)) return model; cached = new RevisionedCachedRealm(loaded, cache, this, model); - cache.addCachedRealm(cached); + cache.addRealm(cached); } else if (realmInvalidations.contains(id)) { return getDelegate().getRealm(id); } else if (managedRealms.containsKey(id)) { @@ -295,7 +295,7 @@ public class LockingCacheRealmProvider implements CacheRealmProvider { @Override public RealmModel getRealmByName(String name) { - CachedRealm cached = cache.getCachedRealmByName(name); + CachedRealm cached = cache.getRealmByName(name); if (cached != null) { logger.tracev("by name cache hit: {0}", cached.getName()); } @@ -305,7 +305,7 @@ public class LockingCacheRealmProvider implements CacheRealmProvider { if (model == null) return null; if (realmInvalidations.contains(model.getId())) return model; cached = new RevisionedCachedRealm(loaded, cache, this, model); - cache.addCachedRealm(cached); + cache.addRealm(cached); } else if (realmInvalidations.contains(cached.getId())) { return getDelegate().getRealmByName(name); } else if (managedRealms.containsKey(cached.getId())) { @@ -332,7 +332,7 @@ public class LockingCacheRealmProvider implements CacheRealmProvider { @Override public boolean removeRealm(String id) { - cache.invalidateCachedRealmById(id); + cache.invalidateRealmById(id); RealmModel realm = getDelegate().getRealm(id); Set realmRoles = null; @@ -376,7 +376,7 @@ public class LockingCacheRealmProvider implements CacheRealmProvider { } else { cached = new RevisionedCachedRealmRole(loaded, model, realm); } - cache.addCachedRole(cached); + cache.addRole(cached); } else if (roleInvalidations.contains(id)) { return getDelegate().getRoleById(id, realm); @@ -402,7 +402,7 @@ public class LockingCacheRealmProvider implements CacheRealmProvider { if (model == null) return null; if (groupInvalidations.contains(id)) return model; cached = new RevisionedCachedGroup(loaded, realm, model); - cache.addCachedGroup(cached); + cache.addGroup(cached); } else if (groupInvalidations.contains(id)) { return getDelegate().getGroupById(id, realm); @@ -416,11 +416,11 @@ public class LockingCacheRealmProvider implements CacheRealmProvider { @Override public ClientModel getClientById(String id, RealmModel realm) { - CachedClient cached = cache.getApplication(id); + CachedClient cached = cache.getClient(id); if (cached != null && !cached.getRealm().equals(realm.getId())) { cached = null; } - if (cached != null && cached.getClientId().equals("client")) { + if (cached != null) { logger.tracev("client by id cache hit: {0}", cached.getClientId()); } @@ -431,7 +431,8 @@ public class LockingCacheRealmProvider implements CacheRealmProvider { if (model == null) return null; if (appInvalidations.contains(id)) return model; cached = new RevisionedCachedClient(loaded, cache, getDelegate(), realm, model); - cache.addCachedClient(cached); + logger.tracev("adding client by id cache miss: {0}", cached.getClientId()); + cache.addClient(cached); } else if (appInvalidations.contains(id)) { return getDelegate().getClientById(id, realm); } else if (managedApplications.containsKey(id)) { @@ -441,6 +442,36 @@ public class LockingCacheRealmProvider implements CacheRealmProvider { managedApplications.put(id, adapter); return adapter; } + + @Override + public ClientModel getClientByClientId(String clientId, RealmModel realm) { + CachedClient cached = cache.getClientByClientId(clientId); + if (cached != null && !cached.getRealm().equals(realm.getId())) { + cached = null; + } + if (cached != null) { + logger.tracev("client by name cache hit: {0}", cached.getClientId()); + } + + if (cached == null) { + Long loaded = UpdateCounter.current(); + if (loaded == null) loaded = UpdateCounter.current(); + 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()); + } + ClientAdapter adapter = new ClientAdapter(realm, cached, this, cache); + managedApplications.put(cached.getId(), adapter); + return adapter; + } + @Override public ClientTemplateModel getClientTemplateById(String id, RealmModel realm) { CachedClientTemplate cached = cache.getClientTemplate(id); @@ -455,7 +486,7 @@ public class LockingCacheRealmProvider implements CacheRealmProvider { if (model == null) return null; if (clientTemplateInvalidations.contains(id)) return model; cached = new RevisionedCachedClientTemplate(loaded, cache, getDelegate(), realm, model); - cache.addCachedClientTemplate(cached); + cache.addClientTemplate(cached); } else if (clientTemplateInvalidations.contains(id)) { return getDelegate().getClientTemplateById(id, realm); } else if (managedClientTemplates.containsKey(id)) { 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 index f143ea3ffd..67230484a1 100755 --- 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 @@ -37,8 +37,6 @@ import org.keycloak.models.cache.CacheRealmProviderFactory; import org.keycloak.models.cache.entities.CachedClient; import org.keycloak.models.cache.entities.CachedRealm; -import java.util.concurrent.ConcurrentHashMap; - /** * @author Bill Burke * @author Stian Thorgersen @@ -49,8 +47,6 @@ public class LockingCacheRealmProviderFactory implements CacheRealmProviderFacto protected volatile LockingRealmCache realmCache; - protected final ConcurrentHashMap realmLookup = new ConcurrentHashMap<>(); - @Override public CacheRealmProvider create(KeycloakSession session) { lazyInit(session); @@ -64,7 +60,7 @@ public class LockingCacheRealmProviderFactory implements CacheRealmProviderFacto 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, realmLookup); + realmCache = new LockingRealmCache(cache, counterCache); } } } @@ -98,7 +94,7 @@ public class LockingCacheRealmProviderFactory implements CacheRealmProviderFacto if (object != null) { if (object instanceof CachedRealm) { CachedRealm realm = (CachedRealm) object; - realmLookup.put(realm.getName(), realm.getId()); + realmCache.getRealmLookup().put(realm.getName(), realm.getId()); log.tracev("Realm added realm={0}", realm.getName()); } } @@ -136,14 +132,14 @@ public class LockingCacheRealmProviderFactory implements CacheRealmProviderFacto if (object instanceof CachedRealm) { CachedRealm realm = (CachedRealm) object; - realmLookup.remove(realm.getName()); + realmCache.getRealmLookup().remove(realm.getName()); for (String r : realm.getRealmRoles().values()) { - realmCache.evictCachedRoleById(r); + realmCache.evictRoleById(r); } for (String c : realm.getClients().values()) { - realmCache.evictCachedApplicationById(c); + realmCache.evictClientById(c); } log.tracev("Realm removed realm={0}", realm.getName()); @@ -151,7 +147,7 @@ public class LockingCacheRealmProviderFactory implements CacheRealmProviderFacto CachedClient client = (CachedClient) object; for (String r : client.getRoles().values()) { - realmCache.evictCachedRoleById(r); + 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/LockingRealmCache.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/locking/LockingRealmCache.java index 2460502d91..5b3060d9c0 100755 --- 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 @@ -27,7 +27,6 @@ import org.keycloak.models.cache.entities.CachedRealm; import org.keycloak.models.cache.entities.CachedRole; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicLong; /** * @author Stian Thorgersen @@ -39,11 +38,11 @@ public class LockingRealmCache implements RealmCache { protected final Cache revisions; protected final Cache cache; - protected final ConcurrentHashMap realmLookup; + protected final ConcurrentHashMap realmLookup = new ConcurrentHashMap<>(); + protected final ConcurrentHashMap clientLookup = new ConcurrentHashMap<>(); - public LockingRealmCache(Cache cache, Cache revisions, ConcurrentHashMap realmLookup) { + public LockingRealmCache(Cache cache, Cache revisions) { this.cache = cache; - this.realmLookup = realmLookup; this.revisions = revisions; } @@ -55,6 +54,14 @@ public class LockingRealmCache implements RealmCache { return revisions; } + public ConcurrentHashMap getRealmLookup() { + return realmLookup; + } + + public ConcurrentHashMap getClientLookup() { + return clientLookup; + } + public Long getCurrentRevision(String id) { return revisions.get(id); } @@ -133,25 +140,25 @@ public class LockingRealmCache implements RealmCache { } @Override - public CachedRealm getCachedRealm(String id) { + public CachedRealm getRealm(String id) { return get(id, CachedRealm.class); } @Override - public void invalidateCachedRealm(CachedRealm realm) { + public void invalidateRealm(CachedRealm realm) { logger.tracev("Invalidating realm {0}", realm.getId()); invalidateObject(realm.getId()); realmLookup.remove(realm.getName()); } @Override - public void invalidateCachedRealmById(String id) { + public void invalidateRealmById(String id) { CachedRealm cached = (CachedRealm) invalidateObject(id); if (cached != null) realmLookup.remove(cached.getName()); } @Override - public void addCachedRealm(CachedRealm realm) { + public void addRealm(CachedRealm realm) { logger.tracev("Adding realm {0}", realm.getId()); addRevisioned(realm.getId(), (Revisioned) realm); realmLookup.put(realm.getName(), realm.getId()); @@ -159,36 +166,46 @@ public class LockingRealmCache implements RealmCache { @Override - public CachedRealm getCachedRealmByName(String name) { + public CachedRealm getRealmByName(String name) { String id = realmLookup.get(name); - return id != null ? getCachedRealm(id) : null; + return id != null ? getRealm(id) : null; } @Override - public CachedClient getApplication(String id) { + public CachedClient getClient(String id) { return get(id, CachedClient.class); } + public CachedClient getClientByClientId(String clientId) { + String id = clientLookup.get(clientId); + return id != null ? getClient(id) : null; + } + @Override - public void invalidateApplication(CachedClient app) { + public void invalidateClient(CachedClient app) { logger.tracev("Removing application {0}", app.getId()); invalidateObject(app.getId()); + clientLookup.remove(app.getClientId()); } @Override - public void addCachedClient(CachedClient app) { + public void addClient(CachedClient app) { logger.tracev("Adding application {0}", app.getId()); addRevisioned(app.getId(), (Revisioned) app); + clientLookup.put(app.getClientId(), app.getId()); } @Override - public void invalidateCachedApplicationById(String id) { + public void invalidateClientById(String id) { CachedClient client = (CachedClient)invalidateObject(id); - if (client != null) logger.tracev("Removing application {0}", client.getClientId()); + if (client != null) { + logger.tracev("Removing application {0}", client.getClientId()); + clientLookup.remove(client.getClientId()); + } } @Override - public void evictCachedApplicationById(String id) { + public void evictClientById(String id) { logger.tracev("Evicting application {0}", id); cache.evict(id); } @@ -205,18 +222,11 @@ public class LockingRealmCache implements RealmCache { } @Override - public void addCachedGroup(CachedGroup role) { + public void addGroup(CachedGroup role) { logger.tracev("Adding group {0}", role.getId()); addRevisioned(role.getId(), (Revisioned) role); } - @Override - public void invalidateCachedGroupById(String id) { - logger.tracev("Removing group {0}", id); - invalidateObject(id); - - } - @Override public void invalidateGroupById(String id) { logger.tracev("Removing group {0}", id); @@ -241,23 +251,17 @@ public class LockingRealmCache implements RealmCache { } @Override - public void evictCachedRoleById(String id) { + public void evictRoleById(String id) { logger.tracev("Evicting role {0}", id); cache.evict(id); } @Override - public void addCachedRole(CachedRole role) { + public void addRole(CachedRole role) { logger.tracev("Adding role {0}", role.getId()); addRevisioned(role.getId(), (Revisioned) role); } - @Override - public void invalidateCachedRoleById(String id) { - logger.tracev("Removing role {0}", id); - invalidateObject(id); - } - @Override public CachedClientTemplate getClientTemplate(String id) { return get(id, CachedClientTemplate.class); @@ -270,19 +274,19 @@ public class LockingRealmCache implements RealmCache { } @Override - public void addCachedClientTemplate(CachedClientTemplate app) { + public void addClientTemplate(CachedClientTemplate app) { logger.tracev("Adding client template {0}", app.getId()); addRevisioned(app.getId(), (Revisioned) app); } @Override - public void invalidateCachedClientTemplateById(String id) { + public void invalidateClientTemplateById(String id) { logger.tracev("Removing client template {0}", id); invalidateObject(id); } @Override - public void evictCachedClientTemplateById(String id) { + public void evictClientTemplateById(String id) { logger.tracev("Evicting client template {0}", id); cache.evict(id); } 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 86bc5a224f..55b6087e09 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 @@ -176,6 +176,17 @@ public class JpaRealmProvider implements RealmProvider { return new ClientAdapter(realm, em, session, app); } + @Override + public ClientModel getClientByClientId(String clientId, RealmModel realm) { + TypedQuery query = em.createNamedQuery("findClientByClientId", ClientEntity.class); + query.setParameter("clientId", clientId); + query.setParameter("realm", realm.getId()); + List results = query.getResultList(); + if (results.isEmpty()) return null; + ClientEntity entity = results.get(0); + return new ClientAdapter(realm, em, session, entity); + } + @Override public ClientTemplateModel getClientTemplateById(String id, RealmModel realm) { ClientTemplateEntity app = em.find(ClientTemplateEntity.class, id); 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 4d632ec14d..eb03e53476 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 @@ -811,7 +811,7 @@ public class RealmAdapter implements RealmModel { @Override public ClientModel getClientByClientId(String clientId) { - return getClientNameMap().get(clientId); + return session.realms().getClientByClientId(clientId, this); } private static final String BROWSER_HEADER_PREFIX = "_browser_header."; 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 15544c62ed..5466a7773f 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,8 @@ 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="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"), }) public class ClientEntity { 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 323bbbe81c..ddfbdefba2 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 @@ -162,6 +162,17 @@ public class MongoRealmProvider implements RealmProvider { return new ClientAdapter(session, realm, appData, invocationContext); } + @Override + public ClientModel getClientByClientId(String clientId, RealmModel realm) { + DBObject query = new QueryBuilder() + .and("realmId").is(realm.getId()) + .and("clientId").is(clientId) + .get(); + MongoClientEntity appEntity = getMongoStore().loadSingleEntity(MongoClientEntity.class, query, invocationContext); + return appEntity == null ? null : new ClientAdapter(session, realm, appEntity, invocationContext); + + } + @Override public ClientTemplateModel getClientTemplateById(String id, RealmModel realm) { MongoClientTemplateEntity appData = getMongoStore().loadEntity(MongoClientTemplateEntity.class, id, invocationContext); 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 bae4589b33..ca7626c3ae 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 @@ -807,12 +807,7 @@ public class RealmAdapter extends AbstractMongoAdapter impleme @Override public ClientModel getClientByClientId(String clientId) { - DBObject query = new QueryBuilder() - .and("realmId").is(getId()) - .and("clientId").is(clientId) - .get(); - MongoClientEntity appEntity = getMongoStore().loadSingleEntity(MongoClientEntity.class, query, invocationContext); - return appEntity == null ? null : new ClientAdapter(session, this, appEntity, invocationContext); + return session.realms().getClientByClientId(clientId, this); } @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 ad9e2d1e9f..59568a3fc9 100755 --- a/server-spi/src/main/java/org/keycloak/models/RealmProvider.java +++ b/server-spi/src/main/java/org/keycloak/models/RealmProvider.java @@ -35,8 +35,11 @@ public interface RealmProvider extends Provider { RealmModel getRealm(String id); RealmModel getRealmByName(String name); - RoleModel getRoleById(String id, RealmModel realm); ClientModel getClientById(String id, RealmModel realm); + ClientModel getClientByClientId(String clientId, RealmModel realm); + + + RoleModel getRoleById(String id, RealmModel realm); ClientTemplateModel getClientTemplateById(String id, RealmModel realm); GroupModel getGroupById(String id, RealmModel realm); diff --git a/server-spi/src/main/java/org/keycloak/models/cache/RealmCache.java b/server-spi/src/main/java/org/keycloak/models/cache/RealmCache.java index 909223b936..2b7b339daf 100755 --- a/server-spi/src/main/java/org/keycloak/models/cache/RealmCache.java +++ b/server-spi/src/main/java/org/keycloak/models/cache/RealmCache.java @@ -30,35 +30,33 @@ import org.keycloak.models.cache.entities.CachedRole; public interface RealmCache { void clear(); - CachedRealm getCachedRealm(String id); + CachedRealm getRealm(String id); - void invalidateCachedRealm(CachedRealm realm); + void invalidateRealm(CachedRealm realm); - void addCachedRealm(CachedRealm realm); + void addRealm(CachedRealm realm); - CachedRealm getCachedRealmByName(String name); + CachedRealm getRealmByName(String name); - void invalidateCachedRealmById(String id); + void invalidateRealmById(String id); - CachedClient getApplication(String id); + CachedClient getClient(String id); - void invalidateApplication(CachedClient app); + void invalidateClient(CachedClient app); - void evictCachedApplicationById(String id); + void evictClientById(String id); - void addCachedClient(CachedClient app); + void addClient(CachedClient app); - void invalidateCachedApplicationById(String id); + void invalidateClientById(String id); CachedRole getRole(String id); void invalidateRole(CachedRole role); - void evictCachedRoleById(String id); + void evictRoleById(String id); - void addCachedRole(CachedRole role); - - void invalidateCachedRoleById(String id); + void addRole(CachedRole role); void invalidateRoleById(String id); @@ -66,9 +64,7 @@ public interface RealmCache { void invalidateGroup(CachedGroup role); - void addCachedGroup(CachedGroup role); - - void invalidateCachedGroupById(String id); + void addGroup(CachedGroup role); void invalidateGroupById(String id); @@ -76,10 +72,10 @@ public interface RealmCache { void invalidateClientTemplate(CachedClientTemplate app); - void evictCachedClientTemplateById(String id); + void evictClientTemplateById(String id); - void addCachedClientTemplate(CachedClientTemplate app); + void addClientTemplate(CachedClientTemplate app); - void invalidateCachedClientTemplateById(String id); + void invalidateClientTemplateById(String id); } diff --git a/server-spi/src/main/java/org/keycloak/models/cache/entities/CachedClient.java b/server-spi/src/main/java/org/keycloak/models/cache/entities/CachedClient.java index ca3e35ba31..727cabcd66 100755 --- a/server-spi/src/main/java/org/keycloak/models/cache/entities/CachedClient.java +++ b/server-spi/src/main/java/org/keycloak/models/cache/entities/CachedClient.java @@ -127,7 +127,7 @@ public class CachedClient implements Serializable { protected void cacheRoles(RealmCache cache, RealmModel realm, ClientModel model) { for (RoleModel role : model.getRoles()) { roles.put(role.getName(), role.getId()); - cache.addCachedRole(new CachedClientRole(id, role, realm)); + cache.addRole(new CachedClientRole(id, role, realm)); } } diff --git a/server-spi/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java b/server-spi/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java index 42ed3bcb39..29b93318bd 100755 --- a/server-spi/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java +++ b/server-spi/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java @@ -39,6 +39,10 @@ import org.keycloak.models.cache.RealmCache; import org.keycloak.common.util.MultivaluedHashMap; import java.io.Serializable; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -89,8 +93,11 @@ public class CachedRealm implements Serializable { protected PasswordPolicy passwordPolicy; protected OTPPolicy otpPolicy; + protected transient PublicKey publicKey; protected String publicKeyPem; + protected transient PrivateKey privateKey; protected String privateKeyPem; + protected transient X509Certificate certificate; protected String certificatePem; protected String codeSecret; @@ -179,8 +186,11 @@ public class CachedRealm implements Serializable { otpPolicy = model.getOTPPolicy(); publicKeyPem = model.getPublicKeyPem(); + publicKey = model.getPublicKey(); privateKeyPem = model.getPrivateKeyPem(); + privateKey = model.getPrivateKey(); certificatePem = model.getCertificatePem(); + certificate = model.getCertificate(); codeSecret = model.getCodeSecret(); loginTheme = model.getLoginTheme(); @@ -265,7 +275,7 @@ public class CachedRealm implements Serializable { for (ClientTemplateModel template : model.getClientTemplates()) { clientTemplates.add(template.getId()); CachedClientTemplate cachedClient = new CachedClientTemplate(cache, delegate, model, template); - cache.addCachedClientTemplate(cachedClient); + cache.addClientTemplate(cachedClient); } } @@ -273,7 +283,7 @@ public class CachedRealm implements Serializable { for (ClientModel client : model.getClients()) { clients.put(client.getClientId(), client.getId()); CachedClient cachedClient = new CachedClient(cache, delegate, model, client); - cache.addCachedClient(cachedClient); + cache.addClient(cachedClient); } } @@ -281,7 +291,7 @@ public class CachedRealm implements Serializable { for (RoleModel role : model.getRoles()) { realmRoles.put(role.getName(), role.getId()); CachedRole cachedRole = new CachedRealmRole(role, model); - cache.addCachedRole(cachedRole); + cache.addRole(cachedRole); } } @@ -584,4 +594,16 @@ public class CachedRealm implements Serializable { public List getClientTemplates() { return clientTemplates; } + + public PublicKey getPublicKey() { + return publicKey; + } + + public PrivateKey getPrivateKey() { + return privateKey; + } + + public X509Certificate getCertificate() { + return certificate; + } } diff --git a/testsuite/stress/src/main/java/org/keycloak/test/stress/MaxRateExecutor.java b/testsuite/stress/src/main/java/org/keycloak/test/stress/MaxRateExecutor.java index c1f0a6b02f..cfc9cd5af9 100755 --- a/testsuite/stress/src/main/java/org/keycloak/test/stress/MaxRateExecutor.java +++ b/testsuite/stress/src/main/java/org/keycloak/test/stress/MaxRateExecutor.java @@ -88,9 +88,9 @@ public class MaxRateExecutor { StressTest stressTest = future.get(); if (i < jobs - threads - 5) completionService.submit(stressTest); } + long end = System.currentTimeMillis() - start; executor.shutdown(); executor.awaitTermination(10, TimeUnit.SECONDS); - long end = System.currentTimeMillis() - start; RateResult rate = new RateResult(result, threads, end); return rate; } @@ -116,10 +116,10 @@ public class MaxRateExecutor { public void printResult(RateResult result) { System.out.println("Threads: " + result.getThreads()); System.out.println("Total Time: " + result.getTime()); + System.out.println("Rate: " + ((double)result.getResult().getIterations()) / ((double)result.getTime())); System.out.println("Successes: " + result.getResult().getSuccess()); System.out.println("Iterations: " + result.getResult().getIterations()); - System.out.println("Average time: " + result.getResult().getAverageTime()); - System.out.println("Rate: " + result.getResult().getRate()); + System.out.println("Average time per iteration: " + result.getResult().getAverageTime()); } @@ -127,7 +127,7 @@ public class MaxRateExecutor { for (RateResult result : allResults) { System.out.println("*******************"); - printSummary(); + printSummary(result); } } public void printSummary(RateResult result) { diff --git a/testsuite/stress/src/test/java/org/keycloak/test/LoginLogoutTest.java b/testsuite/stress/src/test/java/org/keycloak/test/LoginLogoutTest.java index 1348c644d4..351f8f442e 100755 --- a/testsuite/stress/src/test/java/org/keycloak/test/LoginLogoutTest.java +++ b/testsuite/stress/src/test/java/org/keycloak/test/LoginLogoutTest.java @@ -88,8 +88,9 @@ Rate: 0.030480835174883793 } }; MaxRateExecutor executor = new MaxRateExecutor(); - executor.best(factory, 4); + executor.best(factory, 10); executor.printResults(); + executor.printSummary(); } } From 4ff18486811f5c93427ee9791b0a80b9191ce47e Mon Sep 17 00:00:00 2001 From: Bill Burke Date: Fri, 12 Feb 2016 11:38:12 -0500 Subject: [PATCH 4/4] javadoc --- .../infinispan/DefaultCacheRealmProvider.java | 19 +++++++++++ .../locking/LockingCacheRealmProvider.java | 25 +++++++++++++- .../LockingCacheRealmProviderFactory.java | 2 ++ .../infinispan/locking/LockingRealmCache.java | 15 ++++++--- .../keycloak/models/jpa/JpaRealmProvider.java | 33 ++++++++++++++++++- .../org/keycloak/models/jpa/RealmAdapter.java | 31 ++--------------- .../models/jpa/entities/RealmEntity.java | 2 +- .../keycloak/adapters/MongoRealmProvider.java | 11 +++++++ .../mongo/keycloak/adapters/RealmAdapter.java | 5 +-- .../org/keycloak/models/RealmProvider.java | 3 ++ .../services/managers/RealmManager.java | 5 +-- .../keycloak/test/stress/MaxRateExecutor.java | 3 +- 12 files changed, 111 insertions(+), 43 deletions(-) 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 index a177b95f0d..b998b8c02b 100755 --- 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 @@ -351,6 +351,25 @@ public class DefaultCacheRealmProvider implements CacheRealmProvider { 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); 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/locking/LockingCacheRealmProvider.java index a15f4f1f4b..4edb046af5 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/locking/LockingCacheRealmProvider.java @@ -105,6 +105,10 @@ public class LockingCacheRealmProvider implements CacheRealmProvider { return delegate; } + public LockingRealmCache getCache() { + return cache; + } + @Override public void registerRealmInvalidation(String id) { realmInvalidations.add(id); @@ -353,6 +357,25 @@ public class LockingCacheRealmProvider implements CacheRealmProvider { return didIt; } + @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 void close() { if (delegate != null) delegate.close(); @@ -445,7 +468,7 @@ public class LockingCacheRealmProvider implements CacheRealmProvider { @Override public ClientModel getClientByClientId(String clientId, RealmModel realm) { - CachedClient cached = cache.getClientByClientId(clientId); + CachedClient cached = cache.getClientByClientId(realm, clientId); if (cached != null && !cached.getRealm().equals(realm.getId())) { cached = null; } 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 index 67230484a1..23f8e53bba 100755 --- 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 @@ -146,6 +146,8 @@ public class LockingCacheRealmProviderFactory implements CacheRealmProviderFacto } 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); } 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 index 5b3060d9c0..a77c59b8e2 100755 --- 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 @@ -19,6 +19,7 @@ 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; @@ -176,8 +177,8 @@ public class LockingRealmCache implements RealmCache { return get(id, CachedClient.class); } - public CachedClient getClientByClientId(String clientId) { - String id = clientLookup.get(clientId); + public CachedClient getClientByClientId(RealmModel realm, String clientId) { + String id = clientLookup.get(realm.getId() + "." + clientId); return id != null ? getClient(id) : null; } @@ -185,14 +186,14 @@ public class LockingRealmCache implements RealmCache { public void invalidateClient(CachedClient app) { logger.tracev("Removing application {0}", app.getId()); invalidateObject(app.getId()); - clientLookup.remove(app.getClientId()); + clientLookup.remove(getClientIdKey(app)); } @Override public void addClient(CachedClient app) { logger.tracev("Adding application {0}", app.getId()); addRevisioned(app.getId(), (Revisioned) app); - clientLookup.put(app.getClientId(), app.getId()); + clientLookup.put(getClientIdKey(app), app.getId()); } @Override @@ -200,10 +201,14 @@ public class LockingRealmCache implements RealmCache { CachedClient client = (CachedClient)invalidateObject(id); if (client != null) { logger.tracev("Removing application {0}", client.getClientId()); - clientLookup.remove(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); 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 55b6087e09..ce245de661 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 @@ -17,6 +17,7 @@ package org.keycloak.models.jpa; +import org.jboss.logging.Logger; import org.keycloak.migration.MigrationModel; import org.keycloak.models.ClientModel; import org.keycloak.models.ClientTemplateModel; @@ -43,6 +44,7 @@ import java.util.List; * @version $Revision: 1 $ */ public class JpaRealmProvider implements RealmProvider { + protected static final Logger logger = Logger.getLogger(JpaRealmProvider.class); private final KeycloakSession session; protected EntityManager em; @@ -115,7 +117,6 @@ public class JpaRealmProvider implements RealmProvider { if (realm == null) { return false; } - RealmAdapter adapter = new RealmAdapter(session, em, realm); session.users().preRemove(adapter); int num = em.createNamedQuery("deleteGroupRoleMappingsByRealm") @@ -144,6 +145,11 @@ 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; } @@ -187,6 +193,31 @@ public class JpaRealmProvider implements RealmProvider { return new ClientAdapter(realm, em, session, entity); } + @Override + public boolean removeClient(String id, RealmModel realm) { + ClientModel client = getClientById(id, realm); + if (client == null) return false; + + session.users().preRemove(realm, client); + + for (RoleModel role : client.getRoles()) { + client.removeRole(role); + } + + + ClientEntity clientEntity = ((ClientAdapter)client).getEntity(); + em.createNamedQuery("deleteScopeMappingByClient").setParameter("client", clientEntity).executeUpdate(); + em.flush(); + em.remove(clientEntity); // i have no idea why, but this needs to come before deleteScopeMapping + try { + em.flush(); + } catch (RuntimeException e) { + logger.errorv("Unable to delete client entity: {0} from realm {1}", client.getClientId(), realm.getName()); + throw e; + } + return true; + } + @Override public ClientTemplateModel getClientTemplateById(String id, RealmModel realm) { ClientTemplateEntity app = em.find(ClientTemplateEntity.class, id); 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 eb03e53476..da66a3ba38 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 @@ -17,6 +17,7 @@ package org.keycloak.models.jpa; +import org.jboss.logging.Logger; import org.keycloak.connections.jpa.util.JpaUtils; import org.keycloak.common.enums.SslRequired; import org.keycloak.models.AuthenticationExecutionModel; @@ -65,6 +66,7 @@ import java.util.Set; * @version $Revision: 1 $ */ public class RealmAdapter implements RealmModel { + protected static final Logger logger = Logger.getLogger(RealmAdapter.class); protected RealmEntity realm; protected EntityManager em; protected volatile transient PublicKey publicKey; @@ -774,34 +776,7 @@ public class RealmAdapter implements RealmModel { if (id == null) return false; ClientModel client = getClientById(id); if (client == null) return false; - - session.users().preRemove(this, client); - - for (RoleModel role : client.getRoles()) { - client.removeRole(role); - } - - ClientEntity clientEntity = null; - Iterator it = realm.getClients().iterator(); - while (it.hasNext()) { - ClientEntity ae = it.next(); - if (ae.getId().equals(id)) { - clientEntity = ae; - it.remove(); - break; - } - } - for (ClientEntity a : realm.getClients()) { - if (a.getId().equals(id)) { - clientEntity = a; - } - } - if (clientEntity == null) return false; - em.createNamedQuery("deleteScopeMappingByClient").setParameter("client", clientEntity).executeUpdate(); - em.remove(clientEntity); - em.flush(); - - return true; + return session.realms().removeClient(id, this); } @Override 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 76aa238b6a..684d102b2b 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 @@ -191,7 +191,7 @@ public class RealmEntity { @Column(name="ADMIN_EVENTS_DETAILS_ENABLED") protected boolean adminEventsDetailsEnabled; - @OneToOne + @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name="MASTER_ADMIN_CLIENT") protected ClientEntity masterAdminClient; 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 ddfbdefba2..c5d70fb8b7 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 @@ -162,6 +162,17 @@ public class MongoRealmProvider implements RealmProvider { return new ClientAdapter(session, realm, appData, invocationContext); } + @Override + public boolean removeClient(String id, RealmModel realm) { + if (id == null) return false; + ClientModel client = getClientById(id, realm); + if (client == null) return false; + + session.users().preRemove(realm, client); + + return getMongoStore().removeEntity(MongoClientEntity.class, id, invocationContext); + } + @Override public ClientModel getClientByClientId(String clientId, RealmModel realm) { DBObject query = new QueryBuilder() 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 ca7626c3ae..701cced0ea 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 @@ -868,10 +868,7 @@ public class RealmAdapter extends AbstractMongoAdapter impleme if (id == null) return false; ClientModel client = getClientById(id); if (client == null) return false; - - session.users().preRemove(this, client); - - return getMongoStore().removeEntity(MongoClientEntity.class, id, invocationContext); + return session.realms().removeClient(id, this); } @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 59568a3fc9..2b3f5fab3c 100755 --- a/server-spi/src/main/java/org/keycloak/models/RealmProvider.java +++ b/server-spi/src/main/java/org/keycloak/models/RealmProvider.java @@ -40,6 +40,9 @@ public interface RealmProvider extends Provider { RoleModel getRoleById(String id, RealmModel realm); + + boolean removeClient(String id, RealmModel realm); + ClientTemplateModel getClientTemplateById(String id, RealmModel realm); GroupModel getGroupById(String id, RealmModel realm); diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java index dbaaad2a20..55695a0a3e 100755 --- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java +++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java @@ -211,10 +211,11 @@ public class RealmManager implements RealmImporter { public boolean removeRealm(RealmModel realm) { List federationProviders = realm.getUserFederationProviders(); + ClientModel masterAdminClient = realm.getMasterAdminClient(); boolean removed = model.removeRealm(realm.getId()); if (removed) { - if (realm.getMasterAdminClient() != null) { - new ClientManager(this).removeClient(getKeycloakAdminstrationRealm(), realm.getMasterAdminClient()); + if (masterAdminClient != null) { + new ClientManager(this).removeClient(getKeycloakAdminstrationRealm(), masterAdminClient); } UserSessionProvider sessions = session.sessions(); diff --git a/testsuite/stress/src/main/java/org/keycloak/test/stress/MaxRateExecutor.java b/testsuite/stress/src/main/java/org/keycloak/test/stress/MaxRateExecutor.java index cfc9cd5af9..e56aa83e77 100755 --- a/testsuite/stress/src/main/java/org/keycloak/test/stress/MaxRateExecutor.java +++ b/testsuite/stress/src/main/java/org/keycloak/test/stress/MaxRateExecutor.java @@ -11,7 +11,8 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; /** - * Executes all test threads until completion. + * Executes a test N number of times. This is done multiple times over an ever expanding amount of threads to determine + * when the computer is saturated and you can't eek out any more concurrent requests. * * @author Bill Burke * @version $Revision: 1 $