mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
Merged V3.2 to HEAD
17475: ETHREEOH-3295: Fix to AuthorityMigrationPatch - Forces transaction retry if worker thread reaches child authority before a parent authority - Tested on Kev's 3.1.1 repository with ~20,000 bulk loaded users and ~2,000 Share sites - Now completes in 5 minutes as opposed to 45 17461: ETHREEOH-3268: Added MutableAuthenticationService.isAuthenticationCreationAllowed () to allow conditional display of external user invitation UI 17450: ETHREEOH-2762: Correction to previous fix. Do not generate new name when working copy copied back on check in. 17440: ETHREEOH-3295: Fixed logging in FixNameCrcValuesPatch 17439: ETHREEOH-2762: Improved behaviour when a working copy is copied - Working copy aspect already removed the working copy aspect on copy - Now derives a new name from the node checked out from and a UUID, preserving the extension 17438: ETHREEOH-2690: Fix sequencing of jgroups system property setting - declared dependency between internalEHCacheManager and jgroupsPropertySetter 17436: ETHREEOH-3295: Further performance improvements to AuthorityMigrationPatch - authority created at same time as all its parent associations to save lots of reindexing, as per LDAP sync - multi-threaded BatchProcessor (as used by LDAP sync, FixNameCrcValuesPatch) used to process work in 2 threads in batches of 20, report progress every 100 entries and handle transaction retries - BatchProcessor now promoted to its own package 17394: Fix for license issue in local enterprise builds. - Replace Community with Enterprise in version.properties during enterprise war building 17365: ETHREEOH-3229: Visited and fixed all SearchService result set leaks 17362: ETHREEOH-3254: Eliminate needless ping to LDAP server in LDAPAuthenticationComponentImpl.implementationAllowsGuestLogin() 17348: ETHREEOH-3003: Fix NPE in Hyperic when LicenseDescriptor has null fields 17316: Merged V3.1 to V3.2 17315: ETHREEOH-3092: PersonService won't let you create duplicate persons anymore. 17314: ETHREEOH-3158: Fix RepoServerMgmt to work with external authentication methods - AuthenticationService.getCurrentTicket / getNewTicket now call pre authentication check before issuing a new ticket, thus still allowing ticket enforcement when external authentication is in use. 17312: ETHREEOH-3219: Enable resolution of JMX server password file path on JBoss 5 17299: Merged V3.2 to V3.1 (Record only) 17297: ETHREEOH-1593: Changed name of username cookie and fixed login.jsp to decode it properly 17248: ETHREEOH-1593: alfUser cookie value should be base 64 encoded to allow for non-ASCII characters 17297: ETHREEOH-1593: Changed name of username cookie and fixed login.jsp to decode it properly - thanks Kev! 17292: ETHREEOH-1842: Ticket association with HttpSession IDs tracked so that we don't invalidate a ticket in use by multiple sessions prematurely - AuthenticationService validate, getCurrentTicket, etc. methods now take optional sessionId arguments 17269: Fix failing unit test - reinstate original behaviour of AbstractChainingAuthenticationService.getAuthenticationEnabled() 17268: Fix InvitationService - Runs as system to do privileged AuthenticationService actions git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@18105 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -58,10 +58,17 @@ public class AVMLockingPatch extends AbstractPatch
|
||||
{
|
||||
ResultSet results =
|
||||
searchService.query(new StoreRef(STORE), "lucene", "TYPE:\"wca:webfolder\"");
|
||||
for (NodeRef nodeRef : results.getNodeRefs())
|
||||
try
|
||||
{
|
||||
String webProject = (String)nodeService.getProperty(nodeRef, WCMAppModel.PROP_AVMSTORE);
|
||||
fLockingService.addWebProject(webProject);
|
||||
for (NodeRef nodeRef : results.getNodeRefs())
|
||||
{
|
||||
String webProject = (String)nodeService.getProperty(nodeRef, WCMAppModel.PROP_AVMSTORE);
|
||||
fLockingService.addWebProject(webProject);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
results.close();
|
||||
}
|
||||
return I18NUtil.getMessage(MSG_SUCCESS);
|
||||
}
|
||||
|
@@ -59,65 +59,72 @@ public class ActionRuleDecouplingPatch extends AbstractPatch
|
||||
// Get all the node's of type rule in the store
|
||||
int updateCount = 0;
|
||||
ResultSet resultSet = this.searchService.query(storeRef, "lucene", "TYPE:\"" + RuleModel.TYPE_RULE + "\"");
|
||||
for (NodeRef origRuleNodeRef : resultSet.getNodeRefs())
|
||||
try
|
||||
{
|
||||
// Check that this rule need updated
|
||||
if (!this.nodeService.exists(origRuleNodeRef))
|
||||
for (NodeRef origRuleNodeRef : resultSet.getNodeRefs())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Map<QName, Serializable> origProperties = this.nodeService.getProperties(origRuleNodeRef);
|
||||
if (origProperties.containsKey(RuleModel.PROP_EXECUTE_ASYNC) == false)
|
||||
{
|
||||
// 1) Change the type of the rule to be a composite action
|
||||
this.nodeService.setType(origRuleNodeRef, ActionModel.TYPE_COMPOSITE_ACTION);
|
||||
|
||||
// 2) Create a new rule node
|
||||
ChildAssociationRef parentRef = this.nodeService.getPrimaryParent(origRuleNodeRef);
|
||||
NodeRef newRuleNodeRef = this.nodeService.createNode(
|
||||
parentRef.getParentRef(),
|
||||
parentRef.getTypeQName(),
|
||||
parentRef.getQName(),
|
||||
RuleModel.TYPE_RULE).getChildRef();
|
||||
|
||||
// 3) Move the origional rule under the new rule
|
||||
this.nodeService.moveNode(
|
||||
origRuleNodeRef,
|
||||
newRuleNodeRef,
|
||||
RuleModel.ASSOC_ACTION,
|
||||
RuleModel.ASSOC_ACTION);
|
||||
|
||||
// 4) Move the various properties from the origional, onto the new rule
|
||||
Map<QName, Serializable> newProperties = this.nodeService.getProperties(newRuleNodeRef);
|
||||
|
||||
// Set the rule type, execute async and applyToChildren properties on the rule
|
||||
Serializable ruleType = origProperties.get(RuleModel.PROP_RULE_TYPE);
|
||||
origProperties.remove(RuleModel.PROP_RULE_TYPE);
|
||||
newProperties.put(RuleModel.PROP_RULE_TYPE, ruleType);
|
||||
Serializable executeAsync = origProperties.get(ActionModel.PROP_EXECUTE_ASYNCHRONOUSLY);
|
||||
origProperties.remove(ActionModel.PROP_EXECUTE_ASYNCHRONOUSLY);
|
||||
newProperties.put(RuleModel.PROP_EXECUTE_ASYNC, executeAsync);
|
||||
Serializable applyToChildren = origProperties.get(RuleModel.PROP_APPLY_TO_CHILDREN);
|
||||
origProperties.remove(RuleModel.PROP_APPLY_TO_CHILDREN);
|
||||
newProperties.put(RuleModel.PROP_APPLY_TO_CHILDREN, applyToChildren);
|
||||
origProperties.remove(QName.createQName(RuleModel.RULE_MODEL_URI, "owningNodeRef"));
|
||||
|
||||
// Move the action and description values from the composite action onto the rule
|
||||
Serializable title = origProperties.get(ActionModel.PROP_ACTION_TITLE);
|
||||
origProperties.remove(ActionModel.PROP_ACTION_TITLE);
|
||||
Serializable description = origProperties.get(ActionModel.PROP_ACTION_DESCRIPTION);
|
||||
origProperties.remove(ActionModel.PROP_ACTION_DESCRIPTION);
|
||||
newProperties.put(ContentModel.PROP_TITLE, title);
|
||||
newProperties.put(ContentModel.PROP_DESCRIPTION, description);
|
||||
|
||||
// Set the updated property values
|
||||
this.nodeService.setProperties(origRuleNodeRef, origProperties);
|
||||
this.nodeService.setProperties(newRuleNodeRef, newProperties);
|
||||
|
||||
// Increment the update count
|
||||
updateCount++;
|
||||
// Check that this rule need updated
|
||||
if (!this.nodeService.exists(origRuleNodeRef))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Map<QName, Serializable> origProperties = this.nodeService.getProperties(origRuleNodeRef);
|
||||
if (origProperties.containsKey(RuleModel.PROP_EXECUTE_ASYNC) == false)
|
||||
{
|
||||
// 1) Change the type of the rule to be a composite action
|
||||
this.nodeService.setType(origRuleNodeRef, ActionModel.TYPE_COMPOSITE_ACTION);
|
||||
|
||||
// 2) Create a new rule node
|
||||
ChildAssociationRef parentRef = this.nodeService.getPrimaryParent(origRuleNodeRef);
|
||||
NodeRef newRuleNodeRef = this.nodeService.createNode(
|
||||
parentRef.getParentRef(),
|
||||
parentRef.getTypeQName(),
|
||||
parentRef.getQName(),
|
||||
RuleModel.TYPE_RULE).getChildRef();
|
||||
|
||||
// 3) Move the origional rule under the new rule
|
||||
this.nodeService.moveNode(
|
||||
origRuleNodeRef,
|
||||
newRuleNodeRef,
|
||||
RuleModel.ASSOC_ACTION,
|
||||
RuleModel.ASSOC_ACTION);
|
||||
|
||||
// 4) Move the various properties from the origional, onto the new rule
|
||||
Map<QName, Serializable> newProperties = this.nodeService.getProperties(newRuleNodeRef);
|
||||
|
||||
// Set the rule type, execute async and applyToChildren properties on the rule
|
||||
Serializable ruleType = origProperties.get(RuleModel.PROP_RULE_TYPE);
|
||||
origProperties.remove(RuleModel.PROP_RULE_TYPE);
|
||||
newProperties.put(RuleModel.PROP_RULE_TYPE, ruleType);
|
||||
Serializable executeAsync = origProperties.get(ActionModel.PROP_EXECUTE_ASYNCHRONOUSLY);
|
||||
origProperties.remove(ActionModel.PROP_EXECUTE_ASYNCHRONOUSLY);
|
||||
newProperties.put(RuleModel.PROP_EXECUTE_ASYNC, executeAsync);
|
||||
Serializable applyToChildren = origProperties.get(RuleModel.PROP_APPLY_TO_CHILDREN);
|
||||
origProperties.remove(RuleModel.PROP_APPLY_TO_CHILDREN);
|
||||
newProperties.put(RuleModel.PROP_APPLY_TO_CHILDREN, applyToChildren);
|
||||
origProperties.remove(QName.createQName(RuleModel.RULE_MODEL_URI, "owningNodeRef"));
|
||||
|
||||
// Move the action and description values from the composite action onto the rule
|
||||
Serializable title = origProperties.get(ActionModel.PROP_ACTION_TITLE);
|
||||
origProperties.remove(ActionModel.PROP_ACTION_TITLE);
|
||||
Serializable description = origProperties.get(ActionModel.PROP_ACTION_DESCRIPTION);
|
||||
origProperties.remove(ActionModel.PROP_ACTION_DESCRIPTION);
|
||||
newProperties.put(ContentModel.PROP_TITLE, title);
|
||||
newProperties.put(ContentModel.PROP_DESCRIPTION, description);
|
||||
|
||||
// Set the updated property values
|
||||
this.nodeService.setProperties(origRuleNodeRef, origProperties);
|
||||
this.nodeService.setProperties(newRuleNodeRef, newProperties);
|
||||
|
||||
// Increment the update count
|
||||
updateCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
resultSet.close();
|
||||
}
|
||||
|
||||
// Done
|
||||
String msg = I18NUtil.getMessage(MSG_RESULT, updateCount);
|
||||
|
@@ -24,31 +24,37 @@
|
||||
*/
|
||||
package org.alfresco.repo.admin.patch.impl;
|
||||
|
||||
import java.sql.BatchUpdateException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.springframework.extensions.surf.util.I18NUtil;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.admin.patch.AbstractPatch;
|
||||
import org.alfresco.repo.admin.patch.PatchExecuter;
|
||||
import org.alfresco.repo.batch.BatchProcessor;
|
||||
import org.alfresco.repo.importer.ImporterBootstrap;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||
import org.alfresco.repo.security.authority.UnknownAuthorityException;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
|
||||
import org.alfresco.service.cmr.rule.RuleService;
|
||||
import org.alfresco.service.cmr.security.AuthorityService;
|
||||
import org.alfresco.service.cmr.security.AuthorityType;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.service.namespace.RegexQNamePattern;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
|
||||
/**
|
||||
* Migrates authority information previously stored in the user store to the spaces store, using the new structure used
|
||||
@@ -56,14 +62,13 @@ import org.apache.commons.logging.LogFactory;
|
||||
*
|
||||
* @author dward
|
||||
*/
|
||||
public class AuthorityMigrationPatch extends AbstractPatch
|
||||
public class AuthorityMigrationPatch extends AbstractPatch implements ApplicationEventPublisherAware
|
||||
{
|
||||
/** The title we give to the batch process in progress messages / JMX. */
|
||||
private static final String MSG_PROCESS_NAME = "patch.authorityMigration.process.name";
|
||||
|
||||
/** Progress message for authorities. */
|
||||
private static final String MSG_PROGRESS_AUTHORITY = "patch.authorityMigration.progress.authority";
|
||||
|
||||
/** Progress message for associations. */
|
||||
private static final String MSG_PROGRESS_ASSOC = "patch.authorityMigration.progress.assoc";
|
||||
/** The warning message when a 'dangling' assoc is found that can't be created */
|
||||
private static final String MSG_WARNING_INVALID_ASSOC = "patch.authorityMigration.warning.assoc";
|
||||
|
||||
/** Success message. */
|
||||
private static final String MSG_SUCCESS = "patch.authorityMigration.result";
|
||||
@@ -81,15 +86,18 @@ public class AuthorityMigrationPatch extends AbstractPatch
|
||||
/** The old authority members property. */
|
||||
private static final QName PROP_MEMBERS = QName.createQName(ContentModel.USER_MODEL_URI, "members");
|
||||
|
||||
/** The number of items to create in a transaction. */
|
||||
private static final int BATCH_SIZE = 10;
|
||||
|
||||
/** The authority service. */
|
||||
private AuthorityService authorityService;
|
||||
|
||||
/** The rule service. */
|
||||
private RuleService ruleService;
|
||||
|
||||
/** The user bootstrap. */
|
||||
private ImporterBootstrap userBootstrap;
|
||||
|
||||
/** The application event publisher. */
|
||||
private ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
/**
|
||||
* Sets the authority service.
|
||||
*
|
||||
@@ -101,6 +109,17 @@ public class AuthorityMigrationPatch extends AbstractPatch
|
||||
this.authorityService = authorityService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the rule service.
|
||||
*
|
||||
* @param ruleService
|
||||
* the rule service
|
||||
*/
|
||||
public void setRuleService(RuleService ruleService)
|
||||
{
|
||||
this.ruleService = ruleService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user bootstrap.
|
||||
*
|
||||
@@ -112,6 +131,17 @@ public class AuthorityMigrationPatch extends AbstractPatch
|
||||
this.userBootstrap = userBootstrap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the application event publisher.
|
||||
*
|
||||
* @param applicationEventPublisher
|
||||
* the application event publisher
|
||||
*/
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher)
|
||||
{
|
||||
this.applicationEventPublisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively retrieves the authorities under the given node and their associations.
|
||||
*
|
||||
@@ -122,14 +152,14 @@ public class AuthorityMigrationPatch extends AbstractPatch
|
||||
* the node to find authorities below
|
||||
* @param authoritiesToCreate
|
||||
* the authorities to create
|
||||
* @param childAssocs
|
||||
* the child associations
|
||||
* @param parentAssocs
|
||||
* the parent associations
|
||||
* @return count of the number of parent associations
|
||||
*/
|
||||
private void retrieveAuthorities(String parentAuthority, NodeRef nodeRef, Map<String, String> authoritiesToCreate,
|
||||
Map<String, Set<String>> childAssocs)
|
||||
private int retrieveAuthorities(String parentAuthority, NodeRef nodeRef, Map<String, String> authoritiesToCreate,
|
||||
Map<String, Set<String>> parentAssocs)
|
||||
{
|
||||
// If we have a parent authority, prepare a list for recording its children
|
||||
Set<String> children = parentAuthority == null ? null : childAssocs.get(parentAuthority);
|
||||
int assocCount = 0;
|
||||
|
||||
// Process all children
|
||||
List<ChildAssociationRef> cars = this.nodeService.getChildAssocs(nodeRef);
|
||||
@@ -144,20 +174,20 @@ public class AuthorityMigrationPatch extends AbstractPatch
|
||||
authoritiesToCreate.put(authorityName, DefaultTypeConverter.INSTANCE.convert(String.class, this.nodeService
|
||||
.getProperty(current, AuthorityMigrationPatch.PROP_AUTHORITY_DISPLAY_NAME)));
|
||||
|
||||
// If we have a parent, remember the child association
|
||||
// Record the parent association (or empty set if this is a root)
|
||||
Set<String> parents = parentAssocs.get(authorityName);
|
||||
if (parents == null)
|
||||
{
|
||||
parents = new TreeSet<String>();
|
||||
parentAssocs.put(authorityName, parents);
|
||||
}
|
||||
if (parentAuthority != null)
|
||||
{
|
||||
if (children == null)
|
||||
{
|
||||
children = new TreeSet<String>();
|
||||
childAssocs.put(parentAuthority, children);
|
||||
}
|
||||
children.add(authorityName);
|
||||
parents.add(parentAuthority);
|
||||
assocCount++;
|
||||
}
|
||||
|
||||
// loop over properties
|
||||
Set<String> propChildren = childAssocs.get(authorityName);
|
||||
|
||||
Collection<String> members = DefaultTypeConverter.INSTANCE.getCollection(String.class, this.nodeService
|
||||
.getProperty(current, AuthorityMigrationPatch.PROP_MEMBERS));
|
||||
if (members != null)
|
||||
@@ -167,119 +197,89 @@ public class AuthorityMigrationPatch extends AbstractPatch
|
||||
// Believe it or not, some old authorities have null members in them!
|
||||
if (user != null)
|
||||
{
|
||||
if (propChildren == null)
|
||||
Set<String> propParents = parentAssocs.get(user);
|
||||
if (propParents == null)
|
||||
{
|
||||
propChildren = new TreeSet<String>();
|
||||
childAssocs.put(authorityName, propChildren);
|
||||
propParents = new TreeSet<String>();
|
||||
parentAssocs.put(user, propParents);
|
||||
}
|
||||
propChildren.add(user);
|
||||
propParents.add(authorityName);
|
||||
assocCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
retrieveAuthorities(authorityName, current, authoritiesToCreate, childAssocs);
|
||||
assocCount += retrieveAuthorities(authorityName, current, authoritiesToCreate, parentAssocs);
|
||||
}
|
||||
return assocCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the authorities.
|
||||
* Migrates the authorities and their associations.
|
||||
*
|
||||
* @param authoritiesToCreate
|
||||
* the authorities to create
|
||||
* @param parentAssocs
|
||||
* the parent associations to create (if they don't exist already)
|
||||
* @return the number of authorities migrated
|
||||
*/
|
||||
private int migrateAuthorities(Map<String, String> authoritiesToCreate)
|
||||
private void migrateAuthorities(final Map<String, String> authoritiesToCreate, Map<String, Set<String>> parentAssocs)
|
||||
{
|
||||
int processedCount = 0;
|
||||
final Iterator<Map.Entry<String, String>> i = authoritiesToCreate.entrySet().iterator();
|
||||
RetryingTransactionHelper retryingTransactionHelper = this.transactionService.getRetryingTransactionHelper();
|
||||
|
||||
// Process batches in separate transactions for maximum performance
|
||||
while (i.hasNext())
|
||||
BatchProcessor.Worker<Map.Entry<String, Set<String>>> worker = new BatchProcessor.Worker<Map.Entry<String, Set<String>>>()
|
||||
{
|
||||
processedCount += retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback<Integer>()
|
||||
|
||||
public String getIdentifier(Entry<String, Set<String>> entry)
|
||||
{
|
||||
public Integer execute() throws Throwable
|
||||
{
|
||||
int processedCount = 0;
|
||||
do
|
||||
{
|
||||
Map.Entry<String, String> authority = i.next();
|
||||
String authorityName = authority.getKey();
|
||||
boolean existed = AuthorityMigrationPatch.this.authorityService.authorityExists(authorityName);
|
||||
if (existed)
|
||||
{
|
||||
i.remove();
|
||||
}
|
||||
else
|
||||
{
|
||||
AuthorityMigrationPatch.this.authorityService.createAuthority(AuthorityType
|
||||
.getAuthorityType(authorityName), AuthorityMigrationPatch.this.authorityService
|
||||
.getShortName(authorityName), authority.getValue(), null);
|
||||
processedCount++;
|
||||
}
|
||||
}
|
||||
while (processedCount < AuthorityMigrationPatch.BATCH_SIZE && i.hasNext());
|
||||
return processedCount;
|
||||
}
|
||||
}, false, true);
|
||||
return entry.getKey();
|
||||
}
|
||||
|
||||
// Report progress
|
||||
AuthorityMigrationPatch.progress_logger.info(I18NUtil.getMessage(
|
||||
AuthorityMigrationPatch.MSG_PROGRESS_AUTHORITY, processedCount));
|
||||
}
|
||||
return processedCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the group associations.
|
||||
*
|
||||
* @param authoritiesCreated
|
||||
* the authorities created
|
||||
* @param childAssocs
|
||||
* the child associations
|
||||
* @return the number of associations migrated
|
||||
*/
|
||||
private int migrateAssocs(final Map<String, String> authoritiesCreated, Map<String, Set<String>> childAssocs)
|
||||
{
|
||||
int processedCount = 0;
|
||||
final Iterator<Map.Entry<String, Set<String>>> j = childAssocs.entrySet().iterator();
|
||||
RetryingTransactionHelper retryingTransactionHelper = this.transactionService.getRetryingTransactionHelper();
|
||||
|
||||
// Process batches in separate transactions for maximum performance
|
||||
while (j.hasNext())
|
||||
{
|
||||
processedCount += retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback<Integer>()
|
||||
public void process(Entry<String, Set<String>> authority) throws Throwable
|
||||
{
|
||||
public Integer execute() throws Throwable
|
||||
String authorityName = authority.getKey();
|
||||
boolean existed = AuthorityMigrationPatch.this.authorityService.authorityExists(authorityName);
|
||||
Set<String> knownParents;
|
||||
if (existed)
|
||||
{
|
||||
int processedCount = 0;
|
||||
do
|
||||
{
|
||||
Map.Entry<String, Set<String>> childAssoc = j.next();
|
||||
String parentAuthority = childAssoc.getKey();
|
||||
Set<String> knownChildren = authoritiesCreated.containsKey(parentAuthority) ? Collections
|
||||
.<String> emptySet() : AuthorityMigrationPatch.this.authorityService
|
||||
.getContainedAuthorities(AuthorityType.GROUP, parentAuthority, true);
|
||||
for (String authorityName : childAssoc.getValue())
|
||||
{
|
||||
if (!knownChildren.contains(authorityName))
|
||||
{
|
||||
AuthorityMigrationPatch.this.authorityService.addAuthority(parentAuthority,
|
||||
authorityName);
|
||||
processedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (processedCount < AuthorityMigrationPatch.BATCH_SIZE && j.hasNext());
|
||||
return processedCount;
|
||||
knownParents = AuthorityMigrationPatch.this.authorityService.getContainingAuthorities(
|
||||
AuthorityType.GROUP, authorityName, true);
|
||||
}
|
||||
}, false, true);
|
||||
|
||||
// Report progress
|
||||
AuthorityMigrationPatch.progress_logger.info(I18NUtil.getMessage(
|
||||
AuthorityMigrationPatch.MSG_PROGRESS_ASSOC, processedCount));
|
||||
}
|
||||
return processedCount;
|
||||
else
|
||||
{
|
||||
knownParents = Collections.emptySet();
|
||||
AuthorityType authorityType = AuthorityType.getAuthorityType(authorityName);
|
||||
// We have associations to a non-existent authority. If it is a user, just skip it because it must
|
||||
// have been a 'dangling' reference
|
||||
if (authorityType == AuthorityType.USER)
|
||||
{
|
||||
AuthorityMigrationPatch.progress_logger.warn(I18NUtil.getMessage(
|
||||
AuthorityMigrationPatch.MSG_WARNING_INVALID_ASSOC, authorityName));
|
||||
return;
|
||||
}
|
||||
AuthorityMigrationPatch.this.authorityService.createAuthority(authorityType,
|
||||
AuthorityMigrationPatch.this.authorityService.getShortName(authorityName),
|
||||
authoritiesToCreate.get(authorityName), null);
|
||||
}
|
||||
Set<String> parentAssocsToCreate = authority.getValue();
|
||||
parentAssocsToCreate.removeAll(knownParents);
|
||||
if (!parentAssocsToCreate.isEmpty())
|
||||
{
|
||||
try
|
||||
{
|
||||
AuthorityMigrationPatch.this.authorityService.addAuthority(parentAssocsToCreate, authorityName);
|
||||
}
|
||||
catch (UnknownAuthorityException e)
|
||||
{
|
||||
// Let's force a transaction retry if a parent doesn't exist. It may be because we are waiting
|
||||
// for another worker thread to create it
|
||||
throw new BatchUpdateException().initCause(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
// Migrate using 2 threads, 20 authorities per transaction. Log every 100 entries.
|
||||
new BatchProcessor<Map.Entry<String, Set<String>>>(AuthorityMigrationPatch.progress_logger,
|
||||
this.transactionService.getRetryingTransactionHelper(), this.ruleService,
|
||||
this.applicationEventPublisher, parentAssocs.entrySet(), I18NUtil
|
||||
.getMessage(AuthorityMigrationPatch.MSG_PROCESS_NAME), 100, 2, 20).process(worker, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -323,18 +323,71 @@ public class AuthorityMigrationPatch extends AbstractPatch
|
||||
@Override
|
||||
protected String applyInternal() throws Exception
|
||||
{
|
||||
int authorities = 0;
|
||||
int assocs = 0;
|
||||
NodeRef authorityContainer = getAuthorityContainer();
|
||||
int authorities = 0, assocs = 0;
|
||||
if (authorityContainer != null)
|
||||
{
|
||||
// Crawl the old tree of authorities
|
||||
Map<String, String> authoritiesToCreate = new TreeMap<String, String>();
|
||||
Map<String, Set<String>> childAssocs = new TreeMap<String, Set<String>>();
|
||||
retrieveAuthorities(null, authorityContainer, authoritiesToCreate, childAssocs);
|
||||
authorities = migrateAuthorities(authoritiesToCreate);
|
||||
assocs = migrateAssocs(authoritiesToCreate, childAssocs);
|
||||
Map<String, Set<String>> parentAssocs = new TreeMap<String, Set<String>>();
|
||||
assocs = retrieveAuthorities(null, authorityContainer, authoritiesToCreate, parentAssocs);
|
||||
|
||||
// Sort the group associations in parent-first order (root groups first)
|
||||
Map<String, Set<String>> sortedParentAssocs = new LinkedHashMap<String, Set<String>>(
|
||||
parentAssocs.size() * 2);
|
||||
List<String> authorityPath = new ArrayList<String>(5);
|
||||
for (String authority : parentAssocs.keySet())
|
||||
{
|
||||
authorityPath.add(authority);
|
||||
visitGroupAssociations(authorityPath, parentAssocs, sortedParentAssocs);
|
||||
authorityPath.clear();
|
||||
}
|
||||
|
||||
// Recreate the authorities and their associations in parent-first order
|
||||
migrateAuthorities(authoritiesToCreate, sortedParentAssocs);
|
||||
authorities = authoritiesToCreate.size();
|
||||
}
|
||||
// build the result message
|
||||
return I18NUtil.getMessage(AuthorityMigrationPatch.MSG_SUCCESS, authorities, assocs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits the last authority in the given list by recursively visiting its parents in associationsOld and then
|
||||
* adding the authority to associationsNew. Used to sort associationsOld into 'parent-first' order.
|
||||
*
|
||||
* @param authorityPath
|
||||
* The authority to visit, preceeded by all its descendants. Allows detection of cyclic child
|
||||
* associations.
|
||||
* @param associationsOld
|
||||
* the association map to sort
|
||||
* @param associationsNew
|
||||
* the association map to add to in parent-first order
|
||||
*/
|
||||
private static void visitGroupAssociations(List<String> authorityPath, Map<String, Set<String>> associationsOld,
|
||||
Map<String, Set<String>> associationsNew)
|
||||
{
|
||||
String authorityName = authorityPath.get(authorityPath.size() - 1);
|
||||
if (!associationsNew.containsKey(authorityName))
|
||||
{
|
||||
Set<String> associations = associationsOld.get(authorityName);
|
||||
|
||||
if (!associations.isEmpty())
|
||||
{
|
||||
int insertIndex = authorityPath.size();
|
||||
for (String parentAuthority : associations)
|
||||
{
|
||||
// Prevent cyclic paths
|
||||
if (!authorityPath.contains(parentAuthority))
|
||||
{
|
||||
authorityPath.add(parentAuthority);
|
||||
visitGroupAssociations(authorityPath, associationsOld, associationsNew);
|
||||
authorityPath.remove(insertIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
associationsNew.put(authorityName, associations);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -102,12 +102,19 @@ public class ContentFormTypePatch extends AbstractPatch
|
||||
sp.setLanguage(SearchService.LANGUAGE_LUCENE);
|
||||
sp.setQuery("ASPECT:\"" + WCMAppModel.ASPECT_FORM + "\"");
|
||||
ResultSet rs = this.searchService.query(sp);
|
||||
Collection<NodeRef> result = new ArrayList<NodeRef>(rs.length());
|
||||
for (ResultSetRow row : rs)
|
||||
try
|
||||
{
|
||||
result.add(row.getNodeRef());
|
||||
Collection<NodeRef> result = new ArrayList<NodeRef>(rs.length());
|
||||
for (ResultSetRow row : rs)
|
||||
{
|
||||
result.add(row.getNodeRef());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
rs.close();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@@ -38,13 +38,13 @@ import java.util.zip.CRC32;
|
||||
import org.springframework.extensions.surf.util.I18NUtil;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.admin.patch.AbstractPatch;
|
||||
import org.alfresco.repo.batch.BatchProcessor;
|
||||
import org.alfresco.repo.batch.BatchProcessor.Worker;
|
||||
import org.alfresco.repo.domain.ChildAssoc;
|
||||
import org.alfresco.repo.domain.Node;
|
||||
import org.alfresco.repo.domain.QNameDAO;
|
||||
import org.alfresco.repo.domain.hibernate.ChildAssocImpl;
|
||||
import org.alfresco.repo.node.db.NodeDaoService;
|
||||
import org.alfresco.repo.security.sync.BatchProcessor;
|
||||
import org.alfresco.repo.security.sync.BatchProcessor.Worker;
|
||||
import org.alfresco.service.cmr.admin.PatchException;
|
||||
import org.alfresco.service.cmr.rule.RuleService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
@@ -186,7 +186,7 @@ public class FixNameCrcValuesPatch extends AbstractPatch implements ApplicationE
|
||||
public String fixCrcValues() throws Exception
|
||||
{
|
||||
// get the association types to check
|
||||
BatchProcessor<Long> batchProcessor = new BatchProcessor<Long>(transactionService
|
||||
BatchProcessor<Long> batchProcessor = new BatchProcessor<Long>(logger, transactionService
|
||||
.getRetryingTransactionHelper(), ruleService, applicationEventPublisher, findMismatchedCrcs(),
|
||||
"FixNameCrcValuesPatch", 100, 2, 20);
|
||||
|
||||
@@ -224,7 +224,8 @@ public class FixNameCrcValuesPatch extends AbstractPatch implements ApplicationE
|
||||
}
|
||||
// Update the CRCs
|
||||
long childCrc = getCrc(childName);
|
||||
long qnameCrc = ChildAssocImpl.getCrc(assoc.getQName(qnameDAO));
|
||||
QName qname = assoc.getQName(qnameDAO);
|
||||
long qnameCrc = ChildAssocImpl.getCrc(qname);
|
||||
|
||||
// Update the assoc
|
||||
assoc.setChildNodeNameCrc(childCrc);
|
||||
@@ -237,7 +238,7 @@ public class FixNameCrcValuesPatch extends AbstractPatch implements ApplicationE
|
||||
catch (Throwable e)
|
||||
{
|
||||
String msg = I18NUtil.getMessage(MSG_UNABLE_TO_CHANGE, childNode.getId(), childName, oldChildCrc,
|
||||
childCrc, oldQNameCrc, qnameCrc, e.getMessage());
|
||||
childCrc, qname, oldQNameCrc, qnameCrc, e.getMessage());
|
||||
// We just log this and add details to the message file
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
@@ -251,7 +252,8 @@ public class FixNameCrcValuesPatch extends AbstractPatch implements ApplicationE
|
||||
}
|
||||
getSession().clear();
|
||||
// Record
|
||||
writeLine(I18NUtil.getMessage(MSG_REWRITTEN, childNode.getId(), childName, oldChildCrc, childCrc, oldQNameCrc, qnameCrc));
|
||||
writeLine(I18NUtil.getMessage(MSG_REWRITTEN, childNode.getId(), childName, oldChildCrc, childCrc,
|
||||
qname, oldQNameCrc, qnameCrc));
|
||||
}}, true);
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user