diff --git a/org.springframework.test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java b/org.springframework.test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java
index c62c31e35b6..c44aaf7c0a3 100644
--- a/org.springframework.test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java
+++ b/org.springframework.test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2009 the original author or authors.
+ * Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.test.annotation.NotTransactional;
import org.springframework.test.annotation.Rollback;
@@ -40,10 +41,12 @@ import org.springframework.transaction.TransactionException;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
import org.springframework.transaction.interceptor.DelegatingTransactionAttribute;
+import org.springframework.transaction.interceptor.TransactionAspectUtils;
import org.springframework.transaction.interceptor.TransactionAttribute;
import org.springframework.transaction.interceptor.TransactionAttributeSource;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
+import org.springframework.util.StringUtils;
/**
*
@@ -94,14 +97,13 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
protected final TransactionAttributeSource attributeSource = new AnnotationTransactionAttributeSource();
- private TransactionConfigurationAttributes configAttributes;
+ private TransactionConfigurationAttributes configurationAttributes;
private volatile int transactionsStarted = 0;
private final Map transactionContextCache =
Collections.synchronizedMap(new IdentityHashMap());
-
/**
* If the test method of the supplied {@link TestContext test context} is
* configured to run within a transaction, this method will run
@@ -144,8 +146,18 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
logger.debug("Explicit transaction definition [" + transactionDefinition +
"] found for test context [" + testContext + "]");
}
- TransactionContext txContext =
- new TransactionContext(getTransactionManager(testContext), transactionDefinition);
+ String qualifier = transactionAttribute.getQualifier();
+ PlatformTransactionManager tm;
+ if (StringUtils.hasLength(qualifier)) {
+ // Use autowire-capable factory in order to support extended qualifier matching
+ // (only exposed on the internal BeanFactory, not on the ApplicationContext).
+ BeanFactory bf = testContext.getApplicationContext().getAutowireCapableBeanFactory();
+ tm = TransactionAspectUtils.getTransactionManager(bf, qualifier);
+ }
+ else {
+ tm = getTransactionManager(testContext);
+ }
+ TransactionContext txContext = new TransactionContext(tm, transactionDefinition);
runBeforeTransactionMethods(testContext);
startNewTransaction(testContext, txContext);
this.transactionContextCache.put(testMethod, txContext);
@@ -291,18 +303,14 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
* @throws BeansException if an error occurs while retrieving the transaction manager
*/
protected final PlatformTransactionManager getTransactionManager(TestContext testContext) {
- if (this.configAttributes == null) {
- this.configAttributes = retrieveTransactionConfigurationAttributes(testContext.getTestClass());
- }
- String transactionManagerName = this.configAttributes.getTransactionManagerName();
+ String tmName = retrieveConfigurationAttributes(testContext).getTransactionManagerName();
try {
- return (PlatformTransactionManager) testContext.getApplicationContext().getBean(
- transactionManagerName, PlatformTransactionManager.class);
+ return testContext.getApplicationContext().getBean(tmName, PlatformTransactionManager.class);
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Caught exception while retrieving transaction manager with bean name [" +
- transactionManagerName + "] for test context [" + testContext + "]", ex);
+ tmName + "] for test context [" + testContext + "]", ex);
}
throw ex;
}
@@ -317,7 +325,7 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
* @throws Exception if an error occurs while determining the default rollback flag
*/
protected final boolean isDefaultRollback(TestContext testContext) throws Exception {
- return retrieveTransactionConfigurationAttributes(testContext.getTestClass()).isDefaultRollback();
+ return retrieveConfigurationAttributes(testContext).isDefaultRollback();
}
/**
@@ -439,7 +447,6 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
}
/**
- *
* Retrieves the {@link TransactionConfigurationAttributes} for the
* specified {@link Class class} which may optionally declare or inherit a
* {@link TransactionConfiguration @TransactionConfiguration}. If a
@@ -450,33 +457,36 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
* the configuration attributes should be retrieved
* @return a new TransactionConfigurationAttributes instance
*/
- private TransactionConfigurationAttributes retrieveTransactionConfigurationAttributes(Class> clazz) {
- Class annotationType = TransactionConfiguration.class;
- TransactionConfiguration config = clazz.getAnnotation(annotationType);
- if (logger.isDebugEnabled()) {
- logger.debug("Retrieved @TransactionConfiguration [" + config + "] for test class [" + clazz + "]");
- }
+ private TransactionConfigurationAttributes retrieveConfigurationAttributes(TestContext testContext) {
+ if (this.configurationAttributes == null) {
+ Class> clazz = testContext.getTestClass();
+ Class annotationType = TransactionConfiguration.class;
+ TransactionConfiguration config = clazz.getAnnotation(annotationType);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Retrieved @TransactionConfiguration [" + config + "] for test class [" + clazz + "]");
+ }
- String transactionManagerName;
- boolean defaultRollback;
- if (config != null) {
- transactionManagerName = config.transactionManager();
- defaultRollback = config.defaultRollback();
- }
- else {
- transactionManagerName = (String) AnnotationUtils.getDefaultValue(annotationType, "transactionManager");
- defaultRollback = (Boolean) AnnotationUtils.getDefaultValue(annotationType, "defaultRollback");
- }
+ String transactionManagerName;
+ boolean defaultRollback;
+ if (config != null) {
+ transactionManagerName = config.transactionManager();
+ defaultRollback = config.defaultRollback();
+ }
+ else {
+ transactionManagerName = (String) AnnotationUtils.getDefaultValue(annotationType, "transactionManager");
+ defaultRollback = (Boolean) AnnotationUtils.getDefaultValue(annotationType, "defaultRollback");
+ }
- TransactionConfigurationAttributes configAttributes =
- new TransactionConfigurationAttributes(transactionManagerName, defaultRollback);
- if (logger.isDebugEnabled()) {
- logger.debug("Retrieved TransactionConfigurationAttributes [" + configAttributes + "] for class [" + clazz + "]");
+ TransactionConfigurationAttributes configAttributes =
+ new TransactionConfigurationAttributes(transactionManagerName, defaultRollback);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Retrieved TransactionConfigurationAttributes [" + configAttributes + "] for class [" + clazz + "]");
+ }
+ this.configurationAttributes = configAttributes;
}
- return configAttributes;
+ return this.configurationAttributes;
}
-
/**
* Internal context holder for a specific test method.
*/
diff --git a/org.springframework.test/src/test/java/org/springframework/test/context/junit4/MethodLevelTransactionalSpringRunnerTests.java b/org.springframework.test/src/test/java/org/springframework/test/context/junit4/MethodLevelTransactionalSpringRunnerTests.java
index 1140a48ade1..21c8eb62a7f 100644
--- a/org.springframework.test/src/test/java/org/springframework/test/context/junit4/MethodLevelTransactionalSpringRunnerTests.java
+++ b/org.springframework.test/src/test/java/org/springframework/test/context/junit4/MethodLevelTransactionalSpringRunnerTests.java
@@ -16,13 +16,11 @@
package org.springframework.test.context.junit4;
-import static org.junit.Assert.assertEquals;
-import static org.springframework.test.transaction.TransactionTestUtils.assertInTransaction;
-
import javax.annotation.Resource;
import javax.sql.DataSource;
import org.junit.AfterClass;
+import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -34,6 +32,8 @@ import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
+import org.springframework.test.context.transaction.TransactionConfiguration;
+import static org.springframework.test.transaction.TransactionTestUtils.*;
import org.springframework.transaction.annotation.Transactional;
/**
@@ -85,7 +85,7 @@ public class MethodLevelTransactionalSpringRunnerTests extends AbstractTransacti
}
@Test
- @Transactional
+ @Transactional("transactionManager2")
public void modifyTestDataWithinTransaction() {
assertInTransaction(true);
assertEquals("Deleting bob", 1, deletePerson(simpleJdbcTemplate, BOB));
@@ -109,7 +109,7 @@ public class MethodLevelTransactionalSpringRunnerTests extends AbstractTransacti
public static class DatabaseSetup {
@Resource
- public void setDataSource(DataSource dataSource) {
+ public void setDataSource2(DataSource dataSource) {
simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
createPersonTable(simpleJdbcTemplate);
}
diff --git a/org.springframework.test/src/test/java/org/springframework/test/context/junit4/transactionalTests-context.xml b/org.springframework.test/src/test/java/org/springframework/test/context/junit4/transactionalTests-context.xml
index d84211548d3..47767798ce2 100644
--- a/org.springframework.test/src/test/java/org/springframework/test/context/junit4/transactionalTests-context.xml
+++ b/org.springframework.test/src/test/java/org/springframework/test/context/junit4/transactionalTests-context.xml
@@ -4,12 +4,16 @@
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
+ p:driverClassName="org.hsqldb.jdbcDriver" p:url="jdbc:hsqldb:mem:transactional_tests" p:username="sa" p:password=""/>
-
+
+ p:dataSource-ref="dataSource" p:transactionSynchronizationName="SYNCHRONIZATION_NEVER"/>
+
+
+
diff --git a/org.springframework.transaction/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java b/org.springframework.transaction/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java
index 02bd94b64da..d32b4a41a9a 100644
--- a/org.springframework.transaction/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java
+++ b/org.springframework.transaction/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java
@@ -17,7 +17,6 @@
package org.springframework.transaction.interceptor;
import java.lang.reflect.Method;
-import java.util.Map;
import java.util.Properties;
import org.apache.commons.logging.Log;
@@ -28,18 +27,13 @@ import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ListableBeanFactory;
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.beans.factory.config.BeanDefinition;
-import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
-import org.springframework.beans.factory.support.AbstractBeanDefinition;
-import org.springframework.beans.factory.support.AutowireCandidateQualifier;
import org.springframework.core.NamedThreadLocal;
import org.springframework.transaction.NoTransactionException;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.TransactionSystemException;
+import org.springframework.transaction.interceptor.TransactionAspectUtils;
import org.springframework.util.ClassUtils;
-import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
@@ -249,38 +243,7 @@ public abstract class TransactionAspectSupport implements BeanFactoryAware, Init
}
String qualifier = txAttr.getQualifier();
if (StringUtils.hasLength(qualifier)) {
- if (!(this.beanFactory instanceof ConfigurableListableBeanFactory)) {
- throw new IllegalStateException("BeanFactory required to be a ConfigurableListableBeanFactory " +
- "for resolution of qualifier '" + qualifier + "': " + this.beanFactory.getClass());
- }
- ConfigurableListableBeanFactory bf = (ConfigurableListableBeanFactory) this.beanFactory;
- Map tms =
- BeanFactoryUtils.beansOfTypeIncludingAncestors(bf, PlatformTransactionManager.class);
- PlatformTransactionManager chosen = null;
- for (String beanName : tms.keySet()) {
- if (bf.containsBeanDefinition(beanName)) {
- BeanDefinition bd = bf.getBeanDefinition(beanName);
- if (bd instanceof AbstractBeanDefinition) {
- AbstractBeanDefinition abd = (AbstractBeanDefinition) bd;
- AutowireCandidateQualifier candidate = abd.getQualifier(Qualifier.class.getName());
- if ((candidate != null && qualifier.equals(candidate.getAttribute(AutowireCandidateQualifier.VALUE_KEY))) ||
- qualifier.equals(beanName) || ObjectUtils.containsElement(bf.getAliases(beanName), qualifier)) {
- if (chosen != null) {
- throw new IllegalStateException("No unique PlatformTransactionManager bean found " +
- "for qualifier '" + qualifier + "'");
- }
- chosen = tms.get(beanName);
- }
- }
- }
- }
- if (chosen != null) {
- return chosen;
- }
- else {
- throw new IllegalStateException(
- "No matching PlatformTransactionManager bean found for qualifier '" + qualifier + "'");
- }
+ return TransactionAspectUtils.getTransactionManager(this.beanFactory, qualifier);
}
else if (this.transactionManagerBeanName != null) {
return this.beanFactory.getBean(this.transactionManagerBeanName, PlatformTransactionManager.class);
diff --git a/org.springframework.transaction/src/main/java/org/springframework/transaction/interceptor/TransactionAspectUtils.java b/org.springframework.transaction/src/main/java/org/springframework/transaction/interceptor/TransactionAspectUtils.java
new file mode 100644
index 00000000000..de87a67f784
--- /dev/null
+++ b/org.springframework.transaction/src/main/java/org/springframework/transaction/interceptor/TransactionAspectUtils.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2002-2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.transaction.interceptor;
+
+import java.util.Map;
+
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryUtils;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.beans.factory.support.AutowireCandidateQualifier;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.util.ObjectUtils;
+
+/**
+ * Utility methods for obtaining a PlatformTransactionManager by
+ * {@link TransactionAttribute#getQualifier() qualifier value}.
+ *
+ * @author Juergen Hoeller
+ * @since 3.0.2
+ */
+public abstract class TransactionAspectUtils {
+
+ /**
+ * Obtain a PlatformTransactionManager from the given BeanFactory,
+ * matching the given qualifier.
+ * @param beanFactory the BeanFactory to get the PlatformTransactionManager bean from
+ * @param qualifier the qualifier for selecting between multiple PlatformTransactionManager matches
+ * @return the chosen PlatformTransactionManager (never null)
+ * @throws IllegalStateException if no matching PlatformTransactionManager bean found
+ */
+ public static PlatformTransactionManager getTransactionManager(BeanFactory beanFactory, String qualifier) {
+ if (beanFactory instanceof ConfigurableListableBeanFactory) {
+ // Full qualifier matching supported.
+ return getTransactionManager((ConfigurableListableBeanFactory) beanFactory, qualifier);
+ }
+ else if (beanFactory.containsBean(qualifier)) {
+ // Fallback: PlatformTransactionManager at least found by bean name.
+ return beanFactory.getBean(qualifier, PlatformTransactionManager.class);
+ }
+ else {
+ throw new IllegalStateException("No matching PlatformTransactionManager bean found for bean name '" +
+ qualifier + "'! (Note: Qualifier matching not supported because given BeanFactory does not " +
+ "implement ConfigurableListableBeanFactory.)");
+ }
+ }
+
+ /**
+ * Obtain a PlatformTransactionManager from the given BeanFactory,
+ * matching the given qualifier.
+ * @param beanFactory the BeanFactory to get the PlatformTransactionManager bean from
+ * @param qualifier the qualifier for selecting between multiple PlatformTransactionManager matches
+ * @return the chosen PlatformTransactionManager (never null)
+ * @throws IllegalStateException if no matching PlatformTransactionManager bean found
+ */
+ public static PlatformTransactionManager getTransactionManager(ConfigurableListableBeanFactory bf, String qualifier) {
+ Map tms =
+ BeanFactoryUtils.beansOfTypeIncludingAncestors(bf, PlatformTransactionManager.class);
+ PlatformTransactionManager chosen = null;
+ for (String beanName : tms.keySet()) {
+ if (bf.containsBeanDefinition(beanName)) {
+ BeanDefinition bd = bf.getBeanDefinition(beanName);
+ if (bd instanceof AbstractBeanDefinition) {
+ AbstractBeanDefinition abd = (AbstractBeanDefinition) bd;
+ AutowireCandidateQualifier candidate = abd.getQualifier(Qualifier.class.getName());
+ if ((candidate != null && qualifier.equals(candidate.getAttribute(AutowireCandidateQualifier.VALUE_KEY))) ||
+ qualifier.equals(beanName) || ObjectUtils.containsElement(bf.getAliases(beanName), qualifier)) {
+ if (chosen != null) {
+ throw new IllegalStateException("No unique PlatformTransactionManager bean found " +
+ "for qualifier '" + qualifier + "'");
+ }
+ chosen = tms.get(beanName);
+ }
+ }
+ }
+ }
+ if (chosen != null) {
+ return chosen;
+ }
+ else {
+ throw new IllegalStateException("No matching PlatformTransactionManager bean found for qualifier '" +
+ qualifier + "' - neither qualifier match nor bean name match!");
+ }
+ }
+
+}