class CustomPagedLdapDao implements SpringLdapDao {

    @Override
    public List<LdapGroup> findGroupsPaged(LdapTemplate template, AcmLdapSyncConfig config, Optional<String> ldapLastSyncDate)
    {
        SearchControls searchControls = new SearchControls();
        searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        searchControls.setReturningAttributes(new String[] { "cn", "member", "displayName" });

        AggregateDirContextProcessor sortedAndPaged = buildSortedAndPagesProcessor(config, config.getGroupsSortingAttribute());
        AcmGroupContextMapper acmGroupContextMapper = new AcmGroupContextMapper(config, template);

        boolean searchGroups = true;
        boolean skipFirst = false;

        Optional<String> lastSyncTimestamp = ldapLastSyncDate
                .map(it -> convertToDirectorySpecificTimestamp(it, config.getDirectoryType()));

        String searchFilter = buildGroupSearchFilter(config, lastSyncTimestamp);
        List<LdapGroup> ldapGroups = new ArrayList<>();

        while (searchGroups)
        {

            log.debug("search filter: [{}]", searchFilter);
            List<LdapGroup> found = template.search(config.getGroupSearchBase(), searchFilter, searchControls, acmGroupContextMapper,
                    sortedAndPaged);

            if (skipFirst && !found.isEmpty())
            {
                ldapGroups.addAll(found.subList(1, found.size()));
            }
            else
            {
                ldapGroups.addAll(found);
            }

            log.debug("Groups found: [{}]", found.size());

            searchGroups = config.getSyncPageSize() == found.size();
            if (searchGroups)
            {
                skipFirst = true;
                LdapGroup lastFound = found.get(found.size() - 1);
                searchFilter = buildPagedGroupsSearchFilter(config, lastFound.getSortableValue(), lastSyncTimestamp);

                // A change to the search filter requires us to rebuild the search controls... even though
                // the controls will have the same values as before.
                sortedAndPaged = buildSortedAndPagesProcessor(config, config.getGroupsSortingAttribute());
                log.trace("Search filter now: [{}]", searchFilter);
            }
        }

        log.info("LDAP sync number of groups: [{}]", ldapGroups.size());

        return ldapGroups;
    }

}