keycloak-memoizeit

role cached queries

2/22/2016 7:16:35 PM

Changes

Details

diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java
index e636f5a..2094fa2 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java
@@ -463,12 +463,19 @@ public class ClientAdapter implements ClientModel {
     }
 
     @Override
-    public void updateDefaultRoles(String[] defaultRoles) {
+    public void updateDefaultRoles(String... defaultRoles) {
         getDelegateForUpdate();
         updated.updateDefaultRoles(defaultRoles);
     }
 
     @Override
+    public void removeDefaultRoles(String... defaultRoles) {
+        getDelegateForUpdate();
+        updated.removeDefaultRoles(defaultRoles);
+
+    }
+
+    @Override
     public boolean isBearerOnly() {
         if (updated != null) return updated.isBearerOnly();
         return cached.isBearerOnly();
@@ -542,12 +549,10 @@ public class ClientAdapter implements ClientModel {
 
     @Override
     public RoleModel getRole(String name) {
-        if (updated != null) return updated.getRole(name);
-        String id = cached.getRoles().get(name);
-        if (id == null) {
-            return null;
+        for (RoleModel role : getRoles()) {
+            if (role.getName().equals(name)) return role;
         }
-        return cacheSession.getRoleById(id, cachedRealm);
+        return null;
     }
 
     @Override
@@ -575,15 +580,7 @@ public class ClientAdapter implements ClientModel {
 
     @Override
     public Set<RoleModel> getRoles() {
-        if (updated != null) return updated.getRoles();
-
-        Set<RoleModel> roles = new HashSet<RoleModel>();
-        for (String id : cached.getRoles().values()) {
-            RoleModel roleById = cacheSession.getRoleById(id, cachedRealm);
-            if (roleById == null) continue;
-            roles.add(roleById);
-        }
-        return roles;
+        return cacheSession.getClientRoles(cachedRealm, this);
     }
 
     @Override
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedClient.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedClient.java
index c528c8c..cac6bc6 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedClient.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedClient.java
@@ -67,7 +67,6 @@ public class CachedClient extends AbstractRevisioned implements InRealm {
     protected boolean implicitFlowEnabled;
     protected boolean directAccessGrantsEnabled;
     protected boolean serviceAccountsEnabled;
-    protected Map<String, String> roles = new HashMap<String, String>();
     protected int nodeReRegistrationTimeout;
     protected Map<String, Integer> registeredNodes;
     protected String clientTemplate;
@@ -110,7 +109,6 @@ public class CachedClient extends AbstractRevisioned implements InRealm {
         implicitFlowEnabled = model.isImplicitFlowEnabled();
         directAccessGrantsEnabled = model.isDirectAccessGrantsEnabled();
         serviceAccountsEnabled = model.isServiceAccountsEnabled();
-        cacheRoles(model);
 
         nodeReRegistrationTimeout = model.getNodeReRegistrationTimeout();
         registeredNodes = new TreeMap<>(model.getRegisteredNodes());
@@ -122,12 +120,6 @@ public class CachedClient extends AbstractRevisioned implements InRealm {
         useTemplateScope = model.useTemplateScope();
     }
 
-    protected void cacheRoles(ClientModel model) {
-        for (RoleModel role : model.getRoles()) {
-            roles.put(role.getName(), role.getId());
-        }
-    }
-
     public String getClientId() {
         return clientId;
     }
@@ -244,10 +236,6 @@ public class CachedClient extends AbstractRevisioned implements InRealm {
         return serviceAccountsEnabled;
     }
 
-    public Map<String, String> getRoles() {
-        return roles;
-    }
-
     public int getNodeReRegistrationTimeout() {
         return nodeReRegistrationTimeout;
     }
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedRealm.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedRealm.java
index d5e02a2..b93da32 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedRealm.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedRealm.java
@@ -145,8 +145,6 @@ public class CachedRealm extends AbstractRevisioned {
 
     protected List<String> defaultGroups = new LinkedList<String>();
     protected Set<String> groups = new HashSet<String>();
-    protected Map<String, String> realmRoles = new HashMap<String, String>();
-    protected List<String> clients = new LinkedList<>();
     protected List<String> clientTemplates= new LinkedList<>();
     protected boolean internationalizationEnabled;
     protected Set<String> supportedLocales;
@@ -240,10 +238,6 @@ public class CachedRealm extends AbstractRevisioned {
         ClientModel masterAdminClient = model.getMasterAdminClient();
         this.masterAdminClient = (masterAdminClient != null) ? masterAdminClient.getId() : null;
 
-        cacheRealmRoles(model);
-
-        cacheClients(model);
-
         cacheClientTemplates(model);
 
         internationalizationEnabled = model.isInternationalizationEnabled();
@@ -288,19 +282,6 @@ public class CachedRealm extends AbstractRevisioned {
         }
     }
 
-    protected void cacheClients(RealmModel model) {
-        for (ClientModel client : model.getClients()) {
-            clients.add(client.getId());
-        }
-    }
-
-    protected void cacheRealmRoles(RealmModel model) {
-        for (RoleModel role : model.getRoles()) {
-            realmRoles.put(role.getName(), role.getId());
-        }
-    }
-
-
     public String getMasterAdminClient() {
         return masterAdminClient;
     }
@@ -321,14 +302,6 @@ public class CachedRealm extends AbstractRevisioned {
         return defaultRoles;
     }
 
-    public Map<String, String> getRealmRoles() {
-        return realmRoles;
-    }
-
-    public List<String> getClients() {
-        return clients;
-    }
-
     public boolean isEnabled() {
         return enabled;
     }
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/RoleListQuery.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/RoleListQuery.java
new file mode 100755
index 0000000..4b0ab08
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/RoleListQuery.java
@@ -0,0 +1,68 @@
+package org.keycloak.models.cache.infinispan.entities;
+
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.cache.infinispan.entities.AbstractRevisioned;
+import org.keycloak.models.cache.infinispan.entities.ClientQuery;
+import org.keycloak.models.cache.infinispan.entities.RoleQuery;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class RoleListQuery extends AbstractRevisioned implements RoleQuery, InClient {
+    private final Set<String> roles;
+    private final String realm;
+    private final String realmName;
+    private String client;
+
+    public RoleListQuery(Long revisioned, String id, RealmModel realm, Set<String> roles) {
+        super(revisioned, id);
+        this.realm = realm.getId();
+        this.realmName = realm.getName();
+        this.roles = roles;
+    }
+
+    public RoleListQuery(Long revisioned, String id, RealmModel realm, String role) {
+        super(revisioned, id);
+        this.realm = realm.getId();
+        this.realmName = realm.getName();
+        this.roles = new HashSet<>();
+        this.roles.add(role);
+    }
+
+    public RoleListQuery(Long revision, String id, RealmModel realm, Set<String> roles, String client) {
+        this(revision, id, realm, roles);
+        this.client = client;
+    }
+
+    public RoleListQuery(Long revision, String id, RealmModel realm, String role, String client) {
+        this(revision, id, realm, role);
+        this.client = client;
+    }
+
+    @Override
+    public Set<String> getRoles() {
+        return roles;
+    }
+
+    @Override
+    public String getRealm() {
+        return realm;
+    }
+
+    @Override
+    public String getClientId() {
+        return client;
+    }
+
+    @Override
+    public String toString() {
+        return "RoleListQuery{" +
+                "id='" + getId() + "'" +
+                "realmName='" + realmName + '\'' +
+                '}';
+    }
+}
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 bc39f32..030286b 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
@@ -581,12 +581,19 @@ public class RealmAdapter implements RealmModel {
     }
 
     @Override
-    public void updateDefaultRoles(String[] defaultRoles) {
+    public void updateDefaultRoles(String... defaultRoles) {
         getDelegateForUpdate();
         updated.updateDefaultRoles(defaultRoles);
     }
 
     @Override
+    public void removeDefaultRoles(String... defaultRoles) {
+        getDelegateForUpdate();
+        updated.removeDefaultRoles(defaultRoles);
+
+    }
+
+    @Override
     public List<ClientModel> getClients() {
         return cacheSession.getClients(this);
 
@@ -870,12 +877,18 @@ public class RealmAdapter implements RealmModel {
 
     @Override
     public RoleModel getRole(String name) {
-        if (updated != null) return updated.getRole(name);
-        String id = cached.getRealmRoles().get(name);
-        if (id == null) return null;
-        return cacheSession.getRoleById(id, this);
+        for (RoleModel role : getRoles()) {
+            if (role.getName().equals(name)) return role;
+        }
+        return null;
+    }
+
+    @Override
+    public Set<RoleModel> getRoles() {
+        return cacheSession.getRealmRoles(this);
     }
 
+
     @Override
     public RoleModel addRole(String name) {
         getDelegateForUpdate();
@@ -899,18 +912,6 @@ public class RealmAdapter implements RealmModel {
         return updated.removeRole(role);
     }
 
-    @Override
-    public Set<RoleModel> getRoles() {
-        if (updated != null) return updated.getRoles();
-
-        Set<RoleModel> roles = new HashSet<RoleModel>();
-        for (String id : cached.getRealmRoles().values()) {
-            RoleModel roleById = cacheSession.getRoleById(id, this);
-            if (roleById == null) continue;
-            roles.add(roleById);
-        }
-        return Collections.unmodifiableSet(roles);
-    }
 
     @Override
     public boolean isIdentityFederationEnabled() {
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/HasRolePredicate.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/HasRolePredicate.java
index c4a5663..7d32462 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/HasRolePredicate.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/HasRolePredicate.java
@@ -3,6 +3,7 @@ package org.keycloak.models.cache.infinispan.stream;
 import org.keycloak.models.cache.infinispan.entities.CachedGroup;
 import org.keycloak.models.cache.infinispan.entities.CachedRole;
 import org.keycloak.models.cache.infinispan.entities.Revisioned;
+import org.keycloak.models.cache.infinispan.entities.RoleQuery;
 
 import java.io.Serializable;
 import java.util.Map;
@@ -36,6 +37,10 @@ public class HasRolePredicate implements Predicate<Map.Entry<String, Revisioned>
             CachedGroup cachedRole = (CachedGroup)value;
             if (cachedRole.getRoleMappings().contains(role)) return true;
         }
+        if (value instanceof RoleQuery) {
+            RoleQuery roleQuery = (RoleQuery)value;
+            if (roleQuery.getRoles().contains(role)) return true;
+        }
         return false;
     }
 }
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/StreamCacheRealmProvider.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/StreamCacheRealmProvider.java
index def9b83..21c0c7d 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/StreamCacheRealmProvider.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/StreamCacheRealmProvider.java
@@ -37,7 +37,8 @@ import org.keycloak.models.cache.infinispan.entities.CachedRealmRole;
 import org.keycloak.models.cache.infinispan.entities.CachedRole;
 import org.keycloak.models.cache.infinispan.entities.ClientListQuery;
 import org.keycloak.models.cache.infinispan.entities.RealmListQuery;
-import org.keycloak.models.cache.infinispan.entities.Revisioned;
+import org.keycloak.models.cache.infinispan.entities.RoleListQuery;
+import org.keycloak.models.utils.KeycloakModelUtils;
 
 import java.util.Collections;
 import java.util.HashMap;
@@ -77,7 +78,7 @@ import java.util.Set;
  * - realm client lists need to be invalidated and evited whenever a client is added or removed from a realm.  RealmProvider
  * now has addClient/removeClient at its top level.  All adapaters should use these methods so that the appropriate invalidations
  * can be registered.
- * - whenever a client is added/removed the realm of the client is added to a clientListInvalidations set
+ * - whenever a client is added/removed the realm of the client is added to a listInvalidations set
  * this set must be checked before sending back or caching a cached query.  This check is required to
  * avoid caching an uncommitted removal/add in a query cache.
  * - when a client is removed, any queries that contain that client must also be removed.
@@ -104,6 +105,8 @@ import java.util.Set;
 public class StreamCacheRealmProvider implements CacheRealmProvider {
     protected static final Logger logger = Logger.getLogger(StreamCacheRealmProvider.class);
     public static final String REALM_CLIENTS_QUERY_SUFFIX = ".realm.clients";
+    public static final String ROLES_QUERY_SUFFIX = ".roles";
+    public static final String ROLE_BY_NAME_QUERY_SUFFIX = ".role.by-name";
     protected StreamRealmCache cache;
     protected KeycloakSession session;
     protected RealmProvider delegate;
@@ -115,7 +118,7 @@ public class StreamCacheRealmProvider implements CacheRealmProvider {
     protected Map<String, ClientTemplateModel> managedClientTemplates = new HashMap<>();
     protected Map<String, RoleModel> managedRoles = new HashMap<>();
     protected Map<String, GroupModel> managedGroups = new HashMap<>();
-    protected Set<String> clientListInvalidations = new HashSet<>();
+    protected Set<String> listInvalidations = new HashSet<>();
     protected Set<String> invalidations = new HashSet<>();
 
     protected boolean clearAll;
@@ -386,7 +389,7 @@ public class StreamCacheRealmProvider implements CacheRealmProvider {
         invalidations.add(client.getId());
         cache.clientAdded(realm.getId(), client.getId(), invalidations);
         // this is needed so that a new client that hasn't been committed isn't cached in a query
-        clientListInvalidations.add(realm.getId());
+        listInvalidations.add(realm.getId());
         return client;
     }
 
@@ -394,10 +397,17 @@ public class StreamCacheRealmProvider implements CacheRealmProvider {
         return realm + REALM_CLIENTS_QUERY_SUFFIX;
     }
 
+    private String getRolesCacheKey(String container) {
+        return container + ROLES_QUERY_SUFFIX;
+    }
+    private String getRoleByNameCacheKey(String container, String name) {
+        return container + "." + name + ROLES_QUERY_SUFFIX;
+    }
+
     @Override
     public List<ClientModel> getClients(RealmModel realm) {
         String cacheKey = getRealmClientsQueryCacheKey(realm.getId());
-        boolean queryDB = invalidations.contains(cacheKey) || clientListInvalidations.contains(realm.getId());
+        boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(realm.getId());
         if (queryDB) {
             return getDelegate().getClients(realm);
         }
@@ -430,6 +440,7 @@ public class StreamCacheRealmProvider implements CacheRealmProvider {
         return list;
     }
 
+
     @Override
     public boolean removeClient(String id, RealmModel realm) {
         ClientModel client = getClientById(id, realm);
@@ -437,7 +448,7 @@ public class StreamCacheRealmProvider implements CacheRealmProvider {
         // need to invalidate realm client query cache every time client list is changed
         invalidations.add(getRealmClientsQueryCacheKey(realm.getId()));
         invalidations.add(getClientByClientIdCacheKey(client.getClientId(), realm));
-        clientListInvalidations.add(realm.getId());
+        listInvalidations.add(realm.getId());
         registerClientInvalidation(id);
         cache.clientRemoval(realm.getId(), id, invalidations);
         for (RoleModel role : client.getRoles()) {
@@ -452,6 +463,177 @@ public class StreamCacheRealmProvider implements CacheRealmProvider {
     }
 
     @Override
+    public RoleModel addRealmRole(RealmModel realm, String name) {
+        return addRealmRole(realm, KeycloakModelUtils.generateId(), name);
+    }
+
+    @Override
+    public RoleModel addRealmRole(RealmModel realm, String id, String name) {
+        invalidations.add(getRolesCacheKey(realm.getId()));
+        // this is needed so that a new role that hasn't been committed isn't cached in a query
+        listInvalidations.add(realm.getId());
+        RoleModel role = getDelegate().addRealmRole(realm, name);
+        invalidations.add(role.getId());
+        return role;
+    }
+
+    @Override
+    public Set<RoleModel> getRealmRoles(RealmModel realm) {
+        String cacheKey = getRolesCacheKey(realm.getId());
+        boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(realm.getId());
+        if (queryDB) {
+            return getDelegate().getRealmRoles(realm);
+        }
+
+        RoleListQuery query = cache.get(cacheKey, RoleListQuery.class);
+        if (query != null) {
+            logger.tracev("getRealmRoles cache hit: {0}", realm.getName());
+        }
+
+        if (query == null) {
+            Long loaded = cache.getCurrentRevision(cacheKey);
+            Set<RoleModel> model = getDelegate().getRealmRoles(realm);
+            if (model == null) return null;
+            Set<String> ids = new HashSet<>();
+            for (RoleModel role : model) ids.add(role.getId());
+            query = new RoleListQuery(loaded, cacheKey, realm, ids);
+            logger.tracev("adding realm roles cache miss: realm {0} key {1}", realm.getName(), cacheKey);
+            cache.addRevisioned(query);
+            return model;
+        }
+        Set<RoleModel> list = new HashSet<>();
+        for (String id : query.getRoles()) {
+            RoleModel role = session.realms().getRoleById(id, realm);
+            if (role == null) {
+                invalidations.add(cacheKey);
+                return getDelegate().getRealmRoles(realm);
+            }
+            list.add(role);
+        }
+        return list;
+    }
+
+    @Override
+    public Set<RoleModel> getClientRoles(RealmModel realm, ClientModel client) {
+        String cacheKey = getRolesCacheKey(client.getId());
+        boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(client.getId());
+        if (queryDB) {
+            return getDelegate().getClientRoles(realm, client);
+        }
+
+        RoleListQuery query = cache.get(cacheKey, RoleListQuery.class);
+        if (query != null) {
+            logger.tracev("getClientRoles cache hit: {0}", client.getClientId());
+        }
+
+        if (query == null) {
+            Long loaded = cache.getCurrentRevision(cacheKey);
+            Set<RoleModel> model = getDelegate().getClientRoles(realm, client);
+            if (model == null) return null;
+            Set<String> ids = new HashSet<>();
+            for (RoleModel role : model) ids.add(role.getId());
+            query = new RoleListQuery(loaded, cacheKey, realm, ids, client.getClientId());
+            logger.tracev("adding client roles cache miss: client {0} key {1}", client.getClientId(), cacheKey);
+            cache.addRevisioned(query);
+            return model;
+        }
+        Set<RoleModel> list = new HashSet<>();
+        for (String id : query.getRoles()) {
+            RoleModel role = session.realms().getRoleById(id, realm);
+            if (role == null) {
+                invalidations.add(cacheKey);
+                return getDelegate().getClientRoles(realm, client);
+            }
+            list.add(role);
+        }
+        return list;
+    }
+
+    @Override
+    public RoleModel addClientRole(RealmModel realm, ClientModel client, String name) {
+        return addClientRole(realm, client, KeycloakModelUtils.generateId(), name);
+    }
+
+    @Override
+    public RoleModel addClientRole(RealmModel realm, ClientModel client, String id, String name) {
+        invalidations.add(getRolesCacheKey(client.getId()));
+        // this is needed so that a new role that hasn't been committed isn't cached in a query
+        listInvalidations.add(client.getId());
+        RoleModel role = getDelegate().addClientRole(realm, client, id, name);
+        invalidations.add(role.getId());
+        return role;
+    }
+
+    @Override
+    public RoleModel getRealmRole(RealmModel realm, String name) {
+        String cacheKey = getRoleByNameCacheKey(realm.getId(), name);
+        boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(realm.getId());
+        if (queryDB) {
+            return getDelegate().getRealmRole(realm, name);
+        }
+
+        RoleListQuery query = cache.get(cacheKey, RoleListQuery.class);
+        if (query != null) {
+            logger.tracev("getRealmRole cache hit: {0}.{1}", realm.getName(), name);
+        }
+
+        if (query == null) {
+            Long loaded = cache.getCurrentRevision(cacheKey);
+            RoleModel model = getDelegate().getRealmRole(realm, name);
+            if (model == null) return null;
+            query = new RoleListQuery(loaded, cacheKey, realm, model.getId());
+            logger.tracev("adding realm role cache miss: client {0} key {1}", realm.getName(), cacheKey);
+            cache.addRevisioned(query);
+            return model;
+        }
+        RoleModel role = getRoleById(query.getRoles().iterator().next(), realm);
+        if (role == null) {
+            invalidations.add(cacheKey);
+            return getDelegate().getRealmRole(realm, name);
+        }
+        return role;
+    }
+
+    @Override
+    public RoleModel getClientRole(RealmModel realm, ClientModel client, String name) {
+        String cacheKey = getRoleByNameCacheKey(client.getId(), name);
+        boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(client.getId());
+        if (queryDB) {
+            return getDelegate().getClientRole(realm, client, name);
+        }
+
+        RoleListQuery query = cache.get(cacheKey, RoleListQuery.class);
+        if (query != null) {
+            logger.tracev("getClientRole cache hit: {0}.{1}", client.getClientId(), name);
+        }
+
+        if (query == null) {
+            Long loaded = cache.getCurrentRevision(cacheKey);
+            RoleModel model = getDelegate().getClientRole(realm, client, name);
+            if (model == null) return null;
+            query = new RoleListQuery(loaded, cacheKey, realm, model.getId(), client.getClientId());
+            logger.tracev("adding client role cache miss: client {0} key {1}", client.getClientId(), cacheKey);
+            cache.addRevisioned(query);
+            return model;
+        }
+        RoleModel role = getRoleById(query.getRoles().iterator().next(), realm);
+        if (role == null) {
+            invalidations.add(cacheKey);
+            return getDelegate().getClientRole(realm, client, name);
+        }
+        return role;
+    }
+
+    @Override
+    public boolean removeRole(RealmModel realm, RoleModel role) {
+        invalidations.add(getRolesCacheKey(role.getContainer().getId()));
+        invalidations.add(getRoleByNameCacheKey(role.getContainer().getId(), role.getName()));
+        listInvalidations.add(role.getContainer().getId());
+        invalidations.add(role.getId());
+        return getDelegate().removeRole(realm, role);
+    }
+
+    @Override
     public RoleModel getRoleById(String id, RealmModel realm) {
         CachedRole cached = cache.get(id, CachedRole.class);
         if (cached != null && !cached.getRealm().equals(realm.getId())) {
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/StreamRealmCache.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/StreamRealmCache.java
index 027793e..452eee4 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/StreamRealmCache.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/StreamRealmCache.java
@@ -37,6 +37,7 @@ import org.keycloak.models.cache.infinispan.stream.HasRolePredicate;
 import org.keycloak.models.cache.infinispan.stream.InClientPredicate;
 import org.keycloak.models.cache.infinispan.stream.InRealmPredicate;
 import org.keycloak.models.cache.infinispan.stream.RealmQueryPredicate;
+import org.keycloak.models.cache.infinispan.stream.RoleQueryPredicate;
 
 import java.util.HashSet;
 import java.util.Iterator;
@@ -287,9 +288,6 @@ public class StreamRealmCache {
         } else if (object instanceof CachedClient) {
             CachedClient cached = (CachedClient)object;
             Predicate<Map.Entry<String, Revisioned>> predicate = getClientRemovalPredicate(cached.getRealm(), cached.getId());
-            for (String roleId : cached.getRoles().values()) {
-                predicate.or(getRoleRemovalPredicate(roleId));
-            }
             return predicate;
         } else if (object instanceof CachedRole) {
             CachedRole cached = (CachedRole)object;
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
index 8affd11..115d4dd 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
@@ -612,31 +612,17 @@ public class ClientAdapter implements ClientModel {
 
     @Override
     public RoleModel getRole(String name) {
-        TypedQuery<String> query = em.createNamedQuery("getClientRoleIdByName", String.class);
-        query.setParameter("name", name);
-        query.setParameter("client", entity.getId());
-        List<String> roles = query.getResultList();
-        if (roles.size() == 0) return null;
-        return session.realms().getRoleById(roles.get(0), realm);
+        return session.realms().getClientRole(realm, this, name);
     }
 
     @Override
     public RoleModel addRole(String name) {
-        return this.addRole(KeycloakModelUtils.generateId(), name);
+        return session.realms().addClientRole(realm, this, name);
     }
 
     @Override
     public RoleModel addRole(String id, String name) {
-        RoleEntity roleEntity = new RoleEntity();
-        roleEntity.setId(id);
-        roleEntity.setName(name);
-        roleEntity.setClient(entity);
-        roleEntity.setClientRole(true);
-        roleEntity.setRealmId(realm.getId());
-        entity.getRoles().add(roleEntity);
-        em.persist(roleEntity);
-        em.flush();
-        return new RoleAdapter(session, realm, em, roleEntity);
+        return session.realms().addClientRole(realm, this, id, name);
     }
 
     @Override
@@ -650,7 +636,6 @@ public class ClientAdapter implements ClientModel {
         RoleEntity role = RoleAdapter.toRoleEntity(roleModel, em);
         if (!role.isClientRole()) return false;
 
-        entity.getRoles().remove(role);
         entity.getDefaultRoles().remove(role);
         String compositeRoleTable = JpaUtils.getTableNameForNativeQuery("COMPOSITE_ROLE", em);
         em.createNativeQuery("delete from " + compositeRoleTable + " where CHILD_ROLE = :role").setParameter("role", role).executeUpdate();
@@ -666,24 +651,7 @@ public class ClientAdapter implements ClientModel {
 
     @Override
     public Set<RoleModel> getRoles() {
-        Set<RoleModel> list = new HashSet<RoleModel>();
-        TypedQuery<RoleEntity> query = em.createNamedQuery("getClientRoles", RoleEntity.class);
-        query.setParameter("client", entity);
-        List<RoleEntity> roles = query.getResultList();
-        for (RoleEntity roleEntity : roles) {
-            list.add(new RoleAdapter(session, realm, em, roleEntity));
-        }
-
-        /*
-        TypedQuery<String> query = em.createNamedQuery("getClientRoleIds", String.class);
-        query.setParameter("client", entity.getId());
-        List<String> roles = query.getResultList();
-        for (String id : roles) {
-             list.add(session.realms().getRoleById(id, realm));
-        }
-        */
-        return list;
-
+        return session.realms().getClientRoles(realm, this);
     }
 
     @Override
@@ -733,10 +701,10 @@ public class ClientAdapter implements ClientModel {
     }
 
     @Override
-    public void updateDefaultRoles(String[] defaultRoles) {
+    public void updateDefaultRoles(String... defaultRoles) {
         Collection<RoleEntity> entities = entity.getDefaultRoles();
         Set<String> already = new HashSet<String>();
-        List<RoleEntity> remove = new ArrayList<RoleEntity>();
+        List<RoleEntity> remove = new ArrayList<>();
         for (RoleEntity rel : entities) {
             if (!contains(rel.getName(), defaultRoles)) {
                 remove.add(rel);
@@ -757,6 +725,24 @@ public class ClientAdapter implements ClientModel {
     }
 
     @Override
+    public void removeDefaultRoles(String... defaultRoles) {
+        Collection<RoleEntity> entities = entity.getDefaultRoles();
+        List<RoleEntity> remove = new ArrayList<RoleEntity>();
+        for (RoleEntity rel : entities) {
+            if (contains(rel.getName(), defaultRoles)) {
+                remove.add(rel);
+            }
+        }
+        for (RoleEntity entity : remove) {
+            entities.remove(entity);
+        }
+        em.flush();
+    }
+
+
+
+
+    @Override
     public int getNodeReRegistrationTimeout() {
         return entity.getNodeReRegistrationTimeout();
     }
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 fd4e0a5..fc10217 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
@@ -157,9 +157,6 @@ public class ClientEntity {
     @Column(name="NODE_REREG_TIMEOUT")
     private int nodeReRegistrationTimeout;
 
-    @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, mappedBy = "client")
-    Collection<RoleEntity> roles = new ArrayList<RoleEntity>();
-
     @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true)
     @JoinTable(name="CLIENT_DEFAULT_ROLES", joinColumns = { @JoinColumn(name="CLIENT_ID")}, inverseJoinColumns = { @JoinColumn(name="ROLE_ID")})
     Collection<RoleEntity> defaultRoles = new ArrayList<RoleEntity>();
@@ -354,14 +351,6 @@ public class ClientEntity {
         this.managementUrl = managementUrl;
     }
 
-    public Collection<RoleEntity> getRoles() {
-        return roles;
-    }
-
-    public void setRoles(Collection<RoleEntity> roles) {
-        this.roles = roles;
-    }
-
     public Collection<RoleEntity> getDefaultRoles() {
         return defaultRoles;
     }
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 f33aa21..052ce6e 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
@@ -150,9 +150,6 @@ public class RealmEntity {
     @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
     Collection<ClientTemplateEntity> clientTemplates = new ArrayList<>();
 
-    @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, mappedBy = "realm")
-    Collection<RoleEntity> roles = new ArrayList<RoleEntity>();
-
     @ElementCollection
     @MapKeyColumn(name="NAME")
     @Column(name="VALUE")
@@ -418,21 +415,6 @@ public class RealmEntity {
     public void setRequiredCredentials(Collection<RequiredCredentialEntity> requiredCredentials) {
         this.requiredCredentials = requiredCredentials;
     }
-    public Collection<RoleEntity> getRoles() {
-        return roles;
-    }
-
-    public void setRoles(Collection<RoleEntity> roles) {
-        this.roles = roles;
-    }
-
-    public void addRole(RoleEntity role) {
-        if (roles == null) {
-            roles = new ArrayList<RoleEntity>();
-        }
-        roles.add(role);
-    }
-
     public Map<String, String> getSmtpConfig() {
         return smtpConfig;
     }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java
index 5c02336..3d1cae4 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java
@@ -52,6 +52,8 @@ import java.util.Collection;
         @NamedQuery(name="getClientRoleIds", query="select role.id from RoleEntity role where role.client.id = :client"),
         @NamedQuery(name="getClientRoleByName", query="select role from RoleEntity role where role.name = :name and role.client = :client"),
         @NamedQuery(name="getClientRoleIdByName", query="select role.id from RoleEntity role where role.name = :name and role.client.id = :client"),
+        @NamedQuery(name="getRealmRoles", query="select role from RoleEntity role where role.clientRole = false and role.realm = :realm"),
+        @NamedQuery(name="getRealmRoleIds", query="select role.id from RoleEntity role where role.clientRole = false and role.realm.id = :realm"),
         @NamedQuery(name="getRealmRoleByName", query="select role from RoleEntity role where role.clientRole = false and role.name = :name and role.realm = :realm"),
         @NamedQuery(name="getRealmRoleIdByName", query="select role.id from RoleEntity role where role.clientRole = false and role.name = :name and role.realm.id = :realm")
 })
@@ -108,6 +110,8 @@ public class RoleEntity {
         this.realmId = realmId;
     }
 
+
+
     public String getName() {
         return name;
     }
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 6c2ae6b..6080d72 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
@@ -18,6 +18,7 @@
 package org.keycloak.models.jpa;
 
 import org.jboss.logging.Logger;
+import org.keycloak.connections.jpa.util.JpaUtils;
 import org.keycloak.migration.MigrationModel;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.ClientTemplateModel;
@@ -25,6 +26,7 @@ import org.keycloak.models.GroupModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RealmProvider;
+import org.keycloak.models.RoleContainerModel;
 import org.keycloak.models.RoleModel;
 import org.keycloak.models.jpa.entities.ClientEntity;
 import org.keycloak.models.jpa.entities.ClientTemplateEntity;
@@ -37,8 +39,10 @@ import javax.persistence.EntityManager;
 import javax.persistence.TypedQuery;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Set;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -135,15 +139,15 @@ public class JpaRealmProvider implements RealmProvider {
         for (String client : clients) {
             session.realms().removeClient(client, adapter);
         }
-        /*
-        for (ClientEntity a : new LinkedList<>(realm.getClients())) {
-            adapter.removeClient(a.getId());
-        }
-        */
+
         for (ClientTemplateEntity a : new LinkedList<>(realm.getClientTemplates())) {
             adapter.removeClientTemplate(a.getId());
         }
 
+        for (RoleModel role : adapter.getRoles()) {
+            session.realms().removeRole(adapter, role);
+        }
+
         em.remove(realm);
 
         em.flush();
@@ -156,6 +160,112 @@ public class JpaRealmProvider implements RealmProvider {
     }
 
     @Override
+    public RoleModel addRealmRole(RealmModel realm, String name) {
+        return addRealmRole(realm, KeycloakModelUtils.generateId(), name);
+
+    }
+    @Override
+    public RoleModel addRealmRole(RealmModel realm, String id, String name) {
+        RoleEntity entity = new RoleEntity();
+        entity.setId(id);
+        entity.setName(name);
+        RealmEntity ref = em.getReference(RealmEntity.class, realm.getId());
+        entity.setRealm(ref);
+        entity.setRealmId(realm.getId());
+        em.persist(entity);
+        em.flush();
+        return new RoleAdapter(session, realm, em, entity);
+
+    }
+
+    @Override
+    public RoleModel getRealmRole(RealmModel realm, String name) {
+        TypedQuery<String> query = em.createNamedQuery("getRealmRoleIdByName", String.class);
+        query.setParameter("name", name);
+        query.setParameter("realm", realm.getId());
+        List<String> roles = query.getResultList();
+        if (roles.size() == 0) return null;
+        return session.realms().getRoleById(roles.get(0), realm);
+    }
+
+    @Override
+    public RoleModel addClientRole(RealmModel realm, ClientModel client, String name) {
+        return addClientRole(realm, client, KeycloakModelUtils.generateId(), name);
+    }
+    @Override
+    public RoleModel addClientRole(RealmModel realm, ClientModel client, String id, String name) {
+        ClientEntity clientEntity = em.getReference(ClientEntity.class, client.getId());
+        RoleEntity roleEntity = new RoleEntity();
+        roleEntity.setId(id);
+        roleEntity.setName(name);
+        roleEntity.setClient(clientEntity);
+        roleEntity.setClientRole(true);
+        roleEntity.setRealmId(realm.getId());
+        em.persist(roleEntity);
+        em.flush();
+        return new RoleAdapter(session, realm, em, roleEntity);
+    }
+
+    @Override
+    public Set<RoleModel> getRealmRoles(RealmModel realm) {
+        TypedQuery<String> query = em.createNamedQuery("getRealmRoleIds", String.class);
+        query.setParameter("realm", realm.getId());
+        List<String> roles = query.getResultList();
+
+        if (roles.isEmpty()) return Collections.EMPTY_SET;
+        Set<RoleModel> list = new HashSet<RoleModel>();
+        for (String id : roles) {
+            list.add(session.realms().getRoleById(id, realm));
+        }
+        return Collections.unmodifiableSet(list);
+    }
+
+    @Override
+    public RoleModel getClientRole(RealmModel realm, ClientModel client, String name) {
+        TypedQuery<String> query = em.createNamedQuery("getClientRoleIdByName", String.class);
+        query.setParameter("name", name);
+        query.setParameter("client", client.getId());
+        List<String> roles = query.getResultList();
+        if (roles.size() == 0) return null;
+        return session.realms().getRoleById(roles.get(0), realm);
+    }
+
+
+    @Override
+    public Set<RoleModel> getClientRoles(RealmModel realm, ClientModel client) {
+        Set<RoleModel> list = new HashSet<RoleModel>();
+        TypedQuery<String> query = em.createNamedQuery("getClientRoleIds", String.class);
+        query.setParameter("client", client.getId());
+        List<String> roles = query.getResultList();
+        for (String id : roles) {
+            list.add(session.realms().getRoleById(id, realm));
+        }
+        return list;
+
+    }
+
+    @Override
+    public boolean removeRole(RealmModel realm, RoleModel role) {
+        session.users().preRemove(realm, role);
+        RoleEntity roleEntity = em.getReference(RoleEntity.class, role.getId());
+        RoleContainerModel container = role.getContainer();
+        if (container.getDefaultRoles().contains(role.getName())) {
+            container.removeDefaultRoles(role.getName());
+        }
+        String compositeRoleTable = JpaUtils.getTableNameForNativeQuery("COMPOSITE_ROLE", em);
+        em.createNativeQuery("delete from " + compositeRoleTable + " where CHILD_ROLE = :role").setParameter("role", roleEntity).executeUpdate();
+        em.createNamedQuery("deleteScopeMappingByRole").setParameter("role", roleEntity).executeUpdate();
+        em.createNamedQuery("deleteTemplateScopeMappingByRole").setParameter("role", roleEntity).executeUpdate();
+        em.createNamedQuery("deleteGroupRoleMappingsByRole").setParameter("roleId", roleEntity.getId()).executeUpdate();
+
+        em.remove(roleEntity);
+        em.flush();
+
+        return true;
+
+    }
+
+    @Override
     public RoleModel getRoleById(String id, RealmModel realm) {
         RoleEntity entity = em.find(RoleEntity.class, id);
         if (entity == null) return null;
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 ad2024b..16eb142 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
@@ -683,6 +683,21 @@ public class RealmAdapter implements RealmModel {
     }
 
     @Override
+    public void removeDefaultRoles(String... defaultRoles) {
+        Collection<RoleEntity> entities = realm.getDefaultRoles();
+        List<RoleEntity> remove = new ArrayList<RoleEntity>();
+        for (RoleEntity rel : entities) {
+            if (contains(rel.getName(), defaultRoles)) {
+                remove.add(rel);
+            }
+        }
+        for (RoleEntity entity : remove) {
+            entities.remove(entity);
+        }
+        em.flush();
+     }
+
+    @Override
     public List<GroupModel> getDefaultGroups() {
         Collection<GroupEntity> entities = realm.getDefaultGroups();
         if (entities == null || entities.isEmpty()) return Collections.EMPTY_LIST;
@@ -980,66 +995,27 @@ public class RealmAdapter implements RealmModel {
 
     @Override
     public RoleModel getRole(String name) {
-        TypedQuery<String> query = em.createNamedQuery("getRealmRoleIdByName", String.class);
-        query.setParameter("name", name);
-        query.setParameter("realm", realm.getId());
-        List<String> roles = query.getResultList();
-        if (roles.size() == 0) return null;
-        return session.realms().getRoleById(roles.get(0), this);
+        return session.realms().getRealmRole(this, name);
     }
 
     @Override
     public RoleModel addRole(String name) {
-        return this.addRole(KeycloakModelUtils.generateId(), name);
+        return session.realms().addRealmRole(this, name);
     }
 
     @Override
     public RoleModel addRole(String id, String name) {
-        RoleEntity entity = new RoleEntity();
-        entity.setId(id);
-        entity.setName(name);
-        entity.setRealm(realm);
-        entity.setRealmId(realm.getId());
-        realm.getRoles().add(entity);
-        em.persist(entity);
-        em.flush();
-        return new RoleAdapter(session, this, em, entity);
+        return session.realms().addRealmRole(this, id, name);
     }
 
     @Override
     public boolean removeRole(RoleModel role) {
-        if (role == null) {
-            return false;
-        }
-        if (!role.getContainer().equals(this)) return false;
-        session.users().preRemove(this, role);
-        RoleEntity roleEntity = RoleAdapter.toRoleEntity(role, em);
-        realm.getRoles().remove(roleEntity);
-        realm.getDefaultRoles().remove(roleEntity);
-
-        String compositeRoleTable = JpaUtils.getTableNameForNativeQuery("COMPOSITE_ROLE", em);
-        em.createNativeQuery("delete from " + compositeRoleTable + " where CHILD_ROLE = :role").setParameter("role", roleEntity).executeUpdate();
-        em.createNamedQuery("deleteScopeMappingByRole").setParameter("role", roleEntity).executeUpdate();
-        em.createNamedQuery("deleteTemplateScopeMappingByRole").setParameter("role", roleEntity).executeUpdate();
-        em.createNamedQuery("deleteGroupRoleMappingsByRole").setParameter("roleId", roleEntity.getId()).executeUpdate();
-
-        em.remove(roleEntity);
-        em.flush();
-
-        return true;
+        return session.realms().removeRole(this, role);
     }
 
     @Override
     public Set<RoleModel> getRoles() {
-        Collection<RoleEntity> roles = realm.getRoles();
-        if (roles == null) return Collections.EMPTY_SET;
-        Set<RoleModel> list = new HashSet<RoleModel>();
-        for (RoleEntity entity : roles) {
-            list.add(new RoleAdapter(session, this, em, entity));
-            // can't get it from cache cuz of stack overflow
-            // list.add(session.realms().getRoleById(entity.getId(), this));
-        }
-        return Collections.unmodifiableSet(list);
+        return session.realms().getRealmRoles(this);
     }
 
     @Override
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java
index 91c9308..ff18fdf 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java
@@ -567,34 +567,18 @@ public class ClientAdapter extends AbstractMongoAdapter<MongoClientEntity> imple
     }
 
     @Override
-    public RoleAdapter getRole(String name) {
-        DBObject query = new QueryBuilder()
-                .and("name").is(name)
-                .and("clientId").is(getId())
-                .get();
-        MongoRoleEntity role = getMongoStore().loadSingleEntity(MongoRoleEntity.class, query, invocationContext);
-        if (role == null) {
-            return null;
-        } else {
-            return new RoleAdapter(session, getRealm(), role, invocationContext);
-        }
+    public RoleModel getRole(String name) {
+        return session.realms().getClientRole(realm, this, name);
     }
 
     @Override
-    public RoleAdapter addRole(String name) {
-        return this.addRole(null, name);
+    public RoleModel addRole(String name) {
+        return session.realms().addClientRole(realm, this, name);
     }
 
     @Override
-    public RoleAdapter addRole(String id, String name) {
-        MongoRoleEntity roleEntity = new MongoRoleEntity();
-        roleEntity.setId(id);
-        roleEntity.setName(name);
-        roleEntity.setClientId(getId());
-
-        getMongoStore().insertEntity(roleEntity, invocationContext);
-
-        return new RoleAdapter(session, getRealm(), roleEntity, this, invocationContext);
+    public RoleModel addRole(String id, String name) {
+        return session.realms().addClientRole(realm, this, id, name);
     }
 
     @Override
@@ -605,17 +589,7 @@ public class ClientAdapter extends AbstractMongoAdapter<MongoClientEntity> imple
 
     @Override
     public Set<RoleModel> getRoles() {
-        DBObject query = new QueryBuilder()
-                .and("clientId").is(getId())
-                .get();
-        List<MongoRoleEntity> roles = getMongoStore().loadEntities(MongoRoleEntity.class, query, invocationContext);
-
-        Set<RoleModel> result = new HashSet<RoleModel>();
-        for (MongoRoleEntity role : roles) {
-            result.add(new RoleAdapter(session, getRealm(), role, this, invocationContext));
-        }
-
-        return result;
+        return session.realms().getClientRoles(realm, this);
     }
 
     @Override
@@ -653,7 +627,7 @@ public class ClientAdapter extends AbstractMongoAdapter<MongoClientEntity> imple
     }
 
     @Override
-    public void updateDefaultRoles(String[] defaultRoles) {
+    public void updateDefaultRoles(String... defaultRoles) {
         List<String> roleNames = new ArrayList<String>();
         for (String roleName : defaultRoles) {
             RoleModel role = getRole(roleName);
@@ -669,6 +643,17 @@ public class ClientAdapter extends AbstractMongoAdapter<MongoClientEntity> imple
     }
 
     @Override
+    public void removeDefaultRoles(String... defaultRoles) {
+        List<String> roleNames = new ArrayList<String>();
+        for (String role : getMongoEntity().getDefaultRoles()) {
+            if (!RealmAdapter.contains(role, defaultRoles)) roleNames.add(role);
+        }
+        getMongoEntity().setDefaultRoles(roleNames);
+        updateMongoEntity();
+    }
+
+
+    @Override
     public int getNodeReRegistrationTimeout() {
         return getMongoEntity().getNodeReRegistrationTimeout();
     }
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 1c4036b..f8c9b79 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
@@ -40,7 +40,9 @@ import org.keycloak.models.utils.KeycloakModelUtils;
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -210,6 +212,107 @@ public class MongoRealmProvider implements RealmProvider {
     }
 
     @Override
+    public RoleModel addRealmRole(RealmModel realm, String name) {
+        return addRealmRole(realm, KeycloakModelUtils.generateId(), name);
+    }
+
+    @Override
+    public RoleModel addRealmRole(RealmModel realm, String id, String name) {
+        MongoRoleEntity roleEntity = new MongoRoleEntity();
+        roleEntity.setId(id);
+        roleEntity.setName(name);
+        roleEntity.setRealmId(realm.getId());
+
+        getMongoStore().insertEntity(roleEntity, invocationContext);
+
+        return new RoleAdapter(session, realm, roleEntity, realm, invocationContext);
+    }
+
+    @Override
+    public Set<RoleModel> getRealmRoles(RealmModel realm) {
+        DBObject query = new QueryBuilder()
+                .and("realmId").is(realm.getId())
+                .get();
+        List<MongoRoleEntity> roles = getMongoStore().loadEntities(MongoRoleEntity.class, query, invocationContext);
+
+
+        if (roles == null) return Collections.EMPTY_SET;
+        Set<RoleModel> result = new HashSet<RoleModel>();
+        for (MongoRoleEntity role : roles) {
+            result.add(session.realms().getRoleById(role.getId(), realm));
+        }
+
+        return Collections.unmodifiableSet(result);
+
+    }
+
+    @Override
+    public Set<RoleModel> getClientRoles(RealmModel realm, ClientModel client) {
+        DBObject query = new QueryBuilder()
+                .and("clientId").is(client.getId())
+                .get();
+        List<MongoRoleEntity> roles = getMongoStore().loadEntities(MongoRoleEntity.class, query, invocationContext);
+
+        Set<RoleModel> result = new HashSet<RoleModel>();
+        for (MongoRoleEntity role : roles) {
+            result.add(session.realms().getRoleById(role.getId(), realm));
+        }
+
+        return result;
+    }
+
+    @Override
+    public RoleModel getRealmRole(RealmModel realm, String name) {
+        DBObject query = new QueryBuilder()
+                .and("name").is(name)
+                .and("realmId").is(realm.getId())
+                .get();
+        MongoRoleEntity role = getMongoStore().loadSingleEntity(MongoRoleEntity.class, query, invocationContext);
+        if (role == null) {
+            return null;
+        } else {
+            return session.realms().getRoleById(role.getId(), realm);
+        }
+    }
+
+    @Override
+    public RoleModel getClientRole(RealmModel realm, ClientModel client, String name) {
+        DBObject query = new QueryBuilder()
+                .and("name").is(name)
+                .and("clientId").is(client.getId())
+                .get();
+        MongoRoleEntity role = getMongoStore().loadSingleEntity(MongoRoleEntity.class, query, invocationContext);
+        if (role == null) {
+            return null;
+        } else {
+            return session.realms().getRoleById(role.getId(), realm);
+        }
+    }
+
+    @Override
+    public RoleModel addClientRole(RealmModel realm, ClientModel client, String name) {
+        return addClientRole(realm, client, KeycloakModelUtils.generateId(), name);
+    }
+
+    @Override
+    public RoleModel addClientRole(RealmModel realm, ClientModel client, String id, String name) {
+        MongoRoleEntity roleEntity = new MongoRoleEntity();
+        roleEntity.setId(id);
+        roleEntity.setName(name);
+        roleEntity.setClientId(client.getId());
+
+        getMongoStore().insertEntity(roleEntity, invocationContext);
+
+        return new RoleAdapter(session, realm, roleEntity, client, invocationContext);
+    }
+
+    @Override
+    public boolean removeRole(RealmModel realm, RoleModel role) {
+        session.users().preRemove(realm, role);
+        return getMongoStore().removeEntity(MongoRoleEntity.class, role.getId(), invocationContext);
+    }
+
+    @Override
     public boolean removeClient(String id, RealmModel realm) {
         if (id == null) return false;
         ClientModel client = getClientById(id, realm);
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
index edb57d2..6c4b6f9 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
@@ -595,47 +595,30 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
     }
 
     @Override
-    public RoleAdapter getRole(String name) {
-        DBObject query = new QueryBuilder()
-                .and("name").is(name)
-                .and("realmId").is(getId())
-                .get();
-        MongoRoleEntity role = getMongoStore().loadSingleEntity(MongoRoleEntity.class, query, invocationContext);
-        if (role == null) {
-            return null;
-        } else {
-            return new RoleAdapter(session, this, role, this, invocationContext);
-        }
+    public RoleModel getRole(String name) {
+        return session.realms().getRealmRole(this, name);
     }
 
     @Override
     public RoleModel addRole(String name) {
-        return this.addRole(null, name);
+        return session.realms().addRealmRole(this, name);
     }
 
     @Override
     public RoleModel addRole(String id, String name) {
-        MongoRoleEntity roleEntity = new MongoRoleEntity();
-        roleEntity.setId(id);
-        roleEntity.setName(name);
-        roleEntity.setRealmId(getId());
-
-        getMongoStore().insertEntity(roleEntity, invocationContext);
-
-        return new RoleAdapter(session, this, roleEntity, this, invocationContext);
+        return session.realms().addRealmRole(this, id, name);
     }
 
     @Override
     public boolean removeRole(RoleModel role) {
-        return removeRoleById(role.getId());
+        return session.realms().removeRole(this, role);
     }
 
     @Override
     public boolean removeRoleById(String id) {
         RoleModel role = getRoleById(id);
         if (role == null) return false;
-        session.users().preRemove(this, role);
-        return getMongoStore().removeEntity(MongoRoleEntity.class, id, invocationContext);
+        return removeRole(role);
     }
 
     @Override
@@ -657,7 +640,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
 
     @Override
     public RoleModel getRoleById(String id) {
-        return model.getRoleById(id, this);
+        return session.realms().getRoleById(id, this);
     }
 
     @Override
@@ -780,6 +763,24 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
         updateRealm();
     }
 
+    public static boolean contains(String str, String[] array) {
+        for (String s : array) {
+            if (str.equals(s)) return true;
+        }
+        return false;
+    }
+
+
+    @Override
+    public void removeDefaultRoles(String... defaultRoles) {
+        List<String> roleNames = new ArrayList<String>();
+        for (String role : realm.getDefaultRoles()) {
+            if (!contains(role, defaultRoles)) roleNames.add(role);
+        }
+        realm.setDefaultRoles(roleNames);
+        updateRealm();
+    }
+
     @Override
     public List<GroupModel> getDefaultGroups() {
         List<String> entities = realm.getDefaultGroups();
diff --git a/server-spi/src/main/java/org/keycloak/models/ClientModel.java b/server-spi/src/main/java/org/keycloak/models/ClientModel.java
index 8399969..6d96eb0 100755
--- a/server-spi/src/main/java/org/keycloak/models/ClientModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/ClientModel.java
@@ -85,12 +85,6 @@ public interface ClientModel extends RoleContainerModel,  ProtocolMapperContaine
 
     void setBaseUrl(String url);
 
-    List<String> getDefaultRoles();
-
-    void addDefaultRole(String name);
-
-    void updateDefaultRoles(String[] defaultRoles);
-
 
     boolean isBearerOnly();
     void setBearerOnly(boolean only);
diff --git a/server-spi/src/main/java/org/keycloak/models/RealmModel.java b/server-spi/src/main/java/org/keycloak/models/RealmModel.java
index bc7dec5..1c42e45 100755
--- a/server-spi/src/main/java/org/keycloak/models/RealmModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/RealmModel.java
@@ -181,12 +181,6 @@ public interface RealmModel extends RoleContainerModel {
 
     RoleModel getRoleById(String id);
 
-    List<String> getDefaultRoles();
-
-    void addDefaultRole(String name);
-
-    void updateDefaultRoles(String[] defaultRoles);
-
     List<GroupModel> getDefaultGroups();
 
     void addDefaultGroup(GroupModel group);
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 a101ce4..e7584e7 100755
--- a/server-spi/src/main/java/org/keycloak/models/RealmProvider.java
+++ b/server-spi/src/main/java/org/keycloak/models/RealmProvider.java
@@ -21,6 +21,7 @@ import org.keycloak.migration.MigrationModel;
 import org.keycloak.provider.Provider;
 
 import java.util.List;
+import java.util.Set;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -45,6 +46,24 @@ public interface RealmProvider extends Provider {
     ClientModel getClientByClientId(String clientId, RealmModel realm);
 
 
+    RoleModel addRealmRole(RealmModel realm, String name);
+
+    RoleModel addRealmRole(RealmModel realm, String id, String name);
+
+    RoleModel getRealmRole(RealmModel realm, String name);
+
+    RoleModel addClientRole(RealmModel realm, ClientModel client, String name);
+
+    RoleModel addClientRole(RealmModel realm, ClientModel client, String id, String name);
+
+    Set<RoleModel> getRealmRoles(RealmModel realm);
+
+    RoleModel getClientRole(RealmModel realm, ClientModel client, String name);
+
+    Set<RoleModel> getClientRoles(RealmModel realm, ClientModel client);
+
+    boolean removeRole(RealmModel realm, RoleModel role);
+
     RoleModel getRoleById(String id, RealmModel realm);
 
     boolean removeClient(String id, RealmModel realm);
diff --git a/server-spi/src/main/java/org/keycloak/models/RoleContainerModel.java b/server-spi/src/main/java/org/keycloak/models/RoleContainerModel.java
index f405821..24c60b3 100755
--- a/server-spi/src/main/java/org/keycloak/models/RoleContainerModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/RoleContainerModel.java
@@ -17,6 +17,7 @@
 
 package org.keycloak.models;
 
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -24,6 +25,8 @@ import java.util.Set;
  * @version $Revision: 1 $
  */
 public interface RoleContainerModel {
+    String getId();
+
     RoleModel getRole(String name);
 
     RoleModel addRole(String name);
@@ -34,4 +37,11 @@ public interface RoleContainerModel {
 
     Set<RoleModel> getRoles();
 
+    List<String> getDefaultRoles();
+
+    void addDefaultRole(String name);
+
+    void updateDefaultRoles(String... defaultRoles);
+
+    void removeDefaultRoles(String... defaultRoles);
 }