/**
   * Tries to find attribute values based on the given selector data. The result, if successful,
   * must always contain a <code>BagAttribute</code>, even if only one value was found. If no values
   * were found, but no other error occurred, an empty bag is returned.
   *
   * @param contextPath the XPath expression to search against
   * @param attributeType the datatype of the attributes to find
   * @param context the representation of the request data
   * @param xpathVersion the XPath version to use
   * @return the result of attribute retrieval, which will be a bag of attributes or an error
   */
  public EvaluationResult findAttribute(
      String contextPath, URI attributeType, EvaluationCtx context, String xpathVersion) {
    Iterator it = selectorModules.iterator();

    // go through each module in order
    while (it.hasNext()) {
      AttributeFinderModule module = (AttributeFinderModule) (it.next());

      // see if the module can find an attribute value
      EvaluationResult result =
          module.findAttribute(contextPath, attributeType, null, null, context, xpathVersion);

      // if there was an error, we stop right away
      if (result.indeterminate()) {
        logger.info("Error while trying to resolve values: " + result.getStatus().getMessage());
        return result;
      }

      // if the result wasn't empty, then return the result
      BagAttribute bag = (BagAttribute) (result.getAttributeValue());
      if (!bag.isEmpty()) return result;
    }

    // if we got here then there were no errors but there were also no
    // matches, so we have to return an empty bag
    logger.info("Failed to resolve any values for " + contextPath);
    try {
      throw new Exception();
    } catch (Exception ex) {
      ex.printStackTrace();
    }

    return new EvaluationResult(BagAttribute.createEmptyBag(attributeType));
  }
  /**
   * Tries to find attribute values based on the given designator data. The result, if successful,
   * will always contain a <code>BagAttribute</code>, even if only one value was found. If no values
   * were found, but no other error occurred, an empty bag is returned.
   *
   * @param attributeType the datatype of the attributes to find
   * @param attributeId the identifier of the attributes to find
   * @param issuer the issuer of the attributes, or null if unspecified
   * @param category the category of the attribute if the designatorType is SUBJECT_TARGET,
   *     otherwise null
   * @param context the representation of the request data
   * @return the result of attribute retrieval, which will be a bag of attributes or an error
   */
  public EvaluationResult findAttribute(
      URI attributeType, URI attributeId, String issuer, URI category, EvaluationCtx context) {
    Iterator it = designatorModules.iterator();

    logger.debug("Selector modules size ==> " + selectorModules.size());

    // go through each module in order
    while (it.hasNext()) {
      AttributeFinderModule module = (AttributeFinderModule) (it.next());

      // see if the module supports this type

      // see if the module can find an attribute value

      logger.info(
          "Attribute Type => "
              + attributeType
              + " attributeId =>"
              + attributeId
              + " issuer => "
              + issuer
              + " Category => "
              + category
              + " Context => "
              + context);
      EvaluationResult result =
          module.findAttribute(attributeType, attributeId, issuer, category, context);

      // if there was an error, we stop right away
      if (result.indeterminate()) {
        logger.error("Error while trying to resolve values: " + result.getStatus().getMessage());
        return result;
      }

      // if the result wasn't empty, then return the result
      BagAttribute bag = (BagAttribute) (result.getAttributeValue());
      if (!bag.isEmpty()) return result;
    }

    // if we got here then there were no errors but there were also no
    // matches, so we have to return an empty bag
    logger.debug("Failed to resolve any values for " + attributeId.toString());

    /*
    try {
    	throw new Exception();
    } catch (Exception ex) {
    	ex.printStackTrace();
    }
    */

    return new EvaluationResult(BagAttribute.createEmptyBag(attributeType));
  }
  @Override
  public Set<String> findDescendantResources(String parentResourceId, EvaluationCtx context)
      throws Exception {

    EvaluationResult environment;
    String environmentId = null;
    Set<String> resourceNames = null;

    NodeList children = context.getRequestRoot().getChildNodes();
    for (int i = 0; i < children.getLength(); i++) {
      Node child = children.item(i);
      if (child != null) {
        if (PDPConstants.ENVIRONMENT_ELEMENT.equals(child.getLocalName())) {
          if (child.getChildNodes() != null && child.getChildNodes().getLength() > 0) {
            environment =
                context.getAttribute(
                    new URI(StringAttribute.identifier),
                    new URI(PDPConstants.ENVIRONMENT_ID_DEFAULT),
                    null,
                    new URI(XACMLConstants.ENT_CATEGORY));
            if (environment != null
                && environment.getAttributeValue() != null
                && environment.getAttributeValue().isBag()) {
              BagAttribute attr = (BagAttribute) environment.getAttributeValue();
              environmentId = ((AttributeValue) attr.iterator().next()).encode();
            }
          }
        }
      }
    }

    if (isAbstractResourceCacheEnabled) {
      IdentityCacheKey cacheKey;
      String key =
          PDPConstants.RESOURCE_DESCENDANTS
              + parentResourceId
              + (environmentId != null ? environmentId : "");
      tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
      cacheKey = new IdentityCacheKey(tenantId, key);
      IdentityCacheEntry cacheEntry =
          (IdentityCacheEntry) abstractResourceCache.getValueFromCache(cacheKey);
      if (cacheEntry != null) {
        String[] values = cacheEntry.getCacheEntryArray();
        resourceNames = new HashSet<String>(Arrays.asList(values));
        if (log.isDebugEnabled()) {
          log.debug("Carbon Resource Cache Hit");
        }
      }

      if (resourceNames != null) {
        resourceNames = findDescendantResources(parentResourceId, environmentId);
        if (log.isDebugEnabled()) {
          log.debug("Carbon Resource Cache Miss");
        }
        if (resourceNames != null && !resourceNames.isEmpty()) {
          cacheEntry =
              new IdentityCacheEntry(resourceNames.toArray(new String[resourceNames.size()]));
          abstractResourceCache.addToCache(cacheKey, cacheEntry);
        }
      }
    } else {
      resourceNames = findDescendantResources(parentResourceId, environmentId);
    }

    return resourceNames;
  }