diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml
index 978c51393b..14d1875a70 100644
--- a/config/alfresco/core-services-context.xml
+++ b/config/alfresco/core-services-context.xml
@@ -237,12 +237,51 @@
${db.pool.max}
+
+ ${db.pool.min}
+
+
+ ${db.pool.idle}
+ false${db.txn.isolation}
+
+ ${db.pool.wait.max}
+
+
+ ${db.pool.validate.query}
+
+
+ ${db.pool.evict.interval}
+
+
+ ${db.pool.evict.idle.min}
+
+
+ ${db.pool.validate.borrow}
+
+
+ ${db.pool.validate.return}
+
+
+ ${db.pool.evict.validate}
+
+
+ ${db.pool.abandoned.detect}
+
+
+ ${db.pool.abandoned.time}
+
+
+ ${db.pool.statements.enable}
+
+
+ ${db.pool.statements.max}
+
diff --git a/config/alfresco/extension/custom-connection-pool-context.xml.sample b/config/alfresco/extension/custom-connection-pool-context.xml.sample
deleted file mode 100644
index 03d37b4cf3..0000000000
--- a/config/alfresco/extension/custom-connection-pool-context.xml.sample
+++ /dev/null
@@ -1,67 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
- ${db.driver}
-
-
- ${db.url}
-
-
- ${db.username}
-
-
- ${db.password}
-
-
- false
-
-
-
- ${db.pool.initial}
-
-
- ${db.pool.max}
-
-
- 10000
-
-
- select 1
-
-
- 300000
-
-
- 60000
-
-
- false
-
-
- false
-
-
- true
-
-
- true
-
-
- 30
-
-
-
-
diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties
index ed9c7d8924..bf8f6693cd 100644
--- a/config/alfresco/repository.properties
+++ b/config/alfresco/repository.properties
@@ -190,6 +190,19 @@ db.password=alfresco
db.pool.initial=10
db.pool.max=40
db.txn.isolation=-1
+db.pool.statements.enable=true
+db.pool.statements.max=40
+db.pool.min=0
+db.pool.idle=8
+db.pool.wait.max=-1
+db.pool.validate.query=
+db.pool.evict.interval=-1
+db.pool.evict.idle.min=1800000
+db.pool.validate.borrow=true
+db.pool.validate.return=false
+db.pool.evict.validate=false
+db.pool.abandoned.detect=false
+db.pool.abandoned.time=300
# Email configuration
mail.host=
diff --git a/config/alfresco/subsystems/Synchronization/default/default-synchronization-context.xml b/config/alfresco/subsystems/Synchronization/default/default-synchronization-context.xml
index 19f5670947..f67dffe3d3 100644
--- a/config/alfresco/subsystems/Synchronization/default/default-synchronization-context.xml
+++ b/config/alfresco/subsystems/Synchronization/default/default-synchronization-context.xml
@@ -35,6 +35,9 @@
${synchronization.syncWhenMissingPeopleLogIn}
+
+ ${synchronization.syncOnStartup}
+ ${synchronization.autoCreatePeopleOnLogin}
@@ -50,6 +53,9 @@
+
+
+ userRegistry
diff --git a/config/alfresco/subsystems/Synchronization/default/default-synchronization.properties b/config/alfresco/subsystems/Synchronization/default/default-synchronization.properties
index 56ec2d23b3..81ae75786c 100644
--- a/config/alfresco/subsystems/Synchronization/default/default-synchronization.properties
+++ b/config/alfresco/subsystems/Synchronization/default/default-synchronization.properties
@@ -15,5 +15,8 @@ synchronization.import.cron=0 0 0 * * ?
# Should we trigger a differential sync when missing people log in?
synchronization.syncWhenMissingPeopleLogIn=true
+# Should we trigger a differential sync on startup?
+synchronization.syncOnStartup=true
+
# Should we auto create a missing person on log in?
synchronization.autoCreatePeopleOnLogin=true
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/domain/AccessControlListDAO.java b/source/java/org/alfresco/repo/domain/AccessControlListDAO.java
index 96b31479ab..86394e415c 100644
--- a/source/java/org/alfresco/repo/domain/AccessControlListDAO.java
+++ b/source/java/org/alfresco/repo/domain/AccessControlListDAO.java
@@ -83,11 +83,11 @@ public interface AccessControlListDAO
* Update inheritance
*
* @param parent
- * @param mergeFrom
+ * @param inheritFrom
* @param previousId
* @return
*/
- public List setInheritanceForChildren(NodeRef parent, Long mergeFrom);
+ public List setInheritanceForChildren(NodeRef parent, Long inheritFrom);
public Long getIndirectAcl(NodeRef nodeRef);
diff --git a/source/java/org/alfresco/repo/domain/hibernate/AVMAccessControlListDAO.java b/source/java/org/alfresco/repo/domain/hibernate/AVMAccessControlListDAO.java
index bdc8eea9f3..507dcc2b9c 100644
--- a/source/java/org/alfresco/repo/domain/hibernate/AVMAccessControlListDAO.java
+++ b/source/java/org/alfresco/repo/domain/hibernate/AVMAccessControlListDAO.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005-2007 Alfresco Software Limited.
+ * Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -14,12 +14,14 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * As a special exception to the terms and conditions of version 2.0 of
- * the GPL, you may redistribute this Program in connection with Free/Libre
- * and Open Source Software ("FLOSS") applications as described in Alfresco's
- * FLOSS exception. You should have recieved a copy of the text describing
- * the FLOSS exception, and it is also available here:
- * http://www.alfresco.com/legal/licensing" */
+
+ * As a special exception to the terms and conditions of version 2.0 of
+ * the GPL, you may redistribute this Program in connection with Free/Libre
+ * and Open Source Software ("FLOSS") applications as described in Alfresco's
+ * FLOSS exception. You should have received a copy of the text describing
+ * the FLOSS exception, and it is also available here:
+ * http://www.alfresco.com/legal/licensing"
+ */
package org.alfresco.repo.domain.hibernate;
@@ -38,7 +40,6 @@ import org.alfresco.repo.domain.PropertyValue;
import org.alfresco.repo.domain.hibernate.AclDaoComponentImpl.Indirection;
import org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptor;
import org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptor.StoreType;
-import org.alfresco.repo.search.impl.lucene.index.IndexInfo;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.security.permissions.ACLType;
@@ -509,7 +510,7 @@ public class AVMAccessControlListDAO implements AccessControlListDAO
if (descriptor.isLayeredDirectory())
{
- setInheritanceForDirectChildren(descriptor, changeMap, aclDaoComponent.getInheritedAccessControlList(getAclAsSystem(-1, descriptor.getPath()).getId()),
+ setInheritanceForDirectChildren(descriptor, changeMap, getAclAsSystem(-1, descriptor.getPath()).getId(),
indirections);
}
fixUpAcls(descriptor, changeMap, unchanged, unsetAcl, mode, indirections);
@@ -586,10 +587,10 @@ public class AVMAccessControlListDAO implements AccessControlListDAO
}
- private void setInheritanceForDirectChildren(AVMNodeDescriptor descriptor, Map changeMap, Long mergeFrom, Map> indirections)
+ private void setInheritanceForDirectChildren(AVMNodeDescriptor descriptor, Map changeMap, Long inheritFrom, Map> indirections)
{
List changes = new ArrayList();
- setFixedAcls(descriptor, mergeFrom, changes, SetMode.DIRECT_ONLY, false, indirections);
+ setFixedAcls(descriptor, inheritFrom, null, changes, SetMode.DIRECT_ONLY, false, indirections);
for (AclChange change : changes)
{
if (!change.getBefore().equals(change.getAfter()))
@@ -599,7 +600,7 @@ public class AVMAccessControlListDAO implements AccessControlListDAO
}
}
- public List setInheritanceForChildren(NodeRef parent, Long mergeFrom)
+ public List setInheritanceForChildren(NodeRef parent, Long inheritFrom)
{
// Walk children and fix up any that reference the given list ..
// If previous is null we need to visit all descendants with a null acl and set
@@ -615,7 +616,7 @@ public class AVMAccessControlListDAO implements AccessControlListDAO
Map> indirections = buildIndirections();
List changes = new ArrayList();
AVMNodeDescriptor descriptor = fAVMService.lookup(version, path);
- setFixedAcls(descriptor, mergeFrom, changes, SetMode.ALL, false, indirections);
+ setFixedAcls(descriptor, inheritFrom, null, changes, SetMode.ALL, false, indirections);
return changes;
}
@@ -626,16 +627,24 @@ public class AVMAccessControlListDAO implements AccessControlListDAO
}
/**
- * Set and cascade ACls
+ * Support to set a shared ACL on a node and all of its children.
*
* @param descriptor
+ * the descriptor
+ * @param inheritFrom
+ * the parent node's ACL
* @param mergeFrom
+ * the shared ACL, if already known. If null, will be retrieved / created lazily
* @param changes
+ * the list in which to record changes
* @param mode
+ * the mode
* @param set
+ * set the shared ACL on the parent ?
* @param indirections
+ * the indirections
*/
- public void setFixedAcls(AVMNodeDescriptor descriptor, Long mergeFrom, List changes, SetMode mode, boolean set, Map> indirections)
+ public void setFixedAcls(AVMNodeDescriptor descriptor, Long inheritFrom, Long mergeFrom, List changes, SetMode mode, boolean set, Map> indirections)
{
if (descriptor == null)
{
@@ -645,6 +654,12 @@ public class AVMAccessControlListDAO implements AccessControlListDAO
{
if (set)
{
+ // Lazily retrieve/create the shared ACL
+ if (mergeFrom == null)
+ {
+ mergeFrom = aclDaoComponent.getInheritedAccessControlList(inheritFrom);
+ }
+
// Simple set does not require any special COW wire up
// The AVM node will COW as required
DbAccessControlList previous = getAclAsSystem(-1, descriptor.getPath());
@@ -673,6 +688,12 @@ public class AVMAccessControlListDAO implements AccessControlListDAO
for (String key : children.keySet())
{
+ // Lazily retrieve/create the shared ACL
+ if (mergeFrom == null)
+ {
+ mergeFrom = aclDaoComponent.getInheritedAccessControlList(inheritFrom);
+ }
+
AVMNodeDescriptor child = children.get(key);
DbAccessControlList acl = getAclAsSystem(-1, child.getPath());
@@ -682,7 +703,7 @@ public class AVMAccessControlListDAO implements AccessControlListDAO
hibernateSessionHelper.mark();
try
{
- setFixedAcls(child, mergeFrom, changes, mode, true, indirections);
+ setFixedAcls(child, inheritFrom, mergeFrom, changes, mode, true, indirections);
}
finally
{
@@ -709,7 +730,7 @@ public class AVMAccessControlListDAO implements AccessControlListDAO
try
{
setAclAsSystem(child.getPath(), aclDaoComponent.getDbAccessControlList(change.getAfter()));
- setFixedAcls(child, aclDaoComponent.getInheritedAccessControlList(change.getAfter()), newChanges, SetMode.DIRECT_ONLY, false, indirections);
+ setFixedAcls(child, change.getAfter(), null, newChanges, SetMode.DIRECT_ONLY, false, indirections);
changes.addAll(newChanges);
break;
}
@@ -725,7 +746,7 @@ public class AVMAccessControlListDAO implements AccessControlListDAO
hibernateSessionHelper.mark();
try
{
- setFixedAcls(child, mergeFrom, changes, mode, true, indirections);
+ setFixedAcls(child, inheritFrom, mergeFrom, changes, mode, true, indirections);
}
finally
{
@@ -863,7 +884,7 @@ public class AVMAccessControlListDAO implements AccessControlListDAO
List changes = new ArrayList();
- setFixedAcls(node, aclDaoComponent.getInheritedAccessControlList(id), changes, SetMode.DIRECT_ONLY, false, indirections);
+ setFixedAcls(node, id, null, changes, SetMode.DIRECT_ONLY, false, indirections);
for (AclChange change : changes)
{
@@ -909,7 +930,7 @@ public class AVMAccessControlListDAO implements AccessControlListDAO
}
List changes = new ArrayList();
- setFixedAcls(node, aclDaoComponent.getInheritedAccessControlList(getAclAsSystem(-1, node.getPath()).getId()), changes, SetMode.DIRECT_ONLY, false, indirections);
+ setFixedAcls(node, getAclAsSystem(-1, node.getPath()).getId(), null, changes, SetMode.DIRECT_ONLY, false, indirections);
for (AclChange change : changes)
{
@@ -949,7 +970,7 @@ public class AVMAccessControlListDAO implements AccessControlListDAO
}
List changes = new ArrayList();
- setFixedAcls(node, aclDaoComponent.getInheritedAccessControlList(getAclAsSystem(-1, node.getPath()).getId()), changes, SetMode.DIRECT_ONLY, false, indirections);
+ setFixedAcls(node, getAclAsSystem(-1, node.getPath()).getId(), null, changes, SetMode.DIRECT_ONLY, false, indirections);
for (AclChange change : changes)
{
diff --git a/source/java/org/alfresco/repo/domain/hibernate/DMAccessControlListDAO.java b/source/java/org/alfresco/repo/domain/hibernate/DMAccessControlListDAO.java
index 3cf8bef009..fa4c0d3601 100644
--- a/source/java/org/alfresco/repo/domain/hibernate/DMAccessControlListDAO.java
+++ b/source/java/org/alfresco/repo/domain/hibernate/DMAccessControlListDAO.java
@@ -170,7 +170,6 @@ public class DMAccessControlListDAO implements AccessControlListDAO
{
if (!store.getProtocol().equals(StoreRef.PROTOCOL_AVM))
{
- @SuppressWarnings("unused")
CounterSet update;
update = fixOldDmAcls(nodeService.getRootNode(store), null, true);
result.add(update);
@@ -316,10 +315,10 @@ public class DMAccessControlListDAO implements AccessControlListDAO
throw new UnsupportedOperationException();
}
- public List setInheritanceForChildren(NodeRef parent, Long mergeFrom)
+ public List setInheritanceForChildren(NodeRef parent, Long inheritFrom)
{
List changes = new ArrayList();
- setFixedAcls(parent, mergeFrom, changes, false);
+ setFixedAcls(parent, inheritFrom, null, changes, false);
return changes;
}
@@ -329,14 +328,20 @@ public class DMAccessControlListDAO implements AccessControlListDAO
}
/**
- * Support to set ACLs and cascade fo required
+ * Support to set a shared ACL on a node and all of its children
*
* @param nodeRef
+ * the parent node
+ * @param inheritFrom
+ * the parent node's ACL
* @param mergeFrom
+ * the shared ACL, if already known. If null, will be retrieved / created lazily
* @param changes
+ * the list in which to record changes
* @param set
+ * set the shared ACL on the parent ?
*/
- public void setFixedAcls(NodeRef nodeRef, Long mergeFrom, List changes, boolean set)
+ public void setFixedAcls(NodeRef nodeRef, Long inheritFrom, Long mergeFrom, List changes, boolean set)
{
if (nodeRef == null)
{
@@ -346,6 +351,11 @@ public class DMAccessControlListDAO implements AccessControlListDAO
{
if (set)
{
+ // Lazily retrieve/create the shared ACL
+ if (mergeFrom == null)
+ {
+ mergeFrom = aclDaoComponent.getInheritedAccessControlList(inheritFrom);
+ }
setAccessControlList(nodeRef, aclDaoComponent.getDbAccessControlList(mergeFrom));
}
@@ -355,6 +365,12 @@ public class DMAccessControlListDAO implements AccessControlListDAO
{
if (child.isPrimary())
{
+ // Lazily retrieve/create the shared ACL
+ if (mergeFrom == null)
+ {
+ mergeFrom = aclDaoComponent.getInheritedAccessControlList(inheritFrom);
+ }
+
DbAccessControlList acl = getAccessControlList(child.getChildRef());
if (acl == null)
@@ -362,7 +378,7 @@ public class DMAccessControlListDAO implements AccessControlListDAO
hibernateSessionHelper.mark();
try
{
- setFixedAcls(child.getChildRef(), mergeFrom, changes, true);
+ setFixedAcls(child.getChildRef(), inheritFrom, mergeFrom, changes, true);
}
finally
{
@@ -386,7 +402,7 @@ public class DMAccessControlListDAO implements AccessControlListDAO
hibernateSessionHelper.mark();
try
{
- setFixedAcls(child.getChildRef(), mergeFrom, changes, true);
+ setFixedAcls(child.getChildRef(), inheritFrom, mergeFrom, changes, true);
}
finally
{
diff --git a/source/java/org/alfresco/repo/domain/hibernate/DMPermissionsDaoComponentImpl.java b/source/java/org/alfresco/repo/domain/hibernate/DMPermissionsDaoComponentImpl.java
index b8c634ea5d..d18979aa3c 100644
--- a/source/java/org/alfresco/repo/domain/hibernate/DMPermissionsDaoComponentImpl.java
+++ b/source/java/org/alfresco/repo/domain/hibernate/DMPermissionsDaoComponentImpl.java
@@ -62,7 +62,7 @@ public class DMPermissionsDaoComponentImpl extends AbstractPermissionsDaoCompone
List changes = new ArrayList();
DbAccessControlList acl = aclDaoComponent.getDbAccessControlList(id);
changes.add(new AclDaoComponentImpl.AclChangeImpl(null, id, null, acl.getAclType()));
- changes.addAll(getACLDAO(nodeRef).setInheritanceForChildren(nodeRef, aclDaoComponent.getInheritedAccessControlList(id)));
+ changes.addAll(getACLDAO(nodeRef).setInheritanceForChildren(nodeRef, id));
getACLDAO(nodeRef).setAccessControlList(nodeRef, acl);
return new CreationReport(acl, changes);
}
@@ -90,7 +90,7 @@ public class DMPermissionsDaoComponentImpl extends AbstractPermissionsDaoCompone
changes.add(new AclDaoComponentImpl.AclChangeImpl(existing.getId(), id, existing.getAclType(), acl.getAclType()));
changes.addAll(aclDaoComponent.mergeInheritedAccessControlList(existing.getId(), id));
// set this to inherit to children
- changes.addAll(getACLDAO(nodeRef).setInheritanceForChildren(nodeRef, aclDaoComponent.getInheritedAccessControlList(id)));
+ changes.addAll(getACLDAO(nodeRef).setInheritanceForChildren(nodeRef, id));
getACLDAO(nodeRef).setAccessControlList(nodeRef, acl);
return new CreationReport(acl, changes);
@@ -123,7 +123,6 @@ public class DMPermissionsDaoComponentImpl extends AbstractPermissionsDaoCompone
case DEFINING:
if (acl.getInheritsFrom() != null)
{
- @SuppressWarnings("unused")
Long deleted = acl.getId();
Long inheritsFrom = acl.getInheritsFrom();
getACLDAO(nodeRef).setAccessControlList(nodeRef, aclDaoComponent.getDbAccessControlList(inheritsFrom));
@@ -135,7 +134,6 @@ public class DMPermissionsDaoComponentImpl extends AbstractPermissionsDaoCompone
else
{
// TODO: could just cear out existing
- @SuppressWarnings("unused")
Long deleted = acl.getId();
SimpleAccessControlListProperties properties = new SimpleAccessControlListProperties();
properties = new SimpleAccessControlListProperties();
@@ -146,7 +144,7 @@ public class DMPermissionsDaoComponentImpl extends AbstractPermissionsDaoCompone
Long id = aclDaoComponent.createAccessControlList(properties);
getACLDAO(nodeRef).setAccessControlList(nodeRef, aclDaoComponent.getDbAccessControlList(id));
List changes = new ArrayList();
- changes.addAll(getACLDAO(nodeRef).setInheritanceForChildren(nodeRef, aclDaoComponent.getInheritedAccessControlList(id)));
+ changes.addAll(getACLDAO(nodeRef).setInheritanceForChildren(nodeRef, id));
getACLDAO(nodeRef).updateChangedAcls(nodeRef, changes);
aclDaoComponent.deleteAccessControlList(acl.getId());
}
diff --git a/source/java/org/alfresco/repo/domain/hibernate/NodeAccessControlListDAO.java b/source/java/org/alfresco/repo/domain/hibernate/NodeAccessControlListDAO.java
index a1355854e3..e85bcff6ee 100644
--- a/source/java/org/alfresco/repo/domain/hibernate/NodeAccessControlListDAO.java
+++ b/source/java/org/alfresco/repo/domain/hibernate/NodeAccessControlListDAO.java
@@ -102,7 +102,7 @@ public class NodeAccessControlListDAO extends HibernateDaoSupport implements Acc
// Nothing to do here
}
- public List setInheritanceForChildren(NodeRef parent, Long mergeFrom)
+ public List setInheritanceForChildren(NodeRef parent, Long inheritFrom)
{
// Nothing to do here
return Collections. emptyList();
diff --git a/source/java/org/alfresco/repo/domain/hibernate/PermissionsDaoComponentImpl.java b/source/java/org/alfresco/repo/domain/hibernate/PermissionsDaoComponentImpl.java
index c072ef816b..65473bb5cc 100644
--- a/source/java/org/alfresco/repo/domain/hibernate/PermissionsDaoComponentImpl.java
+++ b/source/java/org/alfresco/repo/domain/hibernate/PermissionsDaoComponentImpl.java
@@ -57,7 +57,7 @@ public class PermissionsDaoComponentImpl extends AbstractPermissionsDaoComponent
List changes = new ArrayList();
DbAccessControlList acl = aclDaoComponent.getDbAccessControlList(id);
changes.add(new AclDaoComponentImpl.AclChangeImpl(null, id, null, acl.getAclType()));
- changes.addAll(getACLDAO(nodeRef).setInheritanceForChildren(nodeRef, aclDaoComponent.getInheritedAccessControlList(id)));
+ changes.addAll(getACLDAO(nodeRef).setInheritanceForChildren(nodeRef, id));
getACLDAO(nodeRef).setAccessControlList(nodeRef, acl);
return new CreationReport(acl, changes);
}
@@ -85,7 +85,7 @@ public class PermissionsDaoComponentImpl extends AbstractPermissionsDaoComponent
changes.add(new AclDaoComponentImpl.AclChangeImpl(existing.getId(), id, existing.getAclType(), acl.getAclType()));
changes.addAll(aclDaoComponent.mergeInheritedAccessControlList(existing.getId(), id));
// set this to inherit to children
- changes.addAll(getACLDAO(nodeRef).setInheritanceForChildren(nodeRef, aclDaoComponent.getInheritedAccessControlList(id)));
+ changes.addAll(getACLDAO(nodeRef).setInheritanceForChildren(nodeRef, id));
getACLDAO(nodeRef).setAccessControlList(nodeRef, acl);
return new CreationReport(acl, changes);
@@ -122,7 +122,7 @@ public class PermissionsDaoComponentImpl extends AbstractPermissionsDaoComponent
changes.addAll(aclDaoComponent.mergeInheritedAccessControlList(inheritedAclId, id));
}
// set this to inherit to children
- changes.addAll(getACLDAO(nodeRef).setInheritanceForChildren(nodeRef, aclDaoComponent.getInheritedAccessControlList(id)));
+ changes.addAll(getACLDAO(nodeRef).setInheritanceForChildren(nodeRef, id));
getACLDAO(nodeRef).setAccessControlList(nodeRef, acl);
return new CreationReport(acl, changes);
@@ -173,7 +173,7 @@ public class PermissionsDaoComponentImpl extends AbstractPermissionsDaoComponent
Long id = aclDaoComponent.createAccessControlList(properties);
getACLDAO(nodeRef).setAccessControlList(nodeRef, aclDaoComponent.getDbAccessControlList(id));
List changes = new ArrayList();
- changes.addAll(getACLDAO(nodeRef).setInheritanceForChildren(nodeRef, aclDaoComponent.getInheritedAccessControlList(id)));
+ changes.addAll(getACLDAO(nodeRef).setInheritanceForChildren(nodeRef, id));
getACLDAO(nodeRef).updateChangedAcls(nodeRef, changes);
aclDaoComponent.deleteAccessControlList(acl.getId());
}
diff --git a/source/java/org/alfresco/repo/security/authentication/AbstractAuthenticationComponent.java b/source/java/org/alfresco/repo/security/authentication/AbstractAuthenticationComponent.java
index c418c54dcf..e065eb7497 100644
--- a/source/java/org/alfresco/repo/security/authentication/AbstractAuthenticationComponent.java
+++ b/source/java/org/alfresco/repo/security/authentication/AbstractAuthenticationComponent.java
@@ -46,6 +46,8 @@ import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.transaction.TransactionService;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
/**
* This class abstract the support required to set up and query the Acegi context for security enforcement. There are
@@ -71,6 +73,8 @@ public abstract class AbstractAuthenticationComponent implements AuthenticationC
private TransactionService transactionService;
private UserRegistrySynchronizer userRegistrySynchronizer;
+
+ private final Log logger = LogFactory.getLog(getClass());
public AbstractAuthenticationComponent()
{
@@ -134,14 +138,37 @@ public abstract class AbstractAuthenticationComponent implements AuthenticationC
public void authenticate(String userName, char[] password) throws AuthenticationException
{
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Authenticating user \"" + userName + '"');
+ }
// Support guest login from the login screen
if (isGuestUserName(userName))
{
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("User \"" + userName + "\" recognized as a guest user");
+ }
setGuestUserAsCurrentUser(getUserDomain(userName));
}
else
{
- authenticateImpl(userName, password);
+ try
+ {
+ authenticateImpl(userName, password);
+ }
+ catch (RuntimeException e)
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Failed to authenticate user \"" + userName + '"', e);
+ }
+ throw e;
+ }
+ }
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("User \"" + userName + "\" authenticated successfully");
}
}
@@ -225,11 +252,20 @@ public abstract class AbstractAuthenticationComponent implements AuthenticationC
UserDetails ud = null;
if (isGuestUserName(userName))
{
+ String tenantDomain = getUserDomain(userName);
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Setting the current user to the guest user of tenant domain \"" + tenantDomain + '"');
+ }
GrantedAuthority[] gas = new GrantedAuthority[0];
- ud = new User(getGuestUserName(getUserDomain(userName)), "", true, true, true, true, gas);
+ ud = new User(getGuestUserName(tenantDomain), "", true, true, true, true, gas);
}
else
{
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Setting the current user to \"" + userName + '"');
+ }
ud = getUserDetails(userName);
}
return setUserDetails(ud);
@@ -428,17 +464,27 @@ public abstract class AbstractAuthenticationComponent implements AuthenticationC
{
public String doWork() throws Exception
{
- if (personService.personExists(userName)|| userRegistrySynchronizer.createMissingPerson(userName))
+ if (!personService.personExists(userName))
{
- NodeRef userNode = personService.getPerson(userName);
- if (userNode != null)
+ if (logger.isDebugEnabled())
{
- // Get the person name and use that as the current user to line up with permission
- // checks
- return (String) nodeService.getProperty(userNode, ContentModel.PROP_USERNAME);
+ logger.debug("User \"" + userName
+ + "\" does not exist in Alfresco. Attempting to import / create the user.");
}
+ if (!userRegistrySynchronizer.createMissingPerson(userName))
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Failed to import / create user \"" + userName + '"');
+ }
+ throw new AuthenticationException("User \"" + userName
+ + "\" does not exist in Alfresco");
+ }
}
- throw new AuthenticationException("Person does not exist in Alfresco");
+ NodeRef userNode = personService.getPerson(userName);
+ // Get the person name and use that as the current user to line up with permission
+ // checks
+ return (String) nodeService.getProperty(userNode, ContentModel.PROP_USERNAME);
}
}, getSystemUserName(getUserDomain(userName)));
@@ -499,6 +545,10 @@ public abstract class AbstractAuthenticationComponent implements AuthenticationC
public Authentication setSystemUserAsCurrentUser(String tenantDomain)
{
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Setting the current user to the system user of tenant domain \"" + tenantDomain + '"');
+ }
return authenticationContext.setSystemUserAsCurrentUser(tenantDomain);
}
diff --git a/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizer.java b/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizer.java
index 2ab205eecb..c8594113e2 100644
--- a/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizer.java
+++ b/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizer.java
@@ -40,15 +40,22 @@ import org.alfresco.repo.attributes.MapAttributeValue;
import org.alfresco.repo.management.subsystems.ActivateableBean;
import org.alfresco.repo.management.subsystems.ChildApplicationContextManager;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
+import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
+import org.alfresco.repo.transaction.RetryingTransactionHelper;
+import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
+import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.attributes.AttributeService;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.AuthorityType;
import org.alfresco.service.cmr.security.PersonService;
+import org.alfresco.util.AbstractLifecycleBean;
import org.alfresco.util.PropertyMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationEvent;
/**
* A ChainingUserRegistrySynchronizer is responsible for synchronizing Alfresco's local user (person) and
@@ -67,15 +74,17 @@ import org.springframework.context.ApplicationContext;
* false then only those users and groups modified since the most recent modification date of all the
* objects last queried from the same {@link UserRegistry} are retrieved. In this mode, local users and groups are
* created and updated, but not deleted (except where a name collision with a lower priority {@link UserRegistry} is
- * detected). This 'differential' mode is much faster, and by default is triggered by
+ * detected). This 'differential' mode is much faster, and by default is triggered on subsystem startup and also by
* {@link #createMissingPerson(String)} when a user is successfully authenticated who doesn't yet have a local person
* object in Alfresco. This should mean that new users and their group information are pulled over from LDAP servers as
* and when required.
*
* @author dward
*/
-public class ChainingUserRegistrySynchronizer implements UserRegistrySynchronizer
+public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean implements UserRegistrySynchronizer
{
+ /** The number of users / groups we add at a time in a transaction **/
+ private static final int BATCH_SIZE = 10;
/** The logger. */
private static final Log logger = LogFactory.getLog(ChainingUserRegistrySynchronizer.class);
@@ -104,9 +113,15 @@ public class ChainingUserRegistrySynchronizer implements UserRegistrySynchronize
/** The attribute service. */
private AttributeService attributeService;
- /** Should we trigger a sync when missing people log in? */
+ /** The retrying transaction helper. */
+ private RetryingTransactionHelper retryingTransactionHelper;
+
+ /** Should we trigger a differential sync when missing people log in? */
private boolean syncWhenMissingPeopleLogIn = true;
+ /** Should we trigger a differential sync on startup? */
+ private boolean syncOnStartup = true;
+
/** Should we auto create a missing person on log in? */
private boolean autoCreatePeopleOnLogin = true;
@@ -165,6 +180,17 @@ public class ChainingUserRegistrySynchronizer implements UserRegistrySynchronize
this.attributeService = attributeService;
}
+ /**
+ * Sets the retrying transaction helper.
+ *
+ * @param retryingTransactionHelper
+ * the new retrying transaction helper
+ */
+ public void setRetryingTransactionHelper(RetryingTransactionHelper retryingTransactionHelper)
+ {
+ this.retryingTransactionHelper = retryingTransactionHelper;
+ }
+
/**
* Controls whether we auto create a missing person on log in
*
@@ -177,7 +203,7 @@ public class ChainingUserRegistrySynchronizer implements UserRegistrySynchronize
}
/**
- * Controls whether we trigger a sync when missing people log in
+ * Controls whether we trigger a differential sync when missing people log in
*
* @param syncWhenMissingPeopleLogIn
* if we should trigger a sync when missing people log in
@@ -187,11 +213,22 @@ public class ChainingUserRegistrySynchronizer implements UserRegistrySynchronize
this.syncWhenMissingPeopleLogIn = syncWhenMissingPeopleLogIn;
}
+ /**
+ * Controls whether we trigger a differential sync when the subsystem starts up
+ *
+ * @param syncOnStartup
+ * if we should trigger a sync on startup
+ */
+ public void setSyncOnStartup(boolean syncOnStartup)
+ {
+ this.syncOnStartup = syncOnStartup;
+ }
+
/*
* (non-Javadoc)
- * @see org.alfresco.repo.security.sync.UserRegistrySynchronizer#synchronize(boolean)
+ * @see org.alfresco.repo.security.sync.UserRegistrySynchronizer#synchronize(boolean, boolean)
*/
- public void synchronize(boolean force)
+ public void synchronize(boolean force, boolean splitTxns)
{
Set visitedZoneIds = new TreeSet();
for (String id : this.applicationContextManager.getInstanceIds())
@@ -214,8 +251,14 @@ public class ChainingUserRegistrySynchronizer implements UserRegistrySynchronize
+ id
+ "'; some users and groups previously created by synchronization with this user registry may be removed.");
}
- int personsProcessed = syncPersonsWithPlugin(id, plugin, force, visitedZoneIds);
- int groupsProcessed = syncGroupsWithPlugin(id, plugin, force, visitedZoneIds);
+ // Work out whether we should do the work in a separate transaction (it's most performant if we
+ // bunch it into small transactions, but if we are doing a sync on login, it has to be the same
+ // transaction)
+ boolean requiresNew = splitTxns
+ || AlfrescoTransactionSupport.getTransactionReadState() == TxnReadState.TXN_READ_ONLY;
+
+ int personsProcessed = syncPersonsWithPlugin(id, plugin, force, requiresNew, visitedZoneIds);
+ int groupsProcessed = syncGroupsWithPlugin(id, plugin, force, requiresNew, visitedZoneIds);
if (ChainingUserRegistrySynchronizer.logger.isInfoEnabled())
{
ChainingUserRegistrySynchronizer.logger
@@ -243,7 +286,16 @@ public class ChainingUserRegistrySynchronizer implements UserRegistrySynchronize
{
if (this.syncWhenMissingPeopleLogIn)
{
- synchronize(false);
+ try
+ {
+ synchronize(false, false);
+ }
+ catch (Exception e)
+ {
+ // We don't want to fail the whole login if we can help it
+ ChainingUserRegistrySynchronizer.logger.warn(
+ "User authenticated but failed to sync with user registry", e);
+ }
if (this.personService.personExists(userName))
{
return true;
@@ -273,21 +325,29 @@ public class ChainingUserRegistrySynchronizer implements UserRegistrySynchronize
* @param force
* true if all persons are to be queried. false if only those changed since the
* most recent queried user should be queried.
+ * @param splitTxns
+ * Can the modifications to Alfresco be split across multiple transactions for maximum performance? If
+ * true, users and groups are created/updated in batches of 10 for increased performance. If
+ * false, all users and groups are processed in the current transaction. This is required if
+ * calling synchronously (e.g. in response to an authentication event in the same transaction).
* @param visitedZoneIds
* the set of zone ids already processed. These zones have precedence over the current zone when it comes
* to user name 'collisions'. If a user is queried that already exists locally but is tagged with one of
* the zones in this set, then it will be ignored as this zone has lower priority.
* @return the number of users processed
*/
- private int syncPersonsWithPlugin(String zone, UserRegistry userRegistry, boolean force, Set visitedZoneIds)
+ private int syncPersonsWithPlugin(String zone, UserRegistry userRegistry, boolean force, boolean splitTxns,
+ final Set visitedZoneIds)
{
// Create a prefixed zone ID for use with the authority service
- String zoneId = AuthorityService.ZONE_AUTH_EXT_PREFIX + zone;
+ final String zoneId = AuthorityService.ZONE_AUTH_EXT_PREFIX + zone;
- int processedCount = 0;
- long lastModifiedMillis = force ? -1L : getMostRecentUpdateTime(
+ // The set of zones we associate with new objects (default plus registry specific)
+ final Set zoneSet = getZones(zoneId);
+
+ final long lastModifiedMillis = force ? -1L : getMostRecentUpdateTime(
ChainingUserRegistrySynchronizer.PERSON_LAST_MODIFIED_ATTRIBUTE, zoneId);
- Date lastModified = lastModifiedMillis == -1 ? null : new Date(lastModifiedMillis);
+ final Date lastModified = lastModifiedMillis == -1 ? null : new Date(lastModifiedMillis);
if (ChainingUserRegistrySynchronizer.logger.isInfoEnabled())
{
if (lastModified == null)
@@ -300,82 +360,131 @@ public class ChainingUserRegistrySynchronizer implements UserRegistrySynchronize
+ DateFormat.getDateTimeInstance().format(lastModified) + " from user registry '" + zone + "'");
}
}
- Iterator persons = userRegistry.getPersons(lastModified);
- Set personsToDelete = this.authorityService.getAllAuthoritiesInZone(zoneId, AuthorityType.USER);
+ final Iterator persons = userRegistry.getPersons(lastModified);
+ final Set personsCreated = new TreeSet();
+
+ class CreationWorker implements RetryingTransactionCallback
+ {
+ private long latestTime = lastModifiedMillis;
+
+ public long getLatestTime()
+ {
+ return this.latestTime;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback#execute()
+ */
+ public Integer execute() throws Throwable
+ {
+ int processedCount = 0;
+ do
+ {
+ NodeDescription person = persons.next();
+ PropertyMap personProperties = person.getProperties();
+ String personName = (String) personProperties.get(ContentModel.PROP_USERNAME);
+
+ Set zones = ChainingUserRegistrySynchronizer.this.authorityService
+ .getAuthorityZones(personName);
+ if (zones == null)
+ {
+ // The person did not exist at all
+ if (ChainingUserRegistrySynchronizer.logger.isInfoEnabled())
+ {
+ ChainingUserRegistrySynchronizer.logger.info("Creating user '" + personName + "'");
+ }
+ ChainingUserRegistrySynchronizer.this.personService.createPerson(personProperties, zoneSet);
+ }
+ else if (zones.contains(zoneId))
+ {
+ // The person already existed in this zone: update the person
+ if (ChainingUserRegistrySynchronizer.logger.isInfoEnabled())
+ {
+ ChainingUserRegistrySynchronizer.logger.info("Updating user '" + personName + "'");
+ }
+ ChainingUserRegistrySynchronizer.this.personService.setPersonProperties(personName,
+ personProperties);
+ }
+ else
+ {
+ // The person does not exist in this zone, but may exist in another zone
+ zones.retainAll(visitedZoneIds);
+ if (zones.size() > 0)
+ {
+ // A person that exists in a different zone with higher precedence
+ continue;
+ }
+ // The person existed, but in a zone with lower precedence
+ if (ChainingUserRegistrySynchronizer.logger.isWarnEnabled())
+ {
+ ChainingUserRegistrySynchronizer.logger
+ .warn("Recreating occluded user '"
+ + personName
+ + "'. This user was previously created manually or through synchronization with a lower priority user registry.");
+ }
+ ChainingUserRegistrySynchronizer.this.personService.deletePerson(personName);
+ ChainingUserRegistrySynchronizer.this.personService.createPerson(personProperties, zoneSet);
+ }
+ // Increment the count of processed people
+ personsCreated.add(personName);
+ processedCount++;
+
+ // Maintain the last modified date
+ Date personLastModified = person.getLastModified();
+ if (personLastModified != null)
+ {
+ this.latestTime = Math.max(this.latestTime, personLastModified.getTime());
+ }
+ }
+ while (persons.hasNext() && processedCount < ChainingUserRegistrySynchronizer.BATCH_SIZE);
+ return processedCount;
+ }
+ }
+
+ CreationWorker creations = new CreationWorker();
+ int processedCount = 0;
while (persons.hasNext())
{
- NodeDescription person = persons.next();
- PropertyMap personProperties = person.getProperties();
- String personName = (String) personProperties.get(ContentModel.PROP_USERNAME);
- if (personsToDelete.remove(personName))
- {
- // The person already existed in this zone: update the person
- if (ChainingUserRegistrySynchronizer.logger.isInfoEnabled())
- {
- ChainingUserRegistrySynchronizer.logger.info("Updating user '" + personName + "'");
- }
- this.personService.setPersonProperties(personName, personProperties);
- }
- else
- {
- // The person does not exist in this zone, but may exist in another zone
- Set zones = this.authorityService.getAuthorityZones(personName);
- if (zones != null)
- {
- zones.retainAll(visitedZoneIds);
- if (zones.size() > 0)
- {
- // A person that exists in a different zone with higher precedence
- continue;
- }
- // The person existed, but in a zone with lower precedence
- if (ChainingUserRegistrySynchronizer.logger.isWarnEnabled())
- {
- ChainingUserRegistrySynchronizer.logger
- .warn("Recreating occluded user '"
- + personName
- + "'. This user was previously created manually or through synchronization with a lower priority user registry.");
- }
- this.personService.deletePerson(personName);
- }
- else
- {
- // The person did not exist at all
- if (ChainingUserRegistrySynchronizer.logger.isInfoEnabled())
- {
- ChainingUserRegistrySynchronizer.logger.info("Creating user '" + personName + "'");
- }
- }
- this.personService.createPerson(personProperties, getZones(zoneId));
- }
- // Increment the count of processed people
- processedCount++;
-
- // Maintain the last modified date
- Date personLastModified = person.getLastModified();
- if (personLastModified != null)
- {
- lastModifiedMillis = Math.max(lastModifiedMillis, personLastModified.getTime());
- }
+ processedCount += this.retryingTransactionHelper.doInTransaction(creations, false, splitTxns);
+ }
+ long latestTime = creations.getLatestTime();
+ if (latestTime != -1)
+ {
+ setMostRecentUpdateTime(ChainingUserRegistrySynchronizer.PERSON_LAST_MODIFIED_ATTRIBUTE, zoneId, latestTime);
}
- if (force && !personsToDelete.isEmpty())
+ // Handle deletions if we are doing a full sync
+ if (force)
{
- for (String personName : personsToDelete)
+ class DeletionWorker implements RetryingTransactionCallback
{
- if (ChainingUserRegistrySynchronizer.logger.isWarnEnabled())
+ /*
+ * (non-Javadoc)
+ * @see org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback#execute()
+ */
+ public Integer execute() throws Throwable
{
- ChainingUserRegistrySynchronizer.logger.warn("Deleting user '" + personName + "'");
+ int processedCount = 0;
+ Set personsToDelete = ChainingUserRegistrySynchronizer.this.authorityService
+ .getAllAuthoritiesInZone(zoneId, AuthorityType.USER);
+ personsToDelete.removeAll(personsCreated);
+ for (String personName : personsToDelete)
+ {
+ if (ChainingUserRegistrySynchronizer.logger.isWarnEnabled())
+ {
+ ChainingUserRegistrySynchronizer.logger.warn("Deleting user '" + personName + "'");
+ }
+ ChainingUserRegistrySynchronizer.this.personService.deletePerson(personName);
+ processedCount++;
+ }
+ return processedCount;
}
- this.personService.deletePerson(personName);
- processedCount++;
}
- }
- if (lastModifiedMillis != -1)
- {
- setMostRecentUpdateTime(ChainingUserRegistrySynchronizer.PERSON_LAST_MODIFIED_ATTRIBUTE, zoneId,
- lastModifiedMillis);
+ // Just use a single transaction
+ DeletionWorker deletions = new DeletionWorker();
+ processedCount += this.retryingTransactionHelper.doInTransaction(deletions, false, splitTxns);
}
// Remember we have visited this zone
@@ -395,21 +504,30 @@ public class ChainingUserRegistrySynchronizer implements UserRegistrySynchronize
* @param force
* true if all groups are to be queried. false if only those changed since the
* most recent queried group should be queried.
+ * @param splitTxns
+ * Can the modifications to Alfresco be split across multiple transactions for maximum performance? If
+ * true, users and groups are created/updated in batches of 10 for increased performance. If
+ * false, all users and groups are processed in the current transaction. This is required if
+ * calling synchronously (e.g. in response to an authentication event in the same transaction).
* @param visitedZoneIds
* the set of zone ids already processed. These zones have precedence over the current zone when it comes
* to group name 'collisions'. If a group is queried that already exists locally but is tagged with one
* of the zones in this set, then it will be ignored as this zone has lower priority.
* @return the number of groups processed
*/
- private int syncGroupsWithPlugin(String zone, UserRegistry userRegistry, boolean force, Set visitedZoneIds)
+ private int syncGroupsWithPlugin(String zone, UserRegistry userRegistry, boolean force, boolean splitTxns,
+ final Set visitedZoneIds)
{
// Create a prefixed zone ID for use with the authority service
- String zoneId = AuthorityService.ZONE_AUTH_EXT_PREFIX + zone;
+ final String zoneId = AuthorityService.ZONE_AUTH_EXT_PREFIX + zone;
- int processedCount = 0;
- long lastModifiedMillis = force ? -1L : getMostRecentUpdateTime(
+ // The set of zones we associate with new objects (default plus registry specific)
+ final Set zoneSet = getZones(zoneId);
+
+ final long lastModifiedMillis = force ? -1L : getMostRecentUpdateTime(
ChainingUserRegistrySynchronizer.GROUP_LAST_MODIFIED_ATTRIBUTE, zoneId);
- Date lastModified = lastModifiedMillis == -1 ? null : new Date(lastModifiedMillis);
+ final Date lastModified = lastModifiedMillis == -1 ? null : new Date(lastModifiedMillis);
+
if (ChainingUserRegistrySynchronizer.logger.isInfoEnabled())
{
if (lastModified == null)
@@ -423,137 +541,231 @@ public class ChainingUserRegistrySynchronizer implements UserRegistrySynchronize
}
}
- Iterator groups = userRegistry.getGroups(lastModified);
- Map> groupAssocsToCreate = new TreeMap>();
- Set groupsToDelete = this.authorityService.getAllAuthoritiesInZone(zoneId, AuthorityType.GROUP);
+ final Iterator groups = userRegistry.getGroups(lastModified);
+ final Map> groupAssocsToCreate = new TreeMap>();
+ final Set groupsCreated = new TreeSet();
+
+ class CreationWorker implements RetryingTransactionCallback
+ {
+ private long latestTime = lastModifiedMillis;
+
+ public long getLatestTime()
+ {
+ return this.latestTime;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback#execute()
+ */
+ public Integer execute() throws Throwable
+ {
+ int processedCount = 0;
+ do
+ {
+ NodeDescription group = groups.next();
+ PropertyMap groupProperties = group.getProperties();
+ String groupName = (String) groupProperties.get(ContentModel.PROP_AUTHORITY_NAME);
+ Set groupZones = ChainingUserRegistrySynchronizer.this.authorityService
+ .getAuthorityZones(groupName);
+
+ if (groupZones == null)
+ {
+ // The group did not exist at all
+ String groupShortName = ChainingUserRegistrySynchronizer.this.authorityService
+ .getShortName(groupName);
+ if (ChainingUserRegistrySynchronizer.logger.isInfoEnabled())
+ {
+ ChainingUserRegistrySynchronizer.logger.info("Creating group '" + groupShortName + "'");
+ }
+ // create the group
+ ChainingUserRegistrySynchronizer.this.authorityService.createAuthority(AuthorityType
+ .getAuthorityType(groupName), groupShortName, (String) groupProperties
+ .get(ContentModel.PROP_AUTHORITY_DISPLAY_NAME), zoneSet);
+ Set children = group.getChildAssociations();
+ if (!children.isEmpty())
+ {
+ groupAssocsToCreate.put(groupName, children);
+ }
+ }
+ else if (groupZones.contains(zoneId))
+ {
+ // The group already existed in this zone: update the group
+ Set oldChildren = ChainingUserRegistrySynchronizer.this.authorityService
+ .getContainedAuthorities(null, groupName, true);
+ Set newChildren = group.getChildAssociations();
+ Set toDelete = new TreeSet(oldChildren);
+ Set toAdd = new TreeSet(newChildren);
+ toDelete.removeAll(newChildren);
+ toAdd.removeAll(oldChildren);
+ if (!toAdd.isEmpty())
+ {
+ groupAssocsToCreate.put(groupName, toAdd);
+ }
+ for (String child : toDelete)
+ {
+ if (ChainingUserRegistrySynchronizer.logger.isInfoEnabled())
+ {
+ ChainingUserRegistrySynchronizer.logger.info("Removing '"
+ + ChainingUserRegistrySynchronizer.this.authorityService.getShortName(child)
+ + "' from group '"
+ + ChainingUserRegistrySynchronizer.this.authorityService
+ .getShortName(groupName) + "'");
+ }
+ ChainingUserRegistrySynchronizer.this.authorityService.removeAuthority(groupName, child);
+ }
+ }
+ else
+ {
+ // The group does not exist in this zone, but may exist in another zone
+ groupZones.retainAll(visitedZoneIds);
+ if (groupZones.size() > 0)
+ {
+ // A group that exists in a different zone with higher precedence
+ continue;
+ }
+ String groupShortName = ChainingUserRegistrySynchronizer.this.authorityService
+ .getShortName(groupName);
+ // The group existed, but in a zone with lower precedence
+ if (ChainingUserRegistrySynchronizer.logger.isWarnEnabled())
+ {
+ ChainingUserRegistrySynchronizer.logger
+ .warn("Recreating occluded group '"
+ + groupShortName
+ + "'. This group was previously created manually or through synchronization with a lower priority user registry.");
+ }
+ ChainingUserRegistrySynchronizer.this.authorityService.deleteAuthority(groupName);
+ // create the group
+ ChainingUserRegistrySynchronizer.this.authorityService.createAuthority(AuthorityType
+ .getAuthorityType(groupName), groupShortName, (String) groupProperties
+ .get(ContentModel.PROP_AUTHORITY_DISPLAY_NAME), zoneSet);
+ Set children = group.getChildAssociations();
+ if (!children.isEmpty())
+ {
+ groupAssocsToCreate.put(groupName, children);
+ }
+ }
+
+ // Increment the count of processed groups
+ processedCount++;
+ groupsCreated.add(groupName);
+
+ // Maintain the last modified date
+ Date groupLastModified = group.getLastModified();
+ if (groupLastModified != null)
+ {
+ this.latestTime = Math.max(this.latestTime, groupLastModified.getTime());
+ }
+
+ }
+ while (groups.hasNext() && processedCount < ChainingUserRegistrySynchronizer.BATCH_SIZE);
+ return processedCount;
+ }
+ }
+
+ CreationWorker creations = new CreationWorker();
+ int processedCount = 0;
while (groups.hasNext())
{
- NodeDescription group = groups.next();
- PropertyMap groupProperties = group.getProperties();
- String groupName = (String) groupProperties.get(ContentModel.PROP_AUTHORITY_NAME);
- if (groupsToDelete.remove(groupName))
- {
- // update an existing group in the same zone
- Set oldChildren = this.authorityService.getContainedAuthorities(null, groupName, true);
- Set newChildren = group.getChildAssociations();
- Set toDelete = new TreeSet(oldChildren);
- Set toAdd = new TreeSet(newChildren);
- toDelete.removeAll(newChildren);
- toAdd.removeAll(oldChildren);
- if (!toAdd.isEmpty())
- {
- groupAssocsToCreate.put(groupName, toAdd);
- }
- for (String child : toDelete)
- {
- if (ChainingUserRegistrySynchronizer.logger.isInfoEnabled())
- {
- ChainingUserRegistrySynchronizer.logger.info("Removing '"
- + this.authorityService.getShortName(child) + "' from group '"
- + this.authorityService.getShortName(groupName) + "'");
- }
- this.authorityService.removeAuthority(groupName, child);
- }
- }
- else
- {
- String groupShortName = this.authorityService.getShortName(groupName);
- Set groupZones = this.authorityService.getAuthorityZones(groupName);
- if (groupZones != null)
- {
- groupZones.retainAll(visitedZoneIds);
- if (groupZones.size() > 0)
- {
- // A group that exists in a different zone with higher precedence
- continue;
- }
- // The group existed, but in a zone with lower precedence
- if (ChainingUserRegistrySynchronizer.logger.isWarnEnabled())
- {
- ChainingUserRegistrySynchronizer.logger
- .warn("Recreating occluded group '"
- + groupShortName
- + "'. This group was previously created manually or through synchronization with a lower priority user registry.");
- }
- this.authorityService.deleteAuthority(groupName);
- }
- else
- {
- if (ChainingUserRegistrySynchronizer.logger.isInfoEnabled())
- {
- ChainingUserRegistrySynchronizer.logger.info("Creating group '" + groupShortName + "'");
- }
- }
-
- // create the group
- this.authorityService.createAuthority(AuthorityType.getAuthorityType(groupName), groupShortName,
- (String) groupProperties.get(ContentModel.PROP_AUTHORITY_DISPLAY_NAME), getZones(zoneId));
- Set children = group.getChildAssociations();
- if (!children.isEmpty())
- {
- groupAssocsToCreate.put(groupName, children);
- }
- }
-
- // Increment the count of processed groups
- processedCount++;
-
- // Maintain the last modified date
- Date groupLastModified = group.getLastModified();
- if (groupLastModified != null)
- {
- lastModifiedMillis = Math.max(lastModifiedMillis, groupLastModified.getTime());
- }
+ processedCount += this.retryingTransactionHelper.doInTransaction(creations, false, splitTxns);
+ }
+ long latestTime = creations.getLatestTime();
+ if (latestTime != -1)
+ {
+ setMostRecentUpdateTime(ChainingUserRegistrySynchronizer.GROUP_LAST_MODIFIED_ATTRIBUTE, zoneId, latestTime);
}
// Add the new associations, now that we have created everything
- for (Map.Entry> entry : groupAssocsToCreate.entrySet())
+
+ final Iterator>> groupAssocs = groupAssocsToCreate.entrySet().iterator();
+ class AssocWorker implements RetryingTransactionCallback
{
- for (String child : entry.getValue())
+ /*
+ * (non-Javadoc)
+ * @see org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback#execute()
+ */
+ public Integer execute() throws Throwable
{
- String groupName = entry.getKey();
- if (ChainingUserRegistrySynchronizer.logger.isInfoEnabled())
+ int processedCount = 0;
+ do
{
- ChainingUserRegistrySynchronizer.logger.info("Adding '" + this.authorityService.getShortName(child)
- + "' to group '" + this.authorityService.getShortName(groupName) + "'");
- }
- try
- {
- this.authorityService.addAuthority(groupName, child);
- }
- catch (Exception e)
- {
- // Let's not allow referential integrity problems (dangling references) kill the whole process
- if (ChainingUserRegistrySynchronizer.logger.isWarnEnabled())
+
+ Map.Entry> entry = groupAssocs.next();
+ for (String child : entry.getValue())
{
- ChainingUserRegistrySynchronizer.logger.warn("Failed to add '"
- + this.authorityService.getShortName(child) + "' to group '"
- + this.authorityService.getShortName(groupName) + "'", e);
+ String groupName = entry.getKey();
+ if (ChainingUserRegistrySynchronizer.logger.isInfoEnabled())
+ {
+ ChainingUserRegistrySynchronizer.logger.info("Adding '"
+ + ChainingUserRegistrySynchronizer.this.authorityService.getShortName(child)
+ + "' to group '"
+ + ChainingUserRegistrySynchronizer.this.authorityService.getShortName(groupName)
+ + "'");
+ }
+ try
+ {
+ ChainingUserRegistrySynchronizer.this.authorityService.addAuthority(groupName, child);
+ }
+ catch (Exception e)
+ {
+ // Let's not allow referential integrity problems (dangling references) kill the whole
+ // process
+ if (ChainingUserRegistrySynchronizer.logger.isWarnEnabled())
+ {
+ ChainingUserRegistrySynchronizer.logger.warn("Failed to add '"
+ + ChainingUserRegistrySynchronizer.this.authorityService.getShortName(child)
+ + "' to group '"
+ + ChainingUserRegistrySynchronizer.this.authorityService
+ .getShortName(groupName) + "'", e);
+ }
+ }
+
}
}
- }
+ while (groupAssocs.hasNext() && processedCount < ChainingUserRegistrySynchronizer.BATCH_SIZE);
+ return processedCount;
+ }
+ }
+
+ AssocWorker assocs = new AssocWorker();
+ while (groupAssocs.hasNext())
+ {
+ this.retryingTransactionHelper.doInTransaction(assocs, false, splitTxns);
}
// Delete groups if we have complete information for the zone
- if (force && !groupsToDelete.isEmpty())
+ if (force)
{
- for (String group : groupsToDelete)
+ class DeletionWorker implements RetryingTransactionCallback
{
- if (ChainingUserRegistrySynchronizer.logger.isWarnEnabled())
+ /*
+ * (non-Javadoc)
+ * @see org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback#execute()
+ */
+ public Integer execute() throws Throwable
{
- ChainingUserRegistrySynchronizer.logger.warn("Deleting group '"
- + this.authorityService.getShortName(group) + "'");
+ int processedCount = 0;
+ Set groupsToDelete = ChainingUserRegistrySynchronizer.this.authorityService
+ .getAllAuthoritiesInZone(zoneId, AuthorityType.GROUP);
+ groupsToDelete.removeAll(groupsCreated);
+ for (String group : groupsToDelete)
+ {
+ if (ChainingUserRegistrySynchronizer.logger.isWarnEnabled())
+ {
+ ChainingUserRegistrySynchronizer.logger.warn("Deleting group '"
+ + ChainingUserRegistrySynchronizer.this.authorityService.getShortName(group) + "'");
+ }
+ ChainingUserRegistrySynchronizer.this.authorityService.deleteAuthority(group);
+ processedCount++;
+ }
+ return processedCount;
}
- this.authorityService.deleteAuthority(group);
- processedCount++;
}
- }
- if (lastModifiedMillis != -1)
- {
- setMostRecentUpdateTime(ChainingUserRegistrySynchronizer.GROUP_LAST_MODIFIED_ATTRIBUTE, zoneId,
- lastModifiedMillis);
+ // Just use a single transaction
+ DeletionWorker deletions = new DeletionWorker();
+ processedCount += this.retryingTransactionHelper.doInTransaction(deletions, false, splitTxns);
}
// Remember we have visited this zone
@@ -615,9 +827,49 @@ public class ChainingUserRegistrySynchronizer implements UserRegistrySynchronize
*/
private Set getZones(String zoneId)
{
- HashSet zones = new HashSet(2, 1.0f);
+ Set zones = new HashSet(2, 1.0f);
zones.add(AuthorityService.ZONE_APP_DEFAULT);
zones.add(zoneId);
return zones;
}
+
+ @Override
+ protected void onBootstrap(ApplicationEvent event)
+ {
+ // Do an initial differential sync on startup, using transaction splitting. This ensures that on the very
+ // first startup, we don't have to wait for a very long login operation to trigger the first sync!
+ if (this.syncOnStartup)
+ {
+ AuthenticationUtil.runAs(new RunAsWork