@Override
  public List<IAMGroup> listParentIAMGroups(long groupId) {
    IAMGroup group = _aclGroupDao.findById(groupId);
    if (group == null) {
      throw new InvalidParameterValueException("Unable to find acl group by id " + groupId);
    }

    String path = group.getPath();
    List<String> pathList = new ArrayList<String>();

    String[] parts = path.split("/");

    for (String part : parts) {
      int start = path.indexOf(part);
      if (start > 0) {
        String subPath = path.substring(0, start);
        pathList.add(subPath);
      }
    }

    if (pathList.isEmpty()) {
      return new ArrayList<IAMGroup>();
    }

    SearchBuilder<IAMGroupVO> sb = _aclGroupDao.createSearchBuilder();
    sb.and("paths", sb.entity().getPath(), SearchCriteria.Op.IN);

    SearchCriteria<IAMGroupVO> sc = sb.create();
    sc.setParameters("paths", pathList.toArray());

    List<IAMGroupVO> groups = _aclGroupDao.search(sc, null);

    return new ArrayList<IAMGroup>(groups);
  }
  @Override
  public Pair<List<IAMGroup>, Integer> listIAMGroups(
      Long iamGroupId, String iamGroupName, String path, Long startIndex, Long pageSize) {
    if (iamGroupId != null) {
      IAMGroup group = _aclGroupDao.findById(iamGroupId);
      if (group == null) {
        throw new InvalidParameterValueException("Unable to find acl group by id " + iamGroupId);
      }
    }

    Filter searchFilter = new Filter(IAMGroupVO.class, "id", true, startIndex, pageSize);

    SearchBuilder<IAMGroupVO> sb = _aclGroupDao.createSearchBuilder();
    sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ);
    sb.and("path", sb.entity().getPath(), SearchCriteria.Op.LIKE);
    sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ);

    SearchCriteria<IAMGroupVO> sc = sb.create();

    if (iamGroupName != null) {
      sc.setParameters("name", iamGroupName);
    }

    if (iamGroupId != null) {
      sc.setParameters("id", iamGroupId);
    }

    sc.setParameters("path", path + "%");

    Pair<List<IAMGroupVO>, Integer> groups = _aclGroupDao.searchAndCount(sc, searchFilter);
    return new Pair<List<IAMGroup>, Integer>(
        new ArrayList<IAMGroup>(groups.first()), groups.second());
  }
  @DB
  @Override
  public IAMGroup removeIAMPoliciesFromGroup(final List<Long> policyIds, final Long groupId) {
    // get the Acl Group entity
    IAMGroup group = _aclGroupDao.findById(groupId);
    if (group == null) {
      throw new InvalidParameterValueException(
          "Unable to find acl group: " + groupId + "; failed to remove roles from acl group.");
    }

    Transaction.execute(
        new TransactionCallbackNoReturn() {
          @Override
          public void doInTransactionWithoutResult(TransactionStatus status) {
            // add entries in acl_group_role_map table
            for (Long policyId : policyIds) {
              IAMPolicy policy = _aclPolicyDao.findById(policyId);
              if (policy == null) {
                throw new InvalidParameterValueException(
                    "Unable to find acl policy: "
                        + policyId
                        + "; failed to add policies to acl group.");
              }

              IAMGroupPolicyMapVO grMap =
                  _aclGroupPolicyMapDao.findByGroupAndPolicy(groupId, policyId);
              if (grMap != null) {
                // not removed yet
                _aclGroupPolicyMapDao.remove(grMap.getId());
              }
            }
          }
        });
    return group;
  }
  @DB
  @Override
  public IAMGroup removeAccountsFromGroup(final List<Long> acctIds, final Long groupId) {
    // get the Acl Group entity
    IAMGroup group = _aclGroupDao.findById(groupId);
    if (group == null) {
      throw new InvalidParameterValueException(
          "Unable to find acl group: " + groupId + "; failed to remove accounts from acl group.");
    }

    Transaction.execute(
        new TransactionCallbackNoReturn() {
          @Override
          public void doInTransactionWithoutResult(TransactionStatus status) {
            // remove entries from acl_group_account_map table
            for (Long acctId : acctIds) {
              IAMGroupAccountMapVO grMap =
                  _aclGroupAccountMapDao.findByGroupAndAccount(groupId, acctId);
              if (grMap != null) {
                // not removed yet
                _aclGroupAccountMapDao.remove(grMap.getId());
              }
            }
          }
        });
    return group;
  }
  @DB
  @Override
  public IAMGroup addAccountsToGroup(final List<Long> acctIds, final Long groupId) {
    // get the Acl Group entity
    IAMGroup group = _aclGroupDao.findById(groupId);
    if (group == null) {
      throw new InvalidParameterValueException(
          "Unable to find acl group: " + groupId + "; failed to add accounts to acl group.");
    }

    Transaction.execute(
        new TransactionCallbackNoReturn() {
          @Override
          public void doInTransactionWithoutResult(TransactionStatus status) {
            // add entries in acl_group_account_map table
            for (Long acctId : acctIds) {
              // check account permissions
              IAMGroupAccountMapVO grMap =
                  _aclGroupAccountMapDao.findByGroupAndAccount(groupId, acctId);
              if (grMap == null) {
                // not there already
                grMap = new IAMGroupAccountMapVO(groupId, acctId);
                _aclGroupAccountMapDao.persist(grMap);
              }
            }
          }
        });
    return group;
  }
  @DB
  @Override
  public IAMGroup createIAMGroup(String iamGroupName, String description, String path) {
    // check if the group is already existing
    IAMGroup grp = _aclGroupDao.findByName(path, iamGroupName);
    if (grp != null) {
      throw new InvalidParameterValueException(
          "Unable to create acl group with name "
              + iamGroupName
              + " already exisits for path "
              + path);
    }
    IAMGroupVO rvo = new IAMGroupVO(iamGroupName, description);
    rvo.setPath(path);

    return _aclGroupDao.persist(rvo);
  }
  @SuppressWarnings("unchecked")
  @Override
  public List<IAMGroup> listIAMGroups(long accountId) {

    GenericSearchBuilder<IAMGroupAccountMapVO, Long> groupSB =
        _aclGroupAccountMapDao.createSearchBuilder(Long.class);
    groupSB.selectFields(groupSB.entity().getAclGroupId());
    groupSB.and("account", groupSB.entity().getAccountId(), Op.EQ);
    SearchCriteria<Long> groupSc = groupSB.create();
    groupSc.setParameters("account", accountId);

    List<Long> groupIds = _aclGroupAccountMapDao.customSearch(groupSc, null);

    SearchBuilder<IAMGroupVO> sb = _aclGroupDao.createSearchBuilder();
    sb.and("ids", sb.entity().getId(), Op.IN);
    SearchCriteria<IAMGroupVO> sc = sb.create();
    sc.setParameters("ids", groupIds.toArray(new Object[groupIds.size()]));
    @SuppressWarnings("rawtypes")
    List groups = _aclGroupDao.search(sc, null);
    return groups;
  }
  @DB
  @Override
  public boolean deleteIAMGroup(final Long iamGroupId) {
    // get the Acl Group entity
    final IAMGroup grp = _aclGroupDao.findById(iamGroupId);
    if (grp == null) {
      throw new InvalidParameterValueException(
          "Unable to find acl group: " + iamGroupId + "; failed to delete acl group.");
    }

    Transaction.execute(
        new TransactionCallbackNoReturn() {
          @Override
          public void doInTransactionWithoutResult(TransactionStatus status) {
            // remove this group related entry in acl_group_role_map
            List<IAMGroupPolicyMapVO> groupPolicyMap =
                _aclGroupPolicyMapDao.listByGroupId(grp.getId());
            if (groupPolicyMap != null) {
              for (IAMGroupPolicyMapVO gr : groupPolicyMap) {
                _aclGroupPolicyMapDao.remove(gr.getId());
              }
            }

            // remove this group related entry in acl_group_account table
            List<IAMGroupAccountMapVO> groupAcctMap =
                _aclGroupAccountMapDao.listByGroupId(grp.getId());
            if (groupAcctMap != null) {
              for (IAMGroupAccountMapVO grpAcct : groupAcctMap) {
                _aclGroupAccountMapDao.remove(grpAcct.getId());
              }
            }

            // remove this group from acl_group table
            _aclGroupDao.remove(iamGroupId);
          }
        });

    return true;
  }
  @DB
  @Override
  public IAMGroup attachIAMPoliciesToGroup(final List<Long> policyIds, final Long groupId) {
    // get the Acl Group entity
    IAMGroup group = _aclGroupDao.findById(groupId);
    if (group == null) {
      throw new InvalidParameterValueException(
          "Unable to find acl group: " + groupId + "; failed to add roles to acl group.");
    }

    Transaction.execute(
        new TransactionCallbackNoReturn() {
          @Override
          public void doInTransactionWithoutResult(TransactionStatus status) {
            // add entries in acl_group_policy_map table
            for (Long policyId : policyIds) {
              IAMPolicy policy = _aclPolicyDao.findById(policyId);
              if (policy == null) {
                throw new InvalidParameterValueException(
                    "Unable to find acl policy: "
                        + policyId
                        + "; failed to add policies to acl group.");
              }

              IAMGroupPolicyMapVO grMap =
                  _aclGroupPolicyMapDao.findByGroupAndPolicy(groupId, policyId);
              if (grMap == null) {
                // not there already
                grMap = new IAMGroupPolicyMapVO(groupId, policyId);
                _aclGroupPolicyMapDao.persist(grMap);
              }
            }
          }
        });

    return group;
  }