Java tutorial
/* * #%L * Alfresco Repository * %% * Copyright (C) 2005 - 2016 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of * the paid license agreement will prevail. Otherwise, the software is * provided under the following open source license terms: * * Alfresco is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Alfresco is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * #L% */ package org.alfresco.repo.solr; import java.io.InputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import junit.framework.TestCase; import org.alfresco.model.ContentModel; import org.alfresco.repo.dictionary.DictionaryDAO; import org.alfresco.repo.dictionary.M2Model; import org.alfresco.repo.dictionary.M2Property; import org.alfresco.repo.dictionary.M2Type; import org.alfresco.repo.domain.node.Node; import org.alfresco.repo.domain.node.NodeDAO; import org.alfresco.repo.domain.qname.QNameDAO; import org.alfresco.repo.node.db.DbNodeServiceImpl; import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.repo.solr.SOLRTrackingComponent.NodeMetaDataQueryCallback; import org.alfresco.repo.solr.SOLRTrackingComponent.NodeQueryCallback; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.model.FileInfo; 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.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.transaction.TransactionService; import org.alfresco.test_category.OwnJVMTestsCategory; import org.alfresco.util.ApplicationContextHelper; import org.alfresco.util.PropertyMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.experimental.categories.Category; import org.springframework.context.ConfigurableApplicationContext; /** * Tests tracking component * * @since 4.0 */ @Category(OwnJVMTestsCategory.class) public class SOLRTrackingComponentTest extends TestCase { private static final Log logger = LogFactory.getLog(SOLRTrackingComponentTest.class); private ConfigurableApplicationContext ctx = (ConfigurableApplicationContext) ApplicationContextHelper .getApplicationContext(); private static enum NodeStatus { UPDATED, DELETED; } private AuthenticationComponent authenticationComponent; private TransactionService transactionService; private DictionaryService dictionaryService; private NamespaceService namespaceService; private RetryingTransactionHelper txnHelper; private NodeService nodeService; private FileFolderService fileFolderService; private NodeDAO nodeDAO; private QNameDAO qnameDAO; private DictionaryDAO dictionaryDAO; private SOLRTrackingComponent solrTrackingComponent; private DbNodeServiceImpl dbNodeService; private StoreRef storeRef; private NodeRef rootNodeRef; @Override public void setUp() throws Exception { ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY); transactionService = serviceRegistry.getTransactionService(); txnHelper = transactionService.getRetryingTransactionHelper(); solrTrackingComponent = (SOLRTrackingComponent) ctx.getBean("solrTrackingComponent"); nodeDAO = (NodeDAO) ctx.getBean("nodeDAO"); qnameDAO = (QNameDAO) ctx.getBean("qnameDAO"); dictionaryDAO = (DictionaryDAO) ctx.getBean("dictionaryDAO"); nodeService = (NodeService) ctx.getBean("NodeService"); fileFolderService = (FileFolderService) ctx.getBean("FileFolderService"); dictionaryService = serviceRegistry.getDictionaryService(); namespaceService = serviceRegistry.getNamespaceService(); authenticationComponent = (AuthenticationComponent) ctx.getBean("authenticationComponent"); dbNodeService = (DbNodeServiceImpl) ctx.getBean("dbNodeService"); dbNodeService.setEnableTimestampPropagation(false); authenticationComponent.setSystemUserAsCurrentUser(); storeRef = nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, getName() + System.currentTimeMillis()); rootNodeRef = nodeService.getRootNode(storeRef); } public void testAclChangeSetLimits() { List<AclChangeSet> aclChangeSets = getAclChangeSets(null, null, null, null, 50); // First Long first = aclChangeSets.get(0).getId(); Long firstTime = aclChangeSets.get(1).getId(); List<AclChangeSet> testSets = getAclChangeSets(first, null, first, null, 50); assertEquals(0, testSets.size()); testSets = getAclChangeSets(first, firstTime, first + 1, firstTime, 50); assertEquals(0, testSets.size()); testSets = getAclChangeSets(first, firstTime, first + 1, firstTime + 1, 50); assertEquals(0, testSets.size()); } // This test is no longer valid as we may or may include shared acls not linked to defining ones // If they are not linked to a node they will be counted wring ... // public void testGetAcls_Simple() // { // List<AclChangeSet> cs = getAclChangeSets(null, null, null, null, 50); // assertTrue("Expected results to be limited in number", cs.size() <= 50); // int totalAcls = 0; // for (AclChangeSet aclChangeSet : cs) // { // totalAcls += aclChangeSet.getAclCount(); // } // int totalAclsCheck = 0; // // for (AclChangeSet aclChangeSet : cs) // { // List<Acl> acls = getAcls(Arrays.asList(new Long[]{aclChangeSet.getId()}), null, 200); // assertEquals(aclChangeSet.getAclCount(), acls.size()); // totalAclsCheck += acls.size(); // } // // // Double check number of ACLs // assertEquals("ACL count should have matched", totalAcls, totalAclsCheck); // } public void testGetNodeMetaData() { long startTime = System.currentTimeMillis(); SOLRTest st = new SOLRTest3(txnHelper, fileFolderService, nodeDAO, qnameDAO, nodeService, dictionaryService, rootNodeRef, "testGetNodeMetaData", true, true); List<Long> createdTransactions = st.buildTransactions(); List<Transaction> txns = getTransactions(null, startTime - 1000, null, null, 100); int[] updates = new int[] { 1, 1 }; int[] deletes = new int[] { 0, 1 }; List<Transaction> checkedTransactions = checkTransactions(txns, createdTransactions, updates, deletes); NodeParameters nodeParameters = new NodeParameters(); nodeParameters.setTransactionIds(getTransactionIds(checkedTransactions)); getNodes(nodeParameters, st); NodeMetaDataParameters nodeMetaDataParams = new NodeMetaDataParameters(); nodeMetaDataParams.setNodeIds(st.getNodeIds()); getNodeMetaData(nodeMetaDataParams, null, st); } /** * @param checkedTransactions * @return */ private List<Long> getTransactionIds(List<Transaction> checkedTransactions) { ArrayList<Long> txIds = new ArrayList<Long>(checkedTransactions.size()); for (Transaction txn : checkedTransactions) { txIds.add(txn.getId()); } return txIds; } /** * Call {@link SOLRTrackingComponent#getTransactions(Long, Long, Long, Long, int)} in a transaction */ private List<Transaction> getTransactions(final Long minTxnId, final Long fromCommitTime, final Long maxTxnId, final Long toCommitTimeint, final int maxResults) { RetryingTransactionCallback<List<Transaction>> callback = new RetryingTransactionCallback<List<Transaction>>() { @Override public List<Transaction> execute() throws Throwable { return solrTrackingComponent.getTransactions(minTxnId, fromCommitTime, maxTxnId, toCommitTimeint, maxResults); } }; return transactionService.getRetryingTransactionHelper().doInTransaction(callback, true); } /** * Call {@link SOLRTrackingComponent#getNodes(NodeParameters, NodeQueryCallback)} in a transaction */ private void getNodes(final NodeParameters nodeParameters, final SOLRTest bt) { long startTime = System.currentTimeMillis(); transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Void>() { @Override public Void execute() throws Throwable { solrTrackingComponent.getNodes(nodeParameters, bt); return null; } }, true); long endTime = System.currentTimeMillis(); bt.runNodeChecks(nodeParameters.getMaxResults()); logger.debug("Got " + bt.getActualNodeCount() + " nodes in " + (endTime - startTime) + " ms"); } /** * Call {@link SOLRTrackingComponent#getAcls(List, Long, int)} in a transaction */ @SuppressWarnings("unused") private List<Acl> getAcls(final List<Long> aclChangeSetIds, final Long minAclId, final int maxResults) { RetryingTransactionCallback<List<Acl>> callback = new RetryingTransactionCallback<List<Acl>>() { @Override public List<Acl> execute() throws Throwable { return solrTrackingComponent.getAcls(aclChangeSetIds, minAclId, maxResults); } }; return transactionService.getRetryingTransactionHelper().doInTransaction(callback, true); } /** * Call {@link SOLRTrackingComponent#getAclChangeSets(Long, Long, Long, Long, int)} in a transaction */ private List<AclChangeSet> getAclChangeSets(final Long minAclChangeSetId, final Long fromCommitTime, final Long maxAclChangeSetId, final Long toCommitTime, final int maxResults) { RetryingTransactionCallback<List<AclChangeSet>> callback = new RetryingTransactionCallback<List<AclChangeSet>>() { @Override public List<AclChangeSet> execute() throws Throwable { return solrTrackingComponent.getAclChangeSets(minAclChangeSetId, fromCommitTime, maxAclChangeSetId, toCommitTime, maxResults); } }; return transactionService.getRetryingTransactionHelper().doInTransaction(callback, true); } public void testGetTransactionLimits() { long startTime = System.currentTimeMillis(); SOLRTest st = new SOLRTest3(txnHelper, fileFolderService, nodeDAO, qnameDAO, nodeService, dictionaryService, rootNodeRef, "testGetNodeMetaData", true, true); List<Long> createdTransactions = st.buildTransactions(); // All List<Transaction> txns = getTransactions(null, startTime - 1000, null, null, 100); int[] updates = new int[] { 1, 1 }; int[] deletes = new int[] { 0, 1 }; List<Transaction> checkedTransactions = checkTransactions(txns, createdTransactions, updates, deletes); Long first = checkedTransactions.get(0).getId(); Long firstTime = checkedTransactions.get(0).getCommitTimeMs(); Long last = checkedTransactions.get(1).getId(); Long lastTime = checkedTransactions.get(1).getCommitTimeMs(); // First txns = getTransactions(first, null, first, null, 50); assertEquals(0, txns.size()); txns = getTransactions(first, null, first + 1, null, 50); updates = new int[] { 1 }; deletes = new int[] { 0 }; List<Long> createdTransactionFirst = new ArrayList<Long>(1); createdTransactionFirst.add(createdTransactions.get(0)); checkTransactions(txns, createdTransactionFirst, updates, deletes); txns = getTransactions(first, firstTime, first + 1, firstTime + 1, 50); checkTransactions(txns, createdTransactionFirst, updates, deletes); txns = getTransactions(first, firstTime - 1, first + 1, firstTime, 50); assertEquals(0, txns.size()); // Last txns = getTransactions(last, null, last, null, 50); assertEquals(0, txns.size()); txns = getTransactions(last, null, last + 1, null, 50); updates = new int[] { 1 }; deletes = new int[] { 1 }; List<Long> createdTransactionLast = new ArrayList<Long>(1); createdTransactionLast.add(createdTransactions.get(1)); checkTransactions(txns, createdTransactionLast, updates, deletes); txns = getTransactions(last, lastTime, last + 1, lastTime + 1, 50); checkTransactions(txns, createdTransactionLast, updates, deletes); txns = getTransactions(last, lastTime - 1, last + 1, lastTime, 50); assertEquals(0, txns.size()); } public void testGetNodeMetaDataExludesResidualProperties() { long startTime = System.currentTimeMillis(); SOLRTest st = new SOLRTestResidualProperties(txnHelper, fileFolderService, nodeDAO, qnameDAO, nodeService, dictionaryService, rootNodeRef, "testNodeMetaDataNullPropertyValue", true, true); List<Long> createdTransactions = st.buildTransactions(); List<Transaction> txns = getTransactions(null, startTime - 1000, null, null, 100); int[] updates = new int[] { 2 }; int[] deletes = new int[] { 0 }; List<Transaction> checkedTransactions = checkTransactions(txns, createdTransactions, updates, deletes); NodeParameters nodeParameters = new NodeParameters(); nodeParameters.setTransactionIds(getTransactionIds(checkedTransactions)); getNodes(nodeParameters, st); NodeMetaDataParameters nodeMetaDataParams = new NodeMetaDataParameters(); nodeMetaDataParams.setNodeIds(st.getNodeIds()); getNodeMetaData(nodeMetaDataParams, null, st); } public void testGetNodeMetaData100Nodes() { long startTime = System.currentTimeMillis(); SOLRTest st = new SOLRTest100Nodes(txnHelper, fileFolderService, nodeDAO, qnameDAO, nodeService, dictionaryService, rootNodeRef, "testGetNodeMetaData", true, true); List<Long> createdTransactions = st.buildTransactions(); List<Transaction> txns = getTransactions(null, startTime - 1000, null, null, 100); int[] updates = new int[] { 100 }; int[] deletes = new int[] { 0 }; List<Transaction> checkedTransactions = checkTransactions(txns, createdTransactions, updates, deletes); NodeParameters nodeParameters = new NodeParameters(); nodeParameters.setTransactionIds(getTransactionIds(checkedTransactions)); getNodes(nodeParameters, st); // assertEquals("Unxpected number of nodes", 3, nodeQueryCallback.getSuccessCount()); NodeMetaDataParameters nodeMetaDataParams = new NodeMetaDataParameters(); nodeMetaDataParams.setNodeIds(st.getNodeIds()); getNodeMetaData(nodeMetaDataParams, null, st); nodeMetaDataParams.setMaxResults(20); getNodeMetaData(nodeMetaDataParams, null, st); // assertEquals("Unxpected number of nodes", 3, bt.getSuccessCount()); } public void testNodeMetaDataManyNodes() throws Exception { long fromCommitTime = System.currentTimeMillis(); SOLRTest st = new SOLRTest4(txnHelper, fileFolderService, nodeDAO, qnameDAO, nodeService, dictionaryService, rootNodeRef, "testNodeMetaDataManyNodes", true, false); List<Long> createdTransactions = st.buildTransactions(); List<Transaction> txns = getTransactions(null, fromCommitTime - 1000, null, null, 100); int[] updates = new int[] { 2001 }; int[] deletes = new int[] { 0 }; List<Transaction> checkedTransactions = checkTransactions(txns, createdTransactions, updates, deletes); NodeParameters nodeParameters = new NodeParameters(); nodeParameters.setTransactionIds(getTransactionIds(checkedTransactions)); getNodes(nodeParameters, st); // make sure caches are warm - time last call logger.debug("Cold cache"); NodeMetaDataParameters nodeMetaDataParams = new NodeMetaDataParameters(); nodeMetaDataParams.setNodeIds(st.getNodeIds()); getNodeMetaData(nodeMetaDataParams, null, st); getNodeMetaData(nodeMetaDataParams, null, st); logger.debug("Warm cache"); getNodeMetaData(nodeMetaDataParams, null, st); // clear out node caches nodeDAO.clear(); logger.debug("Cold cache - explicit clear"); nodeMetaDataParams.setMaxResults(800); getNodeMetaData(nodeMetaDataParams, null, st); getNodeMetaData(nodeMetaDataParams, null, st); logger.debug("Warm cache"); getNodeMetaData(nodeMetaDataParams, null, st); logger.debug("Cold cache - explicit clear"); nodeMetaDataParams.setMaxResults(500); getNodeMetaData(nodeMetaDataParams, null, st); getNodeMetaData(nodeMetaDataParams, null, st); logger.debug("Warm cache"); getNodeMetaData(nodeMetaDataParams, null, st); logger.debug("Cold cache - explicit clear"); nodeMetaDataParams.setMaxResults(200); getNodeMetaData(nodeMetaDataParams, null, st); getNodeMetaData(nodeMetaDataParams, null, st); logger.debug("Warm cache"); getNodeMetaData(nodeMetaDataParams, null, st); // clear out node caches nodeDAO.clear(); logger.debug("Cold cache - explicit clear"); getNodeMetaData(nodeMetaDataParams, null, st); } public void testNodeMetaDataCache() throws Exception { long fromCommitTime = System.currentTimeMillis(); SOLRTest st = new SOLRTest4(txnHelper, fileFolderService, nodeDAO, qnameDAO, nodeService, dictionaryService, rootNodeRef, "testNodeMetaDataManyNodes", true, false); List<Long> createdTransactions = st.buildTransactions(); List<Transaction> txns = getTransactions(null, fromCommitTime - 1000, null, null, 100); int[] updates = new int[] { 2001 }; int[] deletes = new int[] { 0 }; List<Transaction> checkedTransactions = checkTransactions(txns, createdTransactions, updates, deletes); NodeParameters nodeParameters = new NodeParameters(); nodeParameters.setTransactionIds(getTransactionIds(checkedTransactions)); getNodes(nodeParameters, st); // clear out node caches nodeDAO.clear(); logger.debug("Cold cache - explicit clear"); NodeMetaDataParameters nodeMetaDataParams = new NodeMetaDataParameters(); nodeMetaDataParams.setNodeIds(st.getNodeIds()); MetaDataResultsFilter filter = new MetaDataResultsFilter(); filter.setIncludeParentAssociations(false); //filter.setIncludePaths(false); filter.setIncludeChildAssociations(false); getNodeMetaData(nodeMetaDataParams, filter, st); } public void testNodeMetaDataNullPropertyValue() throws Exception { long fromCommitTime = System.currentTimeMillis(); SOLRTest st = new SOLRTest5(txnHelper, fileFolderService, nodeDAO, qnameDAO, nodeService, dictionaryService, rootNodeRef, "testNodeMetaDataNullPropertyValue", true, true); List<Long> createdTransactions = st.buildTransactions(); List<Transaction> txns = getTransactions(null, fromCommitTime - 1000, null, null, 100); int[] updates = new int[] { 11 }; int[] deletes = new int[] { 0 }; List<Transaction> checkedTransactions = checkTransactions(txns, createdTransactions, updates, deletes); NodeParameters nodeParameters = new NodeParameters(); nodeParameters.setTransactionIds(getTransactionIds(checkedTransactions)); getNodes(nodeParameters, st); NodeMetaDataParameters nodeMetaDataParams = new NodeMetaDataParameters(); nodeMetaDataParams.setNodeIds(st.getNodeIds()); getNodeMetaData(nodeMetaDataParams, null, st); } public void testFilters() { long startTime = System.currentTimeMillis(); SOLRTest st = new SOLRTest1(txnHelper, fileFolderService, nodeDAO, qnameDAO, nodeService, dictionaryService, rootNodeRef, "testFilters", true, true); List<Long> createdTransactions = st.buildTransactions(); List<Transaction> txns = getTransactions(null, startTime - 1000, null, null, 100); int[] updates = new int[] { 1, 1 }; int[] deletes = new int[] { 0, 1 }; List<Transaction> checkedTransactions = checkTransactions(txns, createdTransactions, updates, deletes); NodeParameters nodeParameters = new NodeParameters(); nodeParameters.setTransactionIds(getTransactionIds(checkedTransactions)); getNodes(nodeParameters, st); NodeMetaDataParameters nodeMetaDataParams = new NodeMetaDataParameters(); nodeMetaDataParams.setNodeIds(st.getNodeIds()); getNodeMetaData(nodeMetaDataParams, null, st); } public void testModelDiffs() { Collection<QName> allModels = dictionaryService.getAllModels(); ModelDiffsTracker tracker = new ModelDiffsTracker(); ModelDiffResults diffResults = tracker.diff(); // number of diffs should equal the number of models in the repository assertEquals("Unexpected number of new models", allModels.size(), diffResults.getNewModels().size()); assertEquals("Expected no removed models", 0, diffResults.getRemovedModels().size()); assertEquals("Expected no changed models", 0, diffResults.getChangedModels().size()); // create a new model InputStream modelStream = getClass().getClassLoader() .getResourceAsStream("org/alfresco/repo/solr/testModel.xml"); M2Model testModel = M2Model.createModel(modelStream); dictionaryDAO.putModel(testModel); // call model diffs - should detect new model ModelDiffResults diffResults1 = tracker.diff(); assertEquals("Expected 1 new model", 1, diffResults1.getNewModels().size()); assertEquals("Unexpected number of changed models", 0, diffResults1.getChangedModels().size()); assertEquals("Unexpected number of removed models", 0, diffResults1.getRemovedModels().size()); AlfrescoModelDiff diff = diffResults1.getNewModels().get(0); assertEquals("Unexpected model name change", QName.createQName(testModel.getName(), namespaceService).toString(), diff.getModelName()); // get current checksum for the test model Long testModelChecksum = tracker.getChecksum(QName.createQName(testModel.getName(), namespaceService)); assertNotNull("", testModelChecksum); // create a new type and add it to the new test model M2Type anotherType = testModel.createType("anothertype"); M2Property prop1 = anotherType.createProperty("prop1"); prop1.setType("d:text"); // call model diffs - should detect test model changes ModelDiffResults diffResults2 = tracker.diff(); List<AlfrescoModelDiff> changedModels = diffResults2.getChangedModels(); assertEquals("Expected no new models", 0, diffResults2.getNewModels().size()); assertEquals("Expected no removed models", 0, diffResults2.getRemovedModels().size()); assertEquals("Expected detection of changed testmodel", 1, changedModels.size()); AlfrescoModelDiff changedModel = changedModels.get(0); assertEquals("Unexpected changed model name", QName.createQName(testModel.getName(), namespaceService).toString(), changedModel.getModelName()); assertNotNull("", changedModel.getOldChecksum().longValue()); assertEquals("Old checksum value is incorrect", testModelChecksum.longValue(), changedModel.getOldChecksum().longValue()); assertNotSame("Expected checksums to be different", changedModel.getOldChecksum(), changedModel.getNewChecksum()); // remove the model dictionaryDAO.removeModel(QName.createQName(testModel.getName(), namespaceService)); // call model diffs - check that the model has been removed ModelDiffResults diffResults3 = tracker.diff(); List<AlfrescoModelDiff> removedModels = diffResults3.getRemovedModels(); assertEquals("Expected 1 removed model", 1, removedModels.size()); QName removedModelName = QName.createQName(removedModels.get(0).getModelName()); String removedModelNamespace = removedModelName.getNamespaceURI(); String removedModelLocalName = removedModelName.getLocalName(); assertEquals("Removed model namespace is incorrect", "http://www.alfresco.org/model/solrtest/1.0", removedModelNamespace); assertEquals("Removed model name is incorrect", "contentmodel", removedModelLocalName); assertEquals("Expected no new models", 0, diffResults3.getNewModels().size()); assertEquals("Expected no changed modeks", 0, diffResults3.getChangedModels().size()); } private static class ModelDiffResults { private List<AlfrescoModelDiff> newModels; private List<AlfrescoModelDiff> changedModels; private List<AlfrescoModelDiff> removedModels; public ModelDiffResults(List<AlfrescoModelDiff> newModels, List<AlfrescoModelDiff> changedModels, List<AlfrescoModelDiff> removedModels) { super(); this.newModels = newModels; this.changedModels = changedModels; this.removedModels = removedModels; } public List<AlfrescoModelDiff> getNewModels() { return newModels; } public List<AlfrescoModelDiff> getChangedModels() { return changedModels; } public List<AlfrescoModelDiff> getRemovedModels() { return removedModels; } } private class ModelDiffsTracker { private Map<QName, Long> trackedModels = new HashMap<QName, Long>(); public ModelDiffResults diff() { List<AlfrescoModelDiff> modelDiffs = solrTrackingComponent.getModelDiffs(trackedModels); List<AlfrescoModelDiff> newModels = new ArrayList<AlfrescoModelDiff>(); List<AlfrescoModelDiff> changedModels = new ArrayList<AlfrescoModelDiff>(); List<AlfrescoModelDiff> removedModels = new ArrayList<AlfrescoModelDiff>(); for (AlfrescoModelDiff diff : modelDiffs) { if (diff.getType().equals(AlfrescoModelDiff.TYPE.NEW)) { newModels.add(diff); trackedModels.put(QName.createQName(diff.getModelName()), diff.getNewChecksum()); } else if (diff.getType().equals(AlfrescoModelDiff.TYPE.CHANGED)) { changedModels.add(diff); } else if (diff.getType().equals(AlfrescoModelDiff.TYPE.REMOVED)) { removedModels.add(diff); } } return new ModelDiffResults(newModels, changedModels, removedModels); } public Long getChecksum(QName modelName) { return trackedModels.get(modelName); } } private static class NodeAssertions { private Set<QName> aspects; private Map<QName, Serializable> properties; private NodeStatus nodeStatus; private Boolean expectAspects = true; private Boolean expectProperties = true; private boolean expectType = true; private boolean expectOwner = true; private boolean expectAssociations = true; private boolean expectPaths = true; private boolean expectAclId = true; public NodeAssertions() { super(); } public boolean isExpectType() { return expectType; } public boolean isExpectOwner() { return expectOwner; } public boolean isExpectAssociations() { return expectAssociations; } public boolean isExpectPaths() { return expectPaths; } public boolean isExpectAclId() { return expectAclId; } public boolean isExpectAspects() { return expectAspects; } public boolean isExpectProperties() { return expectProperties; } public void setNodeStatus(NodeStatus nodeStatus) { this.nodeStatus = nodeStatus; } public NodeStatus getNodeStatus() { return nodeStatus; } public Set<QName> getAspects() { return aspects; } public Map<QName, Serializable> getProperties() { return properties; } } private List<Transaction> checkTransactions(List<Transaction> txns, List<Long> createdTransaction, int[] updates, int[] deletes) { ArrayList<Transaction> matchedTransactions = new ArrayList<Transaction>(); HashSet<Long> toMatch = new HashSet<Long>(); toMatch.addAll(createdTransaction); for (Transaction found : txns) { if (found != null) { if (toMatch.contains(found.getId())) { matchedTransactions.add(found); } } } assertEquals("Number of transactions is incorrect", createdTransaction.size(), matchedTransactions.size()); int i = 0; for (Transaction txn : matchedTransactions) { assertEquals("Number of deletes is incorrect", deletes[i], txn.getDeletes()); assertEquals("Number of updates is incorrect", updates[i], txn.getUpdates()); i++; } return matchedTransactions; } private void getNodeMetaData(final NodeMetaDataParameters params, final MetaDataResultsFilter filter, final SOLRTest bt) { bt.clearNodesMetaData(); long startTime = System.currentTimeMillis(); txnHelper.doInTransaction(new RetryingTransactionCallback<Void>() { @Override public Void execute() throws Throwable { solrTrackingComponent.getNodesMetadata(params, filter, bt); return null; } }, true, true); long endTime = System.currentTimeMillis(); bt.runNodeMetaDataChecks(params.getMaxResults()); logger.debug( "Got " + bt.getActualNodeMetaDataCount() + " node metadatas in " + (endTime - startTime) + " ms"); } private static abstract class SOLRTest implements NodeQueryCallback, NodeMetaDataQueryCallback { protected FileFolderService fileFolderService; protected RetryingTransactionHelper txnHelper; protected NodeService nodeService; protected NodeRef rootNodeRef; protected NodeDAO nodeDAO; protected QNameDAO qnameDAO; protected DictionaryService dictionaryService; protected String containerName; protected Map<NodeRef, NodeAssertions> nodeAssertions; protected boolean doChecks; protected boolean doNodeChecks; protected boolean doMetaDataChecks; protected int successCount = 0; protected int failureCount = 0; protected List<Long> nodeIds; protected long expectedNumMetaDataNodes = 0; protected long actualNodeCount = 0; protected long actualNodeMetaDataCount = 0; SOLRTest(RetryingTransactionHelper txnHelper, FileFolderService fileFolderService, NodeDAO nodeDAO, QNameDAO qnameDAO, NodeService nodeService, DictionaryService dictionaryService, NodeRef rootNodeRef, String containerName, boolean doNodeChecks, boolean doMetaDataChecks) { this.txnHelper = txnHelper; this.nodeService = nodeService; this.rootNodeRef = rootNodeRef; this.fileFolderService = fileFolderService; this.nodeDAO = nodeDAO; this.qnameDAO = qnameDAO; this.dictionaryService = dictionaryService; this.containerName = containerName; this.nodeAssertions = new HashMap<NodeRef, NodeAssertions>(); this.nodeIds = new ArrayList<Long>(getExpectedNumNodes()); this.doNodeChecks = doNodeChecks; this.doMetaDataChecks = doMetaDataChecks; this.doChecks = doNodeChecks || doMetaDataChecks; } void runNodeChecks(int maxResults) { if (doNodeChecks) { if (maxResults != 0 && maxResults != Integer.MAX_VALUE) { assertEquals("Number of returned nodes is incorrect", maxResults, getActualNodeCount()); } else { assertEquals("Number of returned nodes is incorrect", getExpectedNumNodes(), getActualNodeCount()); } assertEquals("Unexpected failures", 0, getFailureCount()); assertEquals("Success count is incorrect", getActualNodeCount(), getSuccessCount()); } } void runNodeMetaDataChecks(int maxResults) { if (maxResults != 0 && maxResults != Integer.MAX_VALUE) { assertEquals("Number of returned nodes is incorrect", maxResults, getActualNodeMetaDataCount()); } else { assertEquals("Number of returned nodes is incorrect", getExpectedNumMetaDataNodes(), getActualNodeMetaDataCount()); } } void clearNodesMetaData() { successCount = 0; failureCount = 0; actualNodeMetaDataCount = 0; nodeAssertions.clear(); } public long getActualNodeCount() { return actualNodeCount; } public long getActualNodeMetaDataCount() { return actualNodeMetaDataCount; } protected long getExpectedNumMetaDataNodes() { return expectedNumMetaDataNodes; } protected abstract int getExpectedNumNodes(); protected abstract List<Long> buildTransactionsInternal(); public NodeAssertions getNodeAssertions(NodeRef nodeRef) { NodeAssertions assertions = nodeAssertions.get(nodeRef); if (assertions == null) { assertions = new NodeAssertions(); nodeAssertions.put(nodeRef, assertions); } return assertions; } protected void setExpectedNodeStatus(NodeRef nodeRef, NodeStatus nodeStatus) { if (nodeStatus == NodeStatus.UPDATED) { expectedNumMetaDataNodes++; } /* if(nodeStatus == NodeStatus.DELETED) { expectedNumMetaDataNodes++; } */ if (doChecks) { NodeAssertions nodeAssertions = getNodeAssertions(nodeRef); nodeAssertions.setNodeStatus(nodeStatus); } } List<Long> buildTransactions() { return buildTransactionsInternal(); } @Override public boolean handleNode(Node node) { actualNodeCount++; if (doNodeChecks) { NodeRef nodeRef = node.getNodeRef(); Boolean isDeleted = node.getDeleted(qnameDAO); nodeIds.add(node.getId()); NodeAssertions expectedStatus = getNodeAssertions(nodeRef); if (expectedStatus == null) { throw new RuntimeException("Unexpected missing assertion for NodeRef " + nodeRef); } if ((expectedStatus.getNodeStatus() == NodeStatus.DELETED && isDeleted) || (expectedStatus.getNodeStatus() == NodeStatus.UPDATED && !isDeleted)) { successCount++; } else { failureCount++; } } return true; } private Map<QName, Serializable> filterResudualProperties(Map<QName, Serializable> sourceProps) { Map<QName, Serializable> props = new HashMap<QName, Serializable>((int) (sourceProps.size() * 1.3)); for (QName propertyQName : sourceProps.keySet()) { PropertyDefinition propDef = dictionaryService.getProperty(propertyQName); if (propDef != null) { props.put(propertyQName, sourceProps.get(propertyQName)); } } return props; } @Override public boolean handleNodeMetaData(NodeMetaData nodeMetaData) { actualNodeMetaDataCount++; if (doMetaDataChecks) { Long nodeId = nodeMetaData.getNodeId(); NodeRef nodeRef = nodeMetaData.getNodeRef(); if (nodeService.exists(nodeRef)) { Set<QName> aspects = nodeMetaData.getAspects(); Set<QName> actualAspects = nodeService.getAspects(nodeRef); assertEquals("Aspects are incorrect", actualAspects, aspects); Map<QName, Serializable> properties = nodeMetaData.getProperties(); // NodeService converts properties so use nodeDAO to get unadulterated property value Map<QName, Serializable> actualProperties = filterResudualProperties( nodeDAO.getNodeProperties(nodeId)); //assertTrue("Properties are incorrect", compareProperties(actualProperties, properties)); assertEquals("Properties are incorrect", actualProperties, properties); NodeAssertions assertions = getNodeAssertions(nodeRef); // NodeAssertions assertions = nodes.get(nodeRef); Set<QName> expectedAspects = assertions.getAspects(); if (expectedAspects != null) { for (QName aspect : expectedAspects) { assertTrue("Expected aspect" + aspect, aspects.contains(aspect)); } } Map<QName, Serializable> expectedProperties = assertions.getProperties(); if (expectedProperties != null) { for (QName propName : expectedProperties.keySet()) { Serializable expectedPropValue = expectedProperties.get(propName); Serializable actualPropValue = properties.get(propName); assertNotNull("Missing property " + propName, actualPropValue); assertEquals("Incorrect property value", expectedPropValue, actualPropValue); } } // TODO complete path tests // List<Path> actualPaths = nodeMetaData.getPaths(); // List<Path> expectedPaths = nodeService.getPaths(nodeRef, false); // assertEquals("Paths are incorrect", expectedPaths, actualPaths); // Include negative checks i.e. make sure we get null if we do NOT expect paths boolean expectPaths = assertions.isExpectPaths(); assertTrue("Expecting paths but didn't get any.", expectPaths == (nodeMetaData.getPaths() != null)); assertTrue("Expecting name path but didn't get it.", expectPaths == (nodeMetaData.getNamePaths() != null)); if (expectPaths) { // Check QName paths // TODO: Check paths // Check name paths Collection<Collection<String>> namePaths = nodeMetaData.getNamePaths(); if (nodeService.getProperty(nodeRef, ContentModel.PROP_NAME) == null) { assertEquals("Expect an empty list where there is no cm:name on the node. ", 0, namePaths.size()); } else { assertTrue("Expect some name paths for the node. ", namePaths.size() > 0); // Check that the last entry is the name of the current node String name = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); String namePathStr = namePaths.toString(); assertTrue("Did not find node name at end. Have " + namePathStr + " and wanted to see " + name, namePathStr.endsWith(name + "]]")); } } boolean expectAspects = assertions.isExpectAspects(); if (expectAspects && nodeMetaData.getAspects() == null) { fail("Expecting aspects but got no aspects"); } else if (!expectAspects && nodeMetaData.getAspects() != null) { fail("Not expecting aspects but got aspects"); } boolean expectProperties = assertions.isExpectProperties(); if (expectProperties && nodeMetaData.getProperties() == null) { fail("Expecting properties but got no properties"); } else if (!expectProperties && nodeMetaData.getProperties() != null) { fail("Not expecting properties but got properties"); } boolean expectType = assertions.isExpectType(); if (expectType && nodeMetaData.getNodeType() == null) { fail("Expecting type but got no type"); } else if (!expectType && nodeMetaData.getNodeType() != null) { fail("Not expecting type but got type"); } boolean expectAclId = assertions.isExpectAclId(); if (expectAclId && nodeMetaData.getAclId() == null) { fail("Expecting acl id but got no acl id"); } else if (!expectAclId && nodeMetaData.getAclId() != null) { fail("Not expecting acl id but got acl id"); } boolean expectAssociations = assertions.isExpectAssociations(); if (expectAssociations && nodeMetaData.getChildAssocs() == null) { fail("Expecting associations but got no associations"); } else if (!expectAssociations && nodeMetaData.getChildAssocs() != null) { fail("Not expecting associations but got associations"); } boolean expectOwner = assertions.isExpectOwner(); if (expectOwner && nodeMetaData.getOwner() == null) { fail("Expecting owner but got no owner"); } else if (!expectOwner && nodeMetaData.getOwner() != null) { fail("Not expecting owner but got owner"); } } } successCount++; return true; } public int getSuccessCount() { return successCount; } public int getFailureCount() { return failureCount; } public List<Long> getNodeIds() { return nodeIds; } } private static class SOLRTest1 extends SOLRTest { private NodeRef container; private NodeRef content1; private NodeRef content2; SOLRTest1(RetryingTransactionHelper txnHelper, FileFolderService fileFolderService, NodeDAO nodeDAO, QNameDAO qnameDAO, NodeService nodeService, DictionaryService dictionaryService, NodeRef rootNodeRef, String containerName, boolean doNodeChecks, boolean doMetaDataChecks) { super(txnHelper, fileFolderService, nodeDAO, qnameDAO, nodeService, dictionaryService, rootNodeRef, containerName, doNodeChecks, doMetaDataChecks); } public int getExpectedNumNodes() { return 3; } protected List<Long> buildTransactionsInternal() { ArrayList<Long> txs = new ArrayList<Long>(2); txs.add(txnHelper.doInTransaction(new RetryingTransactionCallback<Long>() { public Long execute() throws Throwable { PropertyMap props = new PropertyMap(); props.put(ContentModel.PROP_NAME, "Container1"); container = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN, ContentModel.TYPE_FOLDER, props).getChildRef(); FileInfo contentInfo = fileFolderService.create(container, "Content1", ContentModel.TYPE_CONTENT); content1 = contentInfo.getNodeRef(); return nodeDAO.getNodeRefStatus(content1).getDbTxnId(); } })); txs.add(txnHelper.doInTransaction(new RetryingTransactionCallback<Long>() { public Long execute() throws Throwable { FileInfo contentInfo = fileFolderService.create(container, "Content2", ContentModel.TYPE_CONTENT); content2 = contentInfo.getNodeRef(); fileFolderService.delete(content1); return nodeDAO.getNodeRefStatus(content1).getDbTxnId(); } })); setExpectedNodeStatus(container, NodeStatus.UPDATED); setExpectedNodeStatus(content1, NodeStatus.DELETED); setExpectedNodeStatus(content2, NodeStatus.UPDATED); return txs; } } private static class SOLRTest3 extends SOLRTest { private NodeRef container; private NodeRef content1; private NodeRef content2; SOLRTest3(RetryingTransactionHelper txnHelper, FileFolderService fileFolderService, NodeDAO nodeDAO, QNameDAO qnameDAO, NodeService nodeService, DictionaryService dictionaryService, NodeRef rootNodeRef, String containerName, boolean doNodeChecks, boolean doMetaDataChecks) { super(txnHelper, fileFolderService, nodeDAO, qnameDAO, nodeService, dictionaryService, rootNodeRef, containerName, doNodeChecks, doMetaDataChecks); } public int getExpectedNumNodes() { return 3; } protected List<Long> buildTransactionsInternal() { ArrayList<Long> txs = new ArrayList<Long>(2); txs.add(txnHelper.doInTransaction(new RetryingTransactionCallback<Long>() { public Long execute() throws Throwable { PropertyMap props = new PropertyMap(); props.put(ContentModel.PROP_NAME, "Container1"); container = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN, ContentModel.TYPE_FOLDER, props).getChildRef(); FileInfo contentInfo = fileFolderService.create(container, "Content1", ContentModel.TYPE_CONTENT); content1 = contentInfo.getNodeRef(); Map<QName, Serializable> aspectProperties = new HashMap<QName, Serializable>(); aspectProperties.put(ContentModel.PROP_AUTHOR, "steve"); nodeService.addAspect(content1, ContentModel.ASPECT_AUTHOR, aspectProperties); return nodeDAO.getNodeRefStatus(content1).getDbTxnId(); } })); txs.add(txnHelper.doInTransaction(new RetryingTransactionCallback<Long>() { public Long execute() throws Throwable { FileInfo contentInfo = fileFolderService.create(container, "Content2", ContentModel.TYPE_CONTENT); content2 = contentInfo.getNodeRef(); nodeService.addAspect(content2, ContentModel.ASPECT_TEMPORARY, null); fileFolderService.delete(content1); return nodeDAO.getNodeRefStatus(content1).getDbTxnId(); } })); setExpectedNodeStatus(container, NodeStatus.UPDATED); setExpectedNodeStatus(content1, NodeStatus.DELETED); setExpectedNodeStatus(content2, NodeStatus.UPDATED); return txs; } } private static class SOLRTest100Nodes extends SOLRTest { SOLRTest100Nodes(RetryingTransactionHelper txnHelper, FileFolderService fileFolderService, NodeDAO nodeDAO, QNameDAO qnameDAO, NodeService nodeService, DictionaryService dictionaryService, NodeRef rootNodeRef, String containerName, boolean doNodeChecks, boolean doMetaDataChecks) { super(txnHelper, fileFolderService, nodeDAO, qnameDAO, nodeService, dictionaryService, rootNodeRef, containerName, doNodeChecks, doMetaDataChecks); } public int getExpectedNumNodes() { return 100; } protected List<Long> buildTransactionsInternal() { ArrayList<Long> txs = new ArrayList<Long>(2); txs.add(txnHelper.doInTransaction(new RetryingTransactionCallback<Long>() { public Long execute() throws Throwable { PropertyMap props = new PropertyMap(); props.put(ContentModel.PROP_NAME, "Container100Nodes"); NodeRef container = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN, ContentModel.TYPE_FOLDER, props).getChildRef(); setExpectedNodeStatus(container, NodeStatus.UPDATED); for (int i = 0; i < 99; i++) { FileInfo contentInfo = fileFolderService.create(container, "Content" + i, ContentModel.TYPE_CONTENT); NodeRef nodeRef = contentInfo.getNodeRef(); setExpectedNodeStatus(nodeRef, NodeStatus.UPDATED); } return nodeDAO.getNodeRefStatus(container).getDbTxnId(); } })); return txs; } } private static class SOLRTest4 extends SOLRTest { private int numContentNodes = 2000; SOLRTest4(RetryingTransactionHelper txnHelper, FileFolderService fileFolderService, NodeDAO nodeDAO, QNameDAO qnameDAO, NodeService nodeService, DictionaryService dictionaryService, NodeRef rootNodeRef, String containerName, boolean doNodeChecks, boolean doMetaDataChecks) { super(txnHelper, fileFolderService, nodeDAO, qnameDAO, nodeService, dictionaryService, rootNodeRef, containerName, doNodeChecks, doMetaDataChecks); } public int getExpectedNumNodes() { return numContentNodes + 1; } public List<Long> buildTransactionsInternal() { ArrayList<Long> txs = new ArrayList<Long>(2); txs.add(txnHelper.doInTransaction(new RetryingTransactionCallback<Long>() { public Long execute() throws Throwable { PropertyMap props = new PropertyMap(); props.put(ContentModel.PROP_NAME, containerName); NodeRef container = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN, ContentModel.TYPE_FOLDER, props).getChildRef(); setExpectedNodeStatus(container, NodeStatus.UPDATED); for (int i = 0; i < numContentNodes; i++) { FileInfo contentInfo = fileFolderService.create(container, "Content" + i, ContentModel.TYPE_CONTENT); NodeRef nodeRef = contentInfo.getNodeRef(); if (i % 2 == 1) { nodeService.addAspect(nodeRef, ContentModel.ASPECT_TEMPORARY, null); } nodeService.setProperty(nodeRef, ContentModel.PROP_AUTHOR, null); setExpectedNodeStatus(nodeRef, NodeStatus.UPDATED); } return nodeDAO.getNodeRefStatus(container).getDbTxnId(); } })); return txs; } } private static class SOLRTest5 extends SOLRTest { private int numContentNodes = 10; SOLRTest5(RetryingTransactionHelper txnHelper, FileFolderService fileFolderService, NodeDAO nodeDAO, QNameDAO qnameDAO, NodeService nodeService, DictionaryService dictionaryService, NodeRef rootNodeRef, String containerName, boolean doNodeChecks, boolean doMetaDataChecks) { super(txnHelper, fileFolderService, nodeDAO, qnameDAO, nodeService, dictionaryService, rootNodeRef, containerName, doNodeChecks, doMetaDataChecks); } public int getExpectedNumNodes() { return numContentNodes + 1; } public List<Long> buildTransactionsInternal() { ArrayList<Long> txs = new ArrayList<Long>(2); final String titles[] = { "caf\u00E9", "\u00E7edilla", "\u00E0\u00E1\u00E2\u00E3", "\u00EC\u00ED\u00EE\u00EF", "\u00F0\u00F1\u00F2\u00F3\u00F4\u00F5\u00F6", "caf\u00E9", "\u00E7edilla", "\u00E0\u00E1\u00E2\u00E3", "\u00EC\u00ED\u00EE\u00EF", "\u00F0\u00F1\u00F2\u00F3\u00F4\u00F5\u00F6" }; txs.add(txnHelper.doInTransaction(new RetryingTransactionCallback<Long>() { public Long execute() throws Throwable { PropertyMap props = new PropertyMap(); props.put(ContentModel.PROP_NAME, containerName); NodeRef container = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN, ContentModel.TYPE_FOLDER, props).getChildRef(); setExpectedNodeStatus(container, NodeStatus.UPDATED); for (int i = 0; i < numContentNodes; i++) { FileInfo contentInfo = fileFolderService.create(container, "Content" + i, ContentModel.TYPE_CONTENT); NodeRef nodeRef = contentInfo.getNodeRef(); nodeService.addAspect(nodeRef, ContentModel.ASPECT_AUTHOR, null); if (i % 5 == 1) { nodeService.setProperty(nodeRef, ContentModel.PROP_AUTHOR, null); } else { nodeService.setProperty(nodeRef, ContentModel.PROP_AUTHOR, "author" + i); } nodeService.setProperty(nodeRef, ContentModel.PROP_TITLE, titles[i]); setExpectedNodeStatus(nodeRef, NodeStatus.UPDATED); } return nodeDAO.getNodeRefStatus(container).getDbTxnId(); } })); return txs; } } private static class SOLRTestResidualProperties extends SOLRTest { private NodeRef container; private NodeRef content; SOLRTestResidualProperties(RetryingTransactionHelper txnHelper, FileFolderService fileFolderService, NodeDAO nodeDAO, QNameDAO qnameDAO, NodeService nodeService, DictionaryService dictionaryService, NodeRef rootNodeRef, String containerName, boolean doNodeChecks, boolean doMetaDataChecks) { super(txnHelper, fileFolderService, nodeDAO, qnameDAO, nodeService, dictionaryService, rootNodeRef, containerName, doNodeChecks, doMetaDataChecks); } public int getExpectedNumNodes() { return 2; } protected List<Long> buildTransactionsInternal() { ArrayList<Long> txs = new ArrayList<Long>(2); txs.add(txnHelper.doInTransaction(new RetryingTransactionCallback<Long>() { public Long execute() throws Throwable { PropertyMap props = new PropertyMap(); props.put(ContentModel.PROP_NAME, "ContainerResidual"); container = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN, ContentModel.TYPE_FOLDER, props).getChildRef(); FileInfo contentInfo = fileFolderService.create(container, "ContentResidual", ContentModel.TYPE_CONTENT); content = contentInfo.getNodeRef(); nodeService.setProperty(content, QName.createQName("{rubbish}rubbish"), "Rubbish"); return nodeDAO.getNodeRefStatus(container).getDbTxnId(); } })); setExpectedNodeStatus(container, NodeStatus.UPDATED); setExpectedNodeStatus(content, NodeStatus.UPDATED); return txs; } } }