@Override
 public byte[] serializeWalletExtension() {
   lock.lock();
   try {
     ClientState.StoredClientPaymentChannels.Builder builder =
         ClientState.StoredClientPaymentChannels.newBuilder();
     for (StoredClientChannel channel : mapChannels.values()) {
       // First a few asserts to make sure things won't break
       checkState(
           channel.valueToMe.signum() >= 0
               && channel.valueToMe.compareTo(NetworkParameters.MAX_MONEY) < 0);
       checkState(
           channel.refundFees.signum() >= 0
               && channel.refundFees.compareTo(NetworkParameters.MAX_MONEY) < 0);
       checkNotNull(channel.myKey.getPrivKeyBytes());
       checkState(channel.refund.getConfidence().getSource() == TransactionConfidence.Source.SELF);
       final ClientState.StoredClientPaymentChannel.Builder value =
           ClientState.StoredClientPaymentChannel.newBuilder()
               .setId(ByteString.copyFrom(channel.id.getBytes()))
               .setContractTransaction(ByteString.copyFrom(channel.contract.bitcoinSerialize()))
               .setRefundTransaction(ByteString.copyFrom(channel.refund.bitcoinSerialize()))
               .setMyKey(ByteString.copyFrom(channel.myKey.getPrivKeyBytes()))
               .setValueToMe(channel.valueToMe.longValue())
               .setRefundFees(channel.refundFees.longValue());
       if (channel.close != null)
         value.setCloseTransactionHash(ByteString.copyFrom(channel.close.getHash().getBytes()));
       builder.addChannels(value);
     }
     return builder.build().toByteArray();
   } finally {
     lock.unlock();
   }
 }
 @Override
 public String toString() {
   lock.lock();
   try {
     StringBuilder buf = new StringBuilder("Client payment channel states:\n");
     for (StoredClientChannel channel : mapChannels.values())
       buf.append("  ").append(channel).append("\n");
     return buf.toString();
   } finally {
     lock.unlock();
   }
 }
  @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);
  }