From 57e7a4f77eac89311e02d4d3a8beff724065425d Mon Sep 17 00:00:00 2001 From: Andrew Hind Date: Thu, 23 Mar 2006 15:14:22 +0000 Subject: [PATCH] Added support for result set meta data Added size limits for result sets Updated/improved comments git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2573 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../alfresco/repo/search/EmptyResultSet.java | 9 + .../repo/search/SearchServiceTest.java | 259 ++++++++++++++++++ .../repo/search/SimpleResultSetMetaData.java | 61 +++++ .../search/impl/lucene/LuceneIndexerImpl.java | 3 +- .../search/impl/lucene/LuceneResultSet.java | 16 +- .../impl/lucene/LuceneSearcherImpl.java | 4 +- .../results/ChildAssocRefResultSet.java | 9 + .../search/results/DetachedResultSet.java | 12 + .../ACLEntryAfterInvocationProvider.java | 55 +++- .../impl/acegi/FilteringResultSet.java | 14 + .../alfresco/service/cmr/search/LimitBy.java | 11 + .../cmr/search/PermissionEvaluationMode.java | 11 + .../service/cmr/search/ResultSet.java | 41 ++- .../service/cmr/search/ResultSetMetaData.java | 52 ++++ .../service/cmr/search/SearchParameters.java | 152 +++++++++- .../service/cmr/search/SearchStatement.java | 26 ++ 16 files changed, 705 insertions(+), 30 deletions(-) create mode 100644 source/java/org/alfresco/repo/search/SearchServiceTest.java create mode 100644 source/java/org/alfresco/repo/search/SimpleResultSetMetaData.java create mode 100644 source/java/org/alfresco/service/cmr/search/LimitBy.java create mode 100644 source/java/org/alfresco/service/cmr/search/PermissionEvaluationMode.java create mode 100644 source/java/org/alfresco/service/cmr/search/ResultSetMetaData.java diff --git a/source/java/org/alfresco/repo/search/EmptyResultSet.java b/source/java/org/alfresco/repo/search/EmptyResultSet.java index 713d0663b3..e76987de0d 100644 --- a/source/java/org/alfresco/repo/search/EmptyResultSet.java +++ b/source/java/org/alfresco/repo/search/EmptyResultSet.java @@ -24,8 +24,12 @@ import java.util.List; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.Path; +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.search.ResultSetMetaData; import org.alfresco.service.cmr.search.ResultSetRow; +import org.alfresco.service.cmr.search.SearchParameters; public class EmptyResultSet implements ResultSet { @@ -87,4 +91,9 @@ public class EmptyResultSet implements ResultSet // TODO Auto-generated method stub throw new UnsupportedOperationException(); } + + public ResultSetMetaData getResultSetMetaData() + { + return new SimpleResultSetMetaData(LimitBy.UNLIMITED, PermissionEvaluationMode.EAGER, new SearchParameters()); + } } diff --git a/source/java/org/alfresco/repo/search/SearchServiceTest.java b/source/java/org/alfresco/repo/search/SearchServiceTest.java new file mode 100644 index 0000000000..536009e430 --- /dev/null +++ b/source/java/org/alfresco/repo/search/SearchServiceTest.java @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.search; + +import javax.transaction.UserTransaction; + +import junit.framework.TestCase; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.security.authentication.AuthenticationComponent; +import org.alfresco.repo.security.authentication.MutableAuthenticationDao; +import org.alfresco.service.ServiceRegistry; +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.search.LimitBy; +import org.alfresco.service.cmr.search.PermissionEvaluationMode; +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.AuthenticationService; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.ApplicationContextHelper; +import org.springframework.context.ApplicationContext; + +public class SearchServiceTest extends TestCase +{ + + private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); + + private AuthenticationComponent authenticationComponent; + + private AuthenticationService authenticationService; + + private MutableAuthenticationDao authenticationDAO; + + private UserTransaction tx; + + private SearchService pubSearchService; + + private NodeRef rootNodeRef; + + private NodeRef n1; + + private NodeRef n2; + + private NodeRef n3; + + private NodeRef n4; + + private NodeRef n6; + + private NodeRef n5; + + private NodeRef n7; + + private NodeRef n8; + + private NodeRef n9; + + private NodeRef n10; + + private NodeService nodeService; + + private PermissionService pubPermissionService; + + public SearchServiceTest() + { + super(); + } + + public void setUp() throws Exception + { + nodeService = (NodeService) ctx.getBean("dbNodeService"); + authenticationComponent = (AuthenticationComponent) ctx.getBean("authenticationComponentImpl"); + authenticationService = (AuthenticationService) ctx.getBean("authenticationService"); + authenticationDAO = (MutableAuthenticationDao) ctx.getBean("alfDaoImpl"); + pubSearchService = (SearchService) ctx.getBean("SearchService"); + pubPermissionService = (PermissionService) ctx.getBean("PermissionService"); + + this.authenticationComponent.setSystemUserAsCurrentUser(); + + TransactionService transactionService = (TransactionService) ctx.getBean(ServiceRegistry.TRANSACTION_SERVICE + .getLocalName()); + tx = transactionService.getUserTransaction(); + tx.begin(); + + if (!authenticationDAO.userExists("andy")) + { + authenticationService.createAuthentication("andy", "andy".toCharArray()); + } + + if (!authenticationDAO.userExists("admin")) + { + authenticationService.createAuthentication("admin", "admin".toCharArray()); + } + + if (!authenticationDAO.userExists("administrator")) + { + authenticationService.createAuthentication("administrator", "administrator".toCharArray()); + } + + StoreRef storeRef = nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.currentTimeMillis()); + rootNodeRef = nodeService.getRootNode(storeRef); + + n1 = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{test}01"), + ContentModel.TYPE_CONTAINER).getChildRef(); + pubPermissionService.setPermission(n1, "andy", "Read", true); + n2 = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{test}02"), + ContentModel.TYPE_CONTAINER).getChildRef(); + pubPermissionService.setPermission(n2, "andy", "Read", true); + n3 = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{test}03"), + ContentModel.TYPE_CONTAINER).getChildRef(); + pubPermissionService.setPermission(n3, "andy", "Read", true); + n4 = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{test}04"), + ContentModel.TYPE_CONTAINER).getChildRef(); + pubPermissionService.setPermission(n4, "andy", "Read", true); + n5 = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{test}05"), + ContentModel.TYPE_CONTAINER).getChildRef(); + pubPermissionService.setPermission(n5, "andy", "Read", true); + n6 = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{test}06"), + ContentModel.TYPE_CONTAINER).getChildRef(); + n7 = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{test}07"), + ContentModel.TYPE_CONTAINER).getChildRef(); + n8 = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{test}08"), + ContentModel.TYPE_CONTAINER).getChildRef(); + n9 = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{test}09"), + ContentModel.TYPE_CONTAINER).getChildRef(); + n10 = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{test}10"), + ContentModel.TYPE_CONTAINER).getChildRef(); + + } + + @Override + protected void tearDown() throws Exception + { + authenticationComponent.clearCurrentSecurityContext(); + tx.rollback(); + super.tearDown(); + } + + public void testAdmim() + { + authenticationComponent.setCurrentUser("admin"); + SearchParameters sp = new SearchParameters(); + sp.setLanguage(SearchService.LANGUAGE_LUCENE); + sp.setQuery("PATH:\"//*\""); + sp.addStore(rootNodeRef.getStoreRef()); + ResultSet results = pubSearchService.query(sp); + assertEquals(results.length(), 10); + assertNotNull(results.getResultSetMetaData()); + assertEquals(results.getResultSetMetaData().getLimitedBy(), LimitBy.UNLIMITED); + assertEquals(results.getResultSetMetaData().getPermissionEvaluationMode(), PermissionEvaluationMode.EAGER); + results.close(); + + sp.setLimitBy(LimitBy.FINAL_SIZE); + sp.setLimit(20); + results = pubSearchService.query(sp); + assertEquals(results.length(), 10); + assertNotNull(results.getResultSetMetaData()); + assertEquals(results.getResultSetMetaData().getLimitedBy(), LimitBy.UNLIMITED); + assertEquals(results.getResultSetMetaData().getPermissionEvaluationMode(), PermissionEvaluationMode.EAGER); + results.close(); + + sp.setLimitBy(LimitBy.FINAL_SIZE); + sp.setLimit(10); + results = pubSearchService.query(sp); + assertEquals(results.length(), 10); + assertNotNull(results.getResultSetMetaData()); + assertEquals(results.getResultSetMetaData().getLimitedBy(), LimitBy.UNLIMITED); + assertEquals(results.getResultSetMetaData().getPermissionEvaluationMode(), PermissionEvaluationMode.EAGER); + results.close(); + + sp.setLimitBy(LimitBy.FINAL_SIZE); + sp.setLimit(9); + results = pubSearchService.query(sp); + assertEquals(results.length(), 9); + assertNotNull(results.getResultSetMetaData()); + assertEquals(results.getResultSetMetaData().getLimitedBy(), LimitBy.FINAL_SIZE); + assertEquals(results.getResultSetMetaData().getPermissionEvaluationMode(), PermissionEvaluationMode.EAGER); + results.close(); + + sp.setLimitBy(LimitBy.FINAL_SIZE); + sp.setLimit(5); + results = pubSearchService.query(sp); + assertEquals(results.length(), 5); + assertNotNull(results.getResultSetMetaData()); + assertEquals(results.getResultSetMetaData().getLimitedBy(), LimitBy.FINAL_SIZE); + assertEquals(results.getResultSetMetaData().getPermissionEvaluationMode(), PermissionEvaluationMode.EAGER); + results.close(); + } + + public void testAndy() + { + authenticationComponent.setCurrentUser("andy"); + SearchParameters sp = new SearchParameters(); + sp.setLanguage(SearchService.LANGUAGE_LUCENE); + sp.setQuery("PATH:\"//*\""); + sp.addStore(rootNodeRef.getStoreRef()); + ResultSet results = pubSearchService.query(sp); + assertEquals(results.length(), 5); + assertNotNull(results.getResultSetMetaData()); + assertEquals(results.getResultSetMetaData().getLimitedBy(), LimitBy.UNLIMITED); + assertEquals(results.getResultSetMetaData().getPermissionEvaluationMode(), PermissionEvaluationMode.EAGER); + results.close(); + + sp.setLimitBy(LimitBy.FINAL_SIZE); + sp.setLimit(20); + results = pubSearchService.query(sp); + assertEquals(results.length(), 5); + assertNotNull(results.getResultSetMetaData()); + assertEquals(results.getResultSetMetaData().getLimitedBy(), LimitBy.UNLIMITED); + assertEquals(results.getResultSetMetaData().getPermissionEvaluationMode(), PermissionEvaluationMode.EAGER); + results.close(); + + sp.setLimitBy(LimitBy.FINAL_SIZE); + sp.setLimit(5); + results = pubSearchService.query(sp); + assertEquals(results.length(), 5); + assertNotNull(results.getResultSetMetaData()); + assertEquals(results.getResultSetMetaData().getLimitedBy(), LimitBy.UNLIMITED); + assertEquals(results.getResultSetMetaData().getPermissionEvaluationMode(), PermissionEvaluationMode.EAGER); + results.close(); + + sp.setLimitBy(LimitBy.FINAL_SIZE); + sp.setLimit(4); + results = pubSearchService.query(sp); + assertEquals(results.length(), 4); + assertNotNull(results.getResultSetMetaData()); + assertEquals(results.getResultSetMetaData().getLimitedBy(), LimitBy.FINAL_SIZE); + assertEquals(results.getResultSetMetaData().getPermissionEvaluationMode(), PermissionEvaluationMode.EAGER); + results.close(); + + sp.setLimitBy(LimitBy.FINAL_SIZE); + sp.setLimit(2); + results = pubSearchService.query(sp); + assertEquals(results.length(), 2); + assertNotNull(results.getResultSetMetaData()); + assertEquals(results.getResultSetMetaData().getLimitedBy(), LimitBy.FINAL_SIZE); + assertEquals(results.getResultSetMetaData().getPermissionEvaluationMode(), PermissionEvaluationMode.EAGER); + results.close(); + } +} diff --git a/source/java/org/alfresco/repo/search/SimpleResultSetMetaData.java b/source/java/org/alfresco/repo/search/SimpleResultSetMetaData.java new file mode 100644 index 0000000000..86092aea3d --- /dev/null +++ b/source/java/org/alfresco/repo/search/SimpleResultSetMetaData.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.search; + +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.SearchParameters; + +/** + * Simple implementatio of result set meta data. + * + * @author Andy Hind + */ +public class SimpleResultSetMetaData implements ResultSetMetaData +{ + private LimitBy limitedBy; + + private PermissionEvaluationMode permissoinEvaluationMode; + + private SearchParameters searchParameters; + + + public SimpleResultSetMetaData(LimitBy limitedBy, PermissionEvaluationMode permissoinEvaluationMode, SearchParameters searchParameters) + { + super(); + this.limitedBy = limitedBy; + this.permissoinEvaluationMode = permissoinEvaluationMode; + this.searchParameters = searchParameters; + } + + public LimitBy getLimitedBy() + { + return limitedBy; + } + + public PermissionEvaluationMode getPermissionEvaluationMode() + { + return permissoinEvaluationMode; + } + + public SearchParameters getSearchParameters() + { + return searchParameters; + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/lucene/LuceneIndexerImpl.java b/source/java/org/alfresco/repo/search/impl/lucene/LuceneIndexerImpl.java index b708d80059..f639054115 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/LuceneIndexerImpl.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/LuceneIndexerImpl.java @@ -60,6 +60,7 @@ import org.alfresco.service.cmr.repository.Path; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; import org.alfresco.service.cmr.search.ResultSetRow; +import org.alfresco.service.cmr.search.SearchParameters; import org.alfresco.service.namespace.QName; import org.alfresco.util.EqualsHelper; import org.alfresco.util.ISO9075; @@ -1689,7 +1690,7 @@ public class LuceneIndexerImpl extends LuceneBase implements LuceneIndexer throw new LuceneIndexException( "Failed to execute query to find content which needs updating in the index", e); } - results = new LuceneResultSet(hits, searcher, nodeService, null); + results = new LuceneResultSet(hits, searcher, nodeService, null, new SearchParameters()); for (ResultSetRow row : results) { diff --git a/source/java/org/alfresco/repo/search/impl/lucene/LuceneResultSet.java b/source/java/org/alfresco/repo/search/impl/lucene/LuceneResultSet.java index 23b87527f0..36d6f583fe 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/LuceneResultSet.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/LuceneResultSet.java @@ -21,11 +21,16 @@ import java.io.IOException; import org.alfresco.repo.search.AbstractResultSet; import org.alfresco.repo.search.ResultSetRowIterator; import org.alfresco.repo.search.SearcherException; +import org.alfresco.repo.search.SimpleResultSetMetaData; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.Path; +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.apache.lucene.document.Document; import org.apache.lucene.search.Hits; import org.apache.lucene.search.Searcher; @@ -47,18 +52,21 @@ public class LuceneResultSet extends AbstractResultSet private NodeService nodeService; + SearchParameters searchParameters; + /** * Wrap a lucene seach result with node support * * @param storeRef * @param hits */ - public LuceneResultSet(Hits hits, Searcher searcher, NodeService nodeService, Path[]propertyPaths) + public LuceneResultSet(Hits hits, Searcher searcher, NodeService nodeService, Path[]propertyPaths, SearchParameters searchParameters) { super(propertyPaths); this.hits = hits; this.searcher = searcher; this.nodeService = nodeService; + this.searchParameters = searchParameters; } /* @@ -149,4 +157,10 @@ public class LuceneResultSet extends AbstractResultSet { return getRow(n).getChildAssocRef(); } + + + public ResultSetMetaData getResultSetMetaData() + { + return new SimpleResultSetMetaData(LimitBy.UNLIMITED, PermissionEvaluationMode.EAGER, searchParameters); + } } diff --git a/source/java/org/alfresco/repo/search/impl/lucene/LuceneSearcherImpl.java b/source/java/org/alfresco/repo/search/impl/lucene/LuceneSearcherImpl.java index ecf346b712..7740134d69 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/LuceneSearcherImpl.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/LuceneSearcherImpl.java @@ -255,7 +255,7 @@ public class LuceneSearcherImpl extends LuceneBase implements LuceneSearcher } return new LuceneResultSet(hits, searcher, nodeService, searchParameters.getAttributePaths().toArray( - new Path[0])); + new Path[0]), searchParameters); } catch (ParseException e) @@ -290,7 +290,7 @@ public class LuceneSearcherImpl extends LuceneBase implements LuceneSearcher } Hits hits = searcher.search(query); return new LuceneResultSet(hits, searcher, nodeService, searchParameters.getAttributePaths().toArray( - new Path[0])); + new Path[0]), searchParameters); } catch (SAXPathException e) { diff --git a/source/java/org/alfresco/repo/search/results/ChildAssocRefResultSet.java b/source/java/org/alfresco/repo/search/results/ChildAssocRefResultSet.java index 6eb0526c9e..80e246ed01 100644 --- a/source/java/org/alfresco/repo/search/results/ChildAssocRefResultSet.java +++ b/source/java/org/alfresco/repo/search/results/ChildAssocRefResultSet.java @@ -28,11 +28,16 @@ import java.util.Iterator; import java.util.List; import org.alfresco.repo.search.AbstractResultSet; +import org.alfresco.repo.search.SimpleResultSetMetaData; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.Path; +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; public class ChildAssocRefResultSet extends AbstractResultSet { @@ -95,4 +100,8 @@ public class ChildAssocRefResultSet extends AbstractResultSet return nodeService; } + public ResultSetMetaData getResultSetMetaData() + { + return new SimpleResultSetMetaData(LimitBy.UNLIMITED, PermissionEvaluationMode.EAGER, new SearchParameters()); + } } diff --git a/source/java/org/alfresco/repo/search/results/DetachedResultSet.java b/source/java/org/alfresco/repo/search/results/DetachedResultSet.java index 2443040d21..d38de211d7 100644 --- a/source/java/org/alfresco/repo/search/results/DetachedResultSet.java +++ b/source/java/org/alfresco/repo/search/results/DetachedResultSet.java @@ -21,19 +21,26 @@ import java.util.Iterator; import java.util.List; import org.alfresco.repo.search.AbstractResultSet; +import org.alfresco.repo.search.SimpleResultSetMetaData; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.Path; +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.search.ResultSetMetaData; import org.alfresco.service.cmr.search.ResultSetRow; public class DetachedResultSet extends AbstractResultSet { List rows = null; + ResultSetMetaData rsmd; + public DetachedResultSet(ResultSet resultSet, Path[] propertyPaths) { super(propertyPaths); + rsmd = resultSet.getResultSetMetaData(); rows = new ArrayList(resultSet.length()); for (ResultSetRow row : resultSet) { @@ -66,4 +73,9 @@ public class DetachedResultSet extends AbstractResultSet return rows.get(n).getChildAssocRef(); } + public ResultSetMetaData getResultSetMetaData() + { + return new SimpleResultSetMetaData(rsmd.getLimitedBy(), PermissionEvaluationMode.EAGER, rsmd.getSearchParameters()); + } + } diff --git a/source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryAfterInvocationProvider.java b/source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryAfterInvocationProvider.java index 19ecc1cd31..77921b8e2e 100644 --- a/source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryAfterInvocationProvider.java +++ b/source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryAfterInvocationProvider.java @@ -31,11 +31,14 @@ import net.sf.acegisecurity.ConfigAttribute; import net.sf.acegisecurity.ConfigAttributeDefinition; import net.sf.acegisecurity.afterinvocation.AfterInvocationProvider; +import org.alfresco.repo.search.SimpleResultSetMetaData; import org.alfresco.repo.security.permissions.impl.SimplePermissionReference; import org.alfresco.service.cmr.repository.ChildAssociationRef; 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.search.LimitBy; +import org.alfresco.service.cmr.search.PermissionEvaluationMode; import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.security.AccessStatus; import org.alfresco.service.cmr.security.AuthenticationService; @@ -336,25 +339,55 @@ public class ACLEntryAfterInvocationProvider implements AfterInvocationProvider, ResultSet returnedObject) throws AccessDeniedException { - FilteringResultSet filteringResultSet = new FilteringResultSet((ResultSet) returnedObject); - if (returnedObject == null) { return null; } + + FilteringResultSet filteringResultSet = new FilteringResultSet(returnedObject); + List supportedDefinitions = extractSupportedDefinitions(config); + Integer maxSize = null; + if(returnedObject.getResultSetMetaData().getSearchParameters().getLimitBy() == LimitBy.FINAL_SIZE) + { + maxSize = new Integer(returnedObject.getResultSetMetaData().getSearchParameters().getLimit()); + } + if (supportedDefinitions.size() == 0) { - return returnedObject; + if(maxSize == null) + { + return returnedObject; + } + else if (returnedObject.length() > maxSize.intValue()) + { + for(int i = 0; i < maxSize.intValue(); i++) + { + filteringResultSet.setIncluded(i, true); + } + filteringResultSet.setResultSetMetaData(new SimpleResultSetMetaData(LimitBy.FINAL_SIZE, PermissionEvaluationMode.EAGER, returnedObject.getResultSetMetaData().getSearchParameters())); + } + else + { + for(int i = 0; i < maxSize.intValue(); i++) + { + filteringResultSet.setIncluded(i, true); + } + filteringResultSet.setResultSetMetaData(new SimpleResultSetMetaData(LimitBy.UNLIMITED, PermissionEvaluationMode.EAGER, returnedObject.getResultSetMetaData().getSearchParameters())); + } } + + for (int i = 0; i < returnedObject.length(); i++) { + // All permission checks must pass + filteringResultSet.setIncluded(i, true); + for (ConfigAttributeDefintion cad : supportedDefinitions) { - filteringResultSet.setIncluded(i, true); NodeRef testNodeRef = null; if (cad.typeString.equals(AFTER_ACL_NODE)) { @@ -365,15 +398,25 @@ public class ACLEntryAfterInvocationProvider implements AfterInvocationProvider, testNodeRef = returnedObject.getChildAssocRef(i).getParentRef(); } - if (filteringResultSet.getIncluded(i) + if (filteringResultSet.getIncluded(i) && (testNodeRef != null) && (permissionService.hasPermission(testNodeRef, cad.required.toString()) == AccessStatus.DENIED)) { filteringResultSet.setIncluded(i, false); } } + + // Bug out if we are limiting by size + + if((maxSize != null) && (filteringResultSet.length() > maxSize.intValue())) + { + // Renove the last match to fix the correct size + filteringResultSet.setIncluded(i, false); + filteringResultSet.setResultSetMetaData(new SimpleResultSetMetaData(LimitBy.FINAL_SIZE, PermissionEvaluationMode.EAGER, returnedObject.getResultSetMetaData().getSearchParameters())); + return filteringResultSet; + } } - + filteringResultSet.setResultSetMetaData(new SimpleResultSetMetaData(LimitBy.UNLIMITED, PermissionEvaluationMode.EAGER, returnedObject.getResultSetMetaData().getSearchParameters())); return filteringResultSet; } diff --git a/source/java/org/alfresco/repo/security/permissions/impl/acegi/FilteringResultSet.java b/source/java/org/alfresco/repo/security/permissions/impl/acegi/FilteringResultSet.java index 9d767d59d4..8b777fbd68 100644 --- a/source/java/org/alfresco/repo/security/permissions/impl/acegi/FilteringResultSet.java +++ b/source/java/org/alfresco/repo/security/permissions/impl/acegi/FilteringResultSet.java @@ -25,6 +25,7 @@ import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.Path; import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.ResultSetMetaData; import org.alfresco.service.cmr.search.ResultSetRow; public class FilteringResultSet extends ACLEntryAfterInvocationProvider implements ResultSet @@ -32,6 +33,8 @@ public class FilteringResultSet extends ACLEntryAfterInvocationProvider implemen private ResultSet unfiltered; private BitSet inclusionMask; + + private ResultSetMetaData resultSetMetaData; FilteringResultSet(ResultSet unfiltered) { @@ -244,4 +247,15 @@ public class FilteringResultSet extends ACLEntryAfterInvocationProvider implemen } + public ResultSetMetaData getResultSetMetaData() + { + return resultSetMetaData; + } + + public void setResultSetMetaData(ResultSetMetaData resultSetMetaData) + { + this.resultSetMetaData = resultSetMetaData; + } + + } diff --git a/source/java/org/alfresco/service/cmr/search/LimitBy.java b/source/java/org/alfresco/service/cmr/search/LimitBy.java new file mode 100644 index 0000000000..f275298840 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/search/LimitBy.java @@ -0,0 +1,11 @@ +package org.alfresco.service.cmr.search; + +/** + * Enum to describe how the maximum size of the returned result set should be determined. + * + * @author Andy Hind + */ +public enum LimitBy +{ + UNLIMITED, FINAL_SIZE; // NUMBER_OF_PERMISSION_EVALUATIONS +} \ No newline at end of file diff --git a/source/java/org/alfresco/service/cmr/search/PermissionEvaluationMode.java b/source/java/org/alfresco/service/cmr/search/PermissionEvaluationMode.java new file mode 100644 index 0000000000..fc97bec4a8 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/search/PermissionEvaluationMode.java @@ -0,0 +1,11 @@ +package org.alfresco.service.cmr.search; + +/** + * Enum to control how permissions are evaluated. + * + * @author Andy Hind + */ +public enum PermissionEvaluationMode +{ + EAGER; // LAZY +} \ No newline at end of file diff --git a/source/java/org/alfresco/service/cmr/search/ResultSet.java b/source/java/org/alfresco/service/cmr/search/ResultSet.java index 720f30a9d0..3614d1270f 100644 --- a/source/java/org/alfresco/service/cmr/search/ResultSet.java +++ b/source/java/org/alfresco/service/cmr/search/ResultSet.java @@ -53,26 +53,45 @@ public interface ResultSet extends Iterable // Specfic iterator float getScore(int n); /** - * Generate the XML form of this result set + * Close the result set. + * This must be called to allow the release of underlying resources. */ - // Dom getXML(int page, int pageSize, boolean includeMetaData); - /** - * Generate as XML for Reading - */ - // Stream getStream(int page, int pageSize, boolean includeMetaData); - /** - * toString() as above but for the whole set - */ - // String toString(); - // ResultSetMetaData getMetaData(); void close(); + /** + * Get a row from the result set by row index, starting at 0. + * + * @param i + * @return + */ ResultSetRow getRow(int i); + /** + * Get a list of all the node refs in the result set + * @return + */ List getNodeRefs(); + /** + * Get a list of all the child associations in the results set. + * + * @return + */ List getChildAssocRefs(); + /** + * Get the child assoc ref for a particular row. + * + * @param n + * @return + */ ChildAssociationRef getChildAssocRef(int n); + + /** + * Get the meta data for the results set. + * + * @return + */ + ResultSetMetaData getResultSetMetaData(); } diff --git a/source/java/org/alfresco/service/cmr/search/ResultSetMetaData.java b/source/java/org/alfresco/service/cmr/search/ResultSetMetaData.java new file mode 100644 index 0000000000..49dbc6b8fe --- /dev/null +++ b/source/java/org/alfresco/service/cmr/search/ResultSetMetaData.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.service.cmr.search; + +/** + * Meta Data associated with a result set. + * + * @author Andy Hind + */ +public interface ResultSetMetaData +{ + + /** + * Return how, in fact, the result set was limited. + * This may not be how it was requested. + * + * If a limit of 100 were requested and there were 100 or less actual results + * this will report LimitBy.UNLIMITED. + * + * @return + */ + public LimitBy getLimitedBy(); + + /** + * Return how permission evaluations are being made. + * + * @return + */ + public PermissionEvaluationMode getPermissionEvaluationMode(); + + /** + * Get the parameters that were specified to define this search. + * + * @return + */ + public SearchParameters getSearchParameters(); + +} diff --git a/source/java/org/alfresco/service/cmr/search/SearchParameters.java b/source/java/org/alfresco/service/cmr/search/SearchParameters.java index dfd068358a..2ab3faea11 100644 --- a/source/java/org/alfresco/service/cmr/search/SearchParameters.java +++ b/source/java/org/alfresco/service/cmr/search/SearchParameters.java @@ -24,20 +24,41 @@ import org.alfresco.service.cmr.repository.StoreRef; /** * This class provides parameters to define a search. * + * TODO + * - paging of results page number and page size + * - paging isolation - REPEATABLE READ, READ COMMITTED, may SEE ONCE tracking node refs in previous result sets + * - how long repeatable read may be held + * - limit by the number of permission evaluations + * * @author Andy Hind */ public class SearchParameters extends SearchStatement { + /* + * The default limit if someone asks for a limited result set but does not say how to limit.... + */ + private static int DEFAULT_LIMIT = 500; + + /* + * Standard sort definitions for sorting in document and score order. + */ public static final SortDefinition SORT_IN_DOCUMENT_ORDER_ASCENDING = new SortDefinition(SortDefinition.SortType.DOCUMENT, null, true); public static final SortDefinition SORT_IN_DOCUMENT_ORDER_DESCENDING = new SortDefinition(SortDefinition.SortType.DOCUMENT, null, false); public static final SortDefinition SORT_IN_SCORE_ORDER_ASCENDING = new SortDefinition(SortDefinition.SortType.SCORE, null, false); public static final SortDefinition SORT_IN_SCORE_ORDER_DESCENDING = new SortDefinition(SortDefinition.SortType.SCORE, null, true); + /** + * An emum defining if the default action is to "and" or "or" unspecified components in the query register. + * Not all search implementations will support this. + */ public enum Operator { OR, AND } + /* + * Expose as constants + */ public static final Operator OR = Operator.OR; public static final Operator AND = Operator.AND; @@ -54,7 +75,8 @@ public class SearchParameters extends SearchStatement } /** - * Set the stores to be supported - currently there can be only one + * Set the stores to be supported - currently there can be only one. + * Searching across multiple stores is on the todo list. * * @param store */ @@ -68,7 +90,11 @@ public class SearchParameters extends SearchStatement } /** - * Add paths for attributes in the result set + * Add paths for attributes in the result set. + * + * Generally this only makes sense for disconnected results sets. + * These atttributes/paths state what must be present in the result set, akin + * to the selection of columns is sql. * * @param attributePath */ @@ -91,6 +117,13 @@ public class SearchParameters extends SearchStatement * If true, any data in the current transaction will be ignored in the search. * You will not see anything you have added in the current transaction. * + * By default you will see data in the current transaction. + * This effectively gives read committed isolation. + * + * There is a performance overhead for this, at least when using lucene. + * This flag may be set to avoid that performance hit if you know you do not want to find results + * that are yet to be committed (this includes creations, deletions and updates) + * * @param excludeDataInTheCurrentTransaction */ public void excludeDataInTheCurrentTransaction(boolean excludeDataInTheCurrentTransaction) @@ -101,28 +134,36 @@ public class SearchParameters extends SearchStatement /** * Add a sort to the query (for those query languages that do not support it directly) * + * The first sort added is treated as primary, the second as secondary etc. + * + * A helper method to create SortDefinitions. + * * @param field - this is intially a direct attribute on a node not an attribute on the parent etc * TODO: It could be a relative path at some time. * - * - * @param ascending + * @param ascending - true to sort ascending, false for descending. */ public void addSort(String field, boolean ascending) { addSort(new SortDefinition(SortDefinition.SortType.FIELD, field, ascending)); } + /** + * Add a sort definition. + * + * @param sortDefinition - the sort definition to add. Use the static member variables + * for sorting in score and index order. + */ public void addSort(SortDefinition sortDefinition) { sortDefinitions.add(sortDefinition); } /** - * A helper class for sort definition - * @author andyh - * - * TODO To change the template for this generated type comment go to - * Window - Preferences - Java - Code Style - Code Templates + * A helper class for sort definition. + * Encapsulated using the lucene sortType, field name and a flag for ascending/descending. + * + * @author Andy Hind */ public static class SortDefinition { @@ -157,38 +198,131 @@ public class SearchParameters extends SearchStatement } + /** + * Get the list of attribute paths that are guarenteed to be in the result set. + * + * @return + */ public ArrayList getAttributePaths() { return attributePaths; } + /** + * Is data in the current transaction excluded from the search. + * + * @return + */ public boolean excludeDataInTheCurrentTransaction() { return excludeDataInTheCurrentTransaction; } + /** + * Get the query parameters that apply to this query. + * + * @return + */ public ArrayList getQueryParameterDefinitions() { return queryParameterDefinitions; } + /** + * Get the sort definitions that apply to this query. + * + * @return + */ public ArrayList getSortDefinitions() { return sortDefinitions; } + /** + * Get the stores in which this query should find results. + * + * @return + */ public ArrayList getStores() { return stores; } + /** + * Set the default operator for query elements when they are not explicit in the query. + * + * @param defaultOperator + */ public void setDefaultOperator(Operator defaultOperator) { this.defaultOperator = defaultOperator; } + /** + * Get the default operator for query elements when they are not explicit in the query. + * + * @return + */ public Operator getDefaultOperator() { return defaultOperator; } + + private LimitBy limitBy = LimitBy.UNLIMITED; + + private PermissionEvaluationMode permissionEvaluation = PermissionEvaluationMode.EAGER; + + private int limit = DEFAULT_LIMIT; + + /** + * Get how the result set should be limited + * + * @return + */ + public LimitBy getLimitBy() + { + return limitBy; + } + + /** + * Set how the result set should be limited. + * + * @param limitBy + */ + public void setLimitBy(LimitBy limitBy) + { + this.limitBy = limitBy; + } + + /** + * Get when permissions are evaluated. + * + * @return + */ + public PermissionEvaluationMode getPermissionEvaluation() + { + return permissionEvaluation; + } + + /** + * Set when permissions are evaluated. + * + * @param permissionEvaluation + */ + public void setPermissionEvaluation(PermissionEvaluationMode permissionEvaluation) + { + this.permissionEvaluation = permissionEvaluation; + } + + public int getLimit() + { + return limit; + } + + public void setLimit(int limit) + { + this.limit = limit; + } + + } diff --git a/source/java/org/alfresco/service/cmr/search/SearchStatement.java b/source/java/org/alfresco/service/cmr/search/SearchStatement.java index cc12714a32..adfcbb0be7 100644 --- a/source/java/org/alfresco/service/cmr/search/SearchStatement.java +++ b/source/java/org/alfresco/service/cmr/search/SearchStatement.java @@ -32,27 +32,53 @@ public class SearchStatement super(); } + /** + * A constructor that takes both arguments. + * + * @param language + * @param query + */ SearchStatement(String language, String query) { this.language = language; this.query = query; } + /** + * Get the query language. + * + * @return + */ public String getLanguage() { return language; } + /** + * Get the query. + * + * @return + */ public String getQuery() { return query; } + /** + * Set the query language. + * + * @param language - the query language. + */ public void setLanguage(String language) { this.language = language; } + /** + * Set the query string. + * + * @param query - the query string. + */ public void setQuery(String query) { this.query = query;