mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
ACS-247 Query accelerator (#234)
Provides hook points for the query accelerator in alfresco-enterprise-repo. In addition to these, it also includes temporary code to add timing headers to REST API calls. These will be removed as part of REPO-5376. Commits mainly by Bruno and Nana. Merging from old projects and changes from master by Alan.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
* #%L
|
||||
* Alfresco Remote API
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
* Copyright (C) 2005 - 2021 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
@@ -25,6 +25,12 @@
|
||||
*/
|
||||
package org.alfresco.rest.api.search;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.alfresco.repo.search.impl.querymodel.impl.db.DBStats;
|
||||
import org.alfresco.repo.search.impl.querymodel.impl.db.SingleTaskRestartableWatch;
|
||||
import org.alfresco.rest.api.model.Node;
|
||||
import org.alfresco.rest.api.search.context.SearchRequestContext;
|
||||
import org.alfresco.rest.api.search.impl.ResultMapper;
|
||||
@@ -45,14 +51,14 @@ import org.alfresco.service.cmr.search.SearchParameters;
|
||||
import org.alfresco.service.cmr.search.SearchService;
|
||||
import org.alfresco.util.ParameterCheck;
|
||||
import org.alfresco.util.PropertyCheck;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.extensions.webscripts.AbstractWebScript;
|
||||
import org.springframework.extensions.webscripts.WebScriptRequest;
|
||||
import org.springframework.extensions.webscripts.WebScriptResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.springframework.util.StopWatch;
|
||||
import org.springframework.util.StopWatch.TaskInfo;
|
||||
|
||||
/**
|
||||
* An implementation of the {{baseUrl}}/{{networkId}}/public/search/versions/1/search endpoint
|
||||
@@ -62,12 +68,15 @@ import java.util.List;
|
||||
public class SearchApiWebscript extends AbstractWebScript implements RecognizedParamsExtractor, RequestReader, ResponseWriter,
|
||||
InitializingBean
|
||||
{
|
||||
protected static final Log logger = LogFactory.getLog(SearchApiWebscript.class);
|
||||
|
||||
private ServiceRegistry serviceRegistry;
|
||||
private SearchService searchService;
|
||||
private SearchMapper searchMapper;
|
||||
private ResultMapper resultMapper;
|
||||
protected ApiAssistant assistant;
|
||||
protected ResourceWebScriptHelper helper;
|
||||
private boolean statsEnabled;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet()
|
||||
@@ -82,7 +91,7 @@ public class SearchApiWebscript extends AbstractWebScript implements RecognizedP
|
||||
@Override
|
||||
public void execute(WebScriptRequest webScriptRequest, WebScriptResponse webScriptResponse) throws IOException
|
||||
{
|
||||
|
||||
StopWatch apiStopWatch = new StopWatch();
|
||||
try {
|
||||
//Turn JSON into a Java object respresentation
|
||||
SearchQuery searchQuery = extractJsonContent(webScriptRequest, assistant.getJsonHelper(), SearchQuery.class);
|
||||
@@ -97,12 +106,43 @@ public class SearchApiWebscript extends AbstractWebScript implements RecognizedP
|
||||
SearchParameters searchParams = searchMapper.toSearchParameters(params, searchQuery, searchRequestContext);
|
||||
|
||||
//Call searchService
|
||||
apiStopWatch.start("nodes");
|
||||
ResultSet results = searchService.query(searchParams);
|
||||
apiStopWatch.stop();
|
||||
|
||||
//Turn solr results into JSON
|
||||
apiStopWatch.start("props");
|
||||
CollectionWithPagingInfo<Node> resultJson = resultMapper.toCollectionWithPagingInfo(params, searchRequestContext, searchQuery, results);
|
||||
|
||||
//Post-process the request and pass in params, eg. params.getFilter()
|
||||
Object toRender = helper.processAdditionsToTheResponse(null, null, null, params, resultJson);
|
||||
apiStopWatch.stop();
|
||||
|
||||
// store execution stats in a special header if enabled
|
||||
if (statsEnabled)
|
||||
{
|
||||
// store execution time in a special header
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append("api={");
|
||||
sb.append("tot=").append(apiStopWatch.getTotalTimeMillis()).append("ms,");
|
||||
addStopWatchStats(sb, apiStopWatch);
|
||||
sb.append("}; ");
|
||||
|
||||
sb.append("db={");
|
||||
addStopWatchStats(sb, DBStats.queryStopWatch());
|
||||
sb.append("}; ");
|
||||
|
||||
sb.append("query={");
|
||||
addStopWatchStats(sb, DBStats.handlerStopWatch());
|
||||
sb.append(",");
|
||||
addStopWatchStats(sb, DBStats.aclReadStopWatch());
|
||||
sb.append(",");
|
||||
addStopWatchStats(sb, DBStats.aclOwnerStopWatch());
|
||||
sb.append("}");
|
||||
|
||||
webScriptResponse.addHeader("X-Response-Stats", sb.toString());
|
||||
}
|
||||
|
||||
//Write response
|
||||
setResponse(webScriptResponse, DEFAULT_SUCCESS);
|
||||
@@ -113,6 +153,44 @@ public class SearchApiWebscript extends AbstractWebScript implements RecognizedP
|
||||
}
|
||||
}
|
||||
|
||||
private void addStopWatchStats(StringBuilder sb, StopWatch watch)
|
||||
{
|
||||
boolean first = true;
|
||||
|
||||
for (TaskInfo task : watch.getTaskInfo())
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append(",");
|
||||
}
|
||||
|
||||
sb.append(task.getTaskName())
|
||||
.append("=")
|
||||
.append(task.getTimeMillis())
|
||||
.append("ms");
|
||||
|
||||
int pc = Math.round(100 * task.getTimeNanos() / watch.getTotalTimeNanos());
|
||||
sb.append("(")
|
||||
.append(pc).append("%")
|
||||
.append(")");
|
||||
}
|
||||
}
|
||||
|
||||
private void addStopWatchStats(StringBuilder sb, SingleTaskRestartableWatch watch)
|
||||
{
|
||||
long decimillis = (watch.getTotalTimeMicros()+5)/100;
|
||||
double millis = decimillis/10.0;
|
||||
|
||||
sb.append(watch.getName())
|
||||
.append("=")
|
||||
.append(millis)
|
||||
.append("ms");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Params object, parameters come from the SearchQuery json not the request
|
||||
* @param webScriptRequest
|
||||
@@ -165,4 +243,10 @@ public class SearchApiWebscript extends AbstractWebScript implements RecognizedP
|
||||
{
|
||||
this.helper = helper;
|
||||
}
|
||||
|
||||
// receiving as a string because of known issue: https://jira.spring.io/browse/SPR-9989
|
||||
public void setStatsEnabled(String enabled) {
|
||||
this.statsEnabled = Boolean.valueOf(enabled);
|
||||
logger.info("API stats header: " + (this.statsEnabled ? "enabled" : "disabled"));
|
||||
}
|
||||
}
|
||||
|
@@ -23,4 +23,7 @@
|
||||
# See issue REPO-2575 for details.
|
||||
alfresco.restApi.basicAuthScheme=false
|
||||
# REPO-4388 allow CORS headers in transaction response
|
||||
webscripts.transaction.preserveHeadersPattern=Access-Control-.*
|
||||
webscripts.transaction.preserveHeadersPattern=Access-Control-.*
|
||||
|
||||
# REPO-5371 enable stats header in API response (only search atm)
|
||||
webscripts.stats.enabled=false
|
@@ -1010,6 +1010,7 @@
|
||||
<property name="helper" ref="webscriptHelper" />
|
||||
<property name="resultMapper" ref="searchapiResultMapper" />
|
||||
<property name="searchMapper" ref="searchapiSearchMapper" />
|
||||
<property name="statsEnabled" value="${webscripts.stats.enabled}" />
|
||||
</bean>
|
||||
|
||||
<bean id="webscript.org.alfresco.api.SearchSQLApiWebscript.post"
|
||||
|
@@ -2,7 +2,7 @@
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2018 Alfresco Software Limited
|
||||
* Copyright (C) 2005 - 2021 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
@@ -217,7 +217,7 @@ public class SqlSessionMetricsWrapper implements SqlSession
|
||||
PassThroughMetricsResultsHandler passThroughHandler = new PassThroughMetricsResultsHandler(handler, startTime, statement);
|
||||
try
|
||||
{
|
||||
this.select(statement, passThroughHandler);
|
||||
this.sqlSession.select(statement, passThroughHandler);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@@ -2,7 +2,7 @@
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
* Copyright (C) 2005 - 2021 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
@@ -117,7 +117,7 @@ import org.springframework.util.Assert;
|
||||
public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
|
||||
{
|
||||
private static final String CACHE_REGION_ROOT_NODES = "N.RN";
|
||||
private static final String CACHE_REGION_NODES = "N.N";
|
||||
public static final String CACHE_REGION_NODES = "N.N";
|
||||
private static final String CACHE_REGION_ASPECTS = "N.A";
|
||||
private static final String CACHE_REGION_PROPERTIES = "N.P";
|
||||
|
||||
@@ -4868,12 +4868,12 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
|
||||
public Long getMaxTxInNodeIdRange(Long fromNodeId, Long toNodeId)
|
||||
{
|
||||
return selectMaxTxInNodeIdRange(fromNodeId, toNodeId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getNextTxCommitTime(Long fromCommitTime)
|
||||
{
|
||||
return selectNextTxCommitTime(fromCommitTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getNextTxCommitTime(Long fromCommitTime)
|
||||
{
|
||||
return selectNextTxCommitTime(fromCommitTime);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -5043,6 +5043,6 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
|
||||
protected abstract Long selectMaxTxnId();
|
||||
protected abstract Long selectMinUnusedTxnCommitTime();
|
||||
protected abstract Long selectMinTxInNodeIdRange(Long fromNodeId, Long toNodeId);
|
||||
protected abstract Long selectMaxTxInNodeIdRange(Long fromNodeId, Long toNodeId);
|
||||
protected abstract Long selectMaxTxInNodeIdRange(Long fromNodeId, Long toNodeId);
|
||||
protected abstract Long selectNextTxCommitTime(Long fromCommitTime);
|
||||
}
|
||||
|
@@ -1,28 +1,28 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2021 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.repo.domain.node;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
@@ -824,6 +824,27 @@ public class NodePropertyValue implements Cloneable, Serializable
|
||||
return valueType.getOrdinalNumber();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an actual type qualified name, returns the <tt>int</tt> ordinal number
|
||||
* that represents its persisted in the database.
|
||||
*
|
||||
* @param typeQName the type qualified name
|
||||
* @param value the value going to be persisted (optional, null for the default)
|
||||
* @return Returns the <tt>int</tt> representation of the type,
|
||||
* e.g. <b>CONTENT.getOrdinalNumber()</b> for type <b>d:content</b>.
|
||||
*/
|
||||
public static int convertToPersistedTypeOrdinal(QName typeQName, Serializable value)
|
||||
{
|
||||
ValueType valueType = makeValueType(typeQName);
|
||||
if (valueType == null)
|
||||
{
|
||||
throw new AlfrescoRuntimeException(
|
||||
"Unable to get value type from type qname " + typeQName + " and value " + value);
|
||||
}
|
||||
|
||||
return valueType.getPersistedType(value).getOrdinalNumber();
|
||||
}
|
||||
|
||||
/**
|
||||
* If property value of the type <code>QName</code> is supported
|
||||
*
|
||||
|
@@ -2,7 +2,7 @@
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
* Copyright (C) 2005 - 2021 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
@@ -426,7 +426,11 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
|
||||
{
|
||||
NodeEntity node = new NodeEntity();
|
||||
node.setId(id);
|
||||
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("+ Read node with id: "+id);
|
||||
}
|
||||
return template.selectOne(SELECT_NODE_BY_ID, node);
|
||||
}
|
||||
|
||||
@@ -449,7 +453,11 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
|
||||
// Originally for DB2 which has been EOLed, but might now be used by other databases.
|
||||
}
|
||||
node.setUuid(uuid);
|
||||
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("+ Read node with uuid: "+uuid);
|
||||
}
|
||||
return template.selectOne(SELECT_NODE_BY_NODEREF, node);
|
||||
}
|
||||
|
||||
|
@@ -1,28 +1,28 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2021 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.repo.search.impl.lucene;
|
||||
|
||||
import java.io.Serializable;
|
||||
@@ -31,6 +31,7 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.repo.security.permissions.impl.acegi.FilteringResultSet;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
@@ -52,6 +53,8 @@ public class PagingLuceneResultSet implements ResultSet, Serializable
|
||||
SearchParameters searchParameters;
|
||||
|
||||
NodeService nodeService;
|
||||
|
||||
private boolean trimmedResultSet;
|
||||
|
||||
public PagingLuceneResultSet(ResultSet wrapped, SearchParameters searchParameters, NodeService nodeService)
|
||||
{
|
||||
@@ -116,17 +119,23 @@ public class PagingLuceneResultSet implements ResultSet, Serializable
|
||||
|
||||
int max = searchParameters.getMaxItems();
|
||||
int skip = searchParameters.getSkipCount();
|
||||
if ((max >= 0) && (max < (wrapped.length() - skip)))
|
||||
int length = getWrappedResultSetLength();
|
||||
if ((max >= 0) && (max < (length - skip)))
|
||||
{
|
||||
return searchParameters.getMaxItems();
|
||||
}
|
||||
else
|
||||
{
|
||||
int lengthAfterSkipping = wrapped.length() - skip;
|
||||
int lengthAfterSkipping = length - skip;
|
||||
return lengthAfterSkipping < 0 ? 0 : lengthAfterSkipping;
|
||||
}
|
||||
}
|
||||
|
||||
private int getWrappedResultSetLength()
|
||||
{
|
||||
return trimmedResultSet ? wrapped.length() + searchParameters.getSkipCount() : wrapped.length();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
@@ -134,7 +143,14 @@ public class PagingLuceneResultSet implements ResultSet, Serializable
|
||||
*/
|
||||
public int getStart()
|
||||
{
|
||||
return searchParameters.getSkipCount();
|
||||
if (trimmedResultSet)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return searchParameters.getSkipCount();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -254,7 +270,14 @@ public class PagingLuceneResultSet implements ResultSet, Serializable
|
||||
@Override
|
||||
public long getNumberFound()
|
||||
{
|
||||
return wrapped.getNumberFound();
|
||||
if (trimmedResultSet && wrapped instanceof FilteringResultSet)
|
||||
{
|
||||
return ((FilteringResultSet) wrapped).getUnFilteredResultSet().getNumberFound();
|
||||
}
|
||||
else
|
||||
{
|
||||
return wrapped.getNumberFound();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -274,4 +297,9 @@ public class PagingLuceneResultSet implements ResultSet, Serializable
|
||||
{
|
||||
return wrapped.getSpellCheckResult();
|
||||
}
|
||||
|
||||
public void setTrimmedResultSet(boolean b)
|
||||
{
|
||||
this.trimmedResultSet = true;
|
||||
}
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
* Copyright (C) 2005 - 2021 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
@@ -26,6 +26,7 @@
|
||||
package org.alfresco.repo.search.impl.querymodel.impl.db;
|
||||
|
||||
import org.alfresco.repo.search.adaptor.LuceneFunction;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
/**
|
||||
* @author Andy
|
||||
@@ -42,11 +43,13 @@ public class DBQueryBuilderPredicatePartCommand
|
||||
|
||||
String alias;
|
||||
|
||||
private DBQueryBuilderJoinCommandType joinCommandType;
|
||||
|
||||
private LuceneFunction function;
|
||||
QName qName;
|
||||
|
||||
private Long qnameId;
|
||||
DBQueryBuilderJoinCommandType joinCommandType;
|
||||
|
||||
LuceneFunction function;
|
||||
|
||||
Long qnameId;
|
||||
|
||||
/**
|
||||
* @return the qnameId
|
||||
@@ -160,6 +163,16 @@ public class DBQueryBuilderPredicatePartCommand
|
||||
this.alias = alias;
|
||||
}
|
||||
|
||||
public void setQName(QName propertyQName)
|
||||
{
|
||||
this.qName = propertyQName;
|
||||
}
|
||||
|
||||
public QName getQName()
|
||||
{
|
||||
return this.qName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the function
|
||||
*/
|
||||
@@ -198,5 +211,4 @@ public class DBQueryBuilderPredicatePartCommand
|
||||
return alias +"." +fieldName;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,31 +1,40 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2021 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
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.search.impl.querymodel.impl.db.DBStats.aclOwnerStopWatch;
|
||||
import static org.alfresco.repo.search.impl.querymodel.impl.db.DBStats.aclReadStopWatch;
|
||||
import static org.alfresco.repo.search.impl.querymodel.impl.db.DBStats.handlerStopWatch;
|
||||
import static org.alfresco.repo.search.impl.querymodel.impl.db.DBStats.resetStopwatches;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
@@ -33,11 +42,20 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.admin.patch.OptionalPatchApplicationCheckBootstrapBean;
|
||||
import org.alfresco.repo.cache.SimpleCache;
|
||||
import org.alfresco.repo.cache.lookup.EntityLookupCache;
|
||||
import org.alfresco.repo.cache.lookup.EntityLookupCache.EntityLookupCallbackDAOAdaptor;
|
||||
import org.alfresco.repo.domain.node.Node;
|
||||
import org.alfresco.repo.domain.node.NodeDAO;
|
||||
import org.alfresco.repo.domain.node.NodeVersionKey;
|
||||
import org.alfresco.repo.domain.permissions.AclCrudDAO;
|
||||
import org.alfresco.repo.domain.permissions.Authority;
|
||||
import org.alfresco.repo.domain.qname.QNameDAO;
|
||||
import org.alfresco.repo.search.SimpleResultSetMetaData;
|
||||
import org.alfresco.repo.search.impl.lucene.PagingLuceneResultSet;
|
||||
import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext;
|
||||
import org.alfresco.repo.search.impl.querymodel.Query;
|
||||
@@ -46,26 +64,42 @@ import org.alfresco.repo.search.impl.querymodel.QueryEngineResults;
|
||||
import org.alfresco.repo.search.impl.querymodel.QueryModelException;
|
||||
import org.alfresco.repo.search.impl.querymodel.QueryModelFactory;
|
||||
import org.alfresco.repo.search.impl.querymodel.QueryOptions;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||
import org.alfresco.repo.security.permissions.impl.acegi.FilteringResultSet;
|
||||
import org.alfresco.repo.tenant.TenantService;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
|
||||
import org.alfresco.service.cmr.search.LimitBy;
|
||||
import org.alfresco.service.cmr.search.PermissionEvaluationMode;
|
||||
import org.alfresco.service.cmr.search.ResultSet;
|
||||
import org.alfresco.service.cmr.security.PermissionService;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.util.EqualsHelper;
|
||||
import org.alfresco.util.Pair;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.ibatis.session.ResultContext;
|
||||
import org.apache.ibatis.session.ResultHandler;
|
||||
import org.mybatis.spring.SqlSessionTemplate;
|
||||
import org.springframework.util.StopWatch;
|
||||
|
||||
/**
|
||||
* @author Andy
|
||||
*/
|
||||
@NotThreadSafe
|
||||
public class DBQueryEngine implements QueryEngine
|
||||
{
|
||||
private static final Log logger = LogFactory.getLog(DBQueryEngine.class);
|
||||
|
||||
private static final String SELECT_BY_DYNAMIC_QUERY = "alfresco.metadata.query.select_byDynamicQuery";
|
||||
|
||||
private SqlSessionTemplate template;
|
||||
protected SqlSessionTemplate template;
|
||||
|
||||
private QNameDAO qnameDAO;
|
||||
protected QNameDAO qnameDAO;
|
||||
|
||||
private NodeDAO nodeDAO;
|
||||
|
||||
@@ -79,6 +113,45 @@ public class DBQueryEngine implements QueryEngine
|
||||
|
||||
private OptionalPatchApplicationCheckBootstrapBean metadataIndexCheck2;
|
||||
|
||||
private PermissionService permissionService;
|
||||
|
||||
private int maxPermissionChecks;
|
||||
|
||||
private long maxPermissionCheckTimeMillis;
|
||||
|
||||
private SimpleCache<NodeVersionKey, Map<QName, Serializable>> propertiesCache;
|
||||
|
||||
EntityLookupCache<Long, Node, NodeRef> nodesCache;
|
||||
|
||||
private SimpleCache<NodeVersionKey, Set<QName>> aspectsCache;
|
||||
|
||||
private AclCrudDAO aclCrudDAO;
|
||||
|
||||
public void setAclCrudDAO(AclCrudDAO aclCrudDAO)
|
||||
{
|
||||
this.aclCrudDAO = aclCrudDAO;
|
||||
}
|
||||
|
||||
public void setMaxPermissionChecks(int maxPermissionChecks)
|
||||
{
|
||||
this.maxPermissionChecks = maxPermissionChecks;
|
||||
}
|
||||
|
||||
public void setMaxPermissionCheckTimeMillis(long maxPermissionCheckTimeMillis)
|
||||
{
|
||||
this.maxPermissionCheckTimeMillis = maxPermissionCheckTimeMillis;
|
||||
}
|
||||
|
||||
public void setTemplate(SqlSessionTemplate template)
|
||||
{
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
public void setPermissionService(PermissionService permissionService)
|
||||
{
|
||||
this.permissionService = permissionService;
|
||||
}
|
||||
|
||||
public void setMetadataIndexCheck2(OptionalPatchApplicationCheckBootstrapBean metadataIndexCheck2)
|
||||
{
|
||||
this.metadataIndexCheck2 = metadataIndexCheck2;
|
||||
@@ -147,6 +220,9 @@ public class DBQueryEngine implements QueryEngine
|
||||
@Override
|
||||
public QueryEngineResults executeQuery(Query query, QueryOptions options, FunctionEvaluationContext functionContext)
|
||||
{
|
||||
logger.debug("Query request received");
|
||||
resetStopwatches();
|
||||
|
||||
Set<String> selectorGroup = null;
|
||||
if (query.getSource() != null)
|
||||
{
|
||||
@@ -165,13 +241,9 @@ public class DBQueryEngine implements QueryEngine
|
||||
selectorGroup = selectorGroups.get(0);
|
||||
}
|
||||
|
||||
|
||||
HashSet<String> key = new HashSet<String>();
|
||||
key.add("");
|
||||
Map<Set<String>, ResultSet> answer = new HashMap<Set<String>, ResultSet>();
|
||||
DBQuery dbQuery = (DBQuery)query;
|
||||
|
||||
if(options.getStores().size() > 1)
|
||||
if (options.getStores().size() > 1)
|
||||
{
|
||||
throw new QueryModelException("Multi-store queries are not supported");
|
||||
}
|
||||
@@ -179,15 +251,15 @@ public class DBQueryEngine implements QueryEngine
|
||||
// MT
|
||||
StoreRef storeRef = options.getStores().get(0);
|
||||
storeRef = storeRef != null ? tenantService.getName(storeRef) : null;
|
||||
|
||||
Pair<Long, StoreRef> store = nodeDAO.getStore(storeRef);
|
||||
if(store == null)
|
||||
{
|
||||
throw new QueryModelException("Unknown store: "+storeRef);
|
||||
|
||||
Pair<Long, StoreRef> store = nodeDAO.getStore(storeRef);
|
||||
if (store == null)
|
||||
{
|
||||
throw new QueryModelException("Unknown store: "+storeRef);
|
||||
}
|
||||
dbQuery.setStoreId(store.getFirst());
|
||||
Pair<Long, QName> sysDeletedType = qnameDAO.getQName(ContentModel.TYPE_DELETED);
|
||||
if(sysDeletedType == null)
|
||||
if (sysDeletedType == null)
|
||||
{
|
||||
dbQuery.setSysDeletedType(-1L);
|
||||
}
|
||||
@@ -204,20 +276,204 @@ public class DBQueryEngine implements QueryEngine
|
||||
}
|
||||
dbQuery.setSinceTxId(sinceTxId);
|
||||
|
||||
dbQuery.prepare(namespaceService, dictionaryService, qnameDAO, nodeDAO, tenantService, selectorGroup, null, functionContext, metadataIndexCheck2.getPatchApplied());
|
||||
List<Node> nodes = template.selectList(SELECT_BY_DYNAMIC_QUERY, dbQuery);
|
||||
LinkedHashSet<Long> set = new LinkedHashSet<Long>(nodes.size());
|
||||
for(Node node : nodes)
|
||||
{
|
||||
set.add(node.getId());
|
||||
}
|
||||
List<Long> nodeIds = new ArrayList<Long>(set);
|
||||
ResultSet rs = new DBResultSet(options.getAsSearchParmeters(), nodeIds, nodeDAO, nodeService, tenantService, Integer.MAX_VALUE);
|
||||
ResultSet paged = new PagingLuceneResultSet(rs, options.getAsSearchParmeters(), nodeService);
|
||||
logger.debug("- query is being prepared");
|
||||
dbQuery.prepare(namespaceService, dictionaryService, qnameDAO, nodeDAO, tenantService, selectorGroup,
|
||||
null, functionContext, metadataIndexCheck2.getPatchApplied());
|
||||
|
||||
ResultSet resultSet;
|
||||
// TEMPORARY - this first branch of the if statement simply allows us to easily clear the caches for now; it will be removed afterwards
|
||||
if (cleanCacheRequest(options))
|
||||
{
|
||||
nodesCache.clear();
|
||||
propertiesCache.clear();
|
||||
aspectsCache.clear();
|
||||
logger.info("Nodes cache cleared");
|
||||
resultSet = new DBResultSet(options.getAsSearchParmeters(), Collections.emptyList(), nodeDAO, nodeService,
|
||||
tenantService, Integer.MAX_VALUE);
|
||||
}
|
||||
else if (forceOldPermissionResolution(options))
|
||||
{
|
||||
resultSet = selectNodesStandard(options, dbQuery);
|
||||
logger.debug("Selected " +resultSet.length()+ " nodes with standard permission resolution");
|
||||
}
|
||||
else
|
||||
{
|
||||
resultSet = selectNodesWithPermissions(options, dbQuery);
|
||||
logger.debug("Selected " +resultSet.length()+ " nodes with accelerated permission resolution");
|
||||
}
|
||||
|
||||
return asQueryEngineResults(resultSet);
|
||||
}
|
||||
|
||||
protected String pickQueryTemplate(QueryOptions options, DBQuery dbQuery)
|
||||
{
|
||||
logger.debug("- using standard table for the query");
|
||||
return SELECT_BY_DYNAMIC_QUERY;
|
||||
}
|
||||
|
||||
private ResultSet selectNodesStandard(QueryOptions options, DBQuery dbQuery)
|
||||
{
|
||||
List<Node> nodes = removeDuplicates(template.selectList(pickQueryTemplate(options, dbQuery), dbQuery));
|
||||
DBResultSet rs = new DBResultSet(options.getAsSearchParmeters(), nodes, nodeDAO, nodeService, tenantService, Integer.MAX_VALUE);
|
||||
return new PagingLuceneResultSet(rs, options.getAsSearchParmeters(), nodeService);
|
||||
}
|
||||
|
||||
private ResultSet selectNodesWithPermissions(QueryOptions options, DBQuery dbQuery)
|
||||
{
|
||||
NodePermissionAssessor permissionAssessor = createAssessor(options);
|
||||
|
||||
FilteringResultSet resultSet = acceleratedNodeSelection(options, dbQuery, permissionAssessor);
|
||||
|
||||
PagingLuceneResultSet plrs = new PagingLuceneResultSet(resultSet, options.getAsSearchParmeters(), nodeService);
|
||||
plrs.setTrimmedResultSet(true);
|
||||
return plrs;
|
||||
}
|
||||
|
||||
NodePermissionAssessor createAssessor(QueryOptions options)
|
||||
{
|
||||
NodePermissionAssessor permissionAssessor = new NodePermissionAssessor();
|
||||
int maxPermsChecks = options.getMaxPermissionChecks() < 0 ? maxPermissionChecks : options.getMaxPermissionChecks();
|
||||
long maxPermCheckTimeMillis = options.getMaxPermissionCheckTimeMillis() < 0
|
||||
? maxPermissionCheckTimeMillis
|
||||
: options.getMaxPermissionCheckTimeMillis();
|
||||
permissionAssessor.setMaxPermissionChecks(maxPermsChecks);
|
||||
permissionAssessor.setMaxPermissionCheckTimeMillis(maxPermCheckTimeMillis);
|
||||
return permissionAssessor;
|
||||
}
|
||||
|
||||
FilteringResultSet acceleratedNodeSelection(QueryOptions options, DBQuery dbQuery, NodePermissionAssessor permissionAssessor)
|
||||
{
|
||||
StopWatch sw = DBStats.queryStopWatch();
|
||||
List<Node> nodes = new ArrayList<>();
|
||||
int requiredNodes = computeRequiredNodesCount(options);
|
||||
|
||||
logger.debug("- query sent to the database");
|
||||
sw.start("ttfr");
|
||||
template.select(pickQueryTemplate(options, dbQuery), dbQuery, new ResultHandler<Node>()
|
||||
{
|
||||
@Override
|
||||
public void handleResult(ResultContext<? extends Node> context)
|
||||
{
|
||||
handlerStopWatch().start();
|
||||
try
|
||||
{
|
||||
doHandleResult(permissionAssessor, sw, nodes, requiredNodes, context);
|
||||
}
|
||||
finally
|
||||
{
|
||||
handlerStopWatch().stop();
|
||||
}
|
||||
}
|
||||
|
||||
private void doHandleResult(NodePermissionAssessor permissionAssessor, StopWatch sw, List<Node> nodes,
|
||||
int requiredNodes, ResultContext<? extends Node> context)
|
||||
{
|
||||
if (permissionAssessor.isFirstRecord())
|
||||
{
|
||||
sw.stop();
|
||||
sw.start("ttlr");
|
||||
}
|
||||
|
||||
if (nodes.size() >= requiredNodes)
|
||||
{
|
||||
context.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
Node node = context.getResultObject();
|
||||
|
||||
boolean shouldCache = nodes.size() >= options.getSkipCount();
|
||||
if(shouldCache)
|
||||
{
|
||||
logger.debug("- selected node "+nodes.size()+": "+node.getUuid()+" "+node.getId());
|
||||
nodesCache.setValue(node.getId(), node);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.debug("- skipped node "+nodes.size()+": "+node.getUuid()+" "+node.getId());
|
||||
}
|
||||
|
||||
if (permissionAssessor.isIncluded(node))
|
||||
{
|
||||
nodes.add(shouldCache ? node : null);
|
||||
}
|
||||
|
||||
if (permissionAssessor.shouldQuitChecks())
|
||||
{
|
||||
context.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
sw.stop();
|
||||
|
||||
int numberFound = nodes.size();
|
||||
nodes.removeAll(Collections.singleton(null));
|
||||
|
||||
DBResultSet rs = createResultSet(options, nodes, numberFound);
|
||||
FilteringResultSet frs = new FilteringResultSet(rs, formInclusionMask(nodes));
|
||||
frs.setResultSetMetaData(new SimpleResultSetMetaData(LimitBy.UNLIMITED, PermissionEvaluationMode.EAGER, rs.getResultSetMetaData().getSearchParameters()));
|
||||
|
||||
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);
|
||||
dbResultSet.setNumberFound(numberFound);
|
||||
return dbResultSet;
|
||||
}
|
||||
|
||||
private int computeRequiredNodesCount(QueryOptions options)
|
||||
{
|
||||
int maxItems = options.getMaxItems();
|
||||
if (maxItems == -1 || maxItems == Integer.MAX_VALUE)
|
||||
{
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
return maxItems + options.getSkipCount() + 1;
|
||||
}
|
||||
|
||||
private BitSet formInclusionMask(List<Node> nodes)
|
||||
{
|
||||
BitSet inclusionMask = new BitSet(nodes.size());
|
||||
for (int i=0; i < nodes.size(); i++)
|
||||
{
|
||||
inclusionMask.set(i, true);
|
||||
}
|
||||
return inclusionMask;
|
||||
}
|
||||
|
||||
|
||||
private QueryEngineResults asQueryEngineResults(ResultSet paged)
|
||||
{
|
||||
HashSet<String> key = new HashSet<>();
|
||||
key.add("");
|
||||
Map<Set<String>, ResultSet> answer = new HashMap<>();
|
||||
answer.put(key, paged);
|
||||
|
||||
return new QueryEngineResults(answer);
|
||||
}
|
||||
|
||||
private List<Node> removeDuplicates(List<Node> nodes)
|
||||
{
|
||||
LinkedHashSet<Node> uniqueNodes = new LinkedHashSet<>(nodes.size());
|
||||
List<Long> checkedNodeIds = new ArrayList<>(nodes.size());
|
||||
|
||||
for (Node node : nodes)
|
||||
{
|
||||
if (!checkedNodeIds.contains(node.getId()))
|
||||
{
|
||||
checkedNodeIds.add(node.getId());
|
||||
uniqueNodes.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
return new ArrayList<Node>(uniqueNodes);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
@@ -229,4 +485,242 @@ public class DBQueryEngine implements QueryEngine
|
||||
return new DBQueryModelFactory();
|
||||
}
|
||||
|
||||
}
|
||||
public class NodePermissionAssessor
|
||||
{
|
||||
private final boolean isAdminReading;
|
||||
|
||||
private final Authority authority;
|
||||
|
||||
private final Map<Long, Boolean> aclReadCache = new HashMap<>();
|
||||
|
||||
private int checksPerformed;
|
||||
|
||||
private long startTime;
|
||||
|
||||
private int maxPermissionChecks;
|
||||
|
||||
private long maxPermissionCheckTimeMillis;
|
||||
|
||||
public NodePermissionAssessor()
|
||||
{
|
||||
this.checksPerformed = 0;
|
||||
this.maxPermissionChecks = Integer.MAX_VALUE;
|
||||
this.maxPermissionCheckTimeMillis = Long.MAX_VALUE;
|
||||
|
||||
Set<String> authorisations = permissionService.getAuthorisations();
|
||||
this.isAdminReading = authorisations.contains(AuthenticationUtil.getAdminRoleName());
|
||||
|
||||
authority = aclCrudDAO.getAuthority(AuthenticationUtil.getRunAsUser());
|
||||
}
|
||||
|
||||
public boolean isIncluded(Node node)
|
||||
{
|
||||
if (isFirstRecord())
|
||||
{
|
||||
this.startTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
checksPerformed++;
|
||||
return isReallyIncluded(node);
|
||||
}
|
||||
|
||||
public boolean isFirstRecord()
|
||||
{
|
||||
return checksPerformed == 0;
|
||||
}
|
||||
|
||||
boolean isReallyIncluded(Node node)
|
||||
{
|
||||
return isAdminReading ||
|
||||
canRead(node.getAclId()) ||
|
||||
isOwnerReading(node, authority);
|
||||
}
|
||||
|
||||
public void setMaxPermissionChecks(int maxPermissionChecks)
|
||||
{
|
||||
this.maxPermissionChecks = maxPermissionChecks;
|
||||
}
|
||||
|
||||
public boolean shouldQuitChecks()
|
||||
{
|
||||
boolean result = false;
|
||||
|
||||
if (checksPerformed >= maxPermissionChecks)
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
|
||||
if ((System.currentTimeMillis() - startTime) >= maxPermissionCheckTimeMillis)
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setMaxPermissionCheckTimeMillis(long maxPermissionCheckTimeMillis)
|
||||
{
|
||||
this.maxPermissionCheckTimeMillis = maxPermissionCheckTimeMillis;
|
||||
}
|
||||
|
||||
boolean canRead(Long aclId)
|
||||
{
|
||||
aclReadStopWatch().start();
|
||||
try
|
||||
{
|
||||
Boolean res = aclReadCache.get(aclId);
|
||||
if (res == null)
|
||||
{
|
||||
res = canCurrentUserRead(aclId);
|
||||
aclReadCache.put(aclId, res);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
finally
|
||||
{
|
||||
aclReadStopWatch().stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean canCurrentUserRead(Long aclId)
|
||||
{
|
||||
// cache resolved ACLs
|
||||
Set<String> authorities = permissionService.getAuthorisations();
|
||||
|
||||
Set<String> aclReadersDenied = permissionService.getReadersDenied(aclId);
|
||||
for (String auth : aclReadersDenied)
|
||||
{
|
||||
if (authorities.contains(auth))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Set<String> aclReaders = permissionService.getReaders(aclId);
|
||||
for (String auth : aclReaders)
|
||||
{
|
||||
if (authorities.contains(auth))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean isOwnerReading(Node node, Authority authority)
|
||||
{
|
||||
aclOwnerStopWatch().start();
|
||||
try
|
||||
{
|
||||
String owner = getOwner(node);
|
||||
if (EqualsHelper.nullSafeEquals(authority.getAuthority(), owner))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
aclOwnerStopWatch().stop();
|
||||
}
|
||||
}
|
||||
|
||||
private String getOwner(Node node)
|
||||
{
|
||||
nodesCache.setValue(node.getId(), node);
|
||||
Set<QName> nodeAspects = nodeService.getAspects(node.getNodeRef());
|
||||
|
||||
String userName = null;
|
||||
if (nodeAspects.contains(ContentModel.ASPECT_AUDITABLE))
|
||||
{
|
||||
userName = node.getAuditableProperties().getAuditCreator();
|
||||
}
|
||||
else if (nodeAspects.contains(ContentModel.ASPECT_OWNABLE))
|
||||
{
|
||||
Serializable owner = nodeService.getProperty(node.getNodeRef(), ContentModel.PROP_OWNER);
|
||||
userName = DefaultTypeConverter.INSTANCE.convert(String.class, owner);
|
||||
}
|
||||
|
||||
return userName;
|
||||
}
|
||||
|
||||
private boolean cleanCacheRequest(QueryOptions options)
|
||||
{
|
||||
return "xxx".equals(getLocaleLanguage(options));
|
||||
}
|
||||
|
||||
char getMagicCharFromLocale(QueryOptions options, int index)
|
||||
{
|
||||
String lang = getLocaleLanguage(options);
|
||||
return lang.length() > index ? lang.charAt(index) : ' ';
|
||||
}
|
||||
|
||||
private boolean forceOldPermissionResolution(QueryOptions options)
|
||||
{
|
||||
return getMagicCharFromLocale(options, 2) == 's';
|
||||
}
|
||||
|
||||
private String getLocaleLanguage(QueryOptions options)
|
||||
{
|
||||
return options.getLocales().size() == 1 ? options.getLocales().get(0).getLanguage() : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection of nodes cache for clean-up and warm up when required
|
||||
* @param cache The node cache to set
|
||||
*/
|
||||
public void setNodesCache(SimpleCache<Serializable, Serializable> cache)
|
||||
{
|
||||
this.nodesCache = new EntityLookupCache<>(
|
||||
cache,
|
||||
CACHE_REGION_NODES,
|
||||
new ReadonlyLocalCallbackDAO());
|
||||
}
|
||||
|
||||
void setNodesCache(EntityLookupCache<Long, Node, NodeRef> nodesCache)
|
||||
{
|
||||
this.nodesCache = nodesCache;
|
||||
}
|
||||
|
||||
private class ReadonlyLocalCallbackDAO extends EntityLookupCallbackDAOAdaptor<Long, Node, NodeRef>
|
||||
{
|
||||
@Override
|
||||
public Pair<Long, Node> createValue(Node value)
|
||||
{
|
||||
throw new UnsupportedOperationException("Node creation is done externally: " + value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<Long, Node> findByKey(Long nodeId)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeRef getValueKey(Node value)
|
||||
{
|
||||
return value.getNodeRef();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TEMPORARY - Injection of nodes cache for clean-up when required
|
||||
*/
|
||||
public void setPropertiesCache(SimpleCache<NodeVersionKey, Map<QName, Serializable>> propertiesCache)
|
||||
{
|
||||
this.propertiesCache = propertiesCache;
|
||||
}
|
||||
|
||||
/*
|
||||
* TEMPORARY - Injection of nodes cache for clean-up when required
|
||||
*/
|
||||
public void setAspectsCache(SimpleCache<NodeVersionKey, Set<QName>> aspectsCache)
|
||||
{
|
||||
this.aspectsCache = aspectsCache;
|
||||
}
|
||||
}
|
@@ -1,35 +1,35 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2021 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.repo.search.impl.querymodel.impl.db;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.alfresco.repo.domain.node.Node;
|
||||
import org.alfresco.repo.domain.node.NodeDAO;
|
||||
import org.alfresco.repo.search.AbstractResultSet;
|
||||
import org.alfresco.repo.search.SimpleResultSetMetaData;
|
||||
@@ -41,8 +41,7 @@ import org.alfresco.service.cmr.search.LimitBy;
|
||||
import org.alfresco.service.cmr.search.PermissionEvaluationMode;
|
||||
import org.alfresco.service.cmr.search.ResultSetMetaData;
|
||||
import org.alfresco.service.cmr.search.ResultSetRow;
|
||||
import org.alfresco.service.cmr.search.SearchParameters;
|
||||
import org.alfresco.util.Pair;
|
||||
import org.alfresco.service.cmr.search.SearchParameters;
|
||||
|
||||
/**
|
||||
* @author Andy
|
||||
@@ -50,9 +49,7 @@ import org.alfresco.util.Pair;
|
||||
*/
|
||||
public class DBResultSet extends AbstractResultSet
|
||||
{
|
||||
private List<Long> dbids;
|
||||
|
||||
private NodeRef[] nodeRefs;
|
||||
private List<Node> nodes;
|
||||
|
||||
private NodeDAO nodeDao;
|
||||
|
||||
@@ -64,14 +61,16 @@ public class DBResultSet extends AbstractResultSet
|
||||
|
||||
private BitSet prefetch;
|
||||
|
||||
public DBResultSet(SearchParameters searchParameters, List<Long> dbids, NodeDAO nodeDao, NodeService nodeService, TenantService tenantService, int maximumResultsFromUnlimitedQuery)
|
||||
private int numberFound;
|
||||
|
||||
public DBResultSet(SearchParameters searchParameters, List<Node> nodes, NodeDAO nodeDao, NodeService nodeService, TenantService tenantService, int maximumResultsFromUnlimitedQuery)
|
||||
{
|
||||
this.nodeDao = nodeDao;
|
||||
this.dbids = dbids;
|
||||
this.nodes = nodes;
|
||||
this.nodeService = nodeService;
|
||||
this.tenantService = tenantService;
|
||||
this.prefetch = new BitSet(dbids.size());
|
||||
nodeRefs= new NodeRef[(dbids.size())];
|
||||
this.prefetch = new BitSet(nodes.size());
|
||||
this.numberFound = nodes.size();
|
||||
|
||||
final LimitBy limitBy;
|
||||
int maxResults = -1;
|
||||
@@ -96,7 +95,7 @@ public class DBResultSet extends AbstractResultSet
|
||||
}
|
||||
|
||||
this.resultSetMetaData = new SimpleResultSetMetaData(
|
||||
maxResults > 0 && dbids.size() < maxResults ? LimitBy.UNLIMITED : limitBy,
|
||||
maxResults > 0 && nodes.size() < maxResults ? LimitBy.UNLIMITED : limitBy,
|
||||
PermissionEvaluationMode.EAGER, searchParameters);
|
||||
}
|
||||
|
||||
@@ -106,7 +105,12 @@ public class DBResultSet extends AbstractResultSet
|
||||
@Override
|
||||
public int length()
|
||||
{
|
||||
return dbids.size();
|
||||
return nodes.size();
|
||||
}
|
||||
|
||||
public void setNumberFound(int numFound)
|
||||
{
|
||||
this.numberFound = numFound;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
@@ -115,7 +119,7 @@ public class DBResultSet extends AbstractResultSet
|
||||
@Override
|
||||
public long getNumberFound()
|
||||
{
|
||||
return dbids.size();
|
||||
return numberFound;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
@@ -124,8 +128,9 @@ public class DBResultSet extends AbstractResultSet
|
||||
@Override
|
||||
public NodeRef getNodeRef(int n)
|
||||
{
|
||||
prefetch(n);
|
||||
return nodeRefs[n];
|
||||
NodeRef nodeRef = nodes.get(n).getNodeRef();
|
||||
nodeRef = tenantService.getBaseName(nodeRef);
|
||||
return nodeRef;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
@@ -190,61 +195,13 @@ public class DBResultSet extends AbstractResultSet
|
||||
return new DBResultSetRowIterator(this);
|
||||
}
|
||||
|
||||
private void prefetch(int n)
|
||||
public NodeService getNodeService()
|
||||
{
|
||||
|
||||
if (prefetch.get(n))
|
||||
{
|
||||
// The document was already processed
|
||||
return;
|
||||
}
|
||||
// Start at 'n' and process the the next bulk set
|
||||
int bulkFetchSize = getBulkFetchSize();
|
||||
if (bulkFetchSize < 1)
|
||||
{
|
||||
Pair<Long, NodeRef> nodePair = nodeDao.getNodePair(dbids.get(n));
|
||||
NodeRef nodeRef = nodePair == null ? null : nodePair.getSecond();
|
||||
nodeRefs[n] = nodeRef == null ? null : tenantService.getBaseName(nodeRef);
|
||||
return;
|
||||
}
|
||||
|
||||
List<Long> fetchList = new ArrayList<Long>(bulkFetchSize);
|
||||
BitSet done = new BitSet(bulkFetchSize);
|
||||
int totalHits = dbids.size();
|
||||
for (int i = 0; i < bulkFetchSize; i++)
|
||||
{
|
||||
int next = n + i;
|
||||
if (next >= totalHits)
|
||||
{
|
||||
// We've hit the end
|
||||
break;
|
||||
}
|
||||
if (prefetch.get(next))
|
||||
{
|
||||
// This one is in there already
|
||||
continue;
|
||||
}
|
||||
// We store the node and mark it as prefetched
|
||||
prefetch.set(next);
|
||||
|
||||
fetchList.add(dbids.get(next));
|
||||
done.set(next);
|
||||
}
|
||||
// Now bulk fetch
|
||||
if (fetchList.size() > 1)
|
||||
{
|
||||
nodeDao.cacheNodesById(fetchList);
|
||||
for (int i = done.nextSetBit(0); i >= 0; i = done.nextSetBit(i+1))
|
||||
{
|
||||
NodeRef nodeRef = nodeDao.getNodePair(fetchList.get(i)).getSecond();
|
||||
nodeRefs[n+1] = nodeRef == null ? null : tenantService.getBaseName(nodeRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public NodeService getNodeService()
|
||||
{
|
||||
return nodeService;
|
||||
}
|
||||
|
||||
return nodeService;
|
||||
}
|
||||
|
||||
public Node getNode(int n)
|
||||
{
|
||||
return nodes.get(n);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2021 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.repo.search.impl.querymodel.impl.db;
|
||||
|
||||
import org.springframework.util.StopWatch;
|
||||
|
||||
public final class DBStats
|
||||
{
|
||||
public static final ThreadLocal<StopWatch> QUERY_STOPWATCH = new ThreadLocal<StopWatch>();
|
||||
public static final ThreadLocal<SingleTaskRestartableWatch> ACL_READ_STOPWATCH = new ThreadLocal<SingleTaskRestartableWatch>();
|
||||
public static final ThreadLocal<SingleTaskRestartableWatch> ACL_OWNER_STOPWATCH = new ThreadLocal<SingleTaskRestartableWatch>();
|
||||
public static final ThreadLocal<SingleTaskRestartableWatch> HANDLER_STOPWATCH = new ThreadLocal<SingleTaskRestartableWatch>();
|
||||
|
||||
private DBStats() {}
|
||||
|
||||
public static void resetStopwatches()
|
||||
{
|
||||
QUERY_STOPWATCH.set(new StopWatch());
|
||||
HANDLER_STOPWATCH.set(new SingleTaskRestartableWatch("tot"));
|
||||
ACL_READ_STOPWATCH.set(new SingleTaskRestartableWatch("acl"));
|
||||
ACL_OWNER_STOPWATCH.set(new SingleTaskRestartableWatch("own"));
|
||||
}
|
||||
|
||||
public static StopWatch queryStopWatch()
|
||||
{
|
||||
return QUERY_STOPWATCH.get();
|
||||
}
|
||||
|
||||
public static SingleTaskRestartableWatch aclReadStopWatch()
|
||||
{
|
||||
return ACL_READ_STOPWATCH.get();
|
||||
}
|
||||
|
||||
public static SingleTaskRestartableWatch aclOwnerStopWatch()
|
||||
{
|
||||
return ACL_OWNER_STOPWATCH.get();
|
||||
}
|
||||
|
||||
public static SingleTaskRestartableWatch handlerStopWatch()
|
||||
{
|
||||
return HANDLER_STOPWATCH.get();
|
||||
}
|
||||
}
|
@@ -2,7 +2,7 @@
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
* Copyright (C) 2005 - 2021 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
@@ -240,7 +240,8 @@ public class PropertySupport implements DBQueryBuilderComponent
|
||||
predicatePartCommands.add(command);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
command.setQName(propertyQName);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -350,11 +351,11 @@ public class PropertySupport implements DBQueryBuilderComponent
|
||||
|
||||
command.setFieldName(fieldName);
|
||||
command.setFunction(luceneFunction);
|
||||
command.setQName(propertyQName);
|
||||
predicatePartCommands.add(command);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param luceneFunction LuceneFunction
|
||||
@@ -371,5 +372,4 @@ public class PropertySupport implements DBQueryBuilderComponent
|
||||
{
|
||||
this.leftOuter = leftOuter;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,76 @@
|
||||
/*-
|
||||
* #%L
|
||||
* Alfresco Remote API
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2021 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.repo.search.impl.querymodel.impl.db;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
|
||||
|
||||
@NotThreadSafe
|
||||
public class SingleTaskRestartableWatch
|
||||
{
|
||||
private final String name;
|
||||
|
||||
private long startTimeNanos;
|
||||
private long totalTimeNanos;
|
||||
|
||||
public SingleTaskRestartableWatch(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
public void start()
|
||||
{
|
||||
startTimeNanos = System.nanoTime();
|
||||
}
|
||||
|
||||
public void stop()
|
||||
{
|
||||
long elapsedNanos = System.nanoTime() - startTimeNanos;
|
||||
totalTimeNanos += elapsedNanos;
|
||||
}
|
||||
|
||||
public long getTotalTimeNanos()
|
||||
{
|
||||
return totalTimeNanos;
|
||||
}
|
||||
|
||||
public long getTotalTimeMicros()
|
||||
{
|
||||
return TimeUnit.NANOSECONDS.toMicros(totalTimeNanos);
|
||||
}
|
||||
|
||||
public long getTotalTimeMillis()
|
||||
{
|
||||
return TimeUnit.NANOSECONDS.toMillis(totalTimeNanos);
|
||||
}
|
||||
}
|
@@ -2,7 +2,7 @@
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
* Copyright (C) 2005 - 2021 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
@@ -53,7 +53,6 @@ public class DbCmisQueryLanguage extends AbstractLuceneQueryLanguage
|
||||
OptionalPatchApplicationCheckBootstrapBean metadataIndexCheck1;
|
||||
|
||||
OptionalPatchApplicationCheckBootstrapBean metadataIndexCheck2;
|
||||
|
||||
|
||||
/**
|
||||
* @param metadataIndexCheck1 the metadataIndexCheck1 to set
|
||||
@@ -63,7 +62,6 @@ public class DbCmisQueryLanguage extends AbstractLuceneQueryLanguage
|
||||
this.metadataIndexCheck1 = metadataIndexCheck1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param metadataIndexCheck2 the metadataIndexCheck2 to set
|
||||
*/
|
||||
@@ -72,7 +70,6 @@ public class DbCmisQueryLanguage extends AbstractLuceneQueryLanguage
|
||||
this.metadataIndexCheck2 = metadataIndexCheck2;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the query engine
|
||||
*
|
||||
@@ -82,7 +79,6 @@ public class DbCmisQueryLanguage extends AbstractLuceneQueryLanguage
|
||||
{
|
||||
this.queryEngine = queryEngine;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param cmisDictionaryService the cmisDictionaryService to set
|
||||
@@ -109,8 +105,7 @@ public class DbCmisQueryLanguage extends AbstractLuceneQueryLanguage
|
||||
throw new QueryModelException("The patch to add the indexes to support in-transactional metadata queries has not been applied");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private ResultSet executeQueryImpl(SearchParameters searchParameters)
|
||||
{
|
||||
CMISQueryOptions options = CMISQueryOptions.create(searchParameters);
|
||||
@@ -123,25 +118,8 @@ public class DbCmisQueryLanguage extends AbstractLuceneQueryLanguage
|
||||
functionContext.setValidScopes(validScopes);
|
||||
|
||||
CMISQueryParser parser = new CMISQueryParser(options, cmisDictionaryService, joinSupport);
|
||||
org.alfresco.repo.search.impl.querymodel.Query queryModelQuery = parser.parse(new DBQueryModelFactory(), functionContext);
|
||||
org.alfresco.repo.search.impl.querymodel.Query queryModelQuery = parser.parse(queryEngine.getQueryModelFactory(), functionContext);
|
||||
|
||||
// TODO: Remove as this appears to be dead code
|
||||
// // build lucene query
|
||||
// Set<String> selectorGroup = null;
|
||||
// if (queryModelQuery.getSource() != null)
|
||||
// {
|
||||
// List<Set<String>> selectorGroups = queryModelQuery.getSource().getSelectorGroups(functionContext);
|
||||
// if (selectorGroups.size() == 0)
|
||||
// {
|
||||
// throw new UnsupportedOperationException("No selectors");
|
||||
// }
|
||||
// if (selectorGroups.size() > 1)
|
||||
// {
|
||||
// throw new UnsupportedOperationException("Advanced join is not supported");
|
||||
// }
|
||||
// selectorGroup = selectorGroups.get(0);
|
||||
// }
|
||||
//
|
||||
QueryEngineResults results = queryEngine.executeQuery(queryModelQuery, options, functionContext);
|
||||
ResultSet resultSet = results.getResults().values().iterator().next();
|
||||
return resultSet;
|
||||
|
@@ -4,8 +4,8 @@
|
||||
|
||||
<mapper namespace="alfresco.metadata.query">
|
||||
|
||||
<select id="select_byDynamicQuery" fetchSize="200" parameterType="org.alfresco.repo.search.impl.querymodel.impl.db.DBQuery" resultMap="alfresco.node.result_NodeRef">
|
||||
<include refid="sql_select_byDynamicQuery"/>
|
||||
<select id="select_byDynamicQuery" fetchSize="200" parameterType="org.alfresco.repo.search.impl.querymodel.impl.db.DBQuery" resultMap="alfresco.node.result_Node">
|
||||
<include refid="sql_select_byDynamicQuery"/>
|
||||
</select>
|
||||
|
||||
</mapper>
|
@@ -6,9 +6,26 @@
|
||||
|
||||
<sql id="sql_select_byDynamicQuery">
|
||||
select
|
||||
node.id as id
|
||||
node.id as id,
|
||||
node.version as version,
|
||||
store.id as store_id,
|
||||
store.protocol as protocol,
|
||||
store.identifier as identifier,
|
||||
node.uuid as uuid,
|
||||
node.type_qname_id as type_qname_id,
|
||||
node.locale_id as locale_id,
|
||||
node.acl_id as acl_id,
|
||||
txn.id as txn_id,
|
||||
txn.change_txn_id as txn_change_id,
|
||||
node.audit_creator as audit_creator,
|
||||
node.audit_created as audit_created,
|
||||
node.audit_modifier as audit_modifier,
|
||||
node.audit_modified as audit_modified,
|
||||
node.audit_accessed as audit_accessed
|
||||
from
|
||||
alf_node node
|
||||
join alf_store store on (store.id = node.store_id)
|
||||
join alf_transaction txn on (txn.id = node.transaction_id)
|
||||
<foreach item="item" index="index" collection="joins">
|
||||
<choose>
|
||||
<when test="item.type == 'PARENT'">
|
||||
|
@@ -4,7 +4,7 @@
|
||||
|
||||
<mapper namespace="alfresco.metadata.query">
|
||||
|
||||
<select id="select_byDynamicQuery" fetchSize="-2147483648" parameterType="org.alfresco.repo.search.impl.querymodel.impl.db.DBQuery" resultMap="alfresco.node.result_NodeRef">
|
||||
<select id="select_byDynamicQuery" fetchSize="-2147483648" parameterType="org.alfresco.repo.search.impl.querymodel.impl.db.DBQuery" resultMap="alfresco.node.result_Node">
|
||||
<include refid="sql_select_byDynamicQuery"/>
|
||||
</select>
|
||||
|
||||
|
@@ -107,6 +107,7 @@
|
||||
</bean>
|
||||
|
||||
<bean id="search.dbQueryEngineImpl" class="org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryEngine" >
|
||||
<property name="permissionService" ref="permissionService"/>
|
||||
<property name="dictionaryService" ref="dictionaryService" />
|
||||
<property name="namespaceService" ref="namespaceService" />
|
||||
<property name="sqlSessionTemplate" ref="repoSqlSessionTemplate"/>
|
||||
@@ -114,9 +115,19 @@
|
||||
<property name="nodeService" ref="nodeService"/>
|
||||
<property name="nodeDAO" ref="nodeDAO"/>
|
||||
<property name="tenantService" ref="tenantService"/>
|
||||
<property name="nodesCache" ref="node.nodesCache"/>
|
||||
<property name="aspectsCache" ref="node.aspectsCache"/>
|
||||
<property name="propertiesCache" ref="node.propertiesCache"/>
|
||||
<property name="aclCrudDAO" ref="aclCrudDAO"/>
|
||||
<property name="metadataIndexCheck2">
|
||||
<ref bean="metadataQueryIndexesCheck2" />
|
||||
</property>
|
||||
<property name="maxPermissionChecks">
|
||||
<value>${system.acl.maxPermissionChecks}</value>
|
||||
</property>
|
||||
<property name="maxPermissionCheckTimeMillis">
|
||||
<value>${system.acl.maxPermissionCheckTimeMillis}</value>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="search.dbQueryEngine" class="org.springframework.aop.framework.ProxyFactoryBean">
|
||||
|
@@ -2,7 +2,7 @@
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
* Copyright (C) 2005 - 2021 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
@@ -74,6 +74,7 @@ import org.alfresco.util.test.junitrules.TemporaryNodes;
|
||||
import org.alfresco.util.testing.category.LuceneTests;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
@@ -192,6 +193,22 @@ public class QuickShareServiceIntegrationTest
|
||||
user1.getUsername(),
|
||||
"Quick Share Test Node Content");
|
||||
}
|
||||
|
||||
@After public void clearTestData()
|
||||
{
|
||||
if (testNode != null)
|
||||
{
|
||||
AuthenticationUtil.runAs(new RunAsWork<NodeRef>()
|
||||
{
|
||||
@Override
|
||||
public NodeRef doWork() throws Exception
|
||||
{
|
||||
nodeService.deleteNode(testNode);
|
||||
return null;
|
||||
}
|
||||
}, user1.getUsername());
|
||||
}
|
||||
}
|
||||
|
||||
@Test public void getMetaDataFromNodeRefByOwner()
|
||||
{
|
||||
@@ -532,7 +549,8 @@ public class QuickShareServiceIntegrationTest
|
||||
}, user1.getUsername());
|
||||
|
||||
AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser();
|
||||
Assert.assertFalse(nodeService.exists(node));
|
||||
final NodeRef archivedNode = nodeArchiveService.getArchivedNode(node);
|
||||
assertNotNull("Node " + node.toString() + " hasn't been archived hence the deletion was unsuccessful", archivedNode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2021 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.repo.search.impl.querymodel.impl.db;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.repo.cache.SimpleCache;
|
||||
import org.alfresco.repo.cache.lookup.EntityLookupCache;
|
||||
import org.alfresco.repo.domain.node.Node;
|
||||
import org.alfresco.repo.domain.node.NodeVersionKey;
|
||||
import org.alfresco.repo.domain.node.StoreEntity;
|
||||
import org.alfresco.repo.domain.permissions.AuthorityEntity;
|
||||
import org.alfresco.repo.search.impl.querymodel.QueryOptions;
|
||||
import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryEngine.NodePermissionAssessor;
|
||||
import org.alfresco.repo.security.permissions.impl.acegi.FilteringResultSet;
|
||||
import org.alfresco.service.cmr.search.ResultSet;
|
||||
import org.alfresco.service.cmr.search.SearchParameters;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.apache.ibatis.executor.result.DefaultResultContext;
|
||||
import org.apache.ibatis.session.ResultContext;
|
||||
import org.apache.ibatis.session.ResultHandler;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mybatis.spring.SqlSessionTemplate;
|
||||
|
||||
public class DBQueryEngineTest
|
||||
{
|
||||
private static final String SQL_TEMPLATE_PATH = "alfresco.metadata.query.select_byDynamicQuery";
|
||||
|
||||
private DBQueryEngine engine;
|
||||
private SqlSessionTemplate template;
|
||||
private NodePermissionAssessor assessor;
|
||||
private DBQuery dbQuery;
|
||||
private ResultContext<Node> resultContext;
|
||||
private QueryOptions options;
|
||||
private SimpleCache<NodeVersionKey, Map<QName, Serializable>> propertiesCache;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Before
|
||||
public void setup()
|
||||
{
|
||||
engine = new DBQueryEngine();
|
||||
assessor = mock(NodePermissionAssessor.class);
|
||||
dbQuery = mock(DBQuery.class);
|
||||
resultContext = spy(new DefaultResultContext<>());
|
||||
options = createQueryOptions();
|
||||
|
||||
template = mock(SqlSessionTemplate.class);
|
||||
engine.setSqlSessionTemplate(template);
|
||||
|
||||
propertiesCache = mock(SimpleCache.class);
|
||||
engine.setPropertiesCache(propertiesCache);
|
||||
|
||||
engine.nodesCache = mock(EntityLookupCache.class);
|
||||
|
||||
DBStats.resetStopwatches();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGetAFilteringResultSetFromAcceleratedNodeSelection()
|
||||
{
|
||||
withMaxItems(10);
|
||||
|
||||
ResultSet result = engine.acceleratedNodeSelection(options, dbQuery, assessor);
|
||||
|
||||
assertTrue(result instanceof FilteringResultSet);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldResultSetHaveExpectedAmountOfRequiredNodesBasedOnMaxItems()
|
||||
{
|
||||
withMaxItems(5);
|
||||
prepareTemplate(dbQuery, createNodes(20));
|
||||
when(assessor.isIncluded(any(Node.class))).thenReturn(true);
|
||||
|
||||
FilteringResultSet result = engine.acceleratedNodeSelection(options, dbQuery, assessor);
|
||||
|
||||
assertEquals(6, result.length());
|
||||
assertNodePresent(0, result);
|
||||
assertNodePresent(1, result);
|
||||
assertNodePresent(2, result);
|
||||
assertNodePresent(3, result);
|
||||
assertNodePresent(4, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldResultContextBeClosedWhenMaxItemsReached()
|
||||
{
|
||||
withMaxItems(5);
|
||||
prepareTemplate(dbQuery, createNodes(20));
|
||||
when(assessor.isIncluded(any(Node.class))).thenReturn(true);
|
||||
|
||||
FilteringResultSet result = engine.acceleratedNodeSelection(options, dbQuery, assessor);
|
||||
|
||||
verify(resultContext).stop();
|
||||
assertEquals(6, result.length());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldResultSetHaveCorrectAmountOfRequiredNodesWhenSkipCountIsUsed()
|
||||
{
|
||||
withMaxItems(5);
|
||||
withSkipCount(10);
|
||||
prepareTemplate(dbQuery, createNodes(20));
|
||||
when(assessor.isIncluded(any(Node.class))).thenReturn(true);
|
||||
|
||||
FilteringResultSet result = engine.acceleratedNodeSelection(options, dbQuery, assessor);
|
||||
|
||||
assertEquals(6, result.length());
|
||||
assertNodePresent(10, result);
|
||||
assertNodePresent(11, result);
|
||||
assertNodePresent(12, result);
|
||||
assertNodePresent(13, result);
|
||||
assertNodePresent(14, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldResultSetHaveCorrectAmountOfRequiredNodesWhenSomeAreExcludedDueToDeclinedPermission()
|
||||
{
|
||||
withMaxItems(5);
|
||||
List<Node> nodes = createNodes(20);
|
||||
when(assessor.isIncluded(any(Node.class))).thenReturn(true);
|
||||
when(assessor.isIncluded(nodes.get(0))).thenReturn(false);
|
||||
when(assessor.isIncluded(nodes.get(1))).thenReturn(false);
|
||||
when(assessor.isIncluded(nodes.get(2))).thenReturn(false);
|
||||
prepareTemplate(dbQuery, nodes);
|
||||
|
||||
FilteringResultSet result = engine.acceleratedNodeSelection(options, dbQuery, assessor);
|
||||
|
||||
assertEquals(6, result.length());
|
||||
assertNodePresent(3, result);
|
||||
assertNodePresent(4, result);
|
||||
assertNodePresent(5, result);
|
||||
assertNodePresent(6, result);
|
||||
assertNodePresent(7, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotConsiderInaccessibleNodesInResultSetWhenSkippingNodes()
|
||||
{
|
||||
withMaxItems(2);
|
||||
withSkipCount(2);
|
||||
List<Node> nodes = createNodes(6);
|
||||
when(assessor.isIncluded(any(Node.class))).thenReturn(true);
|
||||
when(assessor.isIncluded(nodes.get(2))).thenReturn(false);
|
||||
when(assessor.isIncluded(nodes.get(3))).thenReturn(false);
|
||||
prepareTemplate(dbQuery, nodes);
|
||||
|
||||
FilteringResultSet result = engine.acceleratedNodeSelection(options, dbQuery, assessor);
|
||||
|
||||
assertEquals(2, result.length());
|
||||
assertNodePresent(4, result);
|
||||
assertNodePresent(5, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldQuitCheckingNodePermissionsWhenImposedLimitsAreReached()
|
||||
{
|
||||
prepareTemplate(dbQuery, createNodes(20));
|
||||
when(assessor.shouldQuitChecks()).thenReturn(true);
|
||||
|
||||
FilteringResultSet result = engine.acceleratedNodeSelection(options, dbQuery, assessor);
|
||||
|
||||
assertEquals(0, result.length());
|
||||
verify(resultContext).stop();
|
||||
}
|
||||
|
||||
private void prepareTemplate(DBQuery dbQuery, List<Node> nodes)
|
||||
{
|
||||
doAnswer(invocation -> {
|
||||
ResultHandler<Node> handler = (ResultHandler<Node>)invocation.getArgument(2);
|
||||
for (Node node: nodes)
|
||||
{
|
||||
if (!resultContext.isStopped())
|
||||
{
|
||||
when(resultContext.getResultObject()).thenReturn(node);
|
||||
handler.handleResult(resultContext);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
}).when(template).select(eq(SQL_TEMPLATE_PATH), eq(dbQuery), any());
|
||||
}
|
||||
|
||||
private QueryOptions createQueryOptions()
|
||||
{
|
||||
QueryOptions options = mock(QueryOptions.class);
|
||||
SearchParameters searchParams = mock(SearchParameters.class);
|
||||
when(options.getAsSearchParmeters()).thenReturn(searchParams);
|
||||
return options;
|
||||
}
|
||||
|
||||
private void assertNodePresent(long id, FilteringResultSet result)
|
||||
{
|
||||
DBResultSet rs = (DBResultSet)result.getUnFilteredResultSet();
|
||||
for(int i = 0; i < rs.length(); i++)
|
||||
{
|
||||
if(rs.getNode(i).getId().equals(id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
fail("Node with id " + id + " was not found in the result set");
|
||||
}
|
||||
|
||||
private void withMaxItems(int maxItems)
|
||||
{
|
||||
when(options.getMaxItems()).thenReturn(maxItems);
|
||||
}
|
||||
|
||||
private void withSkipCount(int skipCount)
|
||||
{
|
||||
when(options.getSkipCount()).thenReturn(skipCount);
|
||||
}
|
||||
|
||||
private List<Node> createNodes(int amount)
|
||||
{
|
||||
List<Node> nodes = new ArrayList<>();
|
||||
|
||||
for(int i = 0; i < amount; i++)
|
||||
{
|
||||
nodes.add(createNode(i));
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
private Node createNode(int id)
|
||||
{
|
||||
Node node = spy(Node.class);
|
||||
|
||||
when(node.getId()).thenReturn((long)id);
|
||||
|
||||
StoreEntity store = mock(StoreEntity.class);
|
||||
when(node.getStore()).thenReturn(store );
|
||||
|
||||
return node;
|
||||
}
|
||||
}
|
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2021 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.repo.search.impl.querymodel.impl.db;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import org.alfresco.repo.domain.node.Node;
|
||||
import org.alfresco.repo.domain.permissions.AclCrudDAO;
|
||||
import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryEngine.NodePermissionAssessor;
|
||||
import org.alfresco.service.cmr.security.PermissionService;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class NodePermissionAssessorTest
|
||||
{
|
||||
private NodePermissionAssessor assessor;
|
||||
private Node node;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
{
|
||||
node = mock(Node.class);
|
||||
assessor = createAssessor();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotQuitAssessingPermissionsWhenMaxPermissionChecksLimitIsNotReached()
|
||||
{
|
||||
assessor.setMaxPermissionChecks(5);
|
||||
|
||||
performChecks(3);
|
||||
|
||||
assertFalse(assessor.shouldQuitChecks());
|
||||
verify(assessor, times(3)).isReallyIncluded(node);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldQuitAssessingPermissionsWhenMaxPermissionChecksLimitIsReached()
|
||||
{
|
||||
assessor.setMaxPermissionChecks(5);
|
||||
|
||||
performChecks(20);
|
||||
|
||||
assertTrue(assessor.shouldQuitChecks());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotAssessPermissionsWhenMaxPermissionCheckTimeIsUp() throws Exception
|
||||
{
|
||||
assessor.setMaxPermissionCheckTimeMillis(100);
|
||||
|
||||
assessor.isIncluded(node);
|
||||
Thread.sleep(200);
|
||||
|
||||
assertTrue(assessor.shouldQuitChecks());
|
||||
verify(assessor).isReallyIncluded(node);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAssessPermissionsWhenMaxPermissionCheckTimeIsNotUp() throws Exception
|
||||
{
|
||||
assessor.setMaxPermissionCheckTimeMillis(500);
|
||||
Thread.sleep(200);
|
||||
|
||||
assessor.isIncluded(node);
|
||||
|
||||
assertFalse(assessor.shouldQuitChecks());
|
||||
verify(assessor, atLeastOnce()).isReallyIncluded(node);
|
||||
|
||||
}
|
||||
|
||||
private void performChecks(int checks)
|
||||
{
|
||||
for (int i=0; i < checks; i++)
|
||||
{
|
||||
assessor.isIncluded(node);
|
||||
}
|
||||
}
|
||||
|
||||
private NodePermissionAssessor createAssessor()
|
||||
{
|
||||
AclCrudDAO aclCrudDAO = mock(AclCrudDAO.class);
|
||||
PermissionService permissionService = mock(PermissionService.class);
|
||||
|
||||
DBQueryEngine engine = new DBQueryEngine();
|
||||
engine.setPermissionService(permissionService);
|
||||
engine.setAclCrudDAO(aclCrudDAO);
|
||||
|
||||
NodePermissionAssessor assessor = spy(engine.new NodePermissionAssessor());
|
||||
doReturn(true).when(assessor).isReallyIncluded(any(Node.class));
|
||||
return assessor;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user