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 $