mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-31 17:39:05 +00:00
Feature/acs 1425 long running tmdq (#417)
* ACS-1425 Add store info to result * ACS-1425 Fix unit tests
This commit is contained in:
@@ -1,460 +1,480 @@
|
|||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
* Alfresco Repository
|
* Alfresco Repository
|
||||||
* %%
|
* %%
|
||||||
* Copyright (C) 2005 - 2021 Alfresco Software Limited
|
* Copyright (C) 2005 - 2021 Alfresco Software Limited
|
||||||
* %%
|
* %%
|
||||||
* This file is part of the Alfresco software.
|
* This file is part of the Alfresco software.
|
||||||
* If the software was purchased under a paid Alfresco license, the terms of
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
* the paid license agreement will prevail. Otherwise, the software is
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
* provided under the following open source license terms:
|
* provided under the following open source license terms:
|
||||||
*
|
*
|
||||||
* Alfresco is free software: you can redistribute it and/or modify
|
* Alfresco is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* Alfresco is distributed in the hope that it will be useful,
|
* Alfresco is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU Lesser General Public License for more details.
|
* GNU Lesser General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
package org.alfresco.repo.search.impl.querymodel.impl.db;
|
package org.alfresco.repo.search.impl.querymodel.impl.db;
|
||||||
|
|
||||||
import static org.alfresco.repo.domain.node.AbstractNodeDAOImpl.CACHE_REGION_NODES;
|
import static org.alfresco.repo.domain.node.AbstractNodeDAOImpl.CACHE_REGION_NODES;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.BitSet;
|
import java.util.BitSet;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.annotation.concurrent.NotThreadSafe;
|
import javax.annotation.concurrent.NotThreadSafe;
|
||||||
|
|
||||||
import org.alfresco.model.ContentModel;
|
import org.alfresco.model.ContentModel;
|
||||||
import org.alfresco.repo.admin.patch.OptionalPatchApplicationCheckBootstrapBean;
|
import org.alfresco.repo.admin.patch.OptionalPatchApplicationCheckBootstrapBean;
|
||||||
import org.alfresco.repo.cache.SimpleCache;
|
import org.alfresco.repo.cache.SimpleCache;
|
||||||
import org.alfresco.repo.cache.lookup.EntityLookupCache;
|
import org.alfresco.repo.cache.lookup.EntityLookupCache;
|
||||||
import org.alfresco.repo.cache.lookup.EntityLookupCache.EntityLookupCallbackDAOAdaptor;
|
import org.alfresco.repo.cache.lookup.EntityLookupCache.EntityLookupCallbackDAOAdaptor;
|
||||||
import org.alfresco.repo.domain.node.Node;
|
import org.alfresco.repo.domain.node.Node;
|
||||||
import org.alfresco.repo.domain.node.NodeDAO;
|
import org.alfresco.repo.domain.node.NodeDAO;
|
||||||
import org.alfresco.repo.domain.permissions.AclCrudDAO;
|
import org.alfresco.repo.domain.node.StoreEntity;
|
||||||
import org.alfresco.repo.domain.permissions.Authority;
|
import org.alfresco.repo.domain.permissions.AclCrudDAO;
|
||||||
import org.alfresco.repo.domain.qname.QNameDAO;
|
import org.alfresco.repo.domain.permissions.Authority;
|
||||||
import org.alfresco.repo.search.SimpleResultSetMetaData;
|
import org.alfresco.repo.domain.qname.QNameDAO;
|
||||||
import org.alfresco.repo.search.impl.lucene.PagingLuceneResultSet;
|
import org.alfresco.repo.search.SimpleResultSetMetaData;
|
||||||
import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext;
|
import org.alfresco.repo.search.impl.lucene.PagingLuceneResultSet;
|
||||||
import org.alfresco.repo.search.impl.querymodel.Query;
|
import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext;
|
||||||
import org.alfresco.repo.search.impl.querymodel.QueryEngine;
|
import org.alfresco.repo.search.impl.querymodel.Query;
|
||||||
import org.alfresco.repo.search.impl.querymodel.QueryEngineResults;
|
import org.alfresco.repo.search.impl.querymodel.QueryEngine;
|
||||||
import org.alfresco.repo.search.impl.querymodel.QueryModelException;
|
import org.alfresco.repo.search.impl.querymodel.QueryEngineResults;
|
||||||
import org.alfresco.repo.search.impl.querymodel.QueryModelFactory;
|
import org.alfresco.repo.search.impl.querymodel.QueryModelException;
|
||||||
import org.alfresco.repo.search.impl.querymodel.QueryOptions;
|
import org.alfresco.repo.search.impl.querymodel.QueryModelFactory;
|
||||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
import org.alfresco.repo.search.impl.querymodel.QueryOptions;
|
||||||
import org.alfresco.repo.security.permissions.impl.acegi.FilteringResultSet;
|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||||
import org.alfresco.repo.tenant.TenantService;
|
import org.alfresco.repo.security.permissions.impl.acegi.FilteringResultSet;
|
||||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
import org.alfresco.repo.tenant.TenantService;
|
||||||
import org.alfresco.service.cmr.repository.NodeRef;
|
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||||
import org.alfresco.service.cmr.repository.NodeService;
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
import org.alfresco.service.cmr.repository.StoreRef;
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
import org.alfresco.service.cmr.search.LimitBy;
|
import org.alfresco.service.cmr.repository.StoreRef;
|
||||||
import org.alfresco.service.cmr.search.PermissionEvaluationMode;
|
import org.alfresco.service.cmr.search.LimitBy;
|
||||||
import org.alfresco.service.cmr.search.ResultSet;
|
import org.alfresco.service.cmr.search.PermissionEvaluationMode;
|
||||||
import org.alfresco.service.cmr.security.PermissionService;
|
import org.alfresco.service.cmr.search.ResultSet;
|
||||||
import org.alfresco.service.namespace.NamespaceService;
|
import org.alfresco.service.cmr.security.PermissionService;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.NamespaceService;
|
||||||
import org.alfresco.util.Pair;
|
import org.alfresco.service.namespace.QName;
|
||||||
import org.apache.commons.logging.Log;
|
import org.alfresco.util.Pair;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.ibatis.session.ResultContext;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.apache.ibatis.session.ResultHandler;
|
import org.apache.ibatis.session.ResultContext;
|
||||||
import org.mybatis.spring.SqlSessionTemplate;
|
import org.apache.ibatis.session.ResultHandler;
|
||||||
|
import org.mybatis.spring.SqlSessionTemplate;
|
||||||
/**
|
|
||||||
* @author Andy
|
/**
|
||||||
*/
|
* @author Andy
|
||||||
@NotThreadSafe
|
*/
|
||||||
public class DBQueryEngine implements QueryEngine
|
@NotThreadSafe
|
||||||
{
|
public class DBQueryEngine implements QueryEngine
|
||||||
protected static final Log logger = LogFactory.getLog(DBQueryEngine.class);
|
{
|
||||||
|
protected static final Log logger = LogFactory.getLog(DBQueryEngine.class);
|
||||||
protected static final String SELECT_BY_DYNAMIC_QUERY = "alfresco.metadata.query.select_byDynamicQuery";
|
|
||||||
|
protected static final String SELECT_BY_DYNAMIC_QUERY = "alfresco.metadata.query.select_byDynamicQuery";
|
||||||
protected SqlSessionTemplate template;
|
|
||||||
|
protected SqlSessionTemplate template;
|
||||||
protected QNameDAO qnameDAO;
|
|
||||||
|
protected QNameDAO qnameDAO;
|
||||||
private NodeDAO nodeDAO;
|
|
||||||
|
private NodeDAO nodeDAO;
|
||||||
protected DictionaryService dictionaryService;
|
|
||||||
|
protected DictionaryService dictionaryService;
|
||||||
protected NamespaceService namespaceService;
|
|
||||||
|
protected NamespaceService namespaceService;
|
||||||
protected NodeService nodeService;
|
|
||||||
|
protected NodeService nodeService;
|
||||||
private TenantService tenantService;
|
|
||||||
|
private TenantService tenantService;
|
||||||
private OptionalPatchApplicationCheckBootstrapBean metadataIndexCheck2;
|
|
||||||
|
private OptionalPatchApplicationCheckBootstrapBean metadataIndexCheck2;
|
||||||
protected PermissionService permissionService;
|
|
||||||
|
protected PermissionService permissionService;
|
||||||
private int maxPermissionChecks;
|
|
||||||
|
private int maxPermissionChecks;
|
||||||
private long maxPermissionCheckTimeMillis;
|
|
||||||
|
private long maxPermissionCheckTimeMillis;
|
||||||
protected EntityLookupCache<Long, Node, NodeRef> nodesCache;
|
|
||||||
|
protected EntityLookupCache<Long, Node, NodeRef> nodesCache;
|
||||||
AclCrudDAO aclCrudDAO;
|
|
||||||
|
AclCrudDAO aclCrudDAO;
|
||||||
public void setAclCrudDAO(AclCrudDAO aclCrudDAO)
|
|
||||||
{
|
public void setAclCrudDAO(AclCrudDAO aclCrudDAO)
|
||||||
this.aclCrudDAO = aclCrudDAO;
|
{
|
||||||
}
|
this.aclCrudDAO = aclCrudDAO;
|
||||||
|
}
|
||||||
public void setMaxPermissionChecks(int maxPermissionChecks)
|
|
||||||
{
|
public void setMaxPermissionChecks(int maxPermissionChecks)
|
||||||
this.maxPermissionChecks = maxPermissionChecks;
|
{
|
||||||
}
|
this.maxPermissionChecks = maxPermissionChecks;
|
||||||
|
}
|
||||||
public void setMaxPermissionCheckTimeMillis(long maxPermissionCheckTimeMillis)
|
|
||||||
{
|
public void setMaxPermissionCheckTimeMillis(long maxPermissionCheckTimeMillis)
|
||||||
this.maxPermissionCheckTimeMillis = maxPermissionCheckTimeMillis;
|
{
|
||||||
}
|
this.maxPermissionCheckTimeMillis = maxPermissionCheckTimeMillis;
|
||||||
|
}
|
||||||
public void setTemplate(SqlSessionTemplate template)
|
|
||||||
{
|
public void setTemplate(SqlSessionTemplate template)
|
||||||
this.template = template;
|
{
|
||||||
}
|
this.template = template;
|
||||||
|
}
|
||||||
public void setPermissionService(PermissionService permissionService)
|
|
||||||
{
|
public void setPermissionService(PermissionService permissionService)
|
||||||
this.permissionService = permissionService;
|
{
|
||||||
}
|
this.permissionService = permissionService;
|
||||||
|
}
|
||||||
public void setMetadataIndexCheck2(OptionalPatchApplicationCheckBootstrapBean metadataIndexCheck2)
|
|
||||||
{
|
public void setMetadataIndexCheck2(OptionalPatchApplicationCheckBootstrapBean metadataIndexCheck2)
|
||||||
this.metadataIndexCheck2 = metadataIndexCheck2;
|
{
|
||||||
}
|
this.metadataIndexCheck2 = metadataIndexCheck2;
|
||||||
|
}
|
||||||
public void setTenantService(TenantService tenantService)
|
|
||||||
{
|
public void setTenantService(TenantService tenantService)
|
||||||
this.tenantService = tenantService;
|
{
|
||||||
}
|
this.tenantService = tenantService;
|
||||||
|
}
|
||||||
public final void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate)
|
|
||||||
{
|
public final void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate)
|
||||||
this.template = sqlSessionTemplate;
|
{
|
||||||
}
|
this.template = sqlSessionTemplate;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* @param qnameDAO
|
/**
|
||||||
* the qnameDAO to set
|
* @param qnameDAO
|
||||||
*/
|
* the qnameDAO to set
|
||||||
public void setQnameDAO(QNameDAO qnameDAO)
|
*/
|
||||||
{
|
public void setQnameDAO(QNameDAO qnameDAO)
|
||||||
this.qnameDAO = qnameDAO;
|
{
|
||||||
}
|
this.qnameDAO = qnameDAO;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* @param dictionaryService
|
/**
|
||||||
* the dictionaryService to set
|
* @param dictionaryService
|
||||||
*/
|
* the dictionaryService to set
|
||||||
public void setDictionaryService(DictionaryService dictionaryService)
|
*/
|
||||||
{
|
public void setDictionaryService(DictionaryService dictionaryService)
|
||||||
this.dictionaryService = dictionaryService;
|
{
|
||||||
}
|
this.dictionaryService = dictionaryService;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* @param namespaceService
|
/**
|
||||||
* the namespaceService to set
|
* @param namespaceService
|
||||||
*/
|
* the namespaceService to set
|
||||||
public void setNamespaceService(NamespaceService namespaceService)
|
*/
|
||||||
{
|
public void setNamespaceService(NamespaceService namespaceService)
|
||||||
this.namespaceService = namespaceService;
|
{
|
||||||
}
|
this.namespaceService = namespaceService;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* @param nodeService the nodeService to set
|
/**
|
||||||
*/
|
* @param nodeService the nodeService to set
|
||||||
public void setNodeService(NodeService nodeService)
|
*/
|
||||||
{
|
public void setNodeService(NodeService nodeService)
|
||||||
this.nodeService = nodeService;
|
{
|
||||||
}
|
this.nodeService = nodeService;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* @param nodeDAO the nodeDAO to set
|
/**
|
||||||
*/
|
* @param nodeDAO the nodeDAO to set
|
||||||
public void setNodeDAO(NodeDAO nodeDAO)
|
*/
|
||||||
{
|
public void setNodeDAO(NodeDAO nodeDAO)
|
||||||
this.nodeDAO = nodeDAO;
|
{
|
||||||
}
|
this.nodeDAO = nodeDAO;
|
||||||
|
}
|
||||||
/*
|
|
||||||
* (non-Javadoc)
|
/*
|
||||||
* @see
|
* (non-Javadoc)
|
||||||
* org.alfresco.repo.search.impl.querymodel.QueryEngine#executeQuery(org.alfresco.repo.search.impl.querymodel.Query,
|
* @see
|
||||||
* org.alfresco.repo.search.impl.querymodel.QueryOptions,
|
* org.alfresco.repo.search.impl.querymodel.QueryEngine#executeQuery(org.alfresco.repo.search.impl.querymodel.Query,
|
||||||
* org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext)
|
* org.alfresco.repo.search.impl.querymodel.QueryOptions,
|
||||||
*/
|
* org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext)
|
||||||
@Override
|
*/
|
||||||
public QueryEngineResults executeQuery(Query query, QueryOptions options, FunctionEvaluationContext functionContext)
|
@Override
|
||||||
{
|
public QueryEngineResults executeQuery(Query query, QueryOptions options, FunctionEvaluationContext functionContext)
|
||||||
long start = 0;
|
{
|
||||||
if (logger.isDebugEnabled())
|
long start = 0;
|
||||||
{
|
if (logger.isDebugEnabled())
|
||||||
start = System.currentTimeMillis();
|
{
|
||||||
logger.debug("Query request received");
|
start = System.currentTimeMillis();
|
||||||
}
|
logger.debug("Query request received");
|
||||||
|
}
|
||||||
Set<String> selectorGroup = null;
|
|
||||||
if (query.getSource() != null)
|
Set<String> selectorGroup = null;
|
||||||
{
|
if (query.getSource() != null)
|
||||||
List<Set<String>> selectorGroups = query.getSource().getSelectorGroups(functionContext);
|
{
|
||||||
|
List<Set<String>> selectorGroups = query.getSource().getSelectorGroups(functionContext);
|
||||||
if (selectorGroups.size() == 0)
|
|
||||||
{
|
if (selectorGroups.size() == 0)
|
||||||
throw new QueryModelException("No selectors");
|
{
|
||||||
}
|
throw new QueryModelException("No selectors");
|
||||||
|
}
|
||||||
if (selectorGroups.size() > 1)
|
|
||||||
{
|
if (selectorGroups.size() > 1)
|
||||||
throw new QueryModelException("Advanced join is not supported");
|
{
|
||||||
}
|
throw new QueryModelException("Advanced join is not supported");
|
||||||
|
}
|
||||||
selectorGroup = selectorGroups.get(0);
|
|
||||||
}
|
selectorGroup = selectorGroups.get(0);
|
||||||
|
}
|
||||||
DBQuery dbQuery = (DBQuery)query;
|
|
||||||
|
DBQuery dbQuery = (DBQuery)query;
|
||||||
if (options.getStores().size() > 1)
|
|
||||||
{
|
if (options.getStores().size() > 1)
|
||||||
throw new QueryModelException("Multi-store queries are not supported");
|
{
|
||||||
}
|
throw new QueryModelException("Multi-store queries are not supported");
|
||||||
|
}
|
||||||
// MT
|
|
||||||
StoreRef storeRef = options.getStores().get(0);
|
// MT
|
||||||
storeRef = storeRef != null ? tenantService.getName(storeRef) : null;
|
StoreRef storeRef = options.getStores().get(0);
|
||||||
|
storeRef = storeRef != null ? tenantService.getName(storeRef) : null;
|
||||||
Pair<Long, StoreRef> store = nodeDAO.getStore(storeRef);
|
|
||||||
if (store == null)
|
Pair<Long, StoreRef> store = nodeDAO.getStore(storeRef);
|
||||||
{
|
if (store == null)
|
||||||
throw new QueryModelException("Unknown store: "+storeRef);
|
{
|
||||||
}
|
throw new QueryModelException("Unknown store: "+storeRef);
|
||||||
dbQuery.setStoreId(store.getFirst());
|
}
|
||||||
Pair<Long, QName> sysDeletedType = qnameDAO.getQName(ContentModel.TYPE_DELETED);
|
dbQuery.setStoreId(store.getFirst());
|
||||||
if (sysDeletedType == null)
|
Pair<Long, QName> sysDeletedType = qnameDAO.getQName(ContentModel.TYPE_DELETED);
|
||||||
{
|
if (sysDeletedType == null)
|
||||||
dbQuery.setSysDeletedType(-1L);
|
{
|
||||||
}
|
dbQuery.setSysDeletedType(-1L);
|
||||||
else
|
}
|
||||||
{
|
else
|
||||||
dbQuery.setSysDeletedType(sysDeletedType.getFirst());
|
{
|
||||||
}
|
dbQuery.setSysDeletedType(sysDeletedType.getFirst());
|
||||||
|
}
|
||||||
Long sinceTxId = options.getSinceTxId();
|
|
||||||
if (sinceTxId == null)
|
Long sinceTxId = options.getSinceTxId();
|
||||||
{
|
if (sinceTxId == null)
|
||||||
// By default, return search results for all transactions.
|
{
|
||||||
sinceTxId = -1L;
|
// By default, return search results for all transactions.
|
||||||
}
|
sinceTxId = -1L;
|
||||||
dbQuery.setSinceTxId(sinceTxId);
|
}
|
||||||
|
dbQuery.setSinceTxId(sinceTxId);
|
||||||
logger.debug("- query is being prepared");
|
|
||||||
dbQuery.prepare(namespaceService, dictionaryService, qnameDAO, nodeDAO, tenantService, selectorGroup,
|
logger.debug("- query is being prepared");
|
||||||
null, functionContext, metadataIndexCheck2.getPatchApplied());
|
dbQuery.prepare(namespaceService, dictionaryService, qnameDAO, nodeDAO, tenantService, selectorGroup,
|
||||||
|
null, functionContext, metadataIndexCheck2.getPatchApplied());
|
||||||
ResultSet resultSet;
|
|
||||||
resultSet = selectNodesWithPermissions(options, dbQuery);
|
ResultSet resultSet;
|
||||||
if (logger.isDebugEnabled())
|
resultSet = selectNodesWithPermissions(options, dbQuery);
|
||||||
{
|
if (logger.isDebugEnabled())
|
||||||
long ms = System.currentTimeMillis() - start;
|
{
|
||||||
logger.debug("Selected " + resultSet.length() + " nodes with permission resolution in "+ms+" ms");
|
long ms = System.currentTimeMillis() - start;
|
||||||
}
|
logger.debug("Selected " + resultSet.length() + " nodes with permission resolution in "+ms+" ms");
|
||||||
return asQueryEngineResults(resultSet);
|
}
|
||||||
}
|
return asQueryEngineResults(resultSet);
|
||||||
|
}
|
||||||
protected String pickQueryTemplate(QueryOptions options, DBQuery dbQuery)
|
|
||||||
{
|
protected String pickQueryTemplate(QueryOptions options, DBQuery dbQuery)
|
||||||
logger.debug("- using standard table for the query");
|
{
|
||||||
return SELECT_BY_DYNAMIC_QUERY;
|
logger.debug("- using standard table for the query");
|
||||||
}
|
return SELECT_BY_DYNAMIC_QUERY;
|
||||||
|
}
|
||||||
private ResultSet selectNodesWithPermissions(QueryOptions options, DBQuery dbQuery)
|
|
||||||
{
|
private ResultSet selectNodesWithPermissions(QueryOptions options, DBQuery dbQuery)
|
||||||
Authority authority = aclCrudDAO.getAuthority(AuthenticationUtil.getRunAsUser());
|
{
|
||||||
|
Authority authority = aclCrudDAO.getAuthority(AuthenticationUtil.getRunAsUser());
|
||||||
NodePermissionAssessor permissionAssessor = createAssessor(authority);
|
|
||||||
int maxPermsChecks = options.getMaxPermissionChecks() < 0 ? maxPermissionChecks : options.getMaxPermissionChecks();
|
NodePermissionAssessor permissionAssessor = createAssessor(authority);
|
||||||
long maxPermCheckTimeMillis = options.getMaxPermissionCheckTimeMillis() < 0
|
int maxPermsChecks = options.getMaxPermissionChecks() < 0 ? maxPermissionChecks : options.getMaxPermissionChecks();
|
||||||
? maxPermissionCheckTimeMillis
|
long maxPermCheckTimeMillis = options.getMaxPermissionCheckTimeMillis() < 0
|
||||||
: options.getMaxPermissionCheckTimeMillis();
|
? maxPermissionCheckTimeMillis
|
||||||
permissionAssessor.setMaxPermissionChecks(maxPermsChecks);
|
: options.getMaxPermissionCheckTimeMillis();
|
||||||
permissionAssessor.setMaxPermissionCheckTimeMillis(maxPermCheckTimeMillis);
|
permissionAssessor.setMaxPermissionChecks(maxPermsChecks);
|
||||||
|
permissionAssessor.setMaxPermissionCheckTimeMillis(maxPermCheckTimeMillis);
|
||||||
FilteringResultSet resultSet = acceleratedNodeSelection(options, dbQuery, permissionAssessor);
|
|
||||||
|
FilteringResultSet resultSet = acceleratedNodeSelection(options, dbQuery, permissionAssessor);
|
||||||
PagingLuceneResultSet plrs = new PagingLuceneResultSet(resultSet, options.getAsSearchParmeters(), nodeService);
|
|
||||||
plrs.setTrimmedResultSet(true);
|
PagingLuceneResultSet plrs = new PagingLuceneResultSet(resultSet, options.getAsSearchParmeters(), nodeService);
|
||||||
return plrs;
|
plrs.setTrimmedResultSet(true);
|
||||||
}
|
return plrs;
|
||||||
|
}
|
||||||
protected NodePermissionAssessor createAssessor(Authority authority)
|
|
||||||
{
|
protected NodePermissionAssessor createAssessor(Authority authority)
|
||||||
return new NodePermissionAssessor(nodeService, permissionService, authority, nodesCache);
|
{
|
||||||
}
|
return new NodePermissionAssessor(nodeService, permissionService, authority, nodesCache);
|
||||||
|
}
|
||||||
FilteringResultSet acceleratedNodeSelection(QueryOptions options, DBQuery dbQuery, NodePermissionAssessor permissionAssessor)
|
|
||||||
{
|
FilteringResultSet acceleratedNodeSelection(QueryOptions options, DBQuery dbQuery, NodePermissionAssessor permissionAssessor)
|
||||||
List<Node> nodes = new ArrayList<>();
|
{
|
||||||
int requiredNodes = computeRequiredNodesCount(options);
|
List<Node> nodes = new ArrayList<>();
|
||||||
|
int requiredNodes = computeRequiredNodesCount(options);
|
||||||
logger.debug("- query sent to the database");
|
|
||||||
template.select(pickQueryTemplate(options, dbQuery), dbQuery, new ResultHandler<Node>()
|
logger.debug("- query sent to the database");
|
||||||
{
|
template.select(pickQueryTemplate(options, dbQuery), dbQuery, new ResultHandler<Node>()
|
||||||
@Override
|
{
|
||||||
public void handleResult(ResultContext<? extends Node> context)
|
@Override
|
||||||
{
|
public void handleResult(ResultContext<? extends Node> context)
|
||||||
doHandleResult(permissionAssessor, nodes, requiredNodes, context);
|
{
|
||||||
}
|
doHandleResult(permissionAssessor, nodes, requiredNodes, context);
|
||||||
|
}
|
||||||
private void doHandleResult(NodePermissionAssessor permissionAssessor, List<Node> nodes,
|
|
||||||
int requiredNodes, ResultContext<? extends Node> context)
|
private void doHandleResult(NodePermissionAssessor permissionAssessor, List<Node> nodes,
|
||||||
{
|
int requiredNodes, ResultContext<? extends Node> context)
|
||||||
if (nodes.size() >= requiredNodes)
|
{
|
||||||
{
|
if (nodes.size() >= requiredNodes)
|
||||||
context.stop();
|
{
|
||||||
return;
|
context.stop();
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
Node node = context.getResultObject();
|
|
||||||
|
Node node = context.getResultObject();
|
||||||
boolean shouldCache = nodes.size() >= options.getSkipCount();
|
addStoreInfo(node);
|
||||||
if(shouldCache)
|
|
||||||
{
|
boolean shouldCache = nodes.size() >= options.getSkipCount();
|
||||||
logger.debug("- selected node "+nodes.size()+": "+node.getUuid()+" "+node.getId());
|
if(shouldCache)
|
||||||
nodesCache.setValue(node.getId(), node);
|
{
|
||||||
}
|
logger.debug("- selected node "+nodes.size()+": "+node.getUuid()+" "+node.getId());
|
||||||
else
|
nodesCache.setValue(node.getId(), node);
|
||||||
{
|
}
|
||||||
logger.debug("- skipped node "+nodes.size()+": "+node.getUuid()+" "+node.getId());
|
else
|
||||||
}
|
{
|
||||||
|
logger.debug("- skipped node "+nodes.size()+": "+node.getUuid()+" "+node.getId());
|
||||||
if (permissionAssessor.isIncluded(node))
|
}
|
||||||
{
|
|
||||||
nodes.add(shouldCache ? node : null);
|
if (permissionAssessor.isIncluded(node))
|
||||||
}
|
{
|
||||||
|
nodes.add(shouldCache ? node : null);
|
||||||
if (permissionAssessor.shouldQuitChecks())
|
}
|
||||||
{
|
|
||||||
context.stop();
|
if (permissionAssessor.shouldQuitChecks())
|
||||||
return;
|
{
|
||||||
}
|
context.stop();
|
||||||
}
|
return;
|
||||||
});
|
}
|
||||||
|
}
|
||||||
int numberFound = nodes.size();
|
});
|
||||||
nodes.removeAll(Collections.singleton(null));
|
|
||||||
|
int numberFound = nodes.size();
|
||||||
DBResultSet rs = createResultSet(options, nodes, numberFound);
|
nodes.removeAll(Collections.singleton(null));
|
||||||
FilteringResultSet frs = new FilteringResultSet(rs, formInclusionMask(nodes));
|
|
||||||
frs.setResultSetMetaData(new SimpleResultSetMetaData(LimitBy.UNLIMITED, PermissionEvaluationMode.EAGER, rs.getResultSetMetaData().getSearchParameters()));
|
DBResultSet rs = createResultSet(options, nodes, numberFound);
|
||||||
|
FilteringResultSet frs = new FilteringResultSet(rs, formInclusionMask(nodes));
|
||||||
logger.debug("- query is completed, "+nodes.size()+" nodes loaded");
|
frs.setResultSetMetaData(new SimpleResultSetMetaData(LimitBy.UNLIMITED, PermissionEvaluationMode.EAGER, rs.getResultSetMetaData().getSearchParameters()));
|
||||||
return frs;
|
|
||||||
}
|
logger.debug("- query is completed, "+nodes.size()+" nodes loaded");
|
||||||
|
return frs;
|
||||||
private DBResultSet createResultSet(QueryOptions options, List<Node> nodes, int numberFound)
|
}
|
||||||
{
|
|
||||||
DBResultSet dbResultSet = new DBResultSet(options.getAsSearchParmeters(), nodes, nodeDAO, nodeService, tenantService, Integer.MAX_VALUE);
|
private DBResultSet createResultSet(QueryOptions options, List<Node> nodes, int numberFound)
|
||||||
dbResultSet.setNumberFound(numberFound);
|
{
|
||||||
return dbResultSet;
|
DBResultSet dbResultSet = new DBResultSet(options.getAsSearchParmeters(), nodes, nodeDAO, nodeService, tenantService, Integer.MAX_VALUE);
|
||||||
}
|
dbResultSet.setNumberFound(numberFound);
|
||||||
|
return dbResultSet;
|
||||||
private int computeRequiredNodesCount(QueryOptions options)
|
}
|
||||||
{
|
|
||||||
int maxItems = options.getMaxItems();
|
private int computeRequiredNodesCount(QueryOptions options)
|
||||||
if (maxItems == -1 || maxItems == Integer.MAX_VALUE)
|
{
|
||||||
{
|
int maxItems = options.getMaxItems();
|
||||||
return Integer.MAX_VALUE;
|
if (maxItems == -1 || maxItems == Integer.MAX_VALUE)
|
||||||
}
|
{
|
||||||
|
return Integer.MAX_VALUE;
|
||||||
return maxItems + options.getSkipCount() + 1;
|
}
|
||||||
}
|
|
||||||
|
return maxItems + options.getSkipCount() + 1;
|
||||||
private BitSet formInclusionMask(List<Node> nodes)
|
}
|
||||||
{
|
|
||||||
BitSet inclusionMask = new BitSet(nodes.size());
|
private BitSet formInclusionMask(List<Node> nodes)
|
||||||
for (int i=0; i < nodes.size(); i++)
|
{
|
||||||
{
|
BitSet inclusionMask = new BitSet(nodes.size());
|
||||||
inclusionMask.set(i, true);
|
for (int i=0; i < nodes.size(); i++)
|
||||||
}
|
{
|
||||||
return inclusionMask;
|
inclusionMask.set(i, true);
|
||||||
}
|
}
|
||||||
|
return inclusionMask;
|
||||||
|
}
|
||||||
private QueryEngineResults asQueryEngineResults(ResultSet paged)
|
|
||||||
{
|
|
||||||
HashSet<String> key = new HashSet<>();
|
private QueryEngineResults asQueryEngineResults(ResultSet paged)
|
||||||
key.add("");
|
{
|
||||||
Map<Set<String>, ResultSet> answer = new HashMap<>();
|
HashSet<String> key = new HashSet<>();
|
||||||
answer.put(key, paged);
|
key.add("");
|
||||||
|
Map<Set<String>, ResultSet> answer = new HashMap<>();
|
||||||
return new QueryEngineResults(answer);
|
answer.put(key, paged);
|
||||||
}
|
|
||||||
|
return new QueryEngineResults(answer);
|
||||||
/*
|
}
|
||||||
* (non-Javadoc)
|
|
||||||
* @see org.alfresco.repo.search.impl.querymodel.QueryEngine#getQueryModelFactory()
|
/*
|
||||||
*/
|
* (non-Javadoc)
|
||||||
@Override
|
* @see org.alfresco.repo.search.impl.querymodel.QueryEngine#getQueryModelFactory()
|
||||||
public QueryModelFactory getQueryModelFactory()
|
*/
|
||||||
{
|
@Override
|
||||||
return new DBQueryModelFactory();
|
public QueryModelFactory getQueryModelFactory()
|
||||||
}
|
{
|
||||||
|
return new DBQueryModelFactory();
|
||||||
/**
|
}
|
||||||
* Injection of nodes cache for clean-up and warm up when required
|
|
||||||
* @param cache The node cache to set
|
/**
|
||||||
*/
|
* Injection of nodes cache for clean-up and warm up when required
|
||||||
public void setNodesCache(SimpleCache<Serializable, Serializable> cache)
|
* @param cache The node cache to set
|
||||||
{
|
*/
|
||||||
this.nodesCache = new EntityLookupCache<>(
|
public void setNodesCache(SimpleCache<Serializable, Serializable> cache)
|
||||||
cache,
|
{
|
||||||
CACHE_REGION_NODES,
|
this.nodesCache = new EntityLookupCache<>(
|
||||||
new ReadonlyLocalCallbackDAO());
|
cache,
|
||||||
}
|
CACHE_REGION_NODES,
|
||||||
|
new ReadonlyLocalCallbackDAO());
|
||||||
void setNodesCache(EntityLookupCache<Long, Node, NodeRef> nodesCache)
|
}
|
||||||
{
|
|
||||||
this.nodesCache = nodesCache;
|
void setNodesCache(EntityLookupCache<Long, Node, NodeRef> nodesCache)
|
||||||
}
|
{
|
||||||
|
this.nodesCache = nodesCache;
|
||||||
private class ReadonlyLocalCallbackDAO extends EntityLookupCallbackDAOAdaptor<Long, Node, NodeRef>
|
}
|
||||||
{
|
|
||||||
@Override
|
private class ReadonlyLocalCallbackDAO extends EntityLookupCallbackDAOAdaptor<Long, Node, NodeRef>
|
||||||
public Pair<Long, Node> createValue(Node value)
|
{
|
||||||
{
|
@Override
|
||||||
throw new UnsupportedOperationException("Node creation is done externally: " + value);
|
public Pair<Long, Node> createValue(Node value)
|
||||||
}
|
{
|
||||||
|
throw new UnsupportedOperationException("Node creation is done externally: " + value);
|
||||||
@Override
|
}
|
||||||
public Pair<Long, Node> findByKey(Long nodeId)
|
|
||||||
{
|
@Override
|
||||||
return null;
|
public Pair<Long, Node> findByKey(Long nodeId)
|
||||||
}
|
{
|
||||||
|
return null;
|
||||||
@Override
|
}
|
||||||
public NodeRef getValueKey(Node value)
|
|
||||||
{
|
@Override
|
||||||
return value.getNodeRef();
|
public NodeRef getValueKey(Node value)
|
||||||
}
|
{
|
||||||
}
|
return value.getNodeRef();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addStoreInfo(Node node)
|
||||||
|
{
|
||||||
|
StoreEntity storeEntity = node.getStore();
|
||||||
|
logger.debug("Adding store info for store id " + storeEntity.getId());
|
||||||
|
List<Pair<Long, StoreRef>> stores = nodeDAO.getStores();
|
||||||
|
for (Pair<Long, StoreRef> storeRefPair : stores)
|
||||||
|
{
|
||||||
|
if (storeEntity.getId() == storeRefPair.getFirst())
|
||||||
|
{
|
||||||
|
StoreRef storeRef = storeRefPair.getSecond();
|
||||||
|
storeEntity.setIdentifier(storeRef.getIdentifier());
|
||||||
|
storeEntity.setProtocol(storeRef.getProtocol());
|
||||||
|
logger.debug("Added store info" + storeEntity.toString());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@@ -37,15 +37,19 @@ import static org.mockito.Mockito.verify;
|
|||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.alfresco.repo.cache.lookup.EntityLookupCache;
|
import org.alfresco.repo.cache.lookup.EntityLookupCache;
|
||||||
import org.alfresco.repo.domain.node.Node;
|
import org.alfresco.repo.domain.node.Node;
|
||||||
|
import org.alfresco.repo.domain.node.NodeDAO;
|
||||||
import org.alfresco.repo.domain.node.StoreEntity;
|
import org.alfresco.repo.domain.node.StoreEntity;
|
||||||
import org.alfresco.repo.search.impl.querymodel.QueryOptions;
|
import org.alfresco.repo.search.impl.querymodel.QueryOptions;
|
||||||
import org.alfresco.repo.security.permissions.impl.acegi.FilteringResultSet;
|
import org.alfresco.repo.security.permissions.impl.acegi.FilteringResultSet;
|
||||||
|
import org.alfresco.service.cmr.repository.StoreRef;
|
||||||
import org.alfresco.service.cmr.search.ResultSet;
|
import org.alfresco.service.cmr.search.ResultSet;
|
||||||
import org.alfresco.service.cmr.search.SearchParameters;
|
import org.alfresco.service.cmr.search.SearchParameters;
|
||||||
|
import org.alfresco.util.Pair;
|
||||||
import org.apache.ibatis.executor.result.DefaultResultContext;
|
import org.apache.ibatis.executor.result.DefaultResultContext;
|
||||||
import org.apache.ibatis.session.ResultContext;
|
import org.apache.ibatis.session.ResultContext;
|
||||||
import org.apache.ibatis.session.ResultHandler;
|
import org.apache.ibatis.session.ResultHandler;
|
||||||
@@ -63,6 +67,7 @@ public class DBQueryEngineTest
|
|||||||
private DBQuery dbQuery;
|
private DBQuery dbQuery;
|
||||||
private ResultContext<Node> resultContext;
|
private ResultContext<Node> resultContext;
|
||||||
private QueryOptions options;
|
private QueryOptions options;
|
||||||
|
private NodeDAO nodeDAO;
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Before
|
@Before
|
||||||
@@ -78,6 +83,10 @@ public class DBQueryEngineTest
|
|||||||
engine.setSqlSessionTemplate(template);
|
engine.setSqlSessionTemplate(template);
|
||||||
|
|
||||||
engine.nodesCache = mock(EntityLookupCache.class);
|
engine.nodesCache = mock(EntityLookupCache.class);
|
||||||
|
|
||||||
|
nodeDAO = mock(NodeDAO.class);
|
||||||
|
engine.setNodeDAO(nodeDAO);
|
||||||
|
mockStores();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -260,4 +269,11 @@ public class DBQueryEngineTest
|
|||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void mockStores()
|
||||||
|
{
|
||||||
|
Pair<Long, StoreRef> spacesStore = new Pair<>(6L, new StoreRef("workspace://SpacesStore"));
|
||||||
|
List<Pair<Long, StoreRef>> stores = Arrays.asList(spacesStore);
|
||||||
|
when(nodeDAO.getStores()).thenReturn(stores);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user