Merged V4.1-BUG-FIX to HEAD

42725: Record Only Merge: V3.4-BUG-FIX (3.4.12) to V4.1-BUG-FIX (4.1.2)
      << Record only as 4.1.2 used PDFBOX 1.0.7 rather than 1.0.6 >>
      42721: ALF-14185 PDF not indexed as a result of PDFBOX-1143 workaround in Tika 
   42726: ALF-16388 CLONE: PDF not indexed as a result of PDFBOX-1143 workaround in Tika
      - 4.1 specific fix (uses PDFBox 1.0.7) for the same issue as ALF-14185 on 3.4 (uses PDFBox 1.0.6).
   42736: ALF-16093: Implement new getPeople CQ (eg. if using user admin console and/or Solr unavailable)
   42740: Merged DEV to V4.1-BUG-FIX
      42626: ALF-14336: SOLR indexing fails with unterminated string for PDF uploaded
             Appeared exception due to postgreSQL (http://archives.postgresql.org/pgsql-jdbc/2007-02/msg00107.php).
             Remove '\u0000' characters from the property. 
   42741: Fix for ALF-16332 - Alternative version of AbstractWebScriptViewResolver that uses a ConcurrentHashMap and thus allows multiple views to be resolved at the same time!
   42755: Merged DEV to V4.1-BUG-FIX
     42750 : ALF-16315
   42762: ALF-15616: Merged V3.4-BUG-FIX (3.4.12) to V4.1-BUG-FIX (4.1.2)
      42758: ALF-11956 WCM accessibility
         - tabIndex code. See comment on 17 Oct 2012
           "4) TinyMCE fields are not accessible using the keyboard (you have to use the mouse to select the "click to edit" option) - > It's reproduced for (+) icon, content created on press-release.xsd."
   42768: Merged somehow-lost mergeinfo from r42679
   42769: Merged V3.4-BUG-FIX to V4.1-BUG-FIX
      42738: ALF-12724 CLONE - Activities trigger high CPU usage and lock contention 
      42767: Merged V3.4 to V3.4-BUG-FIX
         42727: ALF-16366: PermissionService calls were updating nodes but not reindexing them, leaving out of sync transactions after a clean bootstrap!


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@42770 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Dave Ward
2012-10-17 19:45:37 +00:00
parent 4dc751ee3a
commit 46f9f8c24e
14 changed files with 1192 additions and 444 deletions

View File

@@ -339,6 +339,7 @@
<property name="storageType" value="org.alfresco.query.CannedQueryFactory"/>
</bean>
<!-- deprecated (see getPeopleCannedQueryFactory) -->
<bean name="peopleGetChildrenCannedQueryFactory" class="org.alfresco.repo.node.getchildren.GetChildrenCannedQueryFactory">
<property name="registry" ref="personServiceCannedQueryRegistry"/>
<property name="dictionaryService" ref="dictionaryService"/>
@@ -351,6 +352,14 @@
<property name="methodSecurity" ref="PersonService_security_getPeople"/>
</bean>
<bean name="getPeopleCannedQueryFactory" class="org.alfresco.repo.security.person.GetPeopleCannedQueryFactory">
<property name="registry" ref="personServiceCannedQueryRegistry"/>
<property name="tenantService" ref="tenantService"/>
<property name="nodeDAO" ref="nodeDAO"/>
<property name="qnameDAO" ref="qnameDAO"/>
<property name="cannedQueryDAO" ref="cannedQueryDAO"/>
</bean>
<bean name="personServicePermissionsManager" class="org.alfresco.repo.security.person.PermissionsManagerImpl" >
<property name="permissionService">
<ref bean="permissionServiceImpl" />

View File

@@ -72,13 +72,16 @@ Inbound settings from iBatis
<typeAlias alias="ChildProperty" type="org.alfresco.repo.domain.node.ChildPropertyEntity"/>
<typeAlias alias="PrimaryChildrenAclUpdate" type="org.alfresco.repo.domain.node.PrimaryChildrenAclUpdateEntity"/>
<!--GetChildren CQ (currently used by FileFolderService.list / PersonService.getPeople) -->
<!--GetChildren CQ (currently used by FileFolderService.list) -->
<typeAlias alias="FilterSortNode" type="org.alfresco.repo.node.getchildren.FilterSortNodeEntity"/>
<!--GetChildren by Auditable CQ -->
<typeAlias alias="NodeBackedEntity" type="org.alfresco.repo.query.NodeBackedEntity"/>
<typeAlias alias="NodeWithTargetsEntity" type="org.alfresco.repo.query.NodeWithTargetsEntity"/>
<!--GetPeople CQ (currently used by PersonService.getPeople) -->
<typeAlias alias="FilterSortPerson" type="org.alfresco.repo.security.person.FilterSortPersonEntity"/>
<!-- Authority CQ -->
<typeAlias alias="AuthorityInfo" type="org.alfresco.repo.security.authority.AuthorityInfoEntity"/>
@@ -223,6 +226,7 @@ Inbound settings from iBatis
<mapper resource="alfresco/ibatis/#resource.dialect#/query-auditable-common-SqlMap.xml"/>
<mapper resource="alfresco/ibatis/#resource.dialect#/query-authorities-common-SqlMap.xml"/>
<mapper resource="alfresco/ibatis/#resource.dialect#/query-blogs-common-SqlMap.xml"/>
<mapper resource="alfresco/ibatis/#resource.dialect#/query-people-common-SqlMap.xml"/>
<mapper resource="alfresco/ibatis/#resource.dialect#/query-calendar-common-SqlMap.xml"/>
<mapper resource="alfresco/ibatis/#resource.dialect#/query-copy-common-SqlMap.xml"/>
<mapper resource="alfresco/ibatis/#resource.dialect#/query-downloads-common-SqlMap.xml"/>

View File

@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="alfresco.query.people">
<!-- -->
<!-- Result Maps -->
<!-- -->
<!-- GetPeople Canned Query -->
<select id="select_GetPeopleCannedQuery" parameterType="FilterSortPerson" resultType="String">
select
childNode.uuid as uuid
from
alf_child_assoc assoc
join alf_node childNode on (childNode.id = assoc.child_node_id)
<if test="prop1qnameId != null">
left join alf_node_properties prop1 on (prop1.node_id = childNode.id and prop1.qname_id = #{prop1qnameId})
</if>
<if test="prop2qnameId != null">
left join alf_node_properties prop2 on (prop2.node_id = childNode.id and prop2.qname_id = #{prop2qnameId})
</if>
<if test="prop3qnameId != null">
left join alf_node_properties prop3 on (prop3.node_id = childNode.id and prop3.qname_id = #{prop3qnameId})
</if>
where
assoc.parent_node_id = #{parentNodeId}
<if test="prop1qnameId != null">
and
(
prop1.string_value like #{pattern} <include refid="alfresco.util.escape"/>
</if>
<if test="prop2qnameId != null">
or prop2.string_value like #{pattern} <include refid="alfresco.util.escape"/>
</if>
<if test="prop3qnameId != null">
or prop3.string_value like #{pattern} <include refid="alfresco.util.escape"/>
</if>
<if test="prop1qnameId != null">
)
</if>
<if test="sort1asc != null">
order by
prop1.string_value <if test="sort1asc == true">ASC</if><if test="sort1asc == false">DESC</if>
</if>
<if test="sort2asc != null">
, prop2.string_value <if test="sort2asc == true">ASC</if><if test="sort2asc == false">DESC</if>
</if>
<if test="sort3asc != null">
, prop3.string_value <if test="sort3asc == true">ASC</if><if test="sort3asc == false">DESC</if>
</if>
</select>
</mapper>

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
* Copyright (C) 2005-2012 Alfresco Software Limited.
*
* This file is part of Alfresco
*
@@ -64,8 +64,6 @@ public abstract class AbstractFeedGenerator implements FeedGenerator
private RepoCtx ctx = null;
private volatile boolean busy;
public void setActivityPostServiceImpl(ActivityPostServiceImpl activityPostServiceImpl)
{
this.activityPostServiceImpl = activityPostServiceImpl;
@@ -135,8 +133,6 @@ public abstract class AbstractFeedGenerator implements FeedGenerator
{
ctx = new RepoCtx(sysAdminParams, repoEndPoint);
ctx.setUserNamesAreCaseSensitive(userNamesAreCaseSensitive);
busy = false;
}
/**
@@ -151,11 +147,6 @@ public abstract class AbstractFeedGenerator implements FeedGenerator
abstract public int getEstimatedGridSize();
protected boolean isActive()
{
return busy;
}
public void execute() throws JobExecutionException
{
checkProperties();
@@ -171,10 +162,11 @@ public abstract class AbstractFeedGenerator implements FeedGenerator
}
String lockToken = null;
LockCallback lockCallback = null;
try
{
JobLockRefreshCallback lockCallback = new LockCallback();
lockCallback = new LockCallback();
lockToken = acquireLock(lockCallback);
@@ -213,7 +205,7 @@ public abstract class AbstractFeedGenerator implements FeedGenerator
}
finally
{
releaseLock(lockToken);
releaseLock(lockCallback, lockToken);
}
}
@@ -221,6 +213,8 @@ public abstract class AbstractFeedGenerator implements FeedGenerator
private class LockCallback implements JobLockRefreshCallback
{
private volatile boolean busy = false;
@Override
public boolean isActive()
{
@@ -251,7 +245,7 @@ public abstract class AbstractFeedGenerator implements FeedGenerator
// Got the lock - now register the refresh callback which will keep the lock alive
jobLockService.refreshLock(lockToken, LOCK_QNAME, LOCK_TTL, lockCallback);
busy = true;
((LockCallback)lockCallback).busy = true;
if (logger.isDebugEnabled())
{
@@ -261,11 +255,11 @@ public abstract class AbstractFeedGenerator implements FeedGenerator
return lockToken;
}
private void releaseLock(String lockToken)
private void releaseLock(LockCallback lockCallback, String lockToken)
{
if (lockToken != null)
if (lockCallback != null && lockToken != null)
{
busy = false;
((LockCallback)lockCallback).busy = false;
jobLockService.releaseLock(lockToken, LOCK_QNAME);

View File

@@ -969,6 +969,10 @@ abstract public class AbstractMappingMetadataExtracter implements MetadataExtrac
}
else
{
if(valueStr.indexOf("\u0000") != -1)
{
valueStr = valueStr.replaceAll("\u0000", "");
}
// Keep the trimmed value
value = valueStr;
}

View File

@@ -1831,6 +1831,13 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
// The node's version has moved on so no need to invalidate caches
}
// ALF-16366: Ensure index impact is accounted for. If the node is being deleted we would expect the
// appropriate events to be fired manually
if (!nodeUpdate.isUpdateTypeQNameId() || !getNodeNotNull(nodeId, false).getDeleted(qnameDAO))
{
nodeIndexer.indexUpdateNode(oldNode.getNodeRef());
}
// Done
if (isDebugEnabled)
{

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
* Copyright (C) 2005-2012 Alfresco Software Limited.
*
* This file is part of Alfresco
*
@@ -48,7 +48,6 @@ import org.alfresco.service.cmr.security.MutableAuthenticationService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.security.PersonService.PersonInfo;
import org.alfresco.service.cmr.usage.ContentUsageService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
import org.alfresco.util.PropertyMap;
@@ -76,7 +75,6 @@ public final class People extends BaseScopableProcessorExtension implements Init
private AuthorityDAO authorityDAO;
private AuthorityService authorityService;
private PersonService personService;
private NamespaceService namespaceService;
private MutableAuthenticationService authenticationService;
private ContentUsageService contentUsageService;
private TenantService tenantService;
@@ -86,6 +84,9 @@ public final class People extends BaseScopableProcessorExtension implements Init
private ValueDerivingMapFactory<ScriptNode, String, Boolean> valueDerivingMapFactory;
private int numRetries = 10;
private int defaultListMaxResults = 5000;
private static final String HINT_CQ_SUFFIX = " [hint:useCQ]";
public void afterPropertiesSet() throws Exception
{
@@ -192,11 +193,6 @@ public final class People extends BaseScopableProcessorExtension implements Init
this.personService = personService;
}
public void setNamespaceService(NamespaceService namespaceService)
{
this.namespaceService = namespaceService;
}
/**
* @param contentUsageService the ContentUsageService to set
*/
@@ -233,6 +229,11 @@ public final class People extends BaseScopableProcessorExtension implements Init
this.userRegistrySynchronizer = userRegistrySynchronizer;
}
public void setDefaultListMaxResults(int defaultListMaxResults)
{
this.defaultListMaxResults = defaultListMaxResults;
}
/**
* Delete a Person with the given username
*
@@ -508,23 +509,27 @@ public final class People extends BaseScopableProcessorExtension implements Init
*/
public Scriptable getPeople(String filter, int maxResults)
{
Object[] people = null;
// TODO - remove open-ended query (eg cutoff at default/configurable max, eg. 5000 people)
if (maxResults <= 0)
boolean useCQ = false;
if (filter != null)
{
maxResults = Integer.MAX_VALUE;
if (filter.endsWith(HINT_CQ_SUFFIX))
{
useCQ = true;
filter = filter.substring(0, filter.length()-HINT_CQ_SUFFIX.length());
}
}
if (filter == null || filter.length() == 0)
Object[] people = null;
if ((maxResults <= 0) || (maxResults > defaultListMaxResults))
{
PagingRequest pagingRequest = new PagingRequest(maxResults, null);
List<PersonInfo> persons = personService.getPeople(null, true, null, pagingRequest).getPage();
people = new Object[persons.size()];
for (int i=0; i<people.length; i++)
{
people[i] = persons.get(i).getNodeRef();
}
// remove open-ended query (eg cutoff at default/configurable max, eg. 5000 people)
maxResults = defaultListMaxResults;
}
if ((filter == null || filter.length() == 0) || useCQ)
{
people = getPeopleImplDB(filter, maxResults);
}
else
{
@@ -536,61 +541,104 @@ public final class People extends BaseScopableProcessorExtension implements Init
int propIndex = term.lastIndexOf(':');
int wildPosition = term.indexOf('*');
if ((t.countTokens() == 1) && (propIndex == -1) && ((wildPosition == -1) || (wildPosition == (term.length() - 1) )))
// simple filter - can use CQ if search fails
useCQ = ((t.countTokens() == 1) && (propIndex == -1) && ((wildPosition == -1) || (wildPosition == (term.length() - 1))));
try
{
// simple non-FTS filter: firstname or lastname or username starting with term (ignoring case)
String propVal = term;
List<Pair<QName, String>> filterProps = new ArrayList<Pair<QName, String>>(3);
filterProps.add(new Pair<QName, String>(ContentModel.PROP_FIRSTNAME, propVal));
filterProps.add(new Pair<QName, String>(ContentModel.PROP_LASTNAME, propVal));
filterProps.add(new Pair<QName, String>(ContentModel.PROP_USERNAME, propVal));
PagingRequest pagingRequest = new PagingRequest(maxResults, null);
List<PersonInfo> persons = personService.getPeople(filterProps, true, null, pagingRequest).getPage();
people = new Object[persons.size()];
for (int i=0; i<people.length; i++)
people = getPeopleImplSearch(term, t, propIndex, maxResults);
}
catch (Throwable err)
{
if (useCQ)
{
people[i] = persons.get(i).getNodeRef();
// search unavailable and/or parser exception - try CQ instead
people = getPeopleImplDB(term, maxResults);
}
}
else
{
SearchParameters params = new SearchParameters();
params.addQueryTemplate("_PERSON", "|%firstName OR |%lastName OR |%userName");
params.setDefaultFieldName("_PERSON");
}
}
}
StringBuilder query = new StringBuilder(256);
if (people == null)
{
people = new Object[0];
}
query.append("TYPE:\"").append(ContentModel.TYPE_PERSON).append("\" AND (");
return Context.getCurrentContext().newArray(getScope(), people);
}
if (t.countTokens() == 1)
{
// single word with no field will go against _PERSON and expand
// canned query
private Object[] getPeopleImplDB(String term, int maxResults)
{
Long start = (logger.isDebugEnabled() ? System.currentTimeMillis() : null);
Object[] people = null;
// fts-alfresco property search i.e. location:"maidenhead"
query.append(term.substring(0, propIndex+1))
.append('"')
.append(term.substring(propIndex+1));
if (propIndex > 0)
{
query.append('"');
}
else
{
query.append("*\"");
}
}
else
{
// scan for non-fts-alfresco property search tokens
int nonFtsTokens = 0;
while (t.hasMoreTokens())
{
if (t.nextToken().indexOf(':') == -1) nonFtsTokens++;
}
t = new StringTokenizer(term, " ");
// simple non-FTS filter: firstname or lastname or username starting with term (ignoring case)
List<QName> filterProps = new ArrayList<QName>(3);
filterProps.add(ContentModel.PROP_FIRSTNAME);
filterProps.add(ContentModel.PROP_LASTNAME);
filterProps.add(ContentModel.PROP_USERNAME);
List<Pair<QName, Boolean>> sortProps = new ArrayList<Pair<QName, Boolean>>(1);
sortProps.add(new Pair<QName, Boolean>(ContentModel.PROP_USERNAME, true));
PagingRequest pagingRequest = new PagingRequest(maxResults, null);
List<PersonInfo> persons = personService.getPeople(term, filterProps, sortProps, pagingRequest).getPage();
people = new Object[persons.size()];
for (int i=0; i<people.length; i++)
{
people[i] = persons.get(i).getNodeRef();
}
if (start != null)
{
logger.debug("getPeople: cq - "+people.length+" items (in "+(System.currentTimeMillis()-start)+" msecs)");
}
return people;
}
// search query
private Object[] getPeopleImplSearch(String term, StringTokenizer t, int propIndex, int maxResults) throws Throwable
{
Long start = (logger.isDebugEnabled() ? System.currentTimeMillis() : null);
Object[] people = null;
SearchParameters params = new SearchParameters();
params.addQueryTemplate("_PERSON", "|%firstName OR |%lastName OR |%userName");
params.setDefaultFieldName("_PERSON");
StringBuilder query = new StringBuilder(256);
query.append("TYPE:\"").append(ContentModel.TYPE_PERSON).append("\" AND (");
if (t.countTokens() == 1)
{
// single word with no field will go against _PERSON and expand
// fts-alfresco property search i.e. location:"maidenhead"
query.append(term.substring(0, propIndex+1))
.append('"')
.append(term.substring(propIndex+1));
if (propIndex > 0)
{
query.append('"');
}
else
{
query.append("*\"");
}
}
else
{
// scan for non-fts-alfresco property search tokens
int nonFtsTokens = 0;
while (t.hasMoreTokens())
{
if (t.nextToken().indexOf(':') == -1) nonFtsTokens++;
}
t = new StringTokenizer(term, " ");
// multiple terms supplied - look for first and second name etc.
// assume first term is first name, any more are second i.e. "Fraun van de Wiels"
@@ -619,76 +667,76 @@ public final class People extends BaseScopableProcessorExtension implements Init
query.append(term);
query.append("*\" ");
firstToken = false;
}
else
{
if (tokenSurname)
{
query.append("OR ");
}
query.append("lastName:\"");
query.append(term);
query.append("*\" ");
firstToken = false;
}
else
{
if (tokenSurname)
{
query.append("OR ");
}
query.append("lastName:\"");
query.append(term);
query.append("*\" ");
tokenSurname = true;
}
}
}
else
{
// fts-alfresco property search i.e. "location:maidenhead"
propIndex = term.lastIndexOf(':');
query.append(term.substring(0, propIndex+1))
.append('"')
.append(term.substring(propIndex+1))
.append('"');
tokenSurname = true;
}
}
}
else
{
// fts-alfresco property search i.e. "location:maidenhead"
propIndex = term.lastIndexOf(':');
query.append(term.substring(0, propIndex+1))
.append('"')
.append(term.substring(propIndex+1))
.append('"');
propertySearch = true;
}
}
}
query.append(")");
propertySearch = true;
}
}
}
query.append(")");
// define the search parameters
params.setLanguage(SearchService.LANGUAGE_FTS_ALFRESCO);
params.addStore(this.storeRef);
params.setQuery(query.toString());
if (maxResults > 0)
{
params.setLimitBy(LimitBy.FINAL_SIZE);
params.setLimit(maxResults);
}
// define the search parameters
params.setLanguage(SearchService.LANGUAGE_FTS_ALFRESCO);
params.addStore(this.storeRef);
params.setQuery(query.toString());
if (maxResults > 0)
{
params.setLimitBy(LimitBy.FINAL_SIZE);
params.setLimit(maxResults);
}
ResultSet results = null;
try
{
results = services.getSearchService().query(params);
people = results.getNodeRefs().toArray();
}
catch (Throwable err)
{
// hide query parse error from users
if (logger.isDebugEnabled())
logger.debug("Failed to execute people search: " + query.toString(), err);
}
finally
{
if (results != null)
{
results.close();
}
}
}
}
}
ResultSet results = null;
try
{
results = services.getSearchService().query(params);
people = results.getNodeRefs().toArray();
if (people == null)
{
people = new Object[0];
}
if (start != null)
{
logger.debug("getPeople: search - "+people.length+" items (in "+(System.currentTimeMillis()-start)+" msecs)");
}
}
catch (Throwable err)
{
if (logger.isDebugEnabled())
{
logger.debug("Failed to execute people search: " + query.toString(), err);
}
return Context.getCurrentContext().newArray(getScope(), people);
throw err;
}
finally
{
if (results != null)
{
results.close();
}
}
return people;
}
/**

View File

@@ -0,0 +1,160 @@
/*
* Copyright (C) 2005-2012 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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/>.
*/
package org.alfresco.repo.security.person;
/**
* Filterable/Sortable Person Entity
*
* Can be optionally filtered/sorted by (up to) three properties - note: sort applied in same order as filter properties (if sort order is not null for given property)
*
* @author janv
* @since 4.1.2
*/
public class FilterSortPersonEntity
{
private Long parentNodeId;
private Long prop1qnameId =null;
private Boolean sort1asc = null;
private Long prop2qnameId = null;
private Boolean sort2asc = null;
private Long prop3qnameId = null;
private Boolean sort3asc = null;
private String pattern;
/**
* Default constructor
*/
public FilterSortPersonEntity()
{
}
public Long getParentNodeId()
{
return parentNodeId;
}
public void setParentNodeId(Long parentNodeId)
{
this.parentNodeId = parentNodeId;
}
public String getPattern()
{
return pattern;
}
protected String escape(String s, char escapeChar)
{
StringBuilder sb = new StringBuilder();
int idx = -1;
int offset = 0;
do
{
idx = s.indexOf(escapeChar, offset);
if(idx != -1)
{
sb.append(s.substring(offset, idx));
sb.append("\\");
sb.append(escapeChar);
offset = idx + 1;
}
}
while(idx != -1);
sb.append(s.substring(offset));
return sb.toString();
}
public void setPattern(String pattern)
{
if (pattern != null)
{
// escape the '%' character with '\' (standard SQL escape character)
//pattern = escape(pattern, '%');
// replace the wildcard character '*' with the one used in database queries i.e. '%'
this.pattern = pattern.replace('*', '%');
}
}
public Long getProp1qnameId()
{
return prop1qnameId;
}
public void setProp1qnameId(Long prop1qnameId)
{
this.prop1qnameId = prop1qnameId;
}
public Boolean getSort1asc()
{
return sort1asc;
}
public void setSort1asc(Boolean sort1asc)
{
this.sort1asc = sort1asc;
}
public Long getProp2qnameId()
{
return prop2qnameId;
}
public void setProp2qnameId(Long prop2qnameId)
{
this.prop2qnameId = prop2qnameId;
}
public Boolean getSort2asc()
{
return sort2asc;
}
public void setSort2asc(Boolean sort2asc)
{
this.sort2asc = sort2asc;
}
public Long getProp3qnameId()
{
return prop3qnameId;
}
public void setProp3qnameId(Long prop3qnameId)
{
this.prop3qnameId = prop3qnameId;
}
public Boolean getSort3asc()
{
return sort3asc;
}
public void setSort3asc(Boolean sort3asc)
{
this.sort3asc = sort3asc;
}
}

View File

@@ -0,0 +1,315 @@
/*
* Copyright (C) 2005-2012 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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/>.
*/
package org.alfresco.repo.security.person;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.query.AbstractCannedQuery;
import org.alfresco.query.CannedQueryParameters;
import org.alfresco.query.CannedQuerySortDetails;
import org.alfresco.query.CannedQuerySortDetails.SortOrder;
import org.alfresco.repo.domain.node.NodeDAO;
import org.alfresco.repo.domain.qname.QNameDAO;
import org.alfresco.repo.domain.query.CannedQueryDAO;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
import org.alfresco.util.ParameterCheck;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* GetPeople canned query
*
* To get paged list of children of a parent node filtered by child type.
* Also optionally filtered and/or sorted by one or more properties (up to three).
*
* @author janv
* @since 4.1.2
*/
public class GetPeopleCannedQuery extends AbstractCannedQuery<NodeRef>
{
private Log logger = LogFactory.getLog(getClass());
private static final String QUERY_NAMESPACE = "alfresco.query.people";
private static final String QUERY_SELECT_GET_PEOPLE = "select_GetPeopleCannedQuery";
public static final int MAX_FILTER_SORT_PROPS = 3;
private NodeDAO nodeDAO;
private QNameDAO qnameDAO;
private CannedQueryDAO cannedQueryDAO;
private TenantService tenantService;
public GetPeopleCannedQuery(
NodeDAO nodeDAO,
QNameDAO qnameDAO,
CannedQueryDAO cannedQueryDAO,
TenantService tenantService,
CannedQueryParameters params)
{
super(params);
this.nodeDAO = nodeDAO;
this.qnameDAO = qnameDAO;
this.cannedQueryDAO = cannedQueryDAO;
this.tenantService = tenantService;
}
@Override
protected List<NodeRef> queryAndFilter(CannedQueryParameters parameters)
{
Long start = (logger.isDebugEnabled() ? System.currentTimeMillis() : null);
// Get parameters
GetPeopleCannedQueryParams paramBean = (GetPeopleCannedQueryParams)parameters.getParameterBean();
// Get parent node
NodeRef parentRef = paramBean.getParentRef();
ParameterCheck.mandatory("nodeRef", parentRef);
Pair<Long, NodeRef> nodePair = nodeDAO.getNodePair(parentRef);
if (nodePair == null)
{
throw new InvalidNodeRefException("Parent node does not exist: " + parentRef, parentRef);
}
Long parentNodeId = nodePair.getFirst();
// Set query params - note: currently using SortableChildEntity to hold (supplemental-) query params
FilterSortPersonEntity params = new FilterSortPersonEntity();
// Set parent node id
params.setParentNodeId(parentNodeId);
// Get filter details
final List<QName> filterProps = paramBean.getFilterProps();
// Get sort details
CannedQuerySortDetails sortDetails = parameters.getSortDetails();
@SuppressWarnings({ "unchecked", "rawtypes" })
final List<Pair<QName, SortOrder>> sortPairs = (List)sortDetails.getSortPairs();
String pattern = paramBean.getPattern();
if ((sortPairs.size() > 0) && ((pattern == null) || (pattern.equals(""))))
{
// note: although no pattern means no filtering required, we currently need to match all if sort required
pattern = "%";
}
else if ((! pattern.endsWith("%")) && (! pattern.endsWith("*")))
{
// implicit startsWith match
pattern = pattern + "%";
}
// Set filter pattern
params.setPattern(pattern);
// Set sort / filter params
// Note - need to keep the sort properties in their requested order
List<QName> sortFilterProps = new ArrayList<QName>(MAX_FILTER_SORT_PROPS);
Map<QName, Boolean> sortAsc = new HashMap<QName, Boolean>(MAX_FILTER_SORT_PROPS);
// add sort props first
for (Pair<QName, SortOrder> sort : sortPairs)
{
QName sortQName = sort.getFirst();
if ((filterProps.size() > 0) && (! filterProps.contains(sortQName)))
{
throw new AlfrescoRuntimeException("GetPeople: cannot sort by a non-filter property: "+sortQName+" (filterStringProps="+filterProps+")");
}
if (! sortFilterProps.contains(sortQName))
{
sortFilterProps.add(sortQName);
sortAsc.put(sortQName, sort.getSecond().equals(SortOrder.ASCENDING));
}
}
// add any additional filter props (not part of sort)
for (QName filterQName : filterProps)
{
if (! sortFilterProps.contains(filterQName))
{
sortFilterProps.add(filterQName);
sortAsc.put(filterQName, null);
}
}
int filterSortPropCnt = sortFilterProps.size();
if (filterSortPropCnt > MAX_FILTER_SORT_PROPS)
{
throw new AlfrescoRuntimeException("GetPeople: exceeded maximum number filter/sort properties: (max="+MAX_FILTER_SORT_PROPS+", actual="+filterSortPropCnt);
}
filterSortPropCnt = setFilterSortParams(sortFilterProps, sortAsc, params);
// filtered and/or sorted - note: permissions not applicable for getPeople
final List<NodeRef> result = new ArrayList<NodeRef>(100);
final PersonQueryCallback c = new DefaultPersonQueryCallback(result);
PersonResultHandler resultHandler = new PersonResultHandler(c);
int offset = parameters.getPageDetails().getSkipResults();
int limit = parameters.getPageDetails().getPageSize();
if (limit != Integer.MAX_VALUE)
{
// to enable hasMore flag
limit++;
}
cannedQueryDAO.executeQuery(QUERY_NAMESPACE, QUERY_SELECT_GET_PEOPLE, params, offset, limit, resultHandler);
resultHandler.done();
if (start != null)
{
logger.debug("Base query: "+result.size()+" in "+(System.currentTimeMillis()-start)+" msecs");
}
return result;
}
// Set filter/sort props (between 0 and 3)
private int setFilterSortParams(List<QName> filterSortProps, Map<QName, Boolean> sortAsc, FilterSortPersonEntity params)
{
int cnt = 0;
int propCnt = 0;
for (QName filterSortProp : filterSortProps)
{
Long sortQNameId = getQNameId(filterSortProp);
Boolean sortOrder = sortAsc.get(filterSortProp); // true = ascending, false = descending, null = unsorted
if (sortQNameId != null)
{
if (propCnt == 0)
{
params.setProp1qnameId(sortQNameId);
params.setSort1asc(sortOrder);
}
else if (propCnt == 1)
{
params.setProp2qnameId(sortQNameId);
params.setSort2asc(sortOrder);
}
else if (propCnt == 2)
{
params.setProp3qnameId(sortQNameId);
params.setSort3asc(sortOrder);
}
else
{
// belts and braces
throw new AlfrescoRuntimeException("GetPeople: unexpected - cannot set sort parameter: "+cnt);
}
propCnt++;
}
else
{
logger.warn("Skipping filter/sort param - cannot find: "+filterSortProp);
break;
}
cnt++;
}
return cnt;
}
private Long getQNameId(QName sortPropQName)
{
Pair<Long, QName> qnamePair = qnameDAO.getQName(sortPropQName);
return (qnamePair == null ? null : qnamePair.getFirst());
}
@Override
protected boolean isApplyPostQuerySorting()
{
// note: sorted as part of the query impl
return false;
}
@Override
protected boolean isApplyPostQueryPermissions()
{
return false;
}
@Override
protected boolean isApplyPostQueryPaging()
{
return false;
}
@Override
protected Pair<Integer, Integer> getTotalResultCount(List<NodeRef> results)
{
return null;
}
protected interface PersonQueryCallback
{
boolean handle(NodeRef personRef);
}
protected class DefaultPersonQueryCallback implements PersonQueryCallback
{
private List<NodeRef> children;
public DefaultPersonQueryCallback(final List<NodeRef> children)
{
this.children = children;
}
@Override
public boolean handle(NodeRef personRef)
{
children.add(tenantService.getBaseName(personRef));
// More results
return true;
}
}
protected class PersonResultHandler implements CannedQueryDAO.ResultHandler<String>
{
private final PersonQueryCallback resultsCallback;
private PersonResultHandler(PersonQueryCallback resultsCallback)
{
this.resultsCallback = resultsCallback;
}
public boolean handleResult(String uuid)
{
// Call back
return resultsCallback.handle(new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, uuid));
}
public void done()
{
}
}
}

View File

@@ -0,0 +1,123 @@
/*
* Copyright (C) 2005-2012 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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/>.
*/
package org.alfresco.repo.security.person;
import java.util.ArrayList;
import java.util.List;
import org.alfresco.query.AbstractCannedQueryFactory;
import org.alfresco.query.CannedQuery;
import org.alfresco.query.CannedQueryPageDetails;
import org.alfresco.query.CannedQueryParameters;
import org.alfresco.query.CannedQuerySortDetails;
import org.alfresco.query.CannedQuerySortDetails.SortOrder;
import org.alfresco.query.PagingRequest;
import org.alfresco.repo.domain.node.NodeDAO;
import org.alfresco.repo.domain.qname.QNameDAO;
import org.alfresco.repo.domain.query.CannedQueryDAO;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
import org.alfresco.util.ParameterCheck;
import org.alfresco.util.PropertyCheck;
/**
* GetPeople canned query factory - to get paged list of people
*
* @author janv
* @since 4.1.2
*/
public class GetPeopleCannedQueryFactory extends AbstractCannedQueryFactory<NodeRef>
{
protected NodeDAO nodeDAO;
protected QNameDAO qnameDAO;
protected CannedQueryDAO cannedQueryDAO;
protected TenantService tenantService;
public void setNodeDAO(NodeDAO nodeDAO)
{
this.nodeDAO = nodeDAO;
}
public void setQnameDAO(QNameDAO qnameDAO)
{
this.qnameDAO = qnameDAO;
}
public void setCannedQueryDAO(CannedQueryDAO cannedQueryDAO)
{
this.cannedQueryDAO = cannedQueryDAO;
}
public void setTenantService(TenantService tenantService)
{
this.tenantService = tenantService;
}
@Override
public CannedQuery<NodeRef> getCannedQuery(CannedQueryParameters parameters)
{
return (CannedQuery<NodeRef>) new GetPeopleCannedQuery(nodeDAO, qnameDAO, cannedQueryDAO, tenantService, parameters);
}
public CannedQuery<NodeRef> getCannedQuery(NodeRef parentRef, String pattern, List<QName> filterProps, List<Pair<QName, Boolean>> sortProps, PagingRequest pagingRequest)
{
ParameterCheck.mandatory("parentRef", parentRef);
ParameterCheck.mandatory("pagingRequest", pagingRequest);
int requestTotalCountMax = pagingRequest.getRequestTotalCountMax();
// specific query params - context (parent) and inclusive filters (property values)
GetPeopleCannedQueryParams paramBean = new GetPeopleCannedQueryParams(tenantService.getName(parentRef), filterProps, pattern);
// page details
CannedQueryPageDetails cqpd = new CannedQueryPageDetails(pagingRequest.getSkipCount(), pagingRequest.getMaxItems(), CannedQueryPageDetails.DEFAULT_PAGE_NUMBER, CannedQueryPageDetails.DEFAULT_PAGE_COUNT);
// sort details
CannedQuerySortDetails cqsd = null;
if (sortProps != null)
{
List<Pair<? extends Object, SortOrder>> sortPairs = new ArrayList<Pair<? extends Object, SortOrder>>(sortProps.size());
for (Pair<QName, Boolean> sortProp : sortProps)
{
sortPairs.add(new Pair<QName, SortOrder>(sortProp.getFirst(), (sortProp.getSecond() ? SortOrder.ASCENDING : SortOrder.DESCENDING)));
}
cqsd = new CannedQuerySortDetails(sortPairs);
}
// create query params holder
CannedQueryParameters params = new CannedQueryParameters(paramBean, cqpd, cqsd, requestTotalCountMax, pagingRequest.getQueryExecutionId());
// return canned query instance
return getCannedQuery(params);
}
@Override
public void afterPropertiesSet() throws Exception
{
super.afterPropertiesSet();
PropertyCheck.mandatory(this, "tenantService", tenantService);
PropertyCheck.mandatory(this, "nodeDAO", nodeDAO);
PropertyCheck.mandatory(this, "qnameDAO", qnameDAO);
PropertyCheck.mandatory(this, "cannedQueryDAO", cannedQueryDAO);
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2005-2012 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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/>.
*/
package org.alfresco.repo.security.person;
import java.util.Collections;
import java.util.List;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
/**
* GetPeople CQ parameters - for query context and filtering
*
* @author janv
* @since 4.1.2
*/
public class GetPeopleCannedQueryParams
{
private NodeRef parentRef;
private List<QName> filterProps = Collections.emptyList();
private String pattern = null;
public GetPeopleCannedQueryParams(
NodeRef parentRef,
List<QName> filterProps,
String pattern)
{
this.parentRef = parentRef;
if (filterProps != null) { this.filterProps = filterProps; }
this.pattern = pattern;
}
public NodeRef getParentRef()
{
return parentRef;
}
public List<QName> getFilterProps()
{
return filterProps;
}
public String getPattern()
{
return pattern;
}
}

View File

@@ -20,7 +20,6 @@ package org.alfresco.repo.security.person;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
@@ -29,12 +28,10 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.query.CannedQuery;
import org.alfresco.query.CannedQueryFactory;
import org.alfresco.query.CannedQueryResults;
import org.alfresco.query.PagingRequest;
@@ -81,9 +78,6 @@ import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.repository.TemplateService;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.cmr.search.LimitBy;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchParameters;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.AuthorityType;
@@ -115,7 +109,8 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per
{
private static Log logger = LogFactory.getLog(PersonServiceImpl.class);
private static final String CANNED_QUERY_PEOPLE_LIST = "peopleGetChildrenCannedQueryFactory";
private static final String CANNED_QUERY_PEOPLE_LIST = "getPeopleCannedQueryFactory";
private static final String CANNED_QUERY_PEOPLE_LIST_DEPRECATED = "peopleGetChildrenCannedQueryFactory";
private static final String DELETE = "DELETE";
private static final String SPLIT = "SPLIT";
@@ -1190,71 +1185,65 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per
return refs;
}
private List<FilterProp> getFilterProps(List<Pair<QName, String>> stringPropFilters, boolean filterIgnoreCase)
{
List<FilterProp> filterProps = null;
if (stringPropFilters != null)
{
filterProps = new ArrayList<FilterProp>(stringPropFilters.size());
for (Pair<QName, String> filterProp : stringPropFilters)
{
String filterStr = filterProp.getSecond();
if ((filterStr == null) || (filterStr.equals("")) || (filterStr.equals("*")))
{
// The wildcard means no filtering is needed on this property
continue;
}
else if (filterStr.endsWith("*"))
{
// The trailing * is implicit
filterStr = filterStr.substring(0, filterStr.length()-1);
}
// Turn this into a canned query filter
filterProps.add(new FilterPropString(filterProp.getFirst(), filterStr, (filterIgnoreCase ? FilterTypeString.STARTSWITH_IGNORECASE : FilterTypeString.STARTSWITH)));
}
}
return filterProps;
}
/**
* {@inheritDoc}
*/
public PagingResults<PersonInfo> getPeople(List<Pair<QName, String>> stringPropFilters, boolean filterIgnoreCase, List<Pair<QName, Boolean>> sortProps, PagingRequest pagingRequest)
public PagingResults<PersonInfo> getPeople(String pattern, List<QName> filterStringProps, List<Pair<QName, Boolean>> sortProps, PagingRequest pagingRequest)
{
ParameterCheck.mandatory("pagingRequest", pagingRequest);
Long start = (logger.isDebugEnabled() ? System.currentTimeMillis() : null);
// TODO Remove this ALF-14127 hot fix code (calling nonCannedGetPeopleQuery(...) once this canned query does not fetch all rows from the database,
// which is very slow when there are a lot of users. 10,000 user takes about 4 seconds. The customer has 90,000.
CannedQueryResults<NodeRef> cqResults = null;
String searchValue = null;
if (filterIgnoreCase && pagingRequest != null && pagingRequest.getQueryExecutionId() == null && pagingRequest.getSkipCount() == 0)
{
searchValue = getSearchOnNameValue(stringPropFilters);
}
if (searchValue != null)
{
cqResults = nonCannedGetPeopleQuery(searchValue, pagingRequest);
}
else
{
NodeRef contextNodeRef = getPeopleContainer();
Set<QName> childTypeQNames = new HashSet<QName>(1);
childTypeQNames.add(ContentModel.TYPE_PERSON);
NodeRef contextNodeRef = getPeopleContainer();
// get canned query
GetChildrenCannedQueryFactory getChildrenCannedQueryFactory = (GetChildrenCannedQueryFactory)cannedQueryRegistry.getNamedObject(CANNED_QUERY_PEOPLE_LIST);
// get canned query
GetPeopleCannedQueryFactory getPeopleCannedQueryFactory = (GetPeopleCannedQueryFactory)cannedQueryRegistry.getNamedObject(CANNED_QUERY_PEOPLE_LIST);
List<FilterProp> filterProps = null;
if (stringPropFilters != null)
{
filterProps = new ArrayList<FilterProp>(stringPropFilters.size());
for (Pair<QName, String> filterProp : stringPropFilters)
{
String filterStr = filterProp.getSecond();
if ((filterStr == null) || (filterStr.equals("")) || (filterStr.equals("*")))
{
// The wildcard means no filtering is needed on this property
continue;
}
else if (filterStr.endsWith("*"))
{
// The trailing * is implicit
filterStr = filterStr.substring(0, filterStr.length()-1);
}
GetPeopleCannedQuery cq = (GetPeopleCannedQuery)getPeopleCannedQueryFactory.getCannedQuery(contextNodeRef, pattern, filterStringProps, sortProps, pagingRequest);
// Turn this into a canned query filter
filterProps.add(new FilterPropString(filterProp.getFirst(), filterStr, (filterIgnoreCase ? FilterTypeString.STARTSWITH_IGNORECASE : FilterTypeString.STARTSWITH)));
}
}
GetChildrenCannedQuery cq = (GetChildrenCannedQuery)getChildrenCannedQueryFactory.getCannedQuery(contextNodeRef, null, null, childTypeQNames, filterProps, sortProps, pagingRequest);
// execute canned query
cqResults = cq.execute();
}
// execute canned query
cqResults = cq.execute();
final CannedQueryResults<NodeRef> results = cqResults;
final List<NodeRef> nodeRefs;
List<NodeRef> nodeRefs;
if (results.getPageCount() > 0)
{
nodeRefs = results.getPages().get(0);
if (nodeRefs.size() > pagingRequest.getMaxItems())
{
// eg. since hasMoreItems added one (for a pre-paged result)
nodeRefs = nodeRefs.subList(0, pagingRequest.getMaxItems());
}
}
else
{
@@ -1284,9 +1273,9 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per
{
logger.debug(
"getPeople: "+cnt+" items in "+(System.currentTimeMillis()-start)+" msecs " +
"[pageNum="+pageNum+",skip="+skipCount+",max="+maxItems+",hasMorePages="+hasMoreItems+
",totalCount="+totalCount+",filters="+stringPropFilters+
",filtersIgnoreCase="+filterIgnoreCase+"]");
"[pageNum="+pageNum+",skip="+skipCount+",max="+maxItems+",hasMorePages="+hasMoreItems+
",totalCount="+totalCount+",pattern="+pattern+",filterStringProps="+filterStringProps+
",sortProps="+sortProps+"]");
}
}
@@ -1326,255 +1315,104 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per
}
/**
* If the search is on first, last and user name only with the same value, return that value,
* otherwise return null.
* {@inheritDoc}
*/
// TODO Remove this ALF-14127 hot fix code once this canned query does not fetch all rows from the database.
private String getSearchOnNameValue(List<Pair<QName, String>> stringPropFilters)
public PagingResults<PersonInfo> getPeople(List<Pair<QName, String>> stringPropFilters, boolean filterIgnoreCase, List<Pair<QName, Boolean>> sortProps, PagingRequest pagingRequest)
{
String filter = null;
if (stringPropFilters != null && stringPropFilters.size() == 3)
{
// Does not check we don't have duplicates.
for (int i=0; i < 3; i++)
{
Pair<QName, String> pair = stringPropFilters.get(i);
if (i == 0)
{
filter = pair.getSecond().trim();
if (filter == null || filter.length() == 0)
{
filter = null;
break;
}
}
ParameterCheck.mandatory("pagingRequest", pagingRequest);
if ((i != 0 && !filter.equals(pair.getSecond().trim()) || !NAME_SEARCH_NAMES.contains(pair.getFirst())))
{
filter = null;
break;
}
}
}
return filter;
}
// TODO Remove this ALF-14127 hot fix code once this canned query does not fetch all rows from the database.
private static final List<QName> NAME_SEARCH_NAMES = Arrays.asList(new QName[] {
ContentModel.PROP_FIRSTNAME, ContentModel.PROP_LASTNAME, ContentModel.PROP_USERNAME});
/**
* Use Solr search based on code in org.alfresco.repo.jscript.People.getPeople(String, int)
*/
// TODO Remove this ALF-14127 hot fix code once this canned query does not fetch all rows from the database.
private CannedQueryResults<NodeRef> nonCannedGetPeopleQuery(String filter, PagingRequest pagingRequest)
{
Long start = (logger.isDebugEnabled() ? System.currentTimeMillis() : null);
int maxResults = pagingRequest != null ? pagingRequest.getMaxItems() : Integer.MAX_VALUE;
if (maxResults <= 0)
CannedQueryResults<NodeRef> cqResults = null;
NodeRef contextNodeRef = getPeopleContainer();
Set<QName> childTypeQNames = new HashSet<QName>(1);
childTypeQNames.add(ContentModel.TYPE_PERSON);
// get canned query
GetChildrenCannedQueryFactory getChildrenCannedQueryFactory = (GetChildrenCannedQueryFactory)cannedQueryRegistry.getNamedObject(CANNED_QUERY_PEOPLE_LIST_DEPRECATED);
List<FilterProp> filterProps = getFilterProps(stringPropFilters, filterIgnoreCase);
GetChildrenCannedQuery cq = (GetChildrenCannedQuery)getChildrenCannedQueryFactory.getCannedQuery(contextNodeRef, null, null, childTypeQNames, filterProps, sortProps, pagingRequest);
// execute canned query
cqResults = cq.execute();
final CannedQueryResults<NodeRef> results = cqResults;
final List<NodeRef> nodeRefs;
if (results.getPageCount() > 0)
{
maxResults = Integer.MAX_VALUE;
}
String term = filter.replace("\\", "").replace("\"", "");
StringTokenizer t = new StringTokenizer(term, " ");
int propIndex = term.indexOf(':');
SearchParameters params = new SearchParameters();
params.addQueryTemplate("_PERSON", "|%firstName OR |%lastName OR |%userName");
params.setDefaultFieldName("_PERSON");
StringBuilder query = new StringBuilder(256);
query.append("TYPE:\"").append(ContentModel.TYPE_PERSON).append("\" AND (");
if (t.countTokens() == 1)
{
// single word with no field will go against _PERSON and expand
// fts-alfresco property search i.e. location:"maidenhead"
query.append(term.substring(0, propIndex + 1)).append('"')
.append(term.substring(propIndex + 1));
if (propIndex > 0)
{
query.append('"');
}
else
{
query.append("*\"");
}
nodeRefs = results.getPages().get(0);
}
else
{
// scan for non-fts-alfresco property search tokens
int nonFtsTokens = 0;
while (t.hasMoreTokens())
{
if (t.nextToken().indexOf(':') == -1)
nonFtsTokens++;
}
t = new StringTokenizer(term, " ");
// multiple terms supplied - look for first and second name etc.
// assume first term is first name, any more are second i.e.
// "Fraun van de Wiels"
// also allow fts-alfresco property search to reduce results
params.setDefaultOperator(SearchParameters.Operator.AND);
boolean firstToken = true;
boolean tokenSurname = false;
boolean propertySearch = false;
while (t.hasMoreTokens())
{
term = t.nextToken();
if (!propertySearch && term.indexOf(':') == -1)
{
if (nonFtsTokens == 1)
{
// simple search: first name, last name and username
// starting with term
query.append("_PERSON:\"");
query.append(term);
query.append("*\" ");
}
else
{
if (firstToken)
{
query.append("firstName:\"");
query.append(term);
query.append("*\" ");
firstToken = false;
}
else
{
if (tokenSurname)
{
query.append("OR ");
}
query.append("lastName:\"");
query.append(term);
query.append("*\" ");
tokenSurname = true;
}
}
}
else
{
// fts-alfresco property search i.e. "location:maidenhead"
propIndex = term.indexOf(':');
query.append(term.substring(0, propIndex + 1)).append('"')
.append(term.substring(propIndex + 1)).append('"');
propertySearch = true;
}
}
}
query.append(")");
// define the search parameters
params.setLanguage(SearchService.LANGUAGE_FTS_ALFRESCO);
params.addStore(this.storeRef);
params.setQuery(query.toString());
if (maxResults > 0)
{
params.setLimitBy(LimitBy.FINAL_SIZE);
params.setLimit(maxResults);
nodeRefs = Collections.emptyList();
}
ResultSet results = null;
List<NodeRef> resultNodeRefs = null;
try
// set total count
final Pair<Integer, Integer> totalCount;
if (pagingRequest.getRequestTotalCountMax() > 0)
{
results = searchService.query(params);
resultNodeRefs = results.getNodeRefs();
totalCount = results.getTotalResultCount();
}
catch (Throwable err)
else
{
resultNodeRefs = Collections.emptyList();
totalCount = null;
}
if (start != null)
{
int cnt = results.getPagedResultCount();
int skipCount = pagingRequest.getSkipCount();
int maxItems = pagingRequest.getMaxItems();
boolean hasMoreItems = results.hasMoreItems();
int pageNum = (skipCount / maxItems) + 1;
// hide query parse error from users
if (logger.isDebugEnabled())
logger.debug("Failed to execute people search: " + query.toString(), err);
}
finally
{
if (results != null)
{
results.close();
logger.debug(
"getPeople: "+cnt+" items in "+(System.currentTimeMillis()-start)+" msecs " +
"[pageNum="+pageNum+",skip="+skipCount+",max="+maxItems+",hasMorePages="+hasMoreItems+
",totalCount="+totalCount+",filters="+stringPropFilters+
",filtersIgnoreCase="+filterIgnoreCase+",sortProps="+sortProps+"]");
}
}
// Turn NodeRefs into a single page of results.
final List<NodeRef> nodRefs = resultNodeRefs;
CannedQueryResults<NodeRef> cqResults = new CannedQueryResults<NodeRef>()
final List<PersonInfo> personInfos = new ArrayList<PersonInfo>(nodeRefs.size());
for (NodeRef nodeRef : nodeRefs)
{
@Override
public CannedQuery<NodeRef> getOriginatingQuery()
{
return null;
}
Map<QName, Serializable> props = nodeService.getProperties(nodeRef);
personInfos.add(new PersonInfo(nodeRef,
(String)props.get(ContentModel.PROP_USERNAME),
(String)props.get(ContentModel.PROP_FIRSTNAME),
(String)props.get(ContentModel.PROP_LASTNAME)));
}
return new PagingResults<PersonInfo>()
{
@Override
public String getQueryExecutionId()
{
return null;
return results.getQueryExecutionId();
}
@Override
public Pair<Integer, Integer> getTotalResultCount()
public List<PersonInfo> getPage()
{
int size = nodRefs.size();
return new Pair<Integer, Integer>(size, size);
return personInfos;
}
@Override
public int getPagedResultCount()
{
return nodRefs.size();
}
@Override
public int getPageCount()
{
return 1;
}
@Override
public NodeRef getSingleResult()
{
if (nodRefs.size() != 1)
{
throw new IllegalStateException(
"There must be exactly one page of one result available.");
}
return nodRefs.get(0);
}
@Override
public List<NodeRef> getPage()
{
return nodRefs;
}
@Override
public List<List<NodeRef>> getPages()
{
return Collections.singletonList(getPage());
}
@Override
public boolean hasMoreItems()
{
return false;
return results.hasMoreItems();
}
@Override
public Pair<Integer, Integer> getTotalResultCount()
{
return totalCount;
}
};
if (logger.isDebugEnabled())
{
logger.debug("nonCannedGetPeopleQuery(\""+filter+"\", "+maxResults+") "+cqResults.getTotalResultCount()+" in "+(System.currentTimeMillis()-start)+" msecs ");
}
return cqResults;
}
/**

View File

@@ -560,7 +560,6 @@ public class PersonTest extends TestCase
personService.deletePerson("Derek");
assertEquals(2, getPeopleCount());
}
public void testPeopleFiltering()
@@ -581,6 +580,113 @@ public class PersonTest extends TestCase
PagingRequest pr = new PagingRequest(100, null);
List<QName> filters = new ArrayList<QName>(4);
filters.clear();
filters.add(ContentModel.PROP_USERNAME);
assertEquals(2, personService.getPeople("y", filters, null, pr).getPage().size());
filters.clear();
filters.add(ContentModel.PROP_USERNAME);
filters.add(ContentModel.PROP_FIRSTNAME);
filters.add(ContentModel.PROP_LASTNAME);
assertEquals(3, personService.getPeople("b", filters, null, pr).getPage().size());
filters.clear();
filters.add(ContentModel.PROP_USERNAME);
assertEquals(2, personService.getPeople("A", filters, null, pr).getPage().size()); // includes "admin"
personService.deletePerson("aa");
filters.clear();
filters.add(ContentModel.PROP_USERNAME);
assertEquals(1, personService.getPeople("a", filters, null, pr).getPage().size()); // includes "admin"
// a* is the same as a
filters.clear();
filters.add(ContentModel.PROP_USERNAME);
assertEquals(1, personService.getPeople("a*", filters, null, pr).getPage().size()); // includes "admin"
// * means everyone
filters.clear();
filters.add(ContentModel.PROP_USERNAME);
assertEquals(5, getPeopleCount());
assertEquals(5, personService.getPeople("*", filters, null, pr).getPage().size());
}
public void testPeopleSortingPaging()
{
personService.setCreateMissingPeople(false);
assertEquals(2, getPeopleCount());
NodeRef p1 = personService.getPerson(AuthenticationUtil.getAdminUserName()); // admin - by default
NodeRef p2 = personService.getPerson(AuthenticationUtil.getGuestUserName()); // guest - by default
NodeRef p3 = personService.createPerson(createDefaultProperties("aa", "Aa", "Aa", "aa@aa", "alfresco", rootNodeRef));
NodeRef p4 = personService.createPerson(createDefaultProperties("cc", "Cc", "Cc", "cc@cc", "alfresco", rootNodeRef));
NodeRef p5 = personService.createPerson(createDefaultProperties("hh", "Hh", "Hh", "hh@hh", "alfresco", rootNodeRef));
NodeRef p6 = personService.createPerson(createDefaultProperties("bb", "Bb", "Bb", "bb@bb", "alfresco", rootNodeRef));
NodeRef p7 = personService.createPerson(createDefaultProperties("dd", "Dd", "Dd", "dd@dd", "alfresco", rootNodeRef));
assertEquals(7, getPeopleCount());
List<Pair<QName, Boolean>> sort = new ArrayList<Pair<QName, Boolean>>(1);
sort.add(new Pair<QName,Boolean>(ContentModel.PROP_USERNAME, true));
// page 1
PagingRequest pr = new PagingRequest(0, 2, null);
PagingResults<PersonInfo> ppr = personService.getPeople(null, null, sort, pr);
List<PersonInfo> results = ppr.getPage();
assertEquals(2, results.size());
assertEquals(p3, results.get(0).getNodeRef());
assertEquals(p1, results.get(1).getNodeRef());
// page 2
pr = new PagingRequest(2, 2, null);
ppr = personService.getPeople(null, null, sort, pr);
results = ppr.getPage();
assertEquals(2, results.size());
assertEquals(p6, results.get(0).getNodeRef());
assertEquals(p4, results.get(1).getNodeRef());
// page 3
pr = new PagingRequest(4, 2, null);
ppr = personService.getPeople(null, null, sort, pr);
results = ppr.getPage();
assertEquals(2, results.size());
assertEquals(p7, results.get(0).getNodeRef());
assertEquals(p2, results.get(1).getNodeRef());
// page 4
pr = new PagingRequest(6, 2, null);
ppr = personService.getPeople(null, null, sort, pr);
results = ppr.getPage();
assertEquals(1, results.size());
assertEquals(p5, results.get(0).getNodeRef());
}
// note: this test can be removed as and when we remove the deprecated "getPeople" impl
public void testPeopleFiltering_deprecatedCQ_via_getChildren()
{
personService.setCreateMissingPeople(false);
assertEquals(2, getPeopleCount());
checkPeopleContain(AuthenticationUtil.getAdminUserName());
checkPeopleContain(AuthenticationUtil.getGuestUserName());
personService.createPerson(createDefaultProperties("aa", "Aa", "Aa", "aa@aa", "alfresco", rootNodeRef));
personService.createPerson(createDefaultProperties("bc", "c", "C", "bc@bc", "alfresco", rootNodeRef));
personService.createPerson(createDefaultProperties("yy", "B", "D", "yy@yy", "alfresco", rootNodeRef));
personService.createPerson(createDefaultProperties("Yz", "yz", "B", "yz@yz", "alfresco", rootNodeRef));
assertEquals(6, getPeopleCount());
PagingRequest pr = new PagingRequest(100, null);
List<Pair<QName, String>> filters = new ArrayList<Pair<QName, String>>(4);
filters.clear();
@@ -618,7 +724,8 @@ public class PersonTest extends TestCase
assertEquals(5, personService.getPeople(filters, true, null, pr).getPage().size());
}
public void testPeopleSortingPaging()
// note: this test can be removed as and when we remove the deprecated "getPeople" impl
public void testPeopleSortingPaging_deprecatedCQ_via_getChildren()
{
personService.setCreateMissingPeople(false);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
* Copyright (C) 2005-2012 Alfresco Software Limited.
*
* This file is part of Alfresco
*
@@ -274,7 +274,25 @@ public interface PersonService
/**
* Get paged list of people optionally filtered and/or sorted
*
* Note: the pattern is applied to filter props (0 to 3) as startsWithIgnoreCase, which are OR'ed together, for example: cm:userName or cm:firstName or cm:lastName
*
* @param pattern pattern to apply to filter props - "startsWith" and "ignoreCase"
* @param filterProps list of filter properties (these are OR'ed)
* @param sortProps sort property, eg. cm:username ascending
* @param pagingRequest skip, max + optional query execution id
* @return
*
* @author janv
* @since 4.1.2
*/
@Auditable(parameters = {"pattern", "filterProps", "sortProps", "pagingRequest"})
public PagingResults<PersonInfo> getPeople(String pattern, List<QName> filterProps, List<Pair<QName, Boolean>> sortProps, PagingRequest pagingRequest);
/**
* Get paged list of people optionally filtered and/or sorted
*
* @param filterProps list of filter properties (with "startsWith" values), eg. cm:username "al" might match "alex", "alice", ...
* @param filterIgnoreCase true to ignore case when filtering, false to be case-sensitive when filtering
* @param sortProps sort property, eg. cm:username ascending
@@ -282,6 +300,7 @@ public interface PersonService
*
* @author janv
* @since 4.0
* @deprecated see getPeople(String pattern, List<QName> filterProps, List<Pair<QName, Boolean>> sortProps, PagingRequest pagingRequest)
*/
@Auditable(parameters = {"stringPropFilters", "filterIgnoreCase", "sortProps", "pagingRequest"})
public PagingResults<PersonInfo> getPeople(List<Pair<QName,String>> stringPropFilters, boolean filterIgnoreCase, List<Pair<QName, Boolean>> sortProps, PagingRequest pagingRequest);