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:
Dave Ward
2009-11-13 12:40:33 +00:00
parent 1fa09a014d
commit abe965cf15
8 changed files with 87 additions and 54 deletions

View File

@@ -56,8 +56,8 @@
<property name="applicationContextManager"> <property name="applicationContextManager">
<ref bean="Authentication" /> <ref bean="Authentication" />
</property> </property>
<property name="retryingTransactionHelper"> <property name="transactionService">
<ref bean="retryingTransactionHelper" /> <ref bean="transactionService" />
</property> </property>
<property name="ruleService"> <property name="ruleService">
<ref bean="ruleService" /> <ref bean="ruleService" />

View File

@@ -2,12 +2,14 @@
# This properties file is used to configure user registry syncronisation (e.g. LDAP) # 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 # Should the scheduled sync job use differential or full queries on the user
# last sync? Note that when true, the sync job will not be able to detect which # registries to determine the set of local users to be updated? When true,
# users or groups have been removed from the directory (but obviously group # each user registry is only queried for those users and groups modified since
# membership changes would still be reflected). When false, a more regular # the most recent modification date of all the objects last queried from that
# differential sync on login can still be enabled. # same source. When <code>false</code> then <i>all</i> users and groups are
synchronization.synchronizeChangesOnly=false # 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 # The cron expression defining when imports should take place
synchronization.import.cron=0 0 0 * * ? synchronization.import.cron=0 0 0 * * ?

View File

@@ -47,6 +47,9 @@ import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.site.SiteService; import org.alfresco.service.cmr.site.SiteService;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; 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.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver; 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 * 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); private static final Log logger = LogFactory.getLog(LocalFeedTaskProcessor.class);
@@ -74,6 +77,7 @@ public class LocalFeedTaskProcessor extends FeedTaskProcessor
private String defaultEncoding; private String defaultEncoding;
private List<String> templateSearchPaths; private List<String> templateSearchPaths;
private boolean useRemoteCallbacks; private boolean useRemoteCallbacks;
private ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
// used to start/end/commit transaction // used to start/end/commit transaction
// note: currently assumes that all dao services are configured with this mapper / data source // 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; this.sqlMapper = sqlMapper;
} }
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{
this.resolver = applicationContext;
}
public void startTransaction() throws SQLException public void startTransaction() throws SQLException
{ {
sqlMapper.startTransaction(); 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. // Helper to return a list of resource document paths based on a search pattern.
private List<String> getPaths(String pattern, String classPath) throws IOException private List<String> getPaths(String pattern, String classPath) throws IOException
{ {
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources(pattern); Resource[] resources = resolver.getResources(pattern);
List<String> documentPaths = new ArrayList<String>(resources.length); List<String> documentPaths = new ArrayList<String>(resources.length);
for (Resource resource : resources) for (Resource resource : resources)

View File

@@ -59,6 +59,7 @@ import org.alfresco.service.cmr.security.AuthorityType;
import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.AbstractLifecycleBean; import org.alfresco.util.AbstractLifecycleBean;
import org.alfresco.util.PropertyMap; import org.alfresco.util.PropertyMap;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
@@ -132,6 +133,9 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
/** The attribute service. */ /** The attribute service. */
private AttributeService attributeService; private AttributeService attributeService;
/** The transaction service. */
private TransactionService transactionService;
/** The retrying transaction helper. */ /** The retrying transaction helper. */
private RetryingTransactionHelper retryingTransactionHelper; private RetryingTransactionHelper retryingTransactionHelper;
@@ -215,14 +219,15 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
} }
/** /**
* Sets the retrying transaction helper. * Sets the transaction service.
* *
* @param retryingTransactionHelper * @param transactionService
* the new retrying transaction helper * 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) * (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; String lockToken = null;
// Let's ensure all exceptions get logged // Let's ensure all exceptions get logged
@@ -380,10 +393,10 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
ChainingUserRegistrySynchronizer.logger ChainingUserRegistrySynchronizer.logger
.info("Synchronizing users and groups with user registry '" + id + "'"); .info("Synchronizing users and groups with user registry '" + id + "'");
} }
if (force && ChainingUserRegistrySynchronizer.logger.isWarnEnabled()) if (allowDeletions && ChainingUserRegistrySynchronizer.logger.isWarnEnabled())
{ {
ChainingUserRegistrySynchronizer.logger ChainingUserRegistrySynchronizer.logger
.warn("Forced synchronization with user registry '" .warn("Full synchronization with user registry '"
+ id + id
+ "'; some users and groups previously created by synchronization with this user registry may be removed."); + "'; 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 boolean requiresNew = splitTxns
|| AlfrescoTransactionSupport.getTransactionReadState() == TxnReadState.TXN_READ_ONLY; || AlfrescoTransactionSupport.getTransactionReadState() == TxnReadState.TXN_READ_ONLY;
syncWithPlugin(id, plugin, force, requiresNew, visitedZoneIds, allZoneIds); syncWithPlugin(id, plugin, forceUpdate, allowDeletions, requiresNew, visitedZoneIds, allZoneIds);
} }
} }
catch (NoSuchBeanDefinitionException e) catch (NoSuchBeanDefinitionException e)
@@ -439,7 +452,7 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
{ {
try try
{ {
synchronize(false, false); synchronize(false, false, false);
} }
catch (Exception e) catch (Exception e)
{ {
@@ -474,8 +487,16 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
* tell those that have been deleted from the registry. * tell those that have been deleted from the registry.
* @param userRegistry * @param userRegistry
* the user registry for the zone. * the user registry for the zone.
* @param force * @param forceUpdate
* <code>true</code> if user and group deletions are to be processed. * 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 * @param splitTxns
* Can the modifications to Alfresco be split across multiple transactions for maximum performance? If * 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 * <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 * recorded against a user or group is invalid for the current authentication chain and whether the user
* or group needs to be 're-zoned'. * or group needs to be 're-zoned'.
*/ */
private void syncWithPlugin(final String zone, UserRegistry userRegistry, boolean force, boolean splitTxns, private void syncWithPlugin(final String zone, UserRegistry userRegistry, boolean forceUpdate,
final Set<String> visitedZoneIds, final Set<String> allZoneIds) boolean allowDeletions, boolean splitTxns, final Set<String> visitedZoneIds, final Set<String> allZoneIds)
{ {
// Create a prefixed zone ID for use with the authority service // Create a prefixed zone ID for use with the authority service
final String zoneId = AuthorityService.ZONE_AUTH_EXT_PREFIX + zone; 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) // The set of zones we associate with new objects (default plus registry specific)
final Set<String> zoneSet = getZones(zoneId); final Set<String> zoneSet = getZones(zoneId);
long lastModifiedMillis = getMostRecentUpdateTime( long lastModifiedMillis = forceUpdate ? -1 : getMostRecentUpdateTime(
ChainingUserRegistrySynchronizer.GROUP_LAST_MODIFIED_ATTRIBUTE, zoneId, splitTxns); ChainingUserRegistrySynchronizer.GROUP_LAST_MODIFIED_ATTRIBUTE, zoneId, splitTxns);
Date lastModified = lastModifiedMillis == -1 ? null : new Date(lastModifiedMillis); Date lastModified = lastModifiedMillis == -1 ? null : new Date(lastModifiedMillis);
@@ -708,7 +729,7 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
Set<String> deletionCandidates = null; Set<String> deletionCandidates = null;
// If we got back some groups, we have to cross reference them with the set of known authorities // 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 // Get current set of known authorities
Set<String> allZoneAuthorities = this.retryingTransactionHelper.doInTransaction( Set<String> allZoneAuthorities = this.retryingTransactionHelper.doInTransaction(
@@ -724,7 +745,7 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
allZoneAuthorities.addAll(groupAnalyzer.getAllZoneAuthorities()); allZoneAuthorities.addAll(groupAnalyzer.getAllZoneAuthorities());
// Prune our set of authorities according to deletions // Prune our set of authorities according to deletions
if (force) if (allowDeletions)
{ {
deletionCandidates = new TreeSet<String>(allZoneAuthorities); deletionCandidates = new TreeSet<String>(allZoneAuthorities);
userRegistry.processDeletions(deletionCandidates); userRegistry.processDeletions(deletionCandidates);
@@ -822,8 +843,8 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
// Process persons and their parent associations // Process persons and their parent associations
lastModifiedMillis = getMostRecentUpdateTime(ChainingUserRegistrySynchronizer.PERSON_LAST_MODIFIED_ATTRIBUTE, lastModifiedMillis = forceUpdate ? -1 : getMostRecentUpdateTime(
zoneId, splitTxns); ChainingUserRegistrySynchronizer.PERSON_LAST_MODIFIED_ATTRIBUTE, zoneId, splitTxns);
lastModified = lastModifiedMillis == -1 ? null : new Date(lastModifiedMillis); lastModified = lastModifiedMillis == -1 ? null : new Date(lastModifiedMillis);
if (ChainingUserRegistrySynchronizer.logger.isInfoEnabled()) if (ChainingUserRegistrySynchronizer.logger.isInfoEnabled())
{ {
@@ -994,7 +1015,7 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
} }
// Delete authorities if we have complete information for the zone // Delete authorities if we have complete information for the zone
if (force) if (allowDeletions)
{ {
BatchProcessor<String> authorityDeletionProcessor = new BatchProcessor<String>( BatchProcessor<String> authorityDeletionProcessor = new BatchProcessor<String>(
this.retryingTransactionHelper, this.ruleService, this.applicationEventPublisher, this.retryingTransactionHelper, this.ruleService, this.applicationEventPublisher,
@@ -1214,7 +1235,7 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl
{ {
try try
{ {
synchronize(false, true); synchronize(false, false, true);
} }
catch (Exception e) catch (Exception e)
{ {

View File

@@ -171,7 +171,7 @@ public class ChainingUserRegistrySynchronizerTest extends TestCase
{ {
newGroup("G2", "U1", "U3", "U4"), newGroup("G6", "U3", "U4", "G7"), newGroup("G7", "U5") 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>() 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("Z1", new NodeDescription[] {},
new NodeDescription[] {}), new MockUserRegistry("Z2", new NodeDescription[] {}, new NodeDescription[] {}), new MockUserRegistry("Z2", new NodeDescription[] {},
new NodeDescription[] {})); new NodeDescription[] {}));
this.synchronizer.synchronize(true, true); this.synchronizer.synchronize(true, true, true);
this.retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback<Object>() this.retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback<Object>()
{ {
@@ -271,7 +271,7 @@ public class ChainingUserRegistrySynchronizerTest extends TestCase
public Object execute() throws Throwable public Object execute() throws Throwable
{ {
ChainingUserRegistrySynchronizerTest.this.synchronizer.synchronize(false, false); ChainingUserRegistrySynchronizerTest.this.synchronizer.synchronize(false, false, false);
// Stay in the same transaction // Stay in the same transaction
assertExists("Z1", "U1"); assertExists("Z1", "U1");
assertEmailEquals("U1", "changeofemail@alfresco.com"); 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") 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>() 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> persons = new ArrayList<NodeDescription>(new RandomPersonCollection(100));
List<NodeDescription> groups = new ArrayList<NodeDescription>(new RandomGroupCollection(100, persons)); List<NodeDescription> groups = new ArrayList<NodeDescription>(new RandomGroupCollection(100, persons));
this.applicationContextManager.setUserRegistries(new MockUserRegistry("Z0", persons, groups)); this.applicationContextManager.setUserRegistries(new MockUserRegistry("Z0", persons, groups));
this.synchronizer.synchronize(true, true); this.synchronizer.synchronize(true, true, true);
tearDownTestUsersAndGroups(); tearDownTestUsersAndGroups();
} }
@@ -394,7 +394,7 @@ public class ChainingUserRegistrySynchronizerTest extends TestCase
}, true, true); }, true, true);
ChainingUserRegistrySynchronizerTest.this.applicationContextManager.setUserRegistries(new MockUserRegistry( ChainingUserRegistrySynchronizerTest.this.applicationContextManager.setUserRegistries(new MockUserRegistry(
"Z0", Collections.<NodeDescription> emptyList(), groups)); "Z0", Collections.<NodeDescription> emptyList(), groups));
ChainingUserRegistrySynchronizerTest.this.synchronizer.synchronize(true, true); ChainingUserRegistrySynchronizerTest.this.synchronizer.synchronize(true, true, true);
tearDownTestUsersAndGroups(); tearDownTestUsersAndGroups();
} }

View File

@@ -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 * 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. * are applied to the local Alfresco copies.
* *
* @param force * @param forceUpdate
* Should a complete or partial set of information be queried from the external sources? When * Should the complete set of users and groups be updated / created locally or just those known to have
* <code>true</code> then <i>all</i> users and groups are queried. With this complete set of information, * changed since the last sync? When <code>true</code> then <i>all</i> users and groups are queried from
* the implementation is able to identify which users and groups have been deleted, so it will delete * the user registry and updated locally. When <code>false</code> then each source is only queried for
* users and groups as well as update and create them. When <code>false</code> then each source is only * those users and groups modified since the most recent modification date of all the objects last
* queried for those users and groups modified since the most recent modification date of all the objects * queried from that same source.
* last queried from that same source. In this mode, local users and groups are created and updated, but * @param allowDeletions
* not deleted. * 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 * @param splitTxns
* Can the modifications to Alfresco be split across multiple transactions for maximum performance? If * 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>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 * <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). * 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);
} }

View File

@@ -32,9 +32,9 @@ import org.quartz.JobExecutionException;
/** /**
* A scheduled job that regularly invokes a {@link UserRegistrySynchronizer}. Supports a * A scheduled job that regularly invokes a {@link UserRegistrySynchronizer}. Supports a
* <code>synchronizeChangesOnly</code> string parameter. When <code>"true"</code> means that the * <code>synchronizeChangesOnly</code> string parameter. When <code>"false"</code> means that the
* {@link UserRegistrySynchronizer#synchronize(boolean)} method will be called with a <code>false</code> argument rather * {@link UserRegistrySynchronizer#synchronize(boolean)} method will be called with a <code>true</code> forceUpdate
* than the default <code>true</code>. * argument rather than the default <code>false</code>.
* *
* @author dward * @author dward
*/ */
@@ -55,7 +55,7 @@ public class UserRegistrySynchronizerJob implements Job
public Object doWork() throws Exception public Object doWork() throws Exception
{ {
userRegistrySynchronizer.synchronize(synchronizeChangesOnly == null userRegistrySynchronizer.synchronize(synchronizeChangesOnly == null
|| !Boolean.parseBoolean(synchronizeChangesOnly), true); || !Boolean.parseBoolean(synchronizeChangesOnly), true, true);
return null; return null;
} }
}, AuthenticationUtil.getSystemUserName()); }, AuthenticationUtil.getSystemUserName());

View File

@@ -15,8 +15,8 @@
<property name="applicationContextManager"> <property name="applicationContextManager">
<ref bean="testApplicationContextManager" /> <ref bean="testApplicationContextManager" />
</property> </property>
<property name="retryingTransactionHelper"> <property name="transactionService">
<ref bean="retryingTransactionHelper" /> <ref bean="transactionService" />
</property> </property>
<property name="ruleService"> <property name="ruleService">
<ref bean="ruleService" /> <ref bean="ruleService" />