class DataStore implements DataCacheProvider, RolesProvider {

    void processGroup(Group group) {

        // if the group has null members we'll replace it with an empty set

        if (group.getGroupMembers() == null) {
            group.setGroupMembers(new ArrayList<>());
        }

        // obtain the previous list of members for the group
        // and determine the list of changes between old and new members

        List<GroupMember> originalMembers = groupMemberCache.getIfPresent(group.getName());
        List<GroupMember> curMembers = originalMembers == null ? new ArrayList<>() : new ArrayList<>(originalMembers);
        List<GroupMember> delMembers = new ArrayList<>(curMembers);
        List<GroupMember> newMembers = new ArrayList<>(group.getGroupMembers());
        List<GroupMember> updMembers = new ArrayList<>(group.getGroupMembers());

        // remove current members from new members

        AuthzHelper.removeGroupMembers(newMembers, curMembers);

        // remove new members from current members
        // which leaves the deleted members.

        AuthzHelper.removeGroupMembers(delMembers, group.getGroupMembers());

        // now let's remove our new members from the member list to
        // get the possible list of users that need to be updated

        AuthzHelper.removeGroupMembers(updMembers, newMembers);

        // update the group member cache with the new members

        groupMemberCache.put(group.getName(), group.getGroupMembers());

        // first process the updated entries

        long currentTime = System.currentTimeMillis();
        for (GroupMember member : updMembers) {

            // it's possible that initially we skipped the entry because it was
            // disabled or expired so we might have no entries in the map

            List<GroupMember> groupMembers = principalGroupCache.getIfPresent(member.getMemberName());
            if (groupMembers == null) {

                // make sure we want to process this user

                if (AuthzHelper.shouldSkipGroupMember(member, currentTime)) {
                    continue;
                }

                // otherwise we'll add this member to our new list

                groupMembers = new ArrayList<>();
                groupMembers.add(member);
                principalGroupCache.put(member.getMemberName(), groupMembers);

            } else {

                // if we need to skip the entry then we'll delete the member
                // from our list otherwise we'll just update it

                if (AuthzHelper.shouldSkipGroupMember(member, currentTime)) {
                    groupMembers.removeIf(item -> item.getGroupName().equalsIgnoreCase(group.getName()));
                } else {
                    // we need to find our entry and update details

                    for (GroupMember mbr : groupMembers) {
                        if (mbr.getGroupName().equalsIgnoreCase(group.getName())) {
                            mbr.setExpiration(member.getExpiration());
                            mbr.setSystemDisabled(member.getSystemDisabled());
                            mbr.setActive(member.getActive());
                            mbr.setApproved(member.getApproved());
                            break;
                        }
                    }
                }
            }
        }

        // now let's add our new members

        for (GroupMember member : newMembers) {

            // skip any disabled and expired users

            if (AuthzHelper.shouldSkipGroupMember(member, currentTime)) {
                continue;
            }

            List<GroupMember> groupMembers = principalGroupCache.getIfPresent(member.getMemberName());
            if (groupMembers == null) {
                groupMembers = new ArrayList<>();
                principalGroupCache.put(member.getMemberName(), groupMembers);
            }
            groupMembers.add(member);
        }

        // process deleted members from the group

        processGroupDeletedMembers(group.getName(), delMembers);
    }

    public  DataStore(ChangeLogStore clogStore, CloudStore cloudStore);

}