public void setRoleBindings(
      Session session, ITenant tenant, String runtimeRoleName, List<String> logicalRoleNames)
      throws NamespaceException, RepositoryException {
    if (tenant == null) {
      tenant = JcrTenantUtils.getTenant(runtimeRoleName, false);
      runtimeRoleName = getPrincipalName(runtimeRoleName);
    }

    if (!TenantUtils.isAccessibleTenant(tenant)) {
      throw new NotFoundException("Tenant " + tenant.getId() + " not found");
    }

    PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session);
    final String phoNsPrefix =
        session.getNamespacePrefix(PentahoJcrConstants.PHO_NS) + ":"; // $NON-NLS-1$
    final String onlyPentahoPattern = phoNsPrefix + "*"; // $NON-NLS-1$
    Node runtimeRolesFolderNode = getRuntimeRolesFolderNode(session, tenant);
    NodeIterator runtimeRoleNodes = runtimeRolesFolderNode.getNodes(onlyPentahoPattern);
    int i = 0;
    while (runtimeRoleNodes.hasNext()) {
      runtimeRoleNodes.nextNode();
      i++;
    }
    if (i == 0) {
      // no bindings setup yet; install bootstrap bindings; bootstrapRoleBindings will now no longer
      // be
      // consulted
      for (Map.Entry<String, List<String>> entry : bootstrapRoleBindings.entrySet()) {
        JcrRoleAuthorizationPolicyUtils.internalSetBindings(
            pentahoJcrConstants,
            runtimeRolesFolderNode,
            entry.getKey(),
            entry.getValue(),
            phoNsPrefix);
      }
    }
    if (!isImmutable(runtimeRoleName)) {
      JcrRoleAuthorizationPolicyUtils.internalSetBindings(
          pentahoJcrConstants,
          runtimeRolesFolderNode,
          runtimeRoleName,
          logicalRoleNames,
          phoNsPrefix);
    } else {
      throw new RuntimeException(
          Messages.getInstance()
              .getString(
                  "JcrRoleAuthorizationPolicyRoleBindingDao.ERROR_0001_ATTEMPT_MOD_IMMUTABLE",
                  runtimeRoleName)); //$NON-NLS-1$
    }
    session.save();
    Assert.isTrue(NodeHelper.hasNode(runtimeRolesFolderNode, phoNsPrefix, runtimeRoleName));

    // update cache
    String roleId = tenantedRoleNameUtils.getPrincipleId(tenant, runtimeRoleName);
    cacheManager.putInRegionCache(LOGICAL_ROLE_BINDINGS_REGION, roleId, logicalRoleNames);
  }
  @Override
  public List<String> getBoundLogicalRoleNames(
      Session session, ITenant tenant, List<String> runtimeRoleNames)
      throws NamespaceException, RepositoryException {
    if ((tenant == null) || (tenant.getId() == null)) {
      return getBoundLogicalRoleNames(session, runtimeRoleNames);
    }

    if (!TenantUtils.isAccessibleTenant(tenant)) {
      return new ArrayList<String>();
    }

    final List<String> uncachedRuntimeRoleNames = new ArrayList<String>();
    final Set<String> cachedBoundLogicalRoleNames = new HashSet<String>();
    for (String runtimeRoleName : runtimeRoleNames) {
      String roleName = tenantedRoleNameUtils.getPrincipleName(runtimeRoleName);
      String roleId = tenantedRoleNameUtils.getPrincipleId(tenant, runtimeRoleName);
      Object fromRegionCache =
          cacheManager.getFromRegionCache(LOGICAL_ROLE_BINDINGS_REGION, roleId);
      if (fromRegionCache != null) {
        cachedBoundLogicalRoleNames.addAll((Collection<String>) fromRegionCache);
      } else {
        uncachedRuntimeRoleNames.add(roleName);
      }
    }
    if (uncachedRuntimeRoleNames.isEmpty()) {
      // no need to hit the repo
      return new ArrayList<String>(cachedBoundLogicalRoleNames);
    }

    PentahoJcrConstants pentahoJcrConstants = new PentahoJcrConstants(session);
    final String phoNsPrefix =
        session.getNamespacePrefix(PentahoJcrConstants.PHO_NS) + ":"; // $NON-NLS-1$
    final String onlyPentahoPattern = phoNsPrefix + "*"; // $NON-NLS-1$
    HashMultimap<String, String> boundLogicalRoleNames = HashMultimap.create();
    Node runtimeRolesFolderNode = getRuntimeRolesFolderNode(session, tenant);
    NodeIterator runtimeRoleNodes = runtimeRolesFolderNode.getNodes(onlyPentahoPattern);
    if (!runtimeRoleNodes.hasNext()) {
      // no bindings setup yet; fall back on bootstrap bindings
      for (String runtimeRoleName : uncachedRuntimeRoleNames) {
        String roleId = tenantedRoleNameUtils.getPrincipleId(tenant, runtimeRoleName);
        if (bootstrapRoleBindings.containsKey(runtimeRoleName)) {
          boundLogicalRoleNames.putAll(roleId, bootstrapRoleBindings.get(runtimeRoleName));
        }
      }
    } else {
      for (String runtimeRoleName : uncachedRuntimeRoleNames) {
        if (NodeHelper.hasNode(runtimeRolesFolderNode, phoNsPrefix, runtimeRoleName)) {
          Node runtimeRoleFolderNode =
              NodeHelper.getNode(runtimeRolesFolderNode, phoNsPrefix, runtimeRoleName);
          if (runtimeRoleFolderNode.hasProperty(pentahoJcrConstants.getPHO_BOUNDROLES())) {
            Value[] values =
                runtimeRoleFolderNode
                    .getProperty(pentahoJcrConstants.getPHO_BOUNDROLES())
                    .getValues();
            String roleId = tenantedRoleNameUtils.getPrincipleId(tenant, runtimeRoleName);
            for (Value value : values) {
              boundLogicalRoleNames.put(roleId, value.getString());
            }
          }
        }
      }
    }
    // now add in immutable bound logical role names
    for (String runtimeRoleName : uncachedRuntimeRoleNames) {
      if (immutableRoleBindings.containsKey(runtimeRoleName)) {
        String roleId = tenantedRoleNameUtils.getPrincipleId(tenant, runtimeRoleName);
        boundLogicalRoleNames.putAll(roleId, immutableRoleBindingNames.get(runtimeRoleName));
      }
    }

    // update cache
    Map<String, Collection<String>> stringCollectionMap = boundLogicalRoleNames.asMap();
    for (Entry<String, Collection<String>> stringCollectionEntry : stringCollectionMap.entrySet()) {
      cacheManager.putInRegionCache(
          LOGICAL_ROLE_BINDINGS_REGION,
          stringCollectionEntry.getKey(),
          stringCollectionEntry.getValue());
    }

    // now add in those runtime roles that have no bindings to the cache
    for (String runtimeRoleName : uncachedRuntimeRoleNames) {
      String roleId = tenantedRoleNameUtils.getPrincipleId(tenant, runtimeRoleName);

      if (cacheManager.getFromRegionCache(LOGICAL_ROLE_BINDINGS_REGION, roleId) == null) {
        cacheManager.putInRegionCache(
            LOGICAL_ROLE_BINDINGS_REGION, roleId, Collections.emptyList());
      }
    }

    // combine cached findings plus ones from repo
    Set<String> res = new HashSet<String>();
    res.addAll(cachedBoundLogicalRoleNames);
    res.addAll(boundLogicalRoleNames.values());
    return new ArrayList<String>(res);
  }