keycloak-memoizeit

group list caching

2/24/2016 7:21:11 PM

Details

diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/GroupListQuery.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/GroupListQuery.java
new file mode 100755
index 0000000..b4cb353
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/GroupListQuery.java
@@ -0,0 +1,44 @@
+package org.keycloak.models.cache.infinispan;
+
+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.GroupQuery;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class GroupListQuery extends AbstractRevisioned implements GroupQuery {
+    private final Set<String> groups;
+    private final String realm;
+    private final String realmName;
+
+    public GroupListQuery(Long revisioned, String id, RealmModel realm, Set<String> groups) {
+        super(revisioned, id);
+        this.realm = realm.getId();
+        this.realmName = realm.getName();
+        this.groups = groups;
+    }
+
+    @Override
+    public Set<String> getGroups() {
+        return groups;
+    }
+
+    @Override
+    public String getRealm() {
+        return realm;
+    }
+
+    @Override
+    public String toString() {
+        return "GroupListQuery{" +
+                "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 030286b..837010c 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
@@ -1304,66 +1304,44 @@ public class RealmAdapter implements RealmModel {
     }
 
     @Override
-    public GroupModel getGroupById(String id) {
-        if (updated != null) return updated.getGroupById(id);
-        return cacheSession.getGroupById(id, this);
+    public GroupModel createGroup(String name) {
+        return cacheSession.createGroup(this, name);
     }
 
     @Override
-    public List<GroupModel> getGroups() {
-        if (updated != null) return updated.getGroups();
-        if (cached.getGroups().isEmpty()) return Collections.EMPTY_LIST;
-        List<GroupModel> list = new LinkedList<>();
-        for (String id : cached.getGroups()) {
-            GroupModel group = cacheSession.getGroupById(id, this);
-            if (group == null) continue;
-            list.add(group);
-        }
-        return Collections.unmodifiableList(list);
+    public GroupModel createGroup(String id, String name) {
+        return cacheSession.createGroup(this, id, name);
     }
 
     @Override
-    public List<GroupModel> getTopLevelGroups() {
-        List<GroupModel> base = getGroups();
-        if (base.isEmpty()) return base;
-        List<GroupModel> copy = new LinkedList<>();
-        for (GroupModel group : base) {
-            if (group.getParent() == null) {
-                copy.add(group);
-            }
-        }
-        return Collections.unmodifiableList(copy);
+    public void addTopLevelGroup(GroupModel subGroup) {
+        cacheSession.addTopLevelGroup(this, subGroup);
+
     }
 
     @Override
-    public boolean removeGroup(GroupModel group) {
-        getDelegateForUpdate();
-        return updated.removeGroup(group);
+    public void moveGroup(GroupModel group, GroupModel toParent) {
+        cacheSession.moveGroup(this, group, toParent);
     }
 
     @Override
-    public GroupModel createGroup(String name) {
-        getDelegateForUpdate();
-        return updated.createGroup(name);
+    public GroupModel getGroupById(String id) {
+        return cacheSession.getGroupById(id, this);
     }
 
     @Override
-    public GroupModel createGroup(String id, String name) {
-        getDelegateForUpdate();
-        return updated.createGroup(id, name);
+    public List<GroupModel> getGroups() {
+        return cacheSession.getGroups(this);
     }
 
     @Override
-    public void addTopLevelGroup(GroupModel subGroup) {
-        getDelegateForUpdate();
-        updated.addTopLevelGroup(subGroup);
-
+    public List<GroupModel> getTopLevelGroups() {
+        return cacheSession.getTopLevelGroups(this);
     }
 
     @Override
-    public void moveGroup(GroupModel group, GroupModel toParent) {
-        getDelegateForUpdate();
-        updated.moveGroup(group, toParent);
+    public boolean removeGroup(GroupModel group) {
+        return cacheSession.removeGroup(this, group);
     }
 
     @Override
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 21c0c7d..2432cc6 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
@@ -397,6 +397,14 @@ public class StreamCacheRealmProvider implements CacheRealmProvider {
         return realm + REALM_CLIENTS_QUERY_SUFFIX;
     }
 
+    private String getGroupsQueryCacheKey(String realm) {
+        return realm + ".groups";
+    }
+
+    private String getTopGroupsQueryCacheKey(String realm) {
+        return realm + ".top.groups";
+    }
+
     private String getRolesCacheKey(String container) {
         return container + ROLES_QUERY_SUFFIX;
     }
@@ -688,6 +696,129 @@ public class StreamCacheRealmProvider implements CacheRealmProvider {
     }
 
     @Override
+    public void moveGroup(RealmModel realm, GroupModel group, GroupModel toParent) {
+        registerGroupInvalidation(group.getId());
+        if (toParent != null) registerGroupInvalidation(toParent.getId());
+        getDelegate().moveGroup(realm, group, toParent);
+    }
+
+    @Override
+    public List<GroupModel> getGroups(RealmModel realm) {
+        String cacheKey = getGroupsQueryCacheKey(realm.getId());
+        boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(realm.getId());
+        if (queryDB) {
+            return getDelegate().getGroups(realm);
+        }
+
+        GroupListQuery query = cache.get(cacheKey, GroupListQuery.class);
+        if (query != null) {
+            logger.tracev("getGroups cache hit: {0}", realm.getName());
+        }
+
+        if (query == null) {
+            Long loaded = cache.getCurrentRevision(cacheKey);
+            List<GroupModel> model = getDelegate().getGroups(realm);
+            if (model == null) return null;
+            Set<String> ids = new HashSet<>();
+            for (GroupModel client : model) ids.add(client.getId());
+            query = new GroupListQuery(loaded, cacheKey, realm, ids);
+            logger.tracev("adding realm getGroups cache miss: realm {0} key {1}", realm.getName(), cacheKey);
+            cache.addRevisioned(query);
+            return model;
+        }
+        List<GroupModel> list = new LinkedList<>();
+        for (String id : query.getGroups()) {
+            GroupModel group = session.realms().getGroupById(id, realm);
+            if (group == null) {
+                invalidations.add(cacheKey);
+                return getDelegate().getGroups(realm);
+            }
+            list.add(group);
+        }
+        return list;
+    }
+
+    @Override
+    public List<GroupModel> getTopLevelGroups(RealmModel realm) {
+        String cacheKey = getTopGroupsQueryCacheKey(realm.getId());
+        boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(realm.getId());
+        if (queryDB) {
+            return getDelegate().getTopLevelGroups(realm);
+        }
+
+        GroupListQuery query = cache.get(cacheKey, GroupListQuery.class);
+        if (query != null) {
+            logger.tracev("getTopLevelGroups cache hit: {0}", realm.getName());
+        }
+
+        if (query == null) {
+            Long loaded = cache.getCurrentRevision(cacheKey);
+            List<GroupModel> model = getDelegate().getTopLevelGroups(realm);
+            if (model == null) return null;
+            Set<String> ids = new HashSet<>();
+            for (GroupModel client : model) ids.add(client.getId());
+            query = new GroupListQuery(loaded, cacheKey, realm, ids);
+            logger.tracev("adding realm getTopLevelGroups cache miss: realm {0} key {1}", realm.getName(), cacheKey);
+            cache.addRevisioned(query);
+            return model;
+        }
+        List<GroupModel> list = new LinkedList<>();
+        for (String id : query.getGroups()) {
+            GroupModel group = session.realms().getGroupById(id, realm);
+            if (group == null) {
+                invalidations.add(cacheKey);
+                return getDelegate().getTopLevelGroups(realm);
+            }
+            list.add(group);
+        }
+        return list;
+    }
+
+    @Override
+    public boolean removeGroup(RealmModel realm, GroupModel group) {
+        registerGroupInvalidation(group.getId());
+        listInvalidations.add(realm.getId());
+        invalidations.add(getGroupsQueryCacheKey(realm.getId()));
+        if (group.getParentId() == null) {
+            invalidations.add(getTopGroupsQueryCacheKey(realm.getId()));
+        } else {
+            registerGroupInvalidation(group.getParentId());
+        }
+        return getDelegate().removeGroup(realm, group);
+    }
+
+    @Override
+    public GroupModel createGroup(RealmModel realm, String name) {
+        GroupModel group = getDelegate().createGroup(realm, name);
+        return groupAdded(realm, group);
+    }
+
+    public GroupModel groupAdded(RealmModel realm, GroupModel group) {
+        listInvalidations.add(realm.getId());
+        invalidations.add(getGroupsQueryCacheKey(realm.getId()));
+        invalidations.add(getTopGroupsQueryCacheKey(realm.getId()));
+        invalidations.add(group.getId());
+        return group;
+    }
+
+    @Override
+    public GroupModel createGroup(RealmModel realm, String id, String name) {
+        GroupModel group = getDelegate().createGroup(realm, id, name);
+        return groupAdded(realm, group);
+    }
+
+    @Override
+    public void addTopLevelGroup(RealmModel realm, GroupModel subGroup) {
+        invalidations.add(getTopGroupsQueryCacheKey(realm.getId()));
+        invalidations.add(subGroup.getId());
+        if (subGroup.getParentId() != null) {
+            registerGroupInvalidation(subGroup.getParentId());
+        }
+        getDelegate().addTopLevelGroup(realm, subGroup);
+
+    }
+
+    @Override
     public ClientModel getClientById(String id, RealmModel realm) {
         CachedClient cached = cache.get(id, CachedClient.class);
         if (cached != null && !cached.getRealm().equals(realm.getId())) {
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/GroupEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/GroupEntity.java
index 0b89c87..6f4c1ec 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/GroupEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/GroupEntity.java
@@ -42,6 +42,7 @@ import java.util.Collection;
         @NamedQuery(name="getAllGroupIdsByRealm", query="select u.id from GroupEntity u where u.realm.id = :realm order by u.name"),
         @NamedQuery(name="getGroupById", query="select u from GroupEntity u where u.id = :id and u.realm = :realm"),
         @NamedQuery(name="getGroupIdsByParent", query="select u.id from GroupEntity u where u.parent = :parent"),
+        @NamedQuery(name="getTopLevelGroupIds", query="select u.id from GroupEntity u where u.parent is null and u.realm.id = :realm"),
         @NamedQuery(name="getGroupCount", query="select count(u) from GroupEntity u where u.realm = :realm"),
         @NamedQuery(name="deleteGroupsByRealm", query="delete from GroupEntity u where u.realm = :realm")
 })
@@ -64,7 +65,9 @@ public class GroupEntity {
     @JoinColumn(name = "REALM_ID")
     private RealmEntity realm;
 
-    @OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true, mappedBy="group")
+    @OneToMany(
+            cascade = CascadeType.REMOVE,
+            orphanRemoval = true, mappedBy="group")
     protected Collection<GroupAttributeEntity> attributes = new ArrayList<GroupAttributeEntity>();
 
     public String getId() {
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 6080d72..635521e 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
@@ -282,6 +282,96 @@ public class JpaRealmProvider implements RealmProvider {
     }
 
     @Override
+    public void moveGroup(RealmModel realm, GroupModel group, GroupModel toParent) {
+        if (toParent != null && group.getId().equals(toParent.getId())) {
+            return;
+        }
+        if (group.getParentId() != null) {
+            group.getParent().removeChild(group);
+        }
+        group.setParent(toParent);
+        if (toParent != null) toParent.addChild(group);
+        else session.realms().addTopLevelGroup(realm, group);
+    }
+
+    @Override
+    public List<GroupModel> getGroups(RealmModel realm) {
+        List<String> groups =  em.createNamedQuery("getAllGroupIdsByRealm", String.class)
+                .setParameter("realm", realm.getId()).getResultList();
+        if (groups == null) return Collections.EMPTY_LIST;
+        List<GroupModel> list = new LinkedList<>();
+        for (String id : groups) {
+            list.add(session.realms().getGroupById(id, realm));
+        }
+        return Collections.unmodifiableList(list);
+    }
+
+    @Override
+    public List<GroupModel> getTopLevelGroups(RealmModel realm) {
+        List<String> groups =  em.createNamedQuery("getTopLevelGroupIds", String.class)
+                .setParameter("realm", realm.getId())
+                .getResultList();
+        if (groups == null) return Collections.EMPTY_LIST;
+        List<GroupModel> list = new LinkedList<>();
+        for (String id : groups) {
+            list.add(session.realms().getGroupById(id, realm));
+        }
+        return Collections.unmodifiableList(list);
+    }
+
+    @Override
+    public boolean removeGroup(RealmModel realm, GroupModel group) {
+        if (group == null) {
+            return false;
+        }
+
+        session.users().preRemove(realm, group);
+
+        realm.removeDefaultGroup(group);
+        for (GroupModel subGroup : group.getSubGroups()) {
+            session.realms().removeGroup(realm, subGroup);
+        }
+        moveGroup(realm, group, null);
+        GroupEntity groupEntity = em.find(GroupEntity.class, group.getId());
+        if (!groupEntity.getRealm().getId().equals(realm.getId())) {
+            return false;
+        }
+        // I don't think we need this as GroupEntity has cascade removal.  It causes batch errors if you turn this on.
+        // em.createNamedQuery("deleteGroupAttributesByGroup").setParameter("group", groupEntity).executeUpdate();
+        em.createNamedQuery("deleteGroupRoleMappingsByGroup").setParameter("group", groupEntity).executeUpdate();
+        em.remove(groupEntity);
+        return true;
+
+
+    }
+
+    @Override
+    public GroupModel createGroup(RealmModel realm, String name) {
+        String id = KeycloakModelUtils.generateId();
+        return createGroup(realm, id, name);
+    }
+
+    @Override
+    public GroupModel createGroup(RealmModel realm, String id, String name) {
+        if (id == null) id = KeycloakModelUtils.generateId();
+        GroupEntity groupEntity = new GroupEntity();
+        groupEntity.setId(id);
+        groupEntity.setName(name);
+        RealmEntity realmEntity = em.getReference(RealmEntity.class, realm.getId());
+        groupEntity.setRealm(realmEntity);
+        em.persist(groupEntity);
+
+        return new GroupAdapter(realm, em, groupEntity);
+    }
+
+    @Override
+    public void addTopLevelGroup(RealmModel realm, GroupModel subGroup) {
+        subGroup.setParent(null);
+    }
+
+
+
+    @Override
     public ClientModel addClient(RealmModel realm, String clientId) {
         return addClient(realm, KeycloakModelUtils.generateId(), clientId);
     }
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 16eb142..3dd9de7 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
@@ -1988,94 +1988,44 @@ public class RealmAdapter implements RealmModel {
     }
 
     @Override
-    public void moveGroup(GroupModel group, GroupModel toParent) {
-        if (toParent != null && group.getId().equals(toParent.getId())) {
-            return;
-        }
-        if (group.getParentId() != null) {
-            group.getParent().removeChild(group);
-        }
-        group.setParent(toParent);
-        if (toParent != null) toParent.addChild(group);
-        else addTopLevelGroup(group);
+    public GroupModel createGroup(String name) {
+        return session.realms().createGroup(this, name);
     }
 
     @Override
-    public GroupModel getGroupById(String id) {
-        return session.realms().getGroupById(id, this);
+    public GroupModel createGroup(String id, String name) {
+        return session.realms().createGroup(this, id, name);
     }
 
     @Override
-    public List<GroupModel> getGroups() {
-        List<String> groups =  em.createNamedQuery("getAllGroupIdsByRealm", String.class)
-                .setParameter("realm", realm.getId()).getResultList();
-        if (groups == null) return Collections.EMPTY_LIST;
-        List<GroupModel> list = new LinkedList<>();
-        for (String id : groups) {
-            list.add(session.realms().getGroupById(id, this));
-        }
-        return Collections.unmodifiableList(list);
+    public void addTopLevelGroup(GroupModel subGroup) {
+        session.realms().addTopLevelGroup(this, subGroup);
+
     }
 
     @Override
-    public List<GroupModel> getTopLevelGroups() {
-        List<GroupModel> base = getGroups();
-        if (base.isEmpty()) return base;
-        List<GroupModel> copy = new LinkedList<>();
-        for (GroupModel group : base) {
-            if (group.getParent() == null) {
-                copy.add(group);
-            }
-        }
-        return Collections.unmodifiableList(copy);
+    public void moveGroup(GroupModel group, GroupModel toParent) {
+        session.realms().moveGroup(this, group, toParent);
     }
 
     @Override
-    public boolean removeGroup(GroupModel group) {
-        if (group == null) {
-            return false;
-        }
-        GroupEntity groupEntity = GroupAdapter.toEntity(group, em);
-        if (!groupEntity.getRealm().getId().equals(getId())) {
-            return false;
-        }
-        realm.getDefaultGroups().remove(groupEntity);
-        for (GroupModel subGroup : group.getSubGroups()) {
-            removeGroup(subGroup);
-        }
-
-
-        session.users().preRemove(this, group);
-        moveGroup(group, null);
-        em.createNamedQuery("deleteGroupAttributesByGroup").setParameter("group", groupEntity).executeUpdate();
-        em.createNamedQuery("deleteGroupRoleMappingsByGroup").setParameter("group", groupEntity).executeUpdate();
-        em.remove(groupEntity);
-        return true;
-
-
+    public GroupModel getGroupById(String id) {
+        return session.realms().getGroupById(id, this);
     }
 
     @Override
-    public GroupModel createGroup(String name) {
-        String id = KeycloakModelUtils.generateId();
-        return createGroup(id, name);
+    public List<GroupModel> getGroups() {
+        return session.realms().getGroups(this);
     }
 
     @Override
-    public GroupModel createGroup(String id, String name) {
-        if (id == null) id = KeycloakModelUtils.generateId();
-        GroupEntity groupEntity = new GroupEntity();
-        groupEntity.setId(id);
-        groupEntity.setName(name);
-        groupEntity.setRealm(realm);
-        em.persist(groupEntity);
-
-        return new GroupAdapter(this, em, groupEntity);
+    public List<GroupModel> getTopLevelGroups() {
+        return session.realms().getTopLevelGroups(this);
     }
 
     @Override
-    public void addTopLevelGroup(GroupModel subGroup) {
-        subGroup.setParent(null);
+    public boolean removeGroup(GroupModel group) {
+        return session.realms().removeGroup(this, group);
     }
 
     @Override
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 f8c9b79..3186f7c 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
@@ -41,6 +41,7 @@ import org.keycloak.models.utils.KeycloakModelUtils;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
 
@@ -155,6 +156,92 @@ public class MongoRealmProvider implements RealmProvider {
     }
 
     @Override
+    public void moveGroup(RealmModel realm, GroupModel group, GroupModel toParent) {
+        if (toParent != null && group.getId().equals(toParent.getId())) {
+            return;
+        }
+        if (group.getParentId() != null) {
+            group.getParent().removeChild(group);
+        }
+        group.setParent(toParent);
+        if (toParent != null) toParent.addChild(group);
+        else session.realms().addTopLevelGroup(realm, group);
+
+    }
+
+    @Override
+    public List<GroupModel> getGroups(RealmModel realm) {
+        DBObject query = new QueryBuilder()
+                .and("realmId").is(realm.getId())
+                .get();
+        List<MongoGroupEntity> groups = getMongoStore().loadEntities(MongoGroupEntity.class, query, invocationContext);
+        if (groups == null) return Collections.EMPTY_LIST;
+
+        List<GroupModel> result = new LinkedList<>();
+
+        if (groups == null) return result;
+        for (MongoGroupEntity group : groups) {
+            result.add(getGroupById(group.getId(), realm));
+        }
+
+        return Collections.unmodifiableList(result);
+    }
+
+    @Override
+    public List<GroupModel> getTopLevelGroups(RealmModel realm) {
+        DBObject query = new QueryBuilder()
+                .and("realmId").is(realm.getId())
+                .and("parentId").is(null)
+                .get();
+        List<MongoGroupEntity> groups = getMongoStore().loadEntities(MongoGroupEntity.class, query, invocationContext);
+        if (groups == null) return Collections.EMPTY_LIST;
+
+        List<GroupModel> result = new LinkedList<>();
+
+        if (groups == null) return result;
+        for (MongoGroupEntity group : groups) {
+            result.add(getGroupById(group.getId(), realm));
+        }
+
+        return Collections.unmodifiableList(result);
+    }
+
+    @Override
+    public boolean removeGroup(RealmModel realm, GroupModel group) {
+        session.users().preRemove(realm, group);
+        realm.removeDefaultGroup(group);
+        for (GroupModel subGroup : group.getSubGroups()) {
+            removeGroup(realm, subGroup);
+        }
+        moveGroup(realm, group, null);
+        return getMongoStore().removeEntity(MongoGroupEntity.class, group.getId(), invocationContext);
+    }
+
+    @Override
+    public GroupModel createGroup(RealmModel realm, String name) {
+        String id = KeycloakModelUtils.generateId();
+        return createGroup(realm, id, name);
+    }
+
+    @Override
+    public GroupModel createGroup(RealmModel realm, String id, String name) {
+        if (id == null) id = KeycloakModelUtils.generateId();
+        MongoGroupEntity group = new MongoGroupEntity();
+        group.setId(id);
+        group.setName(name);
+        group.setRealmId(realm.getId());
+
+        getMongoStore().insertEntity(group, invocationContext);
+
+        return new GroupAdapter(session, realm, group, invocationContext);
+    }
+
+    @Override
+    public void addTopLevelGroup(RealmModel realm, GroupModel subGroup) {
+        subGroup.setParent(null);
+    }
+
+    @Override
     public ClientModel getClientById(String id, RealmModel realm) {
         MongoClientEntity appData = getMongoStore().loadEntity(MongoClientEntity.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 6c4b6f9..b5d6d7c 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
@@ -645,40 +645,23 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
 
     @Override
     public GroupModel createGroup(String name) {
-        String id = KeycloakModelUtils.generateId();
-        return createGroup(id, name);
+        return session.realms().createGroup(this, name);
     }
 
     @Override
     public GroupModel createGroup(String id, String name) {
-        if (id == null) id = KeycloakModelUtils.generateId();
-        MongoGroupEntity group = new MongoGroupEntity();
-        group.setId(id);
-        group.setName(name);
-        group.setRealmId(getId());
-
-        getMongoStore().insertEntity(group, invocationContext);
-
-        return new GroupAdapter(session, this, group, invocationContext);
+        return session.realms().createGroup(this, id, name);
     }
 
     @Override
     public void addTopLevelGroup(GroupModel subGroup) {
-        subGroup.setParent(null);
+        session.realms().addTopLevelGroup(this, subGroup);
 
     }
 
     @Override
     public void moveGroup(GroupModel group, GroupModel toParent) {
-        if (toParent != null && group.getId().equals(toParent.getId())) {
-            return;
-        }
-        if (group.getParentId() != null) {
-            group.getParent().removeChild(group);
-        }
-        group.setParent(toParent);
-        if (toParent != null) toParent.addChild(group);
-        else addTopLevelGroup(group);
+        session.realms().moveGroup(this, group, toParent);
     }
 
     @Override
@@ -688,46 +671,17 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
 
     @Override
     public List<GroupModel> getGroups() {
-        DBObject query = new QueryBuilder()
-                .and("realmId").is(getId())
-                .get();
-        List<MongoGroupEntity> groups = getMongoStore().loadEntities(MongoGroupEntity.class, query, invocationContext);
-        if (groups == null) return Collections.EMPTY_LIST;
-
-        List<GroupModel> result = new LinkedList<>();
-
-        if (groups == null) return result;
-        for (MongoGroupEntity group : groups) {
-            result.add(model.getGroupById(group.getId(), this));
-        }
-
-        return Collections.unmodifiableList(result);
+        return session.realms().getGroups(this);
     }
 
     @Override
     public List<GroupModel> getTopLevelGroups() {
-        List<GroupModel> base = getGroups();
-        if (base.isEmpty()) return base;
-        List<GroupModel> copy = new LinkedList<>();
-        for (GroupModel group : base) {
-            if (group.getParent() == null) {
-                copy.add(group);
-            }
-        }
-        return Collections.unmodifiableList(copy);
+        return session.realms().getTopLevelGroups(this);
     }
 
     @Override
     public boolean removeGroup(GroupModel group) {
-        if (realm.getDefaultGroups() != null) {
-            getMongoStore().pullItemFromList(realm, "defaultGroups", group.getId(), invocationContext);
-        }
-        for (GroupModel subGroup : group.getSubGroups()) {
-            removeGroup(subGroup);
-        }
-        session.users().preRemove(this, group);
-        moveGroup(group, null);
-        return getMongoStore().removeEntity(MongoGroupEntity.class, group.getId(), invocationContext);
+        return session.realms().removeGroup(this, 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 e7584e7..4e6070f 100755
--- a/server-spi/src/main/java/org/keycloak/models/RealmProvider.java
+++ b/server-spi/src/main/java/org/keycloak/models/RealmProvider.java
@@ -36,6 +36,20 @@ public interface RealmProvider extends Provider {
     RealmModel getRealm(String id);
     RealmModel getRealmByName(String name);
 
+    void moveGroup(RealmModel realm, GroupModel group, GroupModel toParent);
+
+    List<GroupModel> getGroups(RealmModel realm);
+
+    List<GroupModel> getTopLevelGroups(RealmModel realm);
+
+    boolean removeGroup(RealmModel realm, GroupModel group);
+
+    GroupModel createGroup(RealmModel realm, String name);
+
+    GroupModel createGroup(RealmModel realm, String id, String name);
+
+    void addTopLevelGroup(RealmModel realm, GroupModel subGroup);
+
     ClientModel addClient(RealmModel realm, String clientId);
 
     ClientModel addClient(RealmModel realm, String id, String clientId);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/LDAPGroupMapper2WaySyncTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/LDAPGroupMapper2WaySyncTest.java
old mode 100644
new mode 100755
diff --git a/testsuite/integration/src/test/resources/META-INF/keycloak-server.json b/testsuite/integration/src/test/resources/META-INF/keycloak-server.json
index 950fd22..be63456 100755
--- a/testsuite/integration/src/test/resources/META-INF/keycloak-server.json
+++ b/testsuite/integration/src/test/resources/META-INF/keycloak-server.json
@@ -62,7 +62,7 @@
             "user": "${keycloak.connectionsJpa.user:sa}",
             "password": "${keycloak.connectionsJpa.password:}",
             "databaseSchema": "${keycloak.connectionsJpa.databaseSchema:update}",
-            "showSql": "${keycloak.connectionsJpa.showSql:false}",
+            "showSql": "${keycloak.connectionsJpa.showSql:true}",
             "formatSql": "${keycloak.connectionsJpa.formatSql:true}"
         }
     },