diff --git a/pom.xml b/pom.xml index 97c2bc579b..edcf07d5b7 100644 --- a/pom.xml +++ b/pom.xml @@ -62,7 +62,7 @@ 2.11.2 3.4.0 1.1.0 - 2.0.20 + 2.0.21 8.8 1.66 3.5.11 @@ -81,7 +81,7 @@ 2.5.9 1.6.2 1.24.1 - 5.2.1.RELEASE + 5.3.4.RELEASE 7.7.10 4.1.2 1.4 diff --git a/repository/pom.xml b/repository/pom.xml index c101232f73..c0db473a9c 100644 --- a/repository/pom.xml +++ b/repository/pom.xml @@ -142,7 +142,7 @@ org.mozilla rhino - 1.7.12 + 1.7.13 org.springframework diff --git a/repository/src/main/java/org/alfresco/opencmis/TransactionAwareHolder.java b/repository/src/main/java/org/alfresco/opencmis/TransactionAwareHolder.java index a5c5991583..e35d75d413 100644 --- a/repository/src/main/java/org/alfresco/opencmis/TransactionAwareHolder.java +++ b/repository/src/main/java/org/alfresco/opencmis/TransactionAwareHolder.java @@ -81,22 +81,19 @@ public class TransactionAwareHolder extends Holder { this.internalHolder = internalHolder; this.value = internalHolder.getValue(); - txListener = new TxAwareHolderListener(); } @Override public T getValue() { - if (TransactionSynchronizationManager.isSynchronizationActive()) - { - AlfrescoTransactionSupport.bindListener(txListener); - } + registerTxListenerIfNeeded(); return this.value; } @Override public void setValue(T value) { + registerTxListenerIfNeeded(); this.value = value; } @@ -109,6 +106,17 @@ public class TransactionAwareHolder extends Holder '}'; } + // MNT-21800 CMIS Web Service Check Out returns error + private void registerTxListenerIfNeeded() + { + if (this.txListener == null && TransactionSynchronizationManager.isSynchronizationActive()) + { + TxAwareHolderListener listener = new TxAwareHolderListener(); + AlfrescoTransactionSupport.bindListener(listener); + this.txListener = listener; + } + } + private class TxAwareHolderListener extends TransactionListenerAdapter { @Override diff --git a/repository/src/main/java/org/alfresco/repo/domain/permissions/ADMAccessControlListDAO.java b/repository/src/main/java/org/alfresco/repo/domain/permissions/ADMAccessControlListDAO.java index 774cccc9bd..8d7b54d07d 100644 --- a/repository/src/main/java/org/alfresco/repo/domain/permissions/ADMAccessControlListDAO.java +++ b/repository/src/main/java/org/alfresco/repo/domain/permissions/ADMAccessControlListDAO.java @@ -430,8 +430,8 @@ public class ADMAccessControlListDAO implements AccessControlListDAO // { // setFixedAcls(child.getId(), inheritFrom, mergeFrom, sharedAclToReplace, changes, false); // } - // Already replaced - if(acl.equals(sharedAclToReplace)) + // Still has old shared ACL or already replaced + if(acl.equals(sharedAclToReplace) || acl.equals(mergeFrom)) { propagateOnChildren = setFixAclPending(child.getId(), inheritFrom, mergeFrom, sharedAclToReplace, changes, false, asyncCall, propagateOnChildren); } diff --git a/repository/src/main/java/org/alfresco/repo/security/permissions/impl/SimplePermissionReference.java b/repository/src/main/java/org/alfresco/repo/security/permissions/impl/SimplePermissionReference.java index e34f22b0c8..9ff1ebc3ab 100644 --- a/repository/src/main/java/org/alfresco/repo/security/permissions/impl/SimplePermissionReference.java +++ b/repository/src/main/java/org/alfresco/repo/security/permissions/impl/SimplePermissionReference.java @@ -1,35 +1,35 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2020 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 . + * #L% + */ package org.alfresco.repo.security.permissions.impl; -import java.util.HashMap; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; /** * A simple permission reference. @@ -37,62 +37,30 @@ import org.alfresco.service.namespace.QName; * @author andyh */ public final class SimplePermissionReference extends AbstractPermissionReference -{ +{ private static final long serialVersionUID = 637302438293417818L; - private static ReadWriteLock lock = new ReentrantReadWriteLock(); - - private static HashMap> instances = new HashMap>(); + //Use thread-safe map initiallized with a slightly larger capacity to reduce the posibility of two or more threads attempting to resize at the same time + private static ConcurrentMap, SimplePermissionReference> instances = new ConcurrentHashMap<>(100, 0.9f, 2); /** - * Factory method to create simple permission refrences + * Factory method to create simple permission references * * @return a simple permission reference */ public static SimplePermissionReference getPermissionReference(QName qName, String name) { - lock.readLock().lock(); - try - { - HashMap typed = instances.get(qName); - if(typed != null) + Pair key = new Pair<>(qName, name); + SimplePermissionReference instance = instances.get(key); + if (instance == null) { - SimplePermissionReference instance = typed.get(name); - if(instance != null) - { - return instance; - } - } - } - finally - { - lock.readLock().unlock(); - } - - lock.writeLock().lock(); - try - { - HashMap typed = instances.get(qName); - if(typed == null) - { - typed = new HashMap(); - instances.put(qName, typed); - } - SimplePermissionReference instance = typed.get(name); - if(instance == null) - { - instance = new SimplePermissionReference(qName, name); - typed.put(name, instance); + instance = new SimplePermissionReference(qName, name); + instances.putIfAbsent(key, instance); } + return instance; - } - finally - { - lock.writeLock().unlock(); - } } - /* * The type */ diff --git a/repository/src/main/java/org/alfresco/repo/solr/SOLRTrackingComponentImpl.java b/repository/src/main/java/org/alfresco/repo/solr/SOLRTrackingComponentImpl.java index 95a240a523..4a45bc649b 100644 --- a/repository/src/main/java/org/alfresco/repo/solr/SOLRTrackingComponentImpl.java +++ b/repository/src/main/java/org/alfresco/repo/solr/SOLRTrackingComponentImpl.java @@ -691,20 +691,18 @@ public class SOLRTrackingComponentImpl implements SOLRTrackingComponent return new ArrayList(visited); } - + /** Get properties that we want to be indexed. */ protected Map getProperties(Long nodeId) { - Map props = null; - // ALF-10641 - // Residual properties are un-indexed -> break serlialisation + // Residual properties are un-indexed -> break serialisation nodeDAO.setCheckNodeConsistency(); Map sourceProps = nodeDAO.getNodeProperties(nodeId); - props = new HashMap((int)(sourceProps.size() * 1.3)); + Map props = new HashMap<>(sourceProps.size()); for(QName propertyQName : sourceProps.keySet()) { PropertyDefinition propDef = dictionaryService.getProperty(propertyQName); - if(propDef != null) + if(propDef != null && propDef.isIndexed()) { props.put(propertyQName, sourceProps.get(propertyQName)); } diff --git a/repository/src/main/resources/alfresco/dbscripts/create/org.alfresco.repo.domain.dialect.MySQLInnoDBDialect/AlfrescoCreate-RepoTables.sql b/repository/src/main/resources/alfresco/dbscripts/create/org.alfresco.repo.domain.dialect.MySQLInnoDBDialect/AlfrescoCreate-RepoTables.sql index 2599697199..76a1107baa 100644 --- a/repository/src/main/resources/alfresco/dbscripts/create/org.alfresco.repo.domain.dialect.MySQLInnoDBDialect/AlfrescoCreate-RepoTables.sql +++ b/repository/src/main/resources/alfresco/dbscripts/create/org.alfresco.repo.domain.dialect.MySQLInnoDBDialect/AlfrescoCreate-RepoTables.sql @@ -169,7 +169,9 @@ CREATE TABLE alf_transaction change_txn_id VARCHAR(56) NOT NULL, commit_time_ms BIGINT, PRIMARY KEY (id), - KEY idx_alf_txn_ctms (commit_time_ms) + KEY idx_alf_txn_ctms (commit_time_ms, id), + KEY idx_alf_txn_ctms_sc (commit_time_ms), + key idx_alf_txn_id_ctms (id, commit_time_ms) ) ENGINE=InnoDB; CREATE TABLE alf_store @@ -210,6 +212,8 @@ CREATE TABLE alf_node KEY idx_alf_node_crd (audit_created, store_id, type_qname_id), KEY idx_alf_node_mor (audit_modifier, store_id, type_qname_id), KEY idx_alf_node_mod (audit_modified, store_id, type_qname_id), + KEY idx_alf_node_ver (version), + KEY idx_alf_node_txn (transaction_id), CONSTRAINT fk_alf_node_acl FOREIGN KEY (acl_id) REFERENCES alf_access_control_list (id), CONSTRAINT fk_alf_node_store FOREIGN KEY (store_id) REFERENCES alf_store (id), CONSTRAINT fk_alf_node_tqn FOREIGN KEY (type_qname_id) REFERENCES alf_qname (id), diff --git a/repository/src/main/resources/alfresco/dbscripts/create/org.alfresco.repo.domain.dialect.MySQLInnoDBDialect/Schema-Reference-ALF.xml b/repository/src/main/resources/alfresco/dbscripts/create/org.alfresco.repo.domain.dialect.MySQLInnoDBDialect/Schema-Reference-ALF.xml index ae71a60993..7d6c902e4d 100644 --- a/repository/src/main/resources/alfresco/dbscripts/create/org.alfresco.repo.domain.dialect.MySQLInnoDBDialect/Schema-Reference-ALF.xml +++ b/repository/src/main/resources/alfresco/dbscripts/create/org.alfresco.repo.domain.dialect.MySQLInnoDBDialect/Schema-Reference-ALF.xml @@ -1681,6 +1681,16 @@ type_qname_id + + + version + + + + + transaction_id + + @@ -2630,6 +2640,18 @@ commit_time_ms + id + + + + + commit_time_ms + + + + + id + commit_time_ms diff --git a/repository/src/main/resources/alfresco/dbscripts/create/org.alfresco.repo.domain.dialect.PostgreSQLDialect/AlfrescoCreate-RepoTables.sql b/repository/src/main/resources/alfresco/dbscripts/create/org.alfresco.repo.domain.dialect.PostgreSQLDialect/AlfrescoCreate-RepoTables.sql index 393e9accec..364625ea54 100644 --- a/repository/src/main/resources/alfresco/dbscripts/create/org.alfresco.repo.domain.dialect.PostgreSQLDialect/AlfrescoCreate-RepoTables.sql +++ b/repository/src/main/resources/alfresco/dbscripts/create/org.alfresco.repo.domain.dialect.PostgreSQLDialect/AlfrescoCreate-RepoTables.sql @@ -184,6 +184,8 @@ CREATE TABLE alf_transaction PRIMARY KEY (id) ); CREATE INDEX idx_alf_txn_ctms ON alf_transaction (commit_time_ms, id); +CREATE INDEX idx_alf_txn_ctms_sc ON alf_transaction (commit_time_ms); +CREATE INDEX idx_alf_txn_id_ctms ON alf_transaction (id, commit_time_ms); CREATE SEQUENCE alf_store_seq START WITH 1 INCREMENT BY 1; CREATE TABLE alf_store @@ -231,6 +233,8 @@ CREATE INDEX fk_alf_node_acl ON alf_node (acl_id); CREATE INDEX fk_alf_node_store ON alf_node (store_id); CREATE INDEX idx_alf_node_tqn ON alf_node (type_qname_id, store_id, id); CREATE INDEX fk_alf_node_loc ON alf_node (locale_id); +CREATE INDEX idx_alf_node_ver ON alf_node (version); +CREATE INDEX idx_alf_node_txn ON alf_node (transaction_id); CREATE INDEX fk_alf_store_root ON alf_store (root_node_id); ALTER TABLE alf_store ADD CONSTRAINT fk_alf_store_root FOREIGN KEY (root_node_id) REFERENCES alf_node (id); diff --git a/repository/src/main/resources/alfresco/dbscripts/create/org.alfresco.repo.domain.dialect.PostgreSQLDialect/Schema-Reference-ALF.xml b/repository/src/main/resources/alfresco/dbscripts/create/org.alfresco.repo.domain.dialect.PostgreSQLDialect/Schema-Reference-ALF.xml index b4fc5abffd..312c35c6d5 100644 --- a/repository/src/main/resources/alfresco/dbscripts/create/org.alfresco.repo.domain.dialect.PostgreSQLDialect/Schema-Reference-ALF.xml +++ b/repository/src/main/resources/alfresco/dbscripts/create/org.alfresco.repo.domain.dialect.PostgreSQLDialect/Schema-Reference-ALF.xml @@ -1739,6 +1739,16 @@ id + + + version + + + + + transaction_id + +
@@ -2703,6 +2713,17 @@ id + + + commit_time_ms + + + + + id + commit_time_ms + +
diff --git a/repository/src/main/resources/alfresco/dbscripts/db-schema-context.xml b/repository/src/main/resources/alfresco/dbscripts/db-schema-context.xml index c8ef744642..786ff0e5d0 100644 --- a/repository/src/main/resources/alfresco/dbscripts/db-schema-context.xml +++ b/repository/src/main/resources/alfresco/dbscripts/db-schema-context.xml @@ -55,6 +55,7 @@ + diff --git a/repository/src/main/resources/alfresco/dbscripts/upgrade/6.3/org.alfresco.repo.domain.dialect.MySQLInnoDBDialect/add-indexes-node-transaction.sql b/repository/src/main/resources/alfresco/dbscripts/upgrade/6.3/org.alfresco.repo.domain.dialect.MySQLInnoDBDialect/add-indexes-node-transaction.sql new file mode 100644 index 0000000000..9040ee0d81 --- /dev/null +++ b/repository/src/main/resources/alfresco/dbscripts/upgrade/6.3/org.alfresco.repo.domain.dialect.MySQLInnoDBDialect/add-indexes-node-transaction.sql @@ -0,0 +1,35 @@ +-- +-- Title: Update alf_node and alf_transaction indexes for more performance +-- Database: MySQL +-- Since: V6.3 +-- Author: Eva Vasques +-- +-- Please contact support@alfresco.com if you need assistance with the upgrade. +-- + +DROP INDEX idx_alf_node_ver; --(optional) +CREATE INDEX idx_alf_node_ver ON alf_node (version); + +DROP INDEX idx_alf_node_txn; --(optional) +CREATE INDEX idx_alf_node_txn ON alf_node (transaction_id); + +DROP INDEX idx_alf_txn_ctms; --(optional) +CREATE INDEX idx_alf_txn_ctms ON alf_transaction (commit_time_ms, id); + +DROP INDEX idx_alf_txn_ctms_sc; --(optional) +CREATE INDEX idx_alf_txn_ctms_sc ON alf_transaction (commit_time_ms); + +DROP INDEX idx_alf_txn_id_ctms; --(optional) +CREATE INDEX idx_alf_txn_id_ctms ON alf_transaction (id, commit_time_ms); + +-- +-- Record script finish +-- +DELETE FROM alf_applied_patch WHERE id = 'patch.db-V6.3-add-indexes-node-transaction'; +INSERT INTO alf_applied_patch + (id, description, fixes_from_schema, fixes_to_schema, applied_to_schema, target_schema, applied_on_date, applied_to_server, was_executed, succeeded, report) + VALUES + ( + 'patch.db-V6.3-add-indexes-node-transaction', 'Create aditional indexes on alf_node and alf_transaction', + 0, 14001, -1, 14002, null, 'UNKNOWN', ${TRUE}, ${TRUE}, 'Script completed' + ); \ No newline at end of file diff --git a/repository/src/main/resources/alfresco/dbscripts/upgrade/6.3/org.alfresco.repo.domain.dialect.PostgreSQLDialect/add-indexes-node-transaction.sql b/repository/src/main/resources/alfresco/dbscripts/upgrade/6.3/org.alfresco.repo.domain.dialect.PostgreSQLDialect/add-indexes-node-transaction.sql new file mode 100644 index 0000000000..10fba2d958 --- /dev/null +++ b/repository/src/main/resources/alfresco/dbscripts/upgrade/6.3/org.alfresco.repo.domain.dialect.PostgreSQLDialect/add-indexes-node-transaction.sql @@ -0,0 +1,32 @@ +-- +-- Title: Update alf_node and alf_transaction indexes for more performance +-- Database: PostgreSQL +-- Since: V6.3 +-- Author: Eva Vasques +-- +-- Please contact support@alfresco.com if you need assistance with the upgrade. +-- + +DROP INDEX idx_alf_node_ver; --(optional) +CREATE INDEX idx_alf_node_ver ON alf_node (version); + +DROP INDEX idx_alf_node_txn; --(optional) +CREATE INDEX idx_alf_node_txn ON alf_node (transaction_id); + +DROP INDEX idx_alf_txn_ctms_sc; --(optional) +CREATE INDEX idx_alf_txn_ctms_sc ON alf_transaction (commit_time_ms); + +DROP INDEX idx_alf_txn_id_ctms; --(optional) +CREATE INDEX idx_alf_txn_id_ctms ON alf_transaction (id, commit_time_ms); + +-- +-- Record script finish +-- +DELETE FROM alf_applied_patch WHERE id = 'patch.db-V6.3-add-indexes-node-transaction'; +INSERT INTO alf_applied_patch + (id, description, fixes_from_schema, fixes_to_schema, applied_to_schema, target_schema, applied_on_date, applied_to_server, was_executed, succeeded, report) + VALUES + ( + 'patch.db-V6.3-add-indexes-node-transaction', 'Create aditional indexes on alf_node and alf_transaction', + 0, 14001, -1, 14002, null, 'UNKNOWN', ${TRUE}, ${TRUE}, 'Script completed' + ); \ No newline at end of file diff --git a/repository/src/main/resources/alfresco/messages/patch-service.properties b/repository/src/main/resources/alfresco/messages/patch-service.properties index 4b90cc7e23..915718a744 100644 --- a/repository/src/main/resources/alfresco/messages/patch-service.properties +++ b/repository/src/main/resources/alfresco/messages/patch-service.properties @@ -402,4 +402,6 @@ patch.db-V5.2-remove-jbpm-tables-from-db.description=Removes all JBPM related ta patch.db-V6.0-change-set-indexes.description=Add additional indexes to support acl tracking. -patch.db-V6.3-remove-alf_server-table.description=Remove alf_server table. \ No newline at end of file +patch.db-V6.3-remove-alf_server-table.description=Remove alf_server table. + +patch.db-V6.3-add-indexes-node-transaction.description=Create additional indexes on alf_node and alf_transaction \ No newline at end of file diff --git a/repository/src/main/resources/alfresco/patch/patch-services-context.xml b/repository/src/main/resources/alfresco/patch/patch-services-context.xml index 797813a1a4..6bc9a5a6a4 100644 --- a/repository/src/main/resources/alfresco/patch/patch-services-context.xml +++ b/repository/src/main/resources/alfresco/patch/patch-services-context.xml @@ -1408,4 +1408,16 @@ classpath:alfresco/dbscripts/upgrade/6.3/${db.script.dialect}/remove-alf_server-table.sql + + + patch.db-V6.3-add-indexes-node-transaction + patch.db-V6.3-add-indexes-node-transaction.description + 0 + 14001 + 14002 + ${system.new-node-transaction-indexes.ignored} + + classpath:alfresco/dbscripts/upgrade/6.3/${db.script.dialect}/add-indexes-node-transaction.sql + + diff --git a/repository/src/main/resources/alfresco/repository.properties b/repository/src/main/resources/alfresco/repository.properties index 773f76d83b..dc06cb26f6 100644 --- a/repository/src/main/resources/alfresco/repository.properties +++ b/repository/src/main/resources/alfresco/repository.properties @@ -3,7 +3,7 @@ repository.name=Main Repository # Schema number -version.schema=14001 +version.schema=14002 # Directory configuration @@ -1332,4 +1332,7 @@ system.prop_table_cleaner.algorithm=V2 # Configure the expiration time of the direct access url. This is the length of time in seconds that the link is valid for. # Note: It is up to the actual ContentStore implementation if it can fulfil this request or not. -alfresco.content.directAccessUrl.lifetimeInSec=300 \ No newline at end of file +alfresco.content.directAccessUrl.lifetimeInSec=300 + +# Creates additional indexes on alf_node and alf_transaction. Recommended for large repositories. +system.new-node-transaction-indexes.ignored=true \ No newline at end of file diff --git a/repository/src/main/resources/alfresco/subsystems/Search/solr4/solr-search-context.xml b/repository/src/main/resources/alfresco/subsystems/Search/solr4/solr-search-context.xml index c111fb94ac..52f5a18f82 100644 --- a/repository/src/main/resources/alfresco/subsystems/Search/solr4/solr-search-context.xml +++ b/repository/src/main/resources/alfresco/subsystems/Search/solr4/solr-search-context.xml @@ -153,18 +153,15 @@ - - - - - - - + + + + diff --git a/repository/src/test/java/org/alfresco/repo/domain/permissions/FixedAclUpdaterTest.java b/repository/src/test/java/org/alfresco/repo/domain/permissions/FixedAclUpdaterTest.java index 16b787afb2..70566e8d0f 100644 --- a/repository/src/test/java/org/alfresco/repo/domain/permissions/FixedAclUpdaterTest.java +++ b/repository/src/test/java/org/alfresco/repo/domain/permissions/FixedAclUpdaterTest.java @@ -25,7 +25,10 @@ */ package org.alfresco.repo.domain.permissions; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Set; import org.alfresco.model.ContentModel; @@ -41,6 +44,7 @@ import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransacti import org.alfresco.repo.transaction.TransactionListenerAdapter; import org.alfresco.service.ServiceRegistry; 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.security.PermissionService; import org.alfresco.service.namespace.QName; @@ -69,6 +73,7 @@ public class FixedAclUpdaterTest extends TestCase private Repository repository; private FixedAclUpdater fixedAclUpdater; private NodeRef folderAsyncCallNodeRef; + private NodeRef folderAsyncCallWithCreateNodeRef; private NodeRef folderSyncCallNodeRef; private PermissionsDaoComponent permissionsDaoComponent; private PermissionService permissionService; @@ -100,6 +105,10 @@ public class FixedAclUpdaterTest extends TestCase filesPerLevel); folderSyncCallNodeRef = txnHelper.doInTransaction(cb2); + RetryingTransactionCallback cb3 = createFolderHierchyCallback(home, fileFolderService, + "rootFolderAsyncWithCreateCall", filesPerLevel); + folderAsyncCallWithCreateNodeRef = txnHelper.doInTransaction(cb3); + // change setFixedAclMaxTransactionTime to lower value so setInheritParentPermissions on created folder // hierarchy require async call setFixedAclMaxTransactionTime(permissionsDaoComponent, home, 50); @@ -147,8 +156,10 @@ public class FixedAclUpdaterTest extends TestCase aspect.add(ContentModel.ASPECT_TEMPORARY); nodeDAO.addNodeAspects(nodeDAO.getNodePair(folderAsyncCallNodeRef).getFirst(), aspect); nodeDAO.addNodeAspects(nodeDAO.getNodePair(folderSyncCallNodeRef).getFirst(), aspect); + nodeDAO.addNodeAspects(nodeDAO.getNodePair(folderAsyncCallWithCreateNodeRef).getFirst(), aspect); fileFolderService.delete(folderAsyncCallNodeRef); fileFolderService.delete(folderSyncCallNodeRef); + fileFolderService.delete(folderAsyncCallWithCreateNodeRef); return null; }, false, true); } @@ -189,7 +200,34 @@ public class FixedAclUpdaterTest extends TestCase testWork(folderAsyncCallNodeRef, true); } + @Test + public void testAsyncWithNodeCreation() + { + testWorkWithNodeCreation(folderAsyncCallWithCreateNodeRef, true); + } + private void testWork(NodeRef folderRef, boolean asyncCall) + { + setPermissionsOnTree(folderRef, asyncCall); + triggerFixedACLJob(folderRef); + } + + private void testWorkWithNodeCreation(NodeRef folderRef, boolean asyncCall) + { + setPermissionsOnTree(folderRef, asyncCall); + + // MNT-21847 - Create a new content in folder that has the aspect applied + txnHelper.doInTransaction((RetryingTransactionCallback) () -> { + NodeRef folderWithPendingAcl = getFirstFolderWithAclPending(folderRef); + assertNotNull("No children folders were found with pendingFixACl aspect", folderWithPendingAcl); + createFile(fileFolderService, folderWithPendingAcl, "NewFile", ContentModel.TYPE_CONTENT); + return null; + }, false, true); + + triggerFixedACLJob(folderRef); + } + + private void setPermissionsOnTree(NodeRef folderRef, boolean asyncCall) { // kick it off by setting inherit parent permissions == false txnHelper.doInTransaction((RetryingTransactionCallback) () -> { @@ -203,21 +241,61 @@ public class FixedAclUpdaterTest extends TestCase assertTrue("There are no nodes to process", getNodesCountWithPendingFixedAclAspect() > 0); return null; }, false, true); + } - // run the fixedAclUpdater until there is nothing more to fix (running the updater - // may create more to fix up) + private void triggerFixedACLJob(NodeRef folder) + { + // run the fixedAclUpdater until there is nothing more to fix (running the updater may create more to fix up) or + // the count doesn't change, meaning we have a problem. txnHelper.doInTransaction((RetryingTransactionCallback) () -> { int count = 0; + int previousCount = 0; do { + previousCount = count; count = fixedAclUpdater.execute(); - } while (count > 0); + } while (count > 0 && previousCount != count); return null; }, false, true); // check if nodes with ASPECT_PENDING_FIX_ACL are processed txnHelper.doInTransaction((RetryingTransactionCallback) () -> { assertEquals("Not all nodes were processed", 0, getNodesCountWithPendingFixedAclAspect()); + //Remove the tree that failed so it does not influence the other test results + removeNodesWithPendingAcl(folder); + return null; + }, false, true); + } + + private NodeRef getFirstFolderWithAclPending(NodeRef parentNodeRef) + { + NodeRef folderWithPendingFixedAcl = null; + List primaryChildFolders = fileFolderService.listFolders(parentNodeRef); + for (int i = 0; i < primaryChildFolders.size(); i++) + { + NodeRef thisChildFolder = primaryChildFolders.get(i).getNodeRef(); + Long thisChildNodeId = nodeDAO.getNodePair(thisChildFolder).getFirst(); + if (nodeDAO.hasNodeAspect(thisChildNodeId, ContentModel.ASPECT_PENDING_FIX_ACL)) + { + folderWithPendingFixedAcl = thisChildFolder; + break; + } + + if (folderWithPendingFixedAcl == null) + { + folderWithPendingFixedAcl = getFirstFolderWithAclPending(thisChildFolder); + } + } + return folderWithPendingFixedAcl; + } + + private void removeNodesWithPendingAcl(NodeRef folder) + { + txnHelper.doInTransaction((RetryingTransactionCallback) () -> { + Set aspect = new HashSet<>(); + aspect.add(ContentModel.ASPECT_TEMPORARY); + nodeDAO.addNodeAspects(nodeDAO.getNodePair(folder).getFirst(), aspect); + fileFolderService.delete(folder); return null; }, false, true); } diff --git a/repository/src/test/java/org/alfresco/repo/solr/SOLRTrackingComponentUnitTest.java b/repository/src/test/java/org/alfresco/repo/solr/SOLRTrackingComponentUnitTest.java new file mode 100644 index 0000000000..316f5398f0 --- /dev/null +++ b/repository/src/test/java/org/alfresco/repo/solr/SOLRTrackingComponentUnitTest.java @@ -0,0 +1,115 @@ +/* + * #%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 . + * #L% + */ +package org.alfresco.repo.solr; + +import static java.util.Collections.emptyMap; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import java.io.Serializable; +import java.util.Map; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.namespace.QName; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +/** Unit tests for {@link org.alfresco.repo.solr.SOLRTrackingComponent}. */ +public class SOLRTrackingComponentUnitTest +{ + /** A pair of QNames for use in the tests. */ + private static final QName FIRST_PROPERTY = QName.createQName("the://first/property"); + private static final QName SECOND_PROPERTY = QName.createQName("the://second/property"); + /** A node id for use in the tests. */ + private static final long NODE_ID = 123L; + + /** The class under test. */ + @InjectMocks + private SOLRTrackingComponentImpl solrTrackingComponent; + @Mock + private NodeDAO nodeDAO; + @Mock + private DictionaryService dictionaryService; + + @Before + public void setUp() + { + initMocks(this); + } + + /** Check that properties of different types can be returned. */ + @Test + public void testGetProperties_indexedPropertiesPassedThrough() + { + Map propertiesFromDB = Map.of(FIRST_PROPERTY, "value1", SECOND_PROPERTY, 2); + when(nodeDAO.getNodeProperties(NODE_ID)).thenReturn(propertiesFromDB); + PropertyDefinition firstDefinition = mock(PropertyDefinition.class); + when(firstDefinition.isIndexed()).thenReturn(true); + when(dictionaryService.getProperty(FIRST_PROPERTY)).thenReturn(firstDefinition); + PropertyDefinition secondDefinition = mock(PropertyDefinition.class); + when(secondDefinition.isIndexed()).thenReturn(true); + when(dictionaryService.getProperty(SECOND_PROPERTY)).thenReturn(secondDefinition); + + Map properties = solrTrackingComponent.getProperties(NODE_ID); + + assertEquals("Expected both properties to be returned.", propertiesFromDB, properties); + } + + /** Check that a property is not indexed if it is not registered in the dictionary service. */ + @Test + public void testGetProperties_propertyWithoutModelIsNotIndexed() + { + Map propertiesFromDB = Map.of(FIRST_PROPERTY, "value1"); + when(nodeDAO.getNodeProperties(NODE_ID)).thenReturn(propertiesFromDB); + when(dictionaryService.getProperty(FIRST_PROPERTY)).thenReturn(null); + + Map properties = solrTrackingComponent.getProperties(NODE_ID); + + assertEquals("Expected residual property to be skipped.", emptyMap(), properties); + } + + /** Check that a property is not indexed if the model contains */ + @Test + public void testGetProperties_propertySkippedIfIndexFalseSet() + { + Map propertiesFromDB = Map.of(FIRST_PROPERTY, "value1"); + when(nodeDAO.getNodeProperties(NODE_ID)).thenReturn(propertiesFromDB); + PropertyDefinition firstDefinition = mock(PropertyDefinition.class); + when(firstDefinition.isIndexed()).thenReturn(false); + when(dictionaryService.getProperty(FIRST_PROPERTY)).thenReturn(firstDefinition); + + Map properties = solrTrackingComponent.getProperties(NODE_ID); + + assertEquals("Unexpected property when index enabled set to false.", emptyMap(), properties); + } +}