private String getAclTenant(long aclId) {
    List<Long> nodeIds = aclDAO.getADMNodesByAcl(aclId, 1);
    if (nodeIds.size() == 0) {
      return null;
    }

    nodeDAO.setCheckNodeConsistency();
    Pair<Long, NodeRef> nodePair = nodeDAO.getNodePair(nodeIds.get(0));
    if (nodePair == null) {
      return null;
    }

    return tenantService.getDomain(nodePair.getSecond().getStoreRef().getIdentifier());
  }
  /**
   * Does a 'breadth first' search of ancestors, caching as it goes
   *
   * @param nodeIds initial list of nodes to visit
   * @return all visited nodes, in no particular order
   */
  private List<Long> cacheAncestors(List<Long> nodeIds) {
    final LinkedList<Long> toVisit = new LinkedList<Long>(nodeIds);
    Set<Long> visited = new TreeSet<Long>();
    Long nodeId;
    nodeDAO.cacheNodesById(toVisit);
    Long lastCached = toVisit.peekLast();
    while ((nodeId = toVisit.pollFirst()) != null) {
      if (visited.add(nodeId)
          && (nodeDAO.getNodeIdStatus(nodeId) != null)
          && (false == nodeDAO.getNodeIdStatus(nodeId).isDeleted())) {
        nodeDAO.getParentAssocs(
            nodeId,
            null,
            null,
            null,
            new ChildAssocRefQueryCallback() {
              @Override
              public boolean preLoadNodes() {
                return false;
              }

              @Override
              public boolean orderResults() {
                return false;
              }

              @Override
              public boolean handle(
                  Pair<Long, ChildAssociationRef> childAssocPair,
                  Pair<Long, NodeRef> parentNodePair,
                  Pair<Long, NodeRef> childNodePair) {
                toVisit.add(parentNodePair.getFirst());
                return true;
              }

              @Override
              public void done() {}
            });
      }
      final boolean nodeIdEqualsLastCached =
          (nodeId == null && lastCached == null) || (nodeId != null && nodeId.equals(lastCached));
      if (nodeIdEqualsLastCached && !toVisit.isEmpty()) {
        nodeDAO.cacheNodesById(toVisit);
        lastCached = toVisit.peekLast();
      }
    }
    return new ArrayList<Long>(visited);
  }
  /*
   * (non-Javadoc)
   * @see org.alfresco.repo.security.authority.AuthorityBridgeDAO#getAuthorityBridgeLinks()
   */
  @Override
  public List<AuthorityBridgeLink> getAuthorityBridgeLinks() {
    Long authorityContainerTypeQNameId = Long.MIN_VALUE;
    Pair<Long, QName> authorityContainerTypeQNamePair =
        qnameDAO.getQName(ContentModel.TYPE_AUTHORITY_CONTAINER);
    if (authorityContainerTypeQNamePair != null) {
      authorityContainerTypeQNameId = authorityContainerTypeQNamePair.getFirst();
    }

    Long memberAssocQNameId = Long.MIN_VALUE;
    Pair<Long, QName> memberAssocQNamePair = qnameDAO.getQName(ContentModel.ASSOC_MEMBER);
    if (memberAssocQNamePair != null) {
      memberAssocQNameId = memberAssocQNamePair.getFirst();
    }

    Long authorityNameQNameId = Long.MIN_VALUE;
    Pair<Long, QName> authorityNameQNamePair = qnameDAO.getQName(ContentModel.PROP_AUTHORITY_NAME);
    if (authorityNameQNamePair != null) {
      authorityNameQNameId = authorityNameQNamePair.getFirst();
    }

    // Get tenenat specifc store id
    StoreRef tenantSpecificStoreRef =
        tenantService.getName(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE);
    Long storeId = Long.MIN_VALUE;
    if (tenantSpecificStoreRef != null) {
      Pair<Long, StoreRef> storePair = nodeDAO.getStore(tenantSpecificStoreRef);
      if (storePair != null) {
        storeId = storePair.getFirst();
      }
    }

    return selectAuthorityBridgeLinks(
        authorityContainerTypeQNameId, memberAssocQNameId, authorityNameQNameId, storeId);
  }
  protected Map<QName, Serializable> getProperties(Long nodeId) {
    Map<QName, Serializable> props = null;

    // ALF-10641
    // Residual properties are un-indexed -> break serlialisation
    nodeDAO.setCheckNodeConsistency();
    Map<QName, Serializable> sourceProps = nodeDAO.getNodeProperties(nodeId);
    props = new HashMap<QName, Serializable>((int) (sourceProps.size() * 1.3));
    for (QName propertyQName : sourceProps.keySet()) {
      PropertyDefinition propDef = dictionaryService.getProperty(propertyQName);
      if (propDef != null) {
        props.put(propertyQName, sourceProps.get(propertyQName));
      }
    }

    return props;
  }
  public CannedQuery<AuthorityInfo> getCannedQuery(
      AuthorityType type,
      NodeRef containerRef,
      String displayNameFilter,
      boolean sortByDisplayName,
      boolean sortAscending,
      PagingRequest pagingRequest) {
    ParameterCheck.mandatory("containerRef", containerRef);
    ParameterCheck.mandatory("pagingRequest", pagingRequest);

    int requestTotalCountMax = pagingRequest.getRequestTotalCountMax();

    Pair<Long, NodeRef> nodePair = nodeDAO.getNodePair(tenantService.getName(containerRef));
    if (nodePair == null) {
      throw new InvalidNodeRefException(
          "Container ref does not exist: " + containerRef, containerRef);
    }

    Long containerNodeId = nodePair.getFirst();

    // specific query params
    GetAuthoritiesCannedQueryParams paramBean =
        new GetAuthoritiesCannedQueryParams(
            type,
            containerNodeId,
            getQNameId(ContentModel.PROP_AUTHORITY_DISPLAY_NAME),
            displayNameFilter);

    // page details
    CannedQueryPageDetails cqpd =
        new CannedQueryPageDetails(
            pagingRequest.getSkipCount(),
            pagingRequest.getMaxItems(),
            CannedQueryPageDetails.DEFAULT_PAGE_NUMBER,
            CannedQueryPageDetails.DEFAULT_PAGE_COUNT);

    // sort details
    CannedQuerySortDetails cqsd = null;
    if (sortByDisplayName) {
      List<Pair<? extends Object, SortOrder>> sortPairs =
          new ArrayList<Pair<? extends Object, SortOrder>>(1);
      sortPairs.add(
          new Pair<String, SortOrder>(
              "sortByName",
              (sortAscending
                  ? SortOrder.ASCENDING
                  : SortOrder.DESCENDING))); // note: sortByName is implied
      cqsd = new CannedQuerySortDetails(sortPairs);
    }

    // create query params holder
    CannedQueryParameters params =
        new CannedQueryParameters(
            paramBean, cqpd, cqsd, requestTotalCountMax, pagingRequest.getQueryExecutionId());

    // return canned query instance
    return getCannedQuery(params);
  }
  private List<Long> preCacheNodes(NodeMetaDataParameters nodeMetaDataParameters) {
    int maxResults = nodeMetaDataParameters.getMaxResults();
    boolean isLimitSet = (maxResults != 0 && maxResults != Integer.MAX_VALUE);

    List<Long> nodeIds = null;
    Iterable<Long> iterable = null;
    List<Long> allNodeIds = nodeMetaDataParameters.getNodeIds();
    if (allNodeIds != null) {
      int toIndex = (maxResults > allNodeIds.size() ? allNodeIds.size() : maxResults);
      nodeIds = isLimitSet ? allNodeIds.subList(0, toIndex) : nodeMetaDataParameters.getNodeIds();
      iterable = nodeMetaDataParameters.getNodeIds();
    } else {
      Long fromNodeId = nodeMetaDataParameters.getFromNodeId();
      Long toNodeId = nodeMetaDataParameters.getToNodeId();
      nodeIds = new ArrayList<Long>(isLimitSet ? maxResults : 100); // TODO better default here?
      iterable = new SequenceIterator(fromNodeId, toNodeId, maxResults);
      int counter = 1;
      for (Long nodeId : iterable) {
        if (isLimitSet && counter++ > maxResults) {
          break;
        }
        nodeIds.add(nodeId);
      }
    }

    // Pre-evaluate ancestors so we can bulk load them
    List<Long> ancestors;
    if (cacheAncestors) {
      ancestors = cacheAncestors(nodeIds);
    } else {
      ancestors = nodeIds;
    }
    // Ensure that we get fresh node references
    nodeDAO.setCheckNodeConsistency();
    // bulk load nodes and their ancestors
    nodeDAO.cacheNodesById(ancestors);

    return nodeIds;
  }
 private Set<QName> getNodeAspects(Long nodeId) {
   Set<QName> aspects = new HashSet<QName>();
   if (null == nodeId) {
     return aspects;
   }
   Set<QName> sourceAspects = nodeDAO.getNodeAspects(nodeId);
   for (QName aspectQName : sourceAspects) {
     AspectDefinition aspect = dictionaryService.getAspect(aspectQName);
     if (aspect != null) {
       aspects.add(aspectQName);
     }
   }
   return aspects;
 }
 private QName getNodeType(Long nodeId) {
   QName result = nodeDAO.getNodeType(nodeId);
   TypeDefinition type = dictionaryService.getType(result);
   return (null == type) ? (null) : (result);
 }
  /** {@inheritDoc} */
  public void getNodesMetadata(
      NodeMetaDataParameters nodeMetaDataParameters,
      MetaDataResultsFilter resultFilter,
      NodeMetaDataQueryCallback callback) {
    if (false == enabled) {
      return;
    }

    NodeMetaDataQueryRowHandler rowHandler = new NodeMetaDataQueryRowHandler(callback);
    boolean includeType = (resultFilter == null ? true : resultFilter.getIncludeType());
    boolean includeProperties = (resultFilter == null ? true : resultFilter.getIncludeProperties());
    boolean includeAspects = (resultFilter == null ? true : resultFilter.getIncludeAspects());
    boolean includePaths = (resultFilter == null ? true : resultFilter.getIncludePaths());
    boolean includeNodeRef = (resultFilter == null ? true : resultFilter.getIncludeNodeRef());
    boolean includeParentAssociations =
        (resultFilter == null ? true : resultFilter.getIncludeParentAssociations());
    boolean includeChildAssociations =
        (resultFilter == null ? true : resultFilter.getIncludeChildAssociations());
    boolean includeOwner = (resultFilter == null ? true : resultFilter.getIncludeOwner());
    boolean includeChildIds = (resultFilter == null ? true : resultFilter.getIncludeChildIds());
    boolean includeTxnId = (resultFilter == null ? true : resultFilter.getIncludeTxnId());

    List<Long> nodeIds = preCacheNodes(nodeMetaDataParameters);

    for (Long nodeId : nodeIds) {
      Status status = nodeDAO.getNodeIdStatus(nodeId);
      if (status == null) {
        // We've been called with the ID of a purged node, probably due to processing a transaction
        // with a
        // cascading delete. Fine to skip and assume it will be processed in a transaction.
        // See org.alfresco.solr.tracker.CoreTracker.updateDescendantAuxDocs(NodeMetaData, boolean,
        // SolrIndexSearcher)
        continue;
      }
      NodeRef nodeRef = status.getNodeRef();

      NodeMetaData nodeMetaData = new NodeMetaData();
      nodeMetaData.setNodeId(nodeId);

      if (includeNodeRef) {
        nodeMetaData.setNodeRef(tenantService.getBaseName(nodeRef, true));
      }

      if (includeTxnId) {
        nodeMetaData.setTxnId(status.getDbTxnId());
      }

      if (status.isDeleted()) {
        rowHandler.processResult(nodeMetaData);
        continue;
      }

      Map<QName, Serializable> props = null;
      Set<QName> aspects = null;

      nodeMetaData.setAclId(nodeDAO.getNodeAclId(nodeId));

      if (includeType) {
        QName nodeType = getNodeType(nodeId);
        if (nodeType != null) {
          nodeMetaData.setNodeType(nodeType);
        } else {
          throw new AlfrescoRuntimeException("Nodes with no type are ignored by SOLR");
        }
      }

      if (includeProperties) {
        if (props == null) {
          props = getProperties(nodeId);
        }
        nodeMetaData.setProperties(props);
      } else {
        nodeMetaData.setProperties(Collections.<QName, Serializable>emptyMap());
      }

      if (includeAspects || includePaths || includeParentAssociations) {
        aspects = getNodeAspects(nodeId);
      }
      nodeMetaData.setAspects(aspects);

      boolean ignoreLargeMetadata =
          (typeIndexFilter.shouldBeIgnored(getNodeType(nodeId))
              || aspectIndexFilter.shouldBeIgnored(getNodeAspects(nodeId)));
      if (!ignoreLargeMetadata
          && (typeIndexFilter.isIgnorePathsForSpecificTypes()
              || aspectIndexFilter.isIgnorePathsForSpecificAspects())) {
        // check if parent should be ignored
        final List<Long> parentIds = new LinkedList<Long>();
        nodeDAO.getParentAssocs(
            nodeId,
            null,
            null,
            true,
            new ChildAssocRefQueryCallback() {
              @Override
              public boolean preLoadNodes() {
                return false;
              }

              @Override
              public boolean orderResults() {
                return false;
              }

              @Override
              public boolean handle(
                  Pair<Long, ChildAssociationRef> childAssocPair,
                  Pair<Long, NodeRef> parentNodePair,
                  Pair<Long, NodeRef> childNodePair) {
                parentIds.add(parentNodePair.getFirst());
                return false;
              }

              @Override
              public void done() {}
            });

        if (!parentIds.isEmpty()) {
          Long parentId = parentIds.iterator().next();
          if (typeIndexFilter.isIgnorePathsForSpecificTypes()) {
            QName parentType = getNodeType(parentId);
            ignoreLargeMetadata = typeIndexFilter.shouldBeIgnored(parentType);
          }
          if (!ignoreLargeMetadata && aspectIndexFilter.isIgnorePathsForSpecificAspects()) {
            ignoreLargeMetadata = aspectIndexFilter.shouldBeIgnored(getNodeAspects(parentId));
          }
        }
      }

      CategoryPaths categoryPaths =
          new CategoryPaths(
              new ArrayList<Pair<Path, QName>>(), new ArrayList<ChildAssociationRef>());
      if (!ignoreLargeMetadata && (includePaths || includeParentAssociations)) {
        if (props == null) {
          props = getProperties(nodeId);
        }
        categoryPaths = getCategoryPaths(status.getNodeRef(), aspects, props);
      }

      if (includePaths && !ignoreLargeMetadata) {
        if (props == null) {
          props = getProperties(nodeId);
        }

        List<Path> directPaths =
            nodeDAO.getPaths(new Pair<Long, NodeRef>(nodeId, status.getNodeRef()), false);

        Collection<Pair<Path, QName>> paths =
            new ArrayList<Pair<Path, QName>>(directPaths.size() + categoryPaths.getPaths().size());
        for (Path path : directPaths) {
          paths.add(new Pair<Path, QName>(path.getBaseNamePath(tenantService), null));
        }
        for (Pair<Path, QName> catPair : categoryPaths.getPaths()) {
          paths.add(
              new Pair<Path, QName>(
                  catPair.getFirst().getBaseNamePath(tenantService), catPair.getSecond()));
        }

        nodeMetaData.setPaths(paths);

        // Calculate name path
        Collection<Collection<String>> namePaths = new ArrayList<Collection<String>>(2);
        nodeMetaData.setNamePaths(namePaths);
        for (Pair<Path, QName> catPair : paths) {
          Path path = catPair.getFirst();

          boolean added = false;
          List<String> namePath = new ArrayList<String>(path.size());
          NEXT_ELEMENT:
          for (Path.Element pathElement : path) {
            if (!(pathElement instanceof ChildAssocElement)) {
              // This is some path element that is terminal to a cm:name path
              break;
            }
            ChildAssocElement pathChildAssocElement = (ChildAssocElement) pathElement;
            NodeRef childNodeRef = pathChildAssocElement.getRef().getChildRef();
            Pair<Long, NodeRef> childNodePair = nodeDAO.getNodePair(childNodeRef);
            if (childNodePair == null) {
              // Gone
              break;
            }
            Long childNodeId = childNodePair.getFirst();
            String childNodeName =
                (String) nodeDAO.getNodeProperty(childNodeId, ContentModel.PROP_NAME);
            if (childNodeName == null) {
              // We have hit a non-name node, which acts as a root for cm:name
              // DH: There is no particular constraint here.  This is just a decision made.
              namePath.clear();
              // We have to continue down the path as there could be a name path lower down
              continue NEXT_ELEMENT;
            }
            // We can finally add the name to the path
            namePath.add(childNodeName);
            // Add the path if this is the first entry in the name path
            if (!added) {
              namePaths.add(namePath);
              added = true;
            }
          }
        }
      }

      nodeMetaData.setTenantDomain(tenantService.getDomain(nodeRef.getStoreRef().getIdentifier()));

      if (includeChildAssociations) {
        final List<ChildAssociationRef> childAssocs = new ArrayList<ChildAssociationRef>(100);
        nodeDAO.getChildAssocs(
            nodeId,
            null,
            null,
            null,
            null,
            null,
            new ChildAssocRefQueryCallback() {
              @Override
              public boolean preLoadNodes() {
                return false;
              }

              @Override
              public boolean orderResults() {
                return false;
              }

              @Override
              public boolean handle(
                  Pair<Long, ChildAssociationRef> childAssocPair,
                  Pair<Long, NodeRef> parentNodePair,
                  Pair<Long, NodeRef> childNodePair) {
                boolean addCurrentChildAssoc = true;
                if (typeIndexFilter.isIgnorePathsForSpecificTypes()) {
                  QName nodeType = nodeDAO.getNodeType(childNodePair.getFirst());
                  addCurrentChildAssoc = !typeIndexFilter.shouldBeIgnored(nodeType);
                }
                if (!addCurrentChildAssoc && aspectIndexFilter.isIgnorePathsForSpecificAspects()) {
                  addCurrentChildAssoc =
                      !aspectIndexFilter.shouldBeIgnored(getNodeAspects(childNodePair.getFirst()));
                }
                if (addCurrentChildAssoc) {
                  childAssocs.add(tenantService.getBaseName(childAssocPair.getSecond(), true));
                }
                return true;
              }

              @Override
              public void done() {}
            });
        nodeMetaData.setChildAssocs(childAssocs);
      }

      if (includeChildIds) {
        final List<Long> childIds = new ArrayList<Long>(100);
        nodeDAO.getChildAssocs(
            nodeId,
            null,
            null,
            null,
            null,
            null,
            new ChildAssocRefQueryCallback() {
              @Override
              public boolean preLoadNodes() {
                return false;
              }

              @Override
              public boolean orderResults() {
                return false;
              }

              @Override
              public boolean handle(
                  Pair<Long, ChildAssociationRef> childAssocPair,
                  Pair<Long, NodeRef> parentNodePair,
                  Pair<Long, NodeRef> childNodePair) {
                boolean addCurrentId = true;
                if (typeIndexFilter.isIgnorePathsForSpecificTypes()) {
                  QName nodeType = nodeDAO.getNodeType(childNodePair.getFirst());
                  addCurrentId = !typeIndexFilter.shouldBeIgnored(nodeType);
                }
                if (!addCurrentId) {
                  addCurrentId =
                      !aspectIndexFilter.shouldBeIgnored(getNodeAspects(childNodePair.getFirst()));
                }
                if (addCurrentId) {
                  childIds.add(childNodePair.getFirst());
                }
                return true;
              }

              @Override
              public void done() {}
            });
        nodeMetaData.setChildIds(childIds);
      }

      if (includeParentAssociations && !ignoreLargeMetadata) {
        final List<ChildAssociationRef> parentAssocs = new ArrayList<ChildAssociationRef>(100);
        nodeDAO.getParentAssocs(
            nodeId,
            null,
            null,
            null,
            new ChildAssocRefQueryCallback() {
              @Override
              public boolean preLoadNodes() {
                return false;
              }

              @Override
              public boolean orderResults() {
                return false;
              }

              @Override
              public boolean handle(
                  Pair<Long, ChildAssociationRef> childAssocPair,
                  Pair<Long, NodeRef> parentNodePair,
                  Pair<Long, NodeRef> childNodePair) {
                parentAssocs.add(tenantService.getBaseName(childAssocPair.getSecond(), true));
                return true;
              }

              @Override
              public void done() {}
            });
        for (ChildAssociationRef ref : categoryPaths.getCategoryParents()) {
          parentAssocs.add(tenantService.getBaseName(ref, true));
        }

        CRC32 crc = new CRC32();
        for (ChildAssociationRef car : parentAssocs) {
          try {
            crc.update(car.toString().getBytes("UTF-8"));
          } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("UTF-8 encoding is not supported");
          }
        }
        nodeMetaData.setParentAssocs(parentAssocs, crc.getValue());

        // TODO non-child associations
        //                Collection<Pair<Long, AssociationRef>> sourceAssocs =
        // nodeDAO.getSourceNodeAssocs(nodeId);
        //                Collection<Pair<Long, AssociationRef>> targetAssocs =
        // nodeDAO.getTargetNodeAssocs(nodeId);
        //
        //                nodeMetaData.setAssocs();
      }

      if (includeOwner) {
        // cached in OwnableService
        nodeMetaData.setOwner(ownableService.getOwner(status.getNodeRef()));
      }

      rowHandler.processResult(nodeMetaData);
    }
  }
  private CategoryPaths getCategoryPaths(
      NodeRef nodeRef, Set<QName> aspects, Map<QName, Serializable> properties) {
    ArrayList<Pair<Path, QName>> categoryPaths = new ArrayList<Pair<Path, QName>>();
    ArrayList<ChildAssociationRef> categoryParents = new ArrayList<ChildAssociationRef>();

    nodeDAO.setCheckNodeConsistency();
    for (QName classRef : aspects) {
      AspectDefinition aspDef = dictionaryService.getAspect(classRef);
      if (!isCategorised(aspDef)) {
        continue;
      }
      LinkedList<Pair<Path, QName>> aspectPaths = new LinkedList<Pair<Path, QName>>();
      for (PropertyDefinition propDef : aspDef.getProperties().values()) {
        if (!propDef.getDataType().getName().equals(DataTypeDefinition.CATEGORY)) {
          // The property is not a category
          continue;
        }
        // Don't try to iterate if the property is null
        Serializable propVal = properties.get(propDef.getName());
        if (propVal == null) {
          continue;
        }
        for (NodeRef catRef : DefaultTypeConverter.INSTANCE.getCollection(NodeRef.class, propVal)) {
          if (catRef == null) {
            continue;
          }
          // can be running in context of System user, hence use input nodeRef
          catRef = tenantService.getName(nodeRef, catRef);

          try {
            Pair<Long, NodeRef> pair = nodeDAO.getNodePair(catRef);
            if (pair != null) {
              for (Path path : nodeDAO.getPaths(pair, false)) {
                aspectPaths.add(new Pair<Path, QName>(path, aspDef.getName()));
              }
            }
          } catch (InvalidNodeRefException e) {
            // If the category does not exists we move on the next
          }
        }
      }
      categoryPaths.addAll(aspectPaths);
    }
    // Add member final element
    for (Pair<Path, QName> pair : categoryPaths) {
      if (pair.getFirst().last() instanceof Path.ChildAssocElement) {
        Path.ChildAssocElement cae = (Path.ChildAssocElement) pair.getFirst().last();
        ChildAssociationRef assocRef = cae.getRef();
        ChildAssociationRef categoryParentRef =
            new ChildAssociationRef(
                assocRef.getTypeQName(),
                assocRef.getChildRef(),
                QName.createQName("member"),
                nodeRef);
        pair.getFirst().append(new Path.ChildAssocElement(categoryParentRef));
        categoryParents.add(categoryParentRef);
      }
    }

    return new CategoryPaths(categoryPaths, categoryParents);
  }
 @Override
 public Long getMaxTxnId() {
   long maxCommitTime = System.currentTimeMillis() + 1L;
   nodeDAO.setCheckNodeConsistency();
   return nodeDAO.getMaxTxnIdByCommitTime(maxCommitTime);
 }
 @Override
 public Long getMaxTxnCommitTime() {
   nodeDAO.setCheckNodeConsistency();
   return nodeDAO.getMaxTxnCommitTime();
 }