From alfresco-repository

SEARCH-2289: Restore the support for SOLR 4 in ACS 6.2+ deployments. (#1063)

This configuration is recommended for upgrading scenarios, where you need to upgrade ACS and to re-index the repository with SOLR 6 while using the system with SOLR 4.

(cherry picked from commit e8bba3ca13880be782c2e4c1d82223e70c370f28)
[MNT-21847] - Aync permissions fail when new nodes are created (#1188)

Fix:
*Changed method setFixedAcls on class ADMAccessControlListDAO to continue to propagate through children to apply the correct acl not only when the current child acl matches the shared acl to replace but also when the current child acl matches the new shared acl
Unit Test:
*Refactored the unit test FixedAclUpdaterTest to be able to add in a new test without repeating code: separating the operations that set the permissions from the one that triggers the job into separate methods
*As it was if one test failed, leaving aspects to be processed, the test would run indefinitely (it was programmed to keep running the job while there where nodes with the aspect). Added a verification to stop triggering the job if the number of nodes with the pendingFixAcl did not change between executions.
*Also, if one test failed, it would leave nodes with pendingFixAcl aspect in the database, and the other tests that ran after would also fail, not completing the goal of processing all nodes with the aspect. If a test fails, the folder structure it ran is now deleted so no nodes with the aspect from that structure are processed by the other tests.
*Added a test to find the first folder in a tree where permissions where set async that has the pendingFixAcl aspect and that creates a new node in it to verify the issue

(cherry picked from commit 443e5e226430a2760492fb82214ad520e7e1cb75)
Bump dependency.pdfbox.version from 2.0.20 to 2.0.21 (#1170)

(cherry picked from commit b93d73dec5f0e94d83f62721842b92e617dfba79)
Bump rhino from 1.7.12 to 1.7.13 (#1185)

(cherry picked from commit a5b5f072c64511999d3d27087a1a82f949371ac8)
Bump spring-security-core from 5.2.1.RELEASE to 5.3.4.RELEASE (#1167)

(cherry picked from commit 54f68d823333254cef74343288e99107494b1e28)
REPO-5339 Improve concurrency of SimplePermissionReference (#1189)

* REPO-5339 Improve concurrency of SimplePermissionReference

* REPO-5339 Change locks to synchronised block

* [REPO-5339] Improve concurrency of SimplePermissionReference
- Move to ConcurrentHashMap
- Capacity set to hold Repo + RM + slight overage for custom permissions
- Getting new SimplePermissionReference will not block while getting existing value.
- If a new value needs to be added to the Map, it will block when adding
- If blocked it will wait, in order, until it can attempt to add
- If it was blocked before it will check first if another operation added before adding as an atomic action

* [REPO-5339] Improve concurrency of SimplePermissionReference
- Simplify getPermissionReference by removing Future

* [REPO-5339] Thread Lock detected for AclReadersGet invocations
- Add load capacity and concurrency level

Co-authored-by: Jared Ottley <jared.ottley@alfresco.com>

(cherry picked from commit 39ded1cc0a364d24f737584eed22bef0918a5359)
[MNT-21766]: Significant degradation of performance as file and site count increase (#1217)

* [MNT-21766]: Significant degradation of performance as file and site
count increase
- Added the following indexes on alf_transaction:
idx_alf_txn_ctms_sc, cols (commit_time_ms)
idx_alf_txn_id_ctms, cols (id, commit_time_ms)
- Added the following indexes on alf_node:
idx_alf_node_ver, cols (version)
idx_alf_node_txn, cols (transaction_id)
- Created patch patch.db-V6.3-add-indexes-node-transaction
- Updated version.schema to 14002
- Added system property system.new-node-transaction-indexes.ignored, set
as true by default to not apply the patch automatically
- Created the MySQL update dbscripts to add the new indexes - and also
drop and recreate the pre-existing index idx_alf_txn_ctms on alf_transaction that was
inconsistent with the other DBMS: was indexing only commit_time_ms when
on all other DBMS index idx_alf_txn_ctms was on (commit_time_ms, id)
- Created the Postgres update dbscripts to add the new indexes

(cherry picked from commit d70746f63a6e6a2154d2e37c577955a17c42256e)
SEARCH-2450 Don't expose properties from solr endpoint if the model says not to index them. (#1228)

(cherry picked from commit 2fea6c9484def402e2bfd7c3717071ff8df69c01)
Fix/MNT-21800 CMIS Web Service Check Out returns error (#1232)

* MNT-21800 : CMIS Web Service Check Out returns error
   Integrate path into codebase.

(cherry picked from commit 51a7793668ba1c969c5de37603190244e3839cd0)
This commit is contained in:
Alan Davis
2020-10-06 00:10:41 +01:00
parent 3ad6bfcf65
commit 602834ff6e
19 changed files with 400 additions and 100 deletions

View File

@@ -62,7 +62,7 @@
<dependency.jackson-databind.version>2.11.2</dependency.jackson-databind.version>
<dependency.cxf.version>3.4.0</dependency.cxf.version>
<dependency.opencmis.version>1.1.0</dependency.opencmis.version>
<dependency.pdfbox.version>2.0.20</dependency.pdfbox.version>
<dependency.pdfbox.version>2.0.21</dependency.pdfbox.version>
<dependency.webscripts.version>8.8</dependency.webscripts.version>
<dependency.bouncycastle.version>1.66</dependency.bouncycastle.version>
<dependency.mockito-core.version>3.5.11</dependency.mockito-core.version>
@@ -81,7 +81,7 @@
<dependency.groovy.version>2.5.9</dependency.groovy.version>
<dependency.javax.mail.version>1.6.2</dependency.javax.mail.version>
<dependency.tika.version>1.24.1</dependency.tika.version>
<dependency.spring-security.version>5.2.1.RELEASE</dependency.spring-security.version>
<dependency.spring-security.version>5.3.4.RELEASE</dependency.spring-security.version>
<dependency.truezip.version>7.7.10</dependency.truezip.version>
<dependency.poi.version>4.1.2</dependency.poi.version>
<dependency.ooxml-schemas.version>1.4</dependency.ooxml-schemas.version>

View File

@@ -142,7 +142,7 @@
<dependency>
<groupId>org.mozilla</groupId>
<artifactId>rhino</artifactId>
<version>1.7.12</version>
<version>1.7.13</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>

View File

@@ -81,22 +81,19 @@ public class TransactionAwareHolder<T> extends Holder<T>
{
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<T> extends Holder<T>
'}';
}
// 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

View File

@@ -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);
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
* #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 <http://www.gnu.org/licenses/>.
* #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<QName, HashMap<String, SimplePermissionReference>> instances = new HashMap<QName, HashMap<String, SimplePermissionReference>>();
//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<Pair<QName, String>, 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<String, SimplePermissionReference> typed = instances.get(qName);
if(typed != null)
Pair<QName, String> 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<String, SimplePermissionReference> typed = instances.get(qName);
if(typed == null)
{
typed = new HashMap<String, SimplePermissionReference>();
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
*/

View File

@@ -691,20 +691,18 @@ public class SOLRTrackingComponentImpl implements SOLRTrackingComponent
return new ArrayList<Long>(visited);
}
/** Get properties that we want to be indexed. */
protected Map<QName, Serializable> getProperties(Long nodeId)
{
Map<QName, Serializable> props = null;
// ALF-10641
// Residual properties are un-indexed -> break serlialisation
// Residual properties are un-indexed -> break serialisation
nodeDAO.setCheckNodeConsistency();
Map<QName, Serializable> sourceProps = nodeDAO.getNodeProperties(nodeId);
props = new HashMap<QName, Serializable>((int)(sourceProps.size() * 1.3));
Map<QName, Serializable> 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));
}

View File

@@ -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),

View File

@@ -1681,6 +1681,16 @@
<columnname>type_qname_id</columnname>
</columnnames>
</index>
<index name="idx_alf_node_ver" unique="false">
<columnnames>
<columnname>version</columnname>
</columnnames>
</index>
<index name="idx_alf_node_txn" unique="false">
<columnnames>
<columnname>transaction_id</columnname>
</columnnames>
</index>
</indexes>
</table>
<table name="alf_node_aspects">
@@ -2630,6 +2640,18 @@
<index name="idx_alf_txn_ctms" unique="false">
<columnnames>
<columnname>commit_time_ms</columnname>
<columnname>id</columnname>
</columnnames>
</index>
<index name="idx_alf_txn_ctms_sc" unique="false">
<columnnames>
<columnname>commit_time_ms</columnname>
</columnnames>
</index>
<index name="idx_alf_txn_id_ctms" unique="false">
<columnnames>
<columnname>id</columnname>
<columnname>commit_time_ms</columnname>
</columnnames>
</index>
</indexes>

View File

@@ -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);

View File

@@ -1739,6 +1739,16 @@
<columnname>id</columnname>
</columnnames>
</index>
<index name="idx_alf_node_ver" unique="false">
<columnnames>
<columnname>version</columnname>
</columnnames>
</index>
<index name="idx_alf_node_txn" unique="false">
<columnnames>
<columnname>transaction_id</columnname>
</columnnames>
</index>
</indexes>
</table>
<table name="alf_node_aspects">
@@ -2703,6 +2713,17 @@
<columnname>id</columnname>
</columnnames>
</index>
<index name="idx_alf_txn_ctms_sc" unique="false">
<columnnames>
<columnname>commit_time_ms</columnname>
</columnnames>
</index>
<index name="idx_alf_txn_id_ctms" unique="false">
<columnnames>
<columnname>id</columnname>
<columnname>commit_time_ms</columnname>
</columnnames>
</index>
</indexes>
</table>
<table name="alf_usage_delta">

View File

@@ -55,6 +55,7 @@
<ref bean="patch.db-V5.2-remove-jbpm-tables-from-db" />
<ref bean="patch.db-V6.0-change-set-indexes" />
<ref bean="patch.db-V6.3-remove-alf_server-table" />
<ref bean="patch.db-V6.3-add-indexes-node-transaction" />
</list>
</property>
</bean>

View File

@@ -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'
);

View File

@@ -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'
);

View File

@@ -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.
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

View File

@@ -1408,4 +1408,16 @@
<value>classpath:alfresco/dbscripts/upgrade/6.3/${db.script.dialect}/remove-alf_server-table.sql</value>
</property>
</bean>
<bean id="patch.db-V6.3-add-indexes-node-transaction" class="org.alfresco.repo.admin.patch.impl.SchemaUpgradeScriptPatch" parent="basePatch">
<property name="id"><value>patch.db-V6.3-add-indexes-node-transaction</value></property>
<property name="description"><value>patch.db-V6.3-add-indexes-node-transaction.description</value></property>
<property name="fixesFromSchema"><value>0</value></property>
<property name="fixesToSchema"><value>14001</value></property>
<property name="targetSchema"><value>14002</value></property>
<property name="ignored"><value>${system.new-node-transaction-indexes.ignored}</value></property>
<property name="scriptUrl">
<value>classpath:alfresco/dbscripts/upgrade/6.3/${db.script.dialect}/add-indexes-node-transaction.sql</value>
</property>
</bean>
</beans>

View File

@@ -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
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

View File

@@ -153,18 +153,15 @@
</bean>
<bean id="solrAdminClient" class="org.alfresco.repo.solr.SOLRAdminClient" init-method="init">
<property name="solrHost" value="${solr.host}"/>
<property name="solrPort" value="${solr.port}"/>
<property name="solrsslPort" value="${solr.port.ssl}"/>
<property name="solrUser" value="${solr.solrUser}"/>
<property name="solrPassword" value="${solr.solrPassword}"/>
<property name="solrPingCronExpression" value="${solr.solrPingCronExpression}"/>
<property name="solrConnectTimeout" value="${solr.solrConnectTimeout}"/>
<property name="httpClientFactory" ref="solrHttpClientFactory"/>
<property name="baseUrl" value="${solr.baseUrl}"/>
<property name="scheduler">
<ref bean="searchSchedulerFactory" />
</property>
<property name="storeMappings">
<ref bean="solr4.store.mappings" />
</property>
<property name="useDynamicShardRegistration" value="${solr.useDynamicShardRegistration}" />
</bean>

View File

@@ -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<NodeRef> 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<Void>) () -> {
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<Void>) () -> {
@@ -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<Void>) () -> {
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<Void>) () -> {
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<FileInfo> 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<Void>) () -> {
Set<QName> aspect = new HashSet<>();
aspect.add(ContentModel.ASPECT_TEMPORARY);
nodeDAO.addNodeAspects(nodeDAO.getNodePair(folder).getFirst(), aspect);
fileFolderService.delete(folder);
return null;
}, false, true);
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
* #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<QName, Serializable> 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<QName, Serializable> 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<QName, Serializable> propertiesFromDB = Map.of(FIRST_PROPERTY, "value1");
when(nodeDAO.getNodeProperties(NODE_ID)).thenReturn(propertiesFromDB);
when(dictionaryService.getProperty(FIRST_PROPERTY)).thenReturn(null);
Map<QName, Serializable> 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 <index enabled="false"/> */
@Test
public void testGetProperties_propertySkippedIfIndexFalseSet()
{
Map<QName, Serializable> 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<QName, Serializable> properties = solrTrackingComponent.getProperties(NODE_ID);
assertEquals("Unexpected property when index enabled set to false.", emptyMap(), properties);
}
}