mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
Merged V3.2 to HEAD
17462: ETHREEOH-3346: New meaning to synchronization.synchronizeChangesOnly property - In the LDAP sync performance optimizations we always used the differential queries to determine the users and groups to be updated. Deletions were determined by a separate query. - This meant that if you ever did want to force the update of all users it wasn't possible. - So now when the flag is false it means don't use differential queries in the scheduled sync job. - The scheduled job now processes deletions regardless. - The default value for the property is now true. 17431: ETHREEOH-3274: Refix NTLM support for share - Fixed NPE introduced by ETHREEOH-2767 - Made web.xml validate against schema for JBoss - Reintroduced missing open comment in webscript-framework-config-custom.xml.sample 17426: ETHREEOH-2997: Fix ticket parameter passing into NTLM/Kerberos WebDAV authentication filters - A NPE was stopping it from working 17425: ETHREEOH-3282: Fixed NPE preventing upload from working with NTLM SSO enabled 17368: ETHREEOH-3197: Use utf8_bin collation in MySQL out of the box to avoid problems with comparison of accented characters 17361: ETHREEOH-3276: Don't attempt to start an LDAP sync when the repository is read only 17347: ETHREEOH-3206: Fix LocalFeedTaskProcessor to work with JBoss 5 git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@17464 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -56,8 +56,8 @@
|
||||
<property name="applicationContextManager">
|
||||
<ref bean="Authentication" />
|
||||
</property>
|
||||
<property name="retryingTransactionHelper">
|
||||
<ref bean="retryingTransactionHelper" />
|
||||
<property name="transactionService">
|
||||
<ref bean="transactionService" />
|
||||
</property>
|
||||
<property name="ruleService">
|
||||
<ref bean="ruleService" />
|
||||
|
@@ -2,12 +2,14 @@
|
||||
# This properties file is used to configure user registry syncronisation (e.g. LDAP)
|
||||
#
|
||||
|
||||
# Should the scheduled sync job only query users and groups changed since the
|
||||
# last sync? Note that when true, the sync job will not be able to detect which
|
||||
# users or groups have been removed from the directory (but obviously group
|
||||
# membership changes would still be reflected). When false, a more regular
|
||||
# differential sync on login can still be enabled.
|
||||
synchronization.synchronizeChangesOnly=false
|
||||
# Should the scheduled sync job use differential or full queries on the user
|
||||
# registries to determine the set of local users to be updated? When true,
|
||||
# each user registry is only queried for those users and groups modified since
|
||||
# the most recent modification date of all the objects last queried from that
|
||||
# same source. When <code>false</code> then <i>all</i> users and groups are
|
||||
# queried from the user registry and updated locally. Nevertheless, a separate
|
||||
# query will be run by the scheduled sync job to determine deletions.
|
||||
synchronization.synchronizeChangesOnly=true
|
||||
|
||||
# The cron expression defining when imports should take place
|
||||
synchronization.import.cron=0 0 0 * * ?
|
||||
|
@@ -47,6 +47,9 @@ import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.site.SiteService;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||
@@ -59,7 +62,7 @@ import freemarker.template.DefaultObjectWrapper;
|
||||
/**
|
||||
* The local (ie. not grid) feed task processor is responsible for processing the individual feed job
|
||||
*/
|
||||
public class LocalFeedTaskProcessor extends FeedTaskProcessor
|
||||
public class LocalFeedTaskProcessor extends FeedTaskProcessor implements ApplicationContextAware
|
||||
{
|
||||
private static final Log logger = LogFactory.getLog(LocalFeedTaskProcessor.class);
|
||||
|
||||
@@ -74,6 +77,7 @@ public class LocalFeedTaskProcessor extends FeedTaskProcessor
|
||||
private String defaultEncoding;
|
||||
private List<String> templateSearchPaths;
|
||||
private boolean useRemoteCallbacks;
|
||||
private ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
|
||||
|
||||
// used to start/end/commit transaction
|
||||
// note: currently assumes that all dao services are configured with this mapper / data source
|
||||
@@ -129,6 +133,11 @@ public class LocalFeedTaskProcessor extends FeedTaskProcessor
|
||||
this.sqlMapper = sqlMapper;
|
||||
}
|
||||
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
|
||||
{
|
||||
this.resolver = applicationContext;
|
||||
}
|
||||
|
||||
public void startTransaction() throws SQLException
|
||||
{
|
||||
sqlMapper.startTransaction();
|
||||
@@ -307,7 +316,6 @@ public class LocalFeedTaskProcessor extends FeedTaskProcessor
|
||||
// Helper to return a list of resource document paths based on a search pattern.
|
||||
private List<String> getPaths(String pattern, String classPath) throws IOException
|
||||
{
|
||||
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
|
||||
Resource[] resources = resolver.getResources(pattern);
|
||||
List<String> documentPaths = new ArrayList<String>(resources.length);
|
||||
for (Resource resource : resources)
|
||||
|
@@ -59,6 +59,7 @@ import org.alfresco.service.cmr.security.AuthorityType;
|
||||
import org.alfresco.service.cmr.security.PersonService;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.service.transaction.TransactionService;
|
||||
import org.alfresco.util.AbstractLifecycleBean;
|
||||
import org.alfresco.util.PropertyMap;
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -132,6 +133,9 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
|
||||
/** The attribute service. */
|
||||
private AttributeService attributeService;
|
||||
|
||||
/** The transaction service. */
|
||||
private TransactionService transactionService;
|
||||
|
||||
/** The retrying transaction helper. */
|
||||
private RetryingTransactionHelper retryingTransactionHelper;
|
||||
|
||||
@@ -215,14 +219,15 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the retrying transaction helper.
|
||||
* Sets the transaction service.
|
||||
*
|
||||
* @param retryingTransactionHelper
|
||||
* the new retrying transaction helper
|
||||
* @param transactionService
|
||||
* the transaction service
|
||||
*/
|
||||
public void setRetryingTransactionHelper(RetryingTransactionHelper retryingTransactionHelper)
|
||||
public void setTransactionService(TransactionService transactionService)
|
||||
{
|
||||
this.retryingTransactionHelper = retryingTransactionHelper;
|
||||
this.transactionService = transactionService;
|
||||
this.retryingTransactionHelper = transactionService.getRetryingTransactionHelper();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -315,10 +320,18 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.alfresco.repo.security.sync.UserRegistrySynchronizer#synchronize(boolean, boolean)
|
||||
* @see org.alfresco.repo.security.sync.UserRegistrySynchronizer#synchronize(boolean, boolean, boolean)
|
||||
*/
|
||||
public void synchronize(boolean force, boolean splitTxns)
|
||||
public void synchronize(boolean forceUpdate, boolean allowDeletions, boolean splitTxns)
|
||||
{
|
||||
// Don't proceed with the sync if the repository is read only
|
||||
if (this.transactionService.isReadOnly())
|
||||
{
|
||||
ChainingUserRegistrySynchronizer.logger
|
||||
.warn("Unable to proceed with user registry synchronization. Repository is read only.");
|
||||
return;
|
||||
}
|
||||
|
||||
String lockToken = null;
|
||||
|
||||
// Let's ensure all exceptions get logged
|
||||
@@ -380,10 +393,10 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
|
||||
ChainingUserRegistrySynchronizer.logger
|
||||
.info("Synchronizing users and groups with user registry '" + id + "'");
|
||||
}
|
||||
if (force && ChainingUserRegistrySynchronizer.logger.isWarnEnabled())
|
||||
if (allowDeletions && ChainingUserRegistrySynchronizer.logger.isWarnEnabled())
|
||||
{
|
||||
ChainingUserRegistrySynchronizer.logger
|
||||
.warn("Forced synchronization with user registry '"
|
||||
.warn("Full synchronization with user registry '"
|
||||
+ id
|
||||
+ "'; some users and groups previously created by synchronization with this user registry may be removed.");
|
||||
}
|
||||
@@ -393,7 +406,7 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
|
||||
boolean requiresNew = splitTxns
|
||||
|| AlfrescoTransactionSupport.getTransactionReadState() == TxnReadState.TXN_READ_ONLY;
|
||||
|
||||
syncWithPlugin(id, plugin, force, requiresNew, visitedZoneIds, allZoneIds);
|
||||
syncWithPlugin(id, plugin, forceUpdate, allowDeletions, requiresNew, visitedZoneIds, allZoneIds);
|
||||
}
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException e)
|
||||
@@ -439,7 +452,7 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
|
||||
{
|
||||
try
|
||||
{
|
||||
synchronize(false, false);
|
||||
synchronize(false, false, false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -474,8 +487,16 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
|
||||
* tell those that have been deleted from the registry.
|
||||
* @param userRegistry
|
||||
* the user registry for the zone.
|
||||
* @param force
|
||||
* <code>true</code> if user and group deletions are to be processed.
|
||||
* @param forceUpdate
|
||||
* Should the complete set of users and groups be updated / created locally or just those known to have
|
||||
* changed since the last sync? When <code>true</code> then <i>all</i> users and groups are queried from
|
||||
* the user registry and updated locally. When <code>false</code> then each source is only queried for
|
||||
* those users and groups modified since the most recent modification date of all the objects last
|
||||
* queried from that same source.
|
||||
* @param allowDeletions
|
||||
* Should a complete set of user and group IDs be queried from the user registries in order to determine
|
||||
* deletions? This parameter is independent of <code>force</code> as a separate query is run to process
|
||||
* updates.
|
||||
* @param splitTxns
|
||||
* Can the modifications to Alfresco be split across multiple transactions for maximum performance? If
|
||||
* <code>true</code>, users and groups are created/updated in batches for increased performance. If
|
||||
@@ -490,8 +511,8 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
|
||||
* recorded against a user or group is invalid for the current authentication chain and whether the user
|
||||
* or group needs to be 're-zoned'.
|
||||
*/
|
||||
private void syncWithPlugin(final String zone, UserRegistry userRegistry, boolean force, boolean splitTxns,
|
||||
final Set<String> visitedZoneIds, final Set<String> allZoneIds)
|
||||
private void syncWithPlugin(final String zone, UserRegistry userRegistry, boolean forceUpdate,
|
||||
boolean allowDeletions, boolean splitTxns, final Set<String> visitedZoneIds, final Set<String> allZoneIds)
|
||||
{
|
||||
// Create a prefixed zone ID for use with the authority service
|
||||
final String zoneId = AuthorityService.ZONE_AUTH_EXT_PREFIX + zone;
|
||||
@@ -499,7 +520,7 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
|
||||
// The set of zones we associate with new objects (default plus registry specific)
|
||||
final Set<String> zoneSet = getZones(zoneId);
|
||||
|
||||
long lastModifiedMillis = getMostRecentUpdateTime(
|
||||
long lastModifiedMillis = forceUpdate ? -1 : getMostRecentUpdateTime(
|
||||
ChainingUserRegistrySynchronizer.GROUP_LAST_MODIFIED_ATTRIBUTE, zoneId, splitTxns);
|
||||
Date lastModified = lastModifiedMillis == -1 ? null : new Date(lastModifiedMillis);
|
||||
|
||||
@@ -708,7 +729,7 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
|
||||
Set<String> deletionCandidates = null;
|
||||
|
||||
// If we got back some groups, we have to cross reference them with the set of known authorities
|
||||
if (force || !groupAssocsToCreate.isEmpty())
|
||||
if (allowDeletions || !groupAssocsToCreate.isEmpty())
|
||||
{
|
||||
// Get current set of known authorities
|
||||
Set<String> allZoneAuthorities = this.retryingTransactionHelper.doInTransaction(
|
||||
@@ -724,7 +745,7 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
|
||||
allZoneAuthorities.addAll(groupAnalyzer.getAllZoneAuthorities());
|
||||
|
||||
// Prune our set of authorities according to deletions
|
||||
if (force)
|
||||
if (allowDeletions)
|
||||
{
|
||||
deletionCandidates = new TreeSet<String>(allZoneAuthorities);
|
||||
userRegistry.processDeletions(deletionCandidates);
|
||||
@@ -822,8 +843,8 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
|
||||
|
||||
// Process persons and their parent associations
|
||||
|
||||
lastModifiedMillis = getMostRecentUpdateTime(ChainingUserRegistrySynchronizer.PERSON_LAST_MODIFIED_ATTRIBUTE,
|
||||
zoneId, splitTxns);
|
||||
lastModifiedMillis = forceUpdate ? -1 : getMostRecentUpdateTime(
|
||||
ChainingUserRegistrySynchronizer.PERSON_LAST_MODIFIED_ATTRIBUTE, zoneId, splitTxns);
|
||||
lastModified = lastModifiedMillis == -1 ? null : new Date(lastModifiedMillis);
|
||||
if (ChainingUserRegistrySynchronizer.logger.isInfoEnabled())
|
||||
{
|
||||
@@ -994,7 +1015,7 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
|
||||
}
|
||||
|
||||
// Delete authorities if we have complete information for the zone
|
||||
if (force)
|
||||
if (allowDeletions)
|
||||
{
|
||||
BatchProcessor<String> authorityDeletionProcessor = new BatchProcessor<String>(
|
||||
this.retryingTransactionHelper, this.ruleService, this.applicationEventPublisher,
|
||||
@@ -1214,7 +1235,7 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
|
||||
{
|
||||
try
|
||||
{
|
||||
synchronize(false, true);
|
||||
synchronize(false, false, true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@@ -171,7 +171,7 @@ public class ChainingUserRegistrySynchronizerTest extends TestCase
|
||||
{
|
||||
newGroup("G2", "U1", "U3", "U4"), newGroup("G6", "U3", "U4", "G7"), newGroup("G7", "U5")
|
||||
}));
|
||||
this.synchronizer.synchronize(true, true);
|
||||
this.synchronizer.synchronize(true, true, true);
|
||||
this.retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback<Object>()
|
||||
{
|
||||
|
||||
@@ -208,7 +208,7 @@ public class ChainingUserRegistrySynchronizerTest extends TestCase
|
||||
new NodeDescription[] {}), new MockUserRegistry("Z1", new NodeDescription[] {},
|
||||
new NodeDescription[] {}), new MockUserRegistry("Z2", new NodeDescription[] {},
|
||||
new NodeDescription[] {}));
|
||||
this.synchronizer.synchronize(true, true);
|
||||
this.synchronizer.synchronize(true, true, true);
|
||||
this.retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback<Object>()
|
||||
{
|
||||
|
||||
@@ -271,7 +271,7 @@ public class ChainingUserRegistrySynchronizerTest extends TestCase
|
||||
public Object execute() throws Throwable
|
||||
{
|
||||
|
||||
ChainingUserRegistrySynchronizerTest.this.synchronizer.synchronize(false, false);
|
||||
ChainingUserRegistrySynchronizerTest.this.synchronizer.synchronize(false, false, false);
|
||||
// Stay in the same transaction
|
||||
assertExists("Z1", "U1");
|
||||
assertEmailEquals("U1", "changeofemail@alfresco.com");
|
||||
@@ -332,7 +332,7 @@ public class ChainingUserRegistrySynchronizerTest extends TestCase
|
||||
{
|
||||
newGroup("G2", "U1", "U3", "U4", "U6"), newGroup("G6", "U3", "U4", "G7"), newGroup("G7", "U4", "U5")
|
||||
}));
|
||||
this.synchronizer.synchronize(true, true);
|
||||
this.synchronizer.synchronize(true, true, true);
|
||||
this.retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback<Object>()
|
||||
{
|
||||
|
||||
@@ -369,7 +369,7 @@ public class ChainingUserRegistrySynchronizerTest extends TestCase
|
||||
List<NodeDescription> persons = new ArrayList<NodeDescription>(new RandomPersonCollection(100));
|
||||
List<NodeDescription> groups = new ArrayList<NodeDescription>(new RandomGroupCollection(100, persons));
|
||||
this.applicationContextManager.setUserRegistries(new MockUserRegistry("Z0", persons, groups));
|
||||
this.synchronizer.synchronize(true, true);
|
||||
this.synchronizer.synchronize(true, true, true);
|
||||
tearDownTestUsersAndGroups();
|
||||
}
|
||||
|
||||
@@ -394,7 +394,7 @@ public class ChainingUserRegistrySynchronizerTest extends TestCase
|
||||
}, true, true);
|
||||
ChainingUserRegistrySynchronizerTest.this.applicationContextManager.setUserRegistries(new MockUserRegistry(
|
||||
"Z0", Collections.<NodeDescription> emptyList(), groups));
|
||||
ChainingUserRegistrySynchronizerTest.this.synchronizer.synchronize(true, true);
|
||||
ChainingUserRegistrySynchronizerTest.this.synchronizer.synchronize(true, true, true);
|
||||
tearDownTestUsersAndGroups();
|
||||
}
|
||||
|
||||
|
@@ -48,19 +48,21 @@ public interface UserRegistrySynchronizer
|
||||
* users and groups last retrieved from the same sources. Any updates and additions made to those users and groups
|
||||
* are applied to the local Alfresco copies.
|
||||
*
|
||||
* @param force
|
||||
* Should a complete or partial set of information be queried from the external sources? When
|
||||
* <code>true</code> then <i>all</i> users and groups are queried. With this complete set of information,
|
||||
* the implementation is able to identify which users and groups have been deleted, so it will delete
|
||||
* users and groups as well as update and create them. When <code>false</code> then each source is only
|
||||
* queried for those users and groups modified since the most recent modification date of all the objects
|
||||
* last queried from that same source. In this mode, local users and groups are created and updated, but
|
||||
* not deleted.
|
||||
* @param forceUpdate
|
||||
* Should the complete set of users and groups be updated / created locally or just those known to have
|
||||
* changed since the last sync? When <code>true</code> then <i>all</i> users and groups are queried from
|
||||
* the user registry and updated locally. When <code>false</code> then each source is only queried for
|
||||
* those users and groups modified since the most recent modification date of all the objects last
|
||||
* queried from that same source.
|
||||
* @param allowDeletions
|
||||
* Should a complete set of user and group IDs be queried from the user registries in order to determine
|
||||
* deletions? This parameter is independent of <code>force</code> as a separate query is run to process
|
||||
* updates.
|
||||
* @param splitTxns
|
||||
* Can the modifications to Alfresco be split across multiple transactions for maximum performance? If
|
||||
* <code>true</code>, users and groups are created/updated in batches of 10 for increased performance. If
|
||||
* <code>false</code>, 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).
|
||||
*/
|
||||
public void synchronize(boolean force, boolean splitTxns);
|
||||
public void synchronize(boolean forceUpdate, boolean allowDeletions, boolean splitTxns);
|
||||
}
|
@@ -32,9 +32,9 @@ import org.quartz.JobExecutionException;
|
||||
|
||||
/**
|
||||
* A scheduled job that regularly invokes a {@link UserRegistrySynchronizer}. Supports a
|
||||
* <code>synchronizeChangesOnly</code> string parameter. When <code>"true"</code> means that the
|
||||
* {@link UserRegistrySynchronizer#synchronize(boolean)} method will be called with a <code>false</code> argument rather
|
||||
* than the default <code>true</code>.
|
||||
* <code>synchronizeChangesOnly</code> string parameter. When <code>"false"</code> means that the
|
||||
* {@link UserRegistrySynchronizer#synchronize(boolean)} method will be called with a <code>true</code> forceUpdate
|
||||
* argument rather than the default <code>false</code>.
|
||||
*
|
||||
* @author dward
|
||||
*/
|
||||
@@ -55,7 +55,7 @@ public class UserRegistrySynchronizerJob implements Job
|
||||
public Object doWork() throws Exception
|
||||
{
|
||||
userRegistrySynchronizer.synchronize(synchronizeChangesOnly == null
|
||||
|| !Boolean.parseBoolean(synchronizeChangesOnly), true);
|
||||
|| !Boolean.parseBoolean(synchronizeChangesOnly), true, true);
|
||||
return null;
|
||||
}
|
||||
}, AuthenticationUtil.getSystemUserName());
|
||||
|
@@ -15,8 +15,8 @@
|
||||
<property name="applicationContextManager">
|
||||
<ref bean="testApplicationContextManager" />
|
||||
</property>
|
||||
<property name="retryingTransactionHelper">
|
||||
<ref bean="retryingTransactionHelper" />
|
||||
<property name="transactionService">
|
||||
<ref bean="transactionService" />
|
||||
</property>
|
||||
<property name="ruleService">
|
||||
<ref bean="ruleService" />
|
||||
|
Reference in New Issue
Block a user