diff --git a/source/java/org/alfresco/repo/node/GetNodesWithAspectCannedQuery.java b/source/java/org/alfresco/repo/node/GetNodesWithAspectCannedQuery.java
new file mode 100644
index 0000000000..0864d2fc60
--- /dev/null
+++ b/source/java/org/alfresco/repo/node/GetNodesWithAspectCannedQuery.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2005-2011 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 .
+ */
+package org.alfresco.repo.node;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.alfresco.query.CannedQueryParameters;
+import org.alfresco.repo.domain.node.NodeDAO;
+import org.alfresco.repo.domain.node.NodeDAO.NodeRefQueryCallback;
+import org.alfresco.repo.security.permissions.impl.acegi.AbstractCannedQueryPermissions;
+import org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityBean;
+import org.alfresco.repo.tenant.TenantService;
+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.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * GetNodesWithAspect canned query
+ *
+ * To get paged list of nodes with the specified aspects.
+ *
+ * @author Nick Burch
+ * @since 4.1
+ */
+public class GetNodesWithAspectCannedQuery extends AbstractCannedQueryPermissions
+{
+ private Log logger = LogFactory.getLog(getClass());
+
+ private NodeDAO nodeDAO;
+ private TenantService tenantService;
+
+ public GetNodesWithAspectCannedQuery(
+ NodeDAO nodeDAO,
+ TenantService tenantService,
+ MethodSecurityBean methodSecurity,
+ CannedQueryParameters params)
+ {
+ super(params, methodSecurity);
+
+ this.nodeDAO = nodeDAO;
+ this.tenantService = tenantService;
+ }
+
+ @Override
+ protected List queryAndFilter(CannedQueryParameters parameters)
+ {
+ Long start = (logger.isDebugEnabled() ? System.currentTimeMillis() : null);
+
+ // Get parameters
+ GetNodesWithAspectCannedQueryParams paramBean = (GetNodesWithAspectCannedQueryParams)parameters.getParameterBean();
+
+ // Get store, if requested
+ final StoreRef storeRef = paramBean.getStoreRef();
+
+ // Note - doesn't currently support sorting
+
+ // Get filter details
+ final Set aspectQNames = paramBean.getAspectQNames();
+
+
+ // Find all the available nodes
+ // Doesn't limit them here, as permissions will be applied post-query
+ // TODO Improve this to permission check and page in-line, so we
+ // can stop the query earlier if possible
+ final List result = new ArrayList(100);
+
+ nodeDAO.getNodesWithAspects(
+ aspectQNames, Long.MIN_VALUE, Long.MAX_VALUE,
+ new NodeRefQueryCallback() {
+ @Override
+ public boolean handle(Pair nodePair)
+ {
+ NodeRef nodeRef = nodePair.getSecond();
+ if (storeRef == null || nodeRef.getStoreRef().equals(storeRef))
+ {
+ result.add(nodeRef);
+ }
+
+ // Always ask for the next one
+ return true;
+ }
+ }
+ );
+
+ if (start != null)
+ {
+ logger.debug("Base query: "+result.size()+" in "+(System.currentTimeMillis()-start)+" msecs");
+ }
+
+ return result;
+ }
+}
diff --git a/source/java/org/alfresco/repo/node/GetNodesWithAspectCannedQueryFactory.java b/source/java/org/alfresco/repo/node/GetNodesWithAspectCannedQueryFactory.java
new file mode 100644
index 0000000000..1583d9b083
--- /dev/null
+++ b/source/java/org/alfresco/repo/node/GetNodesWithAspectCannedQueryFactory.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2005-2011 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 .
+ */
+package org.alfresco.repo.node;
+
+import java.util.Set;
+
+import org.alfresco.query.AbstractCannedQueryFactory;
+import org.alfresco.query.CannedQuery;
+import org.alfresco.query.CannedQueryPageDetails;
+import org.alfresco.query.CannedQueryParameters;
+import org.alfresco.query.PagingRequest;
+import org.alfresco.repo.domain.node.NodeDAO;
+import org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityBean;
+import org.alfresco.repo.tenant.TenantService;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.StoreRef;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.util.ParameterCheck;
+import org.alfresco.util.PropertyCheck;
+
+/**
+ * GetNodesWithAspectCannedQuery canned query factory - to get paged list of
+ * Nodes with a given Aspect
+ *
+ * @author Nick Burch
+ * @since 4.1
+ */
+public class GetNodesWithAspectCannedQueryFactory extends AbstractCannedQueryFactory
+{
+ private NodeDAO nodeDAO;
+ private TenantService tenantService;
+
+ private MethodSecurityBean methodSecurity;
+
+ public void setNodeDAO(NodeDAO nodeDAO)
+ {
+ this.nodeDAO = nodeDAO;
+ }
+
+ public void setTenantService(TenantService tenantService)
+ {
+ this.tenantService = tenantService;
+ }
+
+ public void setMethodSecurity(MethodSecurityBean methodSecurity)
+ {
+ this.methodSecurity = methodSecurity;
+ }
+
+ @Override
+ public CannedQuery getCannedQuery(CannedQueryParameters parameters)
+ {
+ return (CannedQuery) new GetNodesWithAspectCannedQuery(nodeDAO, tenantService, methodSecurity, parameters);
+ }
+
+ /**
+ * Retrieve an unsorted instance of a {@link CannedQuery} based on parameters including
+ * request for a total count (up to a given max)
+ *
+ * @param storeRef the store to search in, if requested
+ * @param aspectQNames qnames of aspects to search for
+ * @param pagingRequest skipCount, maxItems - optionally queryExecutionId and requestTotalCountMax
+ *
+ * @return an implementation that will execute the query
+ */
+ public CannedQuery getCannedQuery(StoreRef storeRef, Set aspectQNames, PagingRequest pagingRequest)
+ {
+ ParameterCheck.mandatory("aspectQNames", aspectQNames);
+ ParameterCheck.mandatory("pagingRequest", pagingRequest);
+
+ int requestTotalCountMax = pagingRequest.getRequestTotalCountMax();
+
+ // specific query params - context (parent) and inclusive filters (child types, property values)
+ GetNodesWithAspectCannedQueryParams paramBean = new GetNodesWithAspectCannedQueryParams(storeRef, aspectQNames);
+
+ // page details
+ CannedQueryPageDetails cqpd = new CannedQueryPageDetails(pagingRequest.getSkipCount(), pagingRequest.getMaxItems(), CannedQueryPageDetails.DEFAULT_PAGE_NUMBER, CannedQueryPageDetails.DEFAULT_PAGE_COUNT);
+
+ // no sort details - no sorting done
+
+ // create query params holder
+ CannedQueryParameters params = new CannedQueryParameters(paramBean, cqpd, null, 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, "methodSecurityInterceptor", methodSecurity);
+ }
+}
diff --git a/source/java/org/alfresco/repo/node/GetNodesWithAspectCannedQueryParams.java b/source/java/org/alfresco/repo/node/GetNodesWithAspectCannedQueryParams.java
new file mode 100644
index 0000000000..ee988bfc5c
--- /dev/null
+++ b/source/java/org/alfresco/repo/node/GetNodesWithAspectCannedQueryParams.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2005-2011 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 .
+ */
+package org.alfresco.repo.node;
+
+import java.util.Set;
+
+import org.alfresco.service.cmr.repository.StoreRef;
+import org.alfresco.service.namespace.QName;
+
+/**
+ * GetNodesWithAspectCannedQuery CQ parameters - for query context and filtering
+ *
+ * @author Nick Burch
+ * @since 4.1
+ */
+public class GetNodesWithAspectCannedQueryParams
+{
+ private StoreRef storeRef;
+
+ private Set aspectQNames = null;
+
+ public GetNodesWithAspectCannedQueryParams(
+ StoreRef storeRef,
+ Set aspectQNames)
+ {
+ this.storeRef = storeRef;
+
+ if (aspectQNames != null && !aspectQNames.isEmpty())
+ {
+ this.aspectQNames = aspectQNames;
+ }
+ else
+ {
+ throw new IllegalArgumentException("At least one Aspect must be given");
+ }
+ }
+
+ public StoreRef getStoreRef()
+ {
+ return storeRef;
+ }
+
+ public Set getAspectQNames()
+ {
+ return aspectQNames;
+ }
+}