diff --git a/config/alfresco/patch/patch-services-context.xml b/config/alfresco/patch/patch-services-context.xml index 8c34c8b220..cb01bdddc5 100644 --- a/config/alfresco/patch/patch-services-context.xml +++ b/config/alfresco/patch/patch-services-context.xml @@ -51,7 +51,7 @@ - + @@ -2395,6 +2395,7 @@ + diff --git a/config/alfresco/public-services-context.xml b/config/alfresco/public-services-context.xml index 0171a798b3..580386d246 100644 --- a/config/alfresco/public-services-context.xml +++ b/config/alfresco/public-services-context.xml @@ -1509,5 +1509,32 @@ - + + + + + org.alfresco.repo.tenant.TenantAdminService + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + diff --git a/config/alfresco/rule-services-context.xml b/config/alfresco/rule-services-context.xml index deac3bf049..5823c48478 100644 --- a/config/alfresco/rule-services-context.xml +++ b/config/alfresco/rule-services-context.xml @@ -143,7 +143,7 @@ - + diff --git a/source/java/org/alfresco/repo/admin/patch/impl/AVMToADMRemoteStorePatch.java b/source/java/org/alfresco/repo/admin/patch/impl/AVMToADMRemoteStorePatch.java index e39e7e06d3..cf2959ac2d 100644 --- a/source/java/org/alfresco/repo/admin/patch/impl/AVMToADMRemoteStorePatch.java +++ b/source/java/org/alfresco/repo/admin/patch/impl/AVMToADMRemoteStorePatch.java @@ -96,6 +96,8 @@ public class AVMToADMRemoteStorePatch extends AbstractPatch private static final int MIGRATE_BATCH_THREADS = 4; private static final int MIGRATE_BATCH_SIZE = 250; + private static final String SEPARATOR = "@"; + private Map siteReferenceCache = null; private SortedMap paths; private SortedMap retryPaths; @@ -198,11 +200,14 @@ public class AVMToADMRemoteStorePatch extends AbstractPatch { this.retryPaths = new TreeMap(); + // get user names that will be used to RunAs and set permissions later + final String systemUser = AuthenticationUtil.getSystemUserName(); + // firstly retrieve all AVM paths and descriptors that we need to process // execute in a single transaction to retrieve the stateless object list - RetryingTransactionCallback work = new RetryingTransactionCallback() + RetryingTransactionCallback work = new RetryingTransactionCallback() { - public Void execute() throws Exception + public String execute() throws Exception { long start = System.currentTimeMillis(); paths = retrieveAVMPaths(); @@ -220,21 +225,17 @@ public class AVMToADMRemoteStorePatch extends AbstractPatch folderPath.add("user"); FileFolderUtil.makeFolders(fileFolderService, surfConfigRef, folderPath, ContentModel.TYPE_FOLDER); - return null; + // return the tenant system user name while in the txn + return tenantAdminService.getDomainUser(systemUser, tenantAdminService.getCurrentUserDomain()); } }; - this.transactionHelper.doInTransaction(work, false, true); + final String tenantSystemUser = this.transactionHelper.doInTransaction(work, false, true); try { // init the siteid to surf-config noderef cache this.siteReferenceCache = new ConcurrentHashMap(16384); - // get user names that will be used to RunAs and set permissions later - String systemUser = AuthenticationUtil.getSystemUserName(); - final String tenantSystemUser = this.tenantAdminService.getDomainUser( - systemUser, this.tenantAdminService.getCurrentUserDomain()); - // build a set of unique site names final Set sites = new HashSet(paths.size()); Matcher matcher; @@ -421,7 +422,7 @@ public class AVMToADMRemoteStorePatch extends AbstractPatch if (this.retryPaths.size() != 0) { logger.info("Retrying " + this.retryPaths.size() + " paths..."); - work = new RetryingTransactionCallback() + RetryingTransactionCallback retrywork = new RetryingTransactionCallback() { public Void execute() throws Exception { @@ -432,7 +433,7 @@ public class AVMToADMRemoteStorePatch extends AbstractPatch return null; } }; - this.transactionHelper.doInTransaction(work, false, true); + this.transactionHelper.doInTransaction(retrywork, false, true); } logger.info("Migrated: " + this.paths.size() + " AVM nodes to DM in " + (System.currentTimeMillis()-start) + "ms"); @@ -650,7 +651,16 @@ public class AVMToADMRemoteStorePatch extends AbstractPatch SortedMap paths = new TreeMap(); - String avmPath = this.avmStore + ":" + this.avmRootPath; + String avmPath; + if (this.tenantAdminService.getCurrentUserDomain().isEmpty()) + { + avmPath = this.avmStore + ":" + this.avmRootPath; + } + else + { + avmPath = SEPARATOR + this.tenantAdminService.getCurrentUserDomain() + SEPARATOR + this.avmStore + ":" + this.avmRootPath; + } + AVMNodeDescriptor node = this.avmService.lookup(-1, avmPath); if (node != null) { diff --git a/source/java/org/alfresco/repo/rule/MiscellaneousRulesTest.java b/source/java/org/alfresco/repo/rule/MiscellaneousRulesTest.java index c451a76537..6d6a8f1b01 100644 --- a/source/java/org/alfresco/repo/rule/MiscellaneousRulesTest.java +++ b/source/java/org/alfresco/repo/rule/MiscellaneousRulesTest.java @@ -25,6 +25,7 @@ import static org.junit.Assert.assertNotNull; import java.io.Serializable; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -36,6 +37,7 @@ import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.ActionService; +import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.CopyService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; @@ -50,6 +52,8 @@ import org.alfresco.util.test.junitrules.ApplicationContextInit; import org.alfresco.util.test.junitrules.RunAsFullyAuthenticatedRule; import org.alfresco.util.test.junitrules.TemporaryNodes; import org.alfresco.util.test.junitrules.TemporarySites; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.junit.Before; import org.junit.BeforeClass; import org.junit.ClassRule; @@ -68,6 +72,8 @@ import org.springframework.context.ApplicationContext; */ public class MiscellaneousRulesTest { + private static final Log log = LogFactory.getLog(MiscellaneousRulesTest.class); + // Static JUnit Rules public static ApplicationContextInit APP_CTXT_INIT = new ApplicationContextInit(); public static AlfrescoPerson TEST_USER = new AlfrescoPerson(APP_CTXT_INIT); @@ -305,4 +311,74 @@ public class MiscellaneousRulesTest } }); } + + @Test public void alf13192_rulesFromFirstFolderMoveToSecondWhenDeleteFirstFolder() throws Exception + { + final NodeRef testSiteDocLib = TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + return SITE_SERVICE.getContainer(testSite.getShortName(), SiteService.DOCUMENT_LIBRARY); + } + }); + assertNotNull("Null doclib", testSiteDocLib); + + final NodeRef folder1 = testNodes.createFolder(testSiteDocLib, "folder 1", TEST_USER.getUsername()); + final NodeRef folder2 = testNodes.createFolder(testSiteDocLib, "folder 2", TEST_USER.getUsername()); + + // Put a rule on folder1. + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + // Clashes with the JUnit annotation @Rule + org.alfresco.service.cmr.rule.Rule rule = new org.alfresco.service.cmr.rule.Rule(); + rule.setRuleType(RuleType.OUTBOUND); + rule.applyToChildren(false); + rule.setRuleDisabled(false); + rule.setTitle("Copy to folder2"); + rule.setExecuteAsynchronously(false); + + Map params = new HashMap(); + params.put(CopyActionExecuter.PARAM_DESTINATION_FOLDER, folder2); + Action copyAction = ACTION_SERVICE.createAction("copy", params); + rule.setAction(copyAction); + + RULE_SERVICE.saveRule(folder1, rule); + + // While we're here, let's log some information about the rules. + List ruleFolders = NODE_SERVICE.getChildAssocs(folder1, RuleModel.ASSOC_RULE_FOLDER, RuleModel.ASSOC_RULE_FOLDER); + assertEquals(1, ruleFolders.size()); + log.debug("Rule SystemFolder noderef is " + ruleFolders.get(0).getChildRef()); + + return null; + } + }); + + // Now delete folder1. + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + log.debug("About to delete the ruled folder: " + folder1); + + NODE_SERVICE.deleteNode(folder1); + + return null; + } + }); + + + // folder2 should have no rules-related elements + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + assertFalse(RULE_SERVICE.hasRules(folder2)); + assertFalse(NODE_SERVICE.hasAspect(folder2, RuleModel.ASPECT_RULES)); + + return null; + } + }); + } } diff --git a/source/java/org/alfresco/repo/rule/ruletrigger/RuleTriggerAbstractBase.java b/source/java/org/alfresco/repo/rule/ruletrigger/RuleTriggerAbstractBase.java index 45c5427d20..778f7f08b1 100644 --- a/source/java/org/alfresco/repo/rule/ruletrigger/RuleTriggerAbstractBase.java +++ b/source/java/org/alfresco/repo/rule/ruletrigger/RuleTriggerAbstractBase.java @@ -53,6 +53,7 @@ public abstract class RuleTriggerAbstractBase implements RuleTrigger IGNORE_TYPES.add(ContentModel.TYPE_THUMBNAIL); // Workaround to prevent rules running on cm:rating nodes (which happened for 'liked' folders ALF-8308 & ALF-8382) IGNORE_TYPES.add(ContentModel.TYPE_RATING); + IGNORE_TYPES.add(ContentModel.TYPE_SYSTEM_FOLDER); IGNORE_ASPECTS = new HashSet(13); IGNORE_ASPECTS.add(ContentModel.ASPECT_TEMPORARY); diff --git a/source/java/org/alfresco/repo/search/impl/lucene/SolrJSONResultSet.java b/source/java/org/alfresco/repo/search/impl/lucene/SolrJSONResultSet.java index db68d9f1d9..e70bc5d3dd 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/SolrJSONResultSet.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/SolrJSONResultSet.java @@ -49,6 +49,8 @@ public class SolrJSONResultSet implements ResultSet private ArrayList> page; + private ArrayList refs; + private ResultSetMetaData rsmd; private Long status; @@ -89,18 +91,23 @@ public class SolrJSONResultSet implements ResultSet int numDocs = docs.length(); page = new ArrayList>(numDocs); + refs = new ArrayList(numDocs); for(int i = 0; i < numDocs; i++) { JSONObject doc = docs.getJSONObject(i); JSONArray dbids = doc.getJSONArray("DBID"); Long dbid = dbids.getLong(0); Float score = Float.valueOf(doc.getString("score")); - page.add(new Pair(dbid, score)); - for(Iterator it = doc.keys(); it.hasNext(); /* */) + NodeRef nodeRef = nodeService.getNodeRef(dbid); + + if(nodeRef != null) { - String key = (String)it.next(); + page.add(new Pair(dbid, score)); + refs.add(nodeRef); } + + } if(json.has("facet_counts")) @@ -212,16 +219,7 @@ public class SolrJSONResultSet implements ResultSet @Override public NodeRef getNodeRef(int n) { - // TODO: lost nodes? - NodeRef nodeRef = nodeService.getNodeRef(page.get(n).getFirst()); - if(nodeRef != null) - { - return nodeRef; - } - else - { - return new NodeRef(new StoreRef("missing", "missing"), "missing"); - } + return refs.get(n); } /* @@ -231,12 +229,7 @@ public class SolrJSONResultSet implements ResultSet @Override public List getNodeRefs() { - ArrayList refs = new ArrayList(page.size()); - for(int i = 0; i < page.size(); i++ ) - { - refs.add( getNodeRef(i)); - } - return refs; + return Collections.unmodifiableList(refs); } /* diff --git a/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java b/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java index 6aadca05f0..7f48425a01 100644 --- a/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java +++ b/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java @@ -73,6 +73,7 @@ import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.invitation.InvitationException; import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.InvalidNodeRefException; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; @@ -1282,11 +1283,14 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per final List personInfos = new ArrayList(nodeRefs.size()); for (NodeRef nodeRef : nodeRefs) { - Map props = nodeService.getProperties(nodeRef); - personInfos.add(new PersonInfo(nodeRef, - (String)props.get(ContentModel.PROP_USERNAME), - (String)props.get(ContentModel.PROP_FIRSTNAME), - (String)props.get(ContentModel.PROP_LASTNAME))); + if (nodeService.exists(nodeRef)) + { + Map props = nodeService.getProperties(nodeRef); + personInfos.add(new PersonInfo(nodeRef, + (String)props.get(ContentModel.PROP_USERNAME), + (String)props.get(ContentModel.PROP_FIRSTNAME), + (String)props.get(ContentModel.PROP_LASTNAME))); + } } return new PagingResults()