INT-1451 move added trasactional advice to the advice chain, made 'advice-chain' and 'transactional' mutually exclusive

This commit is contained in:
Oleg Zhurakousky
2010-09-28 11:52:49 -04:00
parent b06fc5b15f
commit 9d6d433408
9 changed files with 95 additions and 105 deletions

View File

@@ -33,6 +33,7 @@ import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.integration.endpoint.AbstractPollingEndpoint;
import org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
@@ -91,15 +92,13 @@ public class PollerParser extends AbstractBeanDefinitionParser {
configureTrigger(element, metadataBuilder, parserContext);
IntegrationNamespaceUtils.setValueIfAttributeDefined(metadataBuilder, element, "max-messages-per-poll");
IntegrationNamespaceUtils.setValueIfAttributeDefined(metadataBuilder, element, "receive-timeout");
Element adviceChainElement = DomUtils.getChildElementByTagName(element, "advice-chain");
if (adviceChainElement != null) {
configureAdviceChain(adviceChainElement, metadataBuilder, parserContext);
}
Element txElement = DomUtils.getChildElementByTagName(element, "transactional");
if (txElement != null) {
configureTransactionAttributes(txElement, metadataBuilder, parserContext);
}
Element adviceChainElement = DomUtils.getChildElementByTagName(element, "advice-chain");
configureAdviceChain(adviceChainElement, txElement, metadataBuilder, parserContext);
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(metadataBuilder, element, "task-executor");
return metadataBuilder.getBeanDefinition();
}
@@ -203,7 +202,7 @@ public class PollerParser extends AbstractBeanDefinitionParser {
* and other "transactionDefinition" properties. This advisor will be applied on Polling Task proxy
* (see {@link AbstractPollingEndpoint}).
*/
private void configureTransactionAttributes(Element txElement, BeanDefinitionBuilder targetBuilder, ParserContext parserContext) {
private BeanDefinition configureTransactionAttributes(Element txElement, BeanDefinitionBuilder targetBuilder, ParserContext parserContext) {
String TX_PKG_PREFIX = "org.springframework.transaction.interceptor";
BeanDefinitionBuilder txDefinitionBuilder =
BeanDefinitionBuilder.genericBeanDefinition(TX_PKG_PREFIX + ".DefaultTransactionAttribute");
@@ -219,40 +218,43 @@ public class PollerParser extends AbstractBeanDefinitionParser {
BeanDefinitionBuilder.genericBeanDefinition(TX_PKG_PREFIX + ".TransactionInterceptor");
txInterceptorBuilder.addPropertyReference("transactionManager", txElement.getAttribute("transaction-manager"));
txInterceptorBuilder.addPropertyValue("transactionAttributeSource", attributeSourceBuilder.getBeanDefinition());
BeanDefinitionBuilder txAdvisorBuilder = BeanDefinitionBuilder.genericBeanDefinition(TX_PKG_PREFIX + ".TransactionAttributeSourceAdvisor");
txAdvisorBuilder.addConstructorArgValue(txInterceptorBuilder.getBeanDefinition());
targetBuilder.addPropertyValue("transactionAdvisor", txAdvisorBuilder.getBeanDefinition());
return txInterceptorBuilder.getBeanDefinition();
}
/**
* Parses the 'advice-chain' element's sub-elements.
*/
@SuppressWarnings("unchecked")
private void configureAdviceChain(Element adviceChainElement, BeanDefinitionBuilder targetBuilder, ParserContext parserContext) {
@SuppressWarnings({ "unchecked", "rawtypes" })
private void configureAdviceChain(Element adviceChainElement, Element txElement, BeanDefinitionBuilder targetBuilder, ParserContext parserContext) {
ManagedList adviceChain = new ManagedList();
NodeList childNodes = adviceChainElement.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node child = childNodes.item(i);
if (child.getNodeType() == Node.ELEMENT_NODE) {
Element childElement = (Element) child;
String localName = child.getLocalName();
if ("bean".equals(localName)) {
BeanDefinitionHolder holder = parserContext.getDelegate().parseBeanDefinitionElement(
childElement, targetBuilder.getBeanDefinition());
parserContext.registerBeanComponent(new BeanComponentDefinition(holder));
adviceChain.add(new RuntimeBeanReference(holder.getBeanName()));
}
else if ("ref".equals(localName)) {
String ref = childElement.getAttribute("bean");
adviceChain.add(new RuntimeBeanReference(ref));
}
else {
BeanDefinition customBeanDefinition = parserContext.getDelegate().parseCustomElement(
childElement, targetBuilder.getBeanDefinition());
if (customBeanDefinition == null) {
parserContext.getReaderContext().error(
"failed to parse custom element '" + localName + "'", childElement);
if (txElement != null){
adviceChain.add(this.configureTransactionAttributes(txElement, targetBuilder, parserContext));
}
if (adviceChainElement != null){
NodeList childNodes = adviceChainElement.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node child = childNodes.item(i);
if (child.getNodeType() == Node.ELEMENT_NODE) {
Element childElement = (Element) child;
String localName = child.getLocalName();
if ("bean".equals(localName)) {
BeanDefinitionHolder holder = parserContext.getDelegate().parseBeanDefinitionElement(
childElement, targetBuilder.getBeanDefinition());
parserContext.registerBeanComponent(new BeanComponentDefinition(holder));
adviceChain.add(new RuntimeBeanReference(holder.getBeanName()));
}
else if ("ref".equals(localName)) {
String ref = childElement.getAttribute("bean");
adviceChain.add(new RuntimeBeanReference(ref));
}
else {
BeanDefinition customBeanDefinition = parserContext.getDelegate().parseCustomElement(
childElement, targetBuilder.getBeanDefinition());
if (customBeanDefinition == null) {
parserContext.getReaderContext().error(
"failed to parse custom element '" + localName + "'", childElement);
}
}
}
}

View File

@@ -20,7 +20,6 @@ import java.util.concurrent.Callable;
import java.util.concurrent.ScheduledFuture;
import org.aopalliance.aop.Advice;
import org.springframework.aop.Advisor;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.core.task.SyncTaskExecutor;
@@ -107,17 +106,10 @@ public abstract class AbstractPollingEndpoint extends AbstractEndpoint implement
}
};
Advisor transactionAdvice = this.pollerMetadata.getTransactionAdvisor();
List<Advice> adviceChain = this.pollerMetadata.getAdviceChain();
if (transactionAdvice != null || !CollectionUtils.isEmpty(adviceChain)){
if (!CollectionUtils.isEmpty(adviceChain)){
ProxyFactory proxyFactory = new ProxyFactory(pollingTask);
// Add Transaction advice first
if (transactionAdvice != null){
proxyFactory.addAdvisor(transactionAdvice);
}
// . . .then add the rest of the advises
if (!CollectionUtils.isEmpty(adviceChain)){
for (Advice advice : adviceChain) {
proxyFactory.addAdvice(advice);

View File

@@ -19,7 +19,6 @@ package org.springframework.integration.scheduling;
import java.util.List;
import org.aopalliance.aop.Advice;
import org.springframework.aop.Advisor;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.support.PeriodicTrigger;
@@ -41,16 +40,6 @@ public class PollerMetadata {
private List<Advice> adviceChain;
private volatile TaskExecutor taskExecutor;
private volatile Advisor transactionAdvice;
public Advisor getTransactionAdvisor() {
return transactionAdvice;
}
public void setTransactionAdvisor(Advisor transactionAdvice) {
this.transactionAdvice = transactionAdvice;
}
public void setTrigger(Trigger trigger) {
this.trigger = trigger;

View File

@@ -1057,29 +1057,31 @@
<xsd:element name="cron-trigger" type="cronTriggerType" />
</xsd:choice>
<!-- Poller sub-elements are deprecated since Spring Integration 2.0 -->
<xsd:element name="transactional" type="transactionalType" minOccurs="0" maxOccurs="1" />
<xsd:element name="advice-chain" minOccurs="0" maxOccurs="1">
<xsd:complexType>
<xsd:sequence>
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="ref" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:attribute name="bean" type="xsd:string" use="required">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="java.lang.Object" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:any namespace="##other" processContents="strict" minOccurs="0" maxOccurs="unbounded" />
</xsd:choice>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:choice>
<xsd:element name="transactional" type="transactionalType" minOccurs="0" maxOccurs="1" />
<xsd:element name="advice-chain" minOccurs="0" maxOccurs="1">
<xsd:complexType>
<xsd:sequence>
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="ref" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:attribute name="bean" type="xsd:string" use="required">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="java.lang.Object" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:any namespace="##other" processContents="strict" minOccurs="0" maxOccurs="unbounded" />
</xsd:choice>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:sequence>
<xsd:attribute name="fixed-delay" type="xsd:string">
<xsd:annotation>

View File

@@ -33,7 +33,6 @@ import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.aop.Advisor;
import org.springframework.integration.Message;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.core.MessageSource;
@@ -102,12 +101,10 @@ public class SourcePollingChannelAdapterFactoryBeanTests {
pollerMetadata.setMaxMessagesPerPoll(1);
final AtomicInteger count = new AtomicInteger();
final MethodInterceptor txAdvice = mock(MethodInterceptor.class);
pollerMetadata.setTransactionAdvisor(new Advisor() {
public boolean isPerInstance() {
return false;
}
public Advice getAdvice() {
return txAdvice;
adviceChain.add(new MethodInterceptor() {
public Object invoke(MethodInvocation invocation) throws Throwable {
count.incrementAndGet();
return invocation.proceed();
}
});
when(txAdvice.invoke(Mockito.any(MethodInvocation.class))).thenAnswer(new Answer() {

View File

@@ -30,6 +30,7 @@ import org.aopalliance.intercept.MethodInvocation;
import org.junit.Test;
import org.springframework.aop.Advisor;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
@@ -41,10 +42,11 @@ import org.springframework.integration.test.util.TestUtils;
import org.springframework.integration.util.TestTransactionManager;
import org.springframework.transaction.IllegalTransactionStateException;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor;
import org.springframework.transaction.interceptor.TransactionInterceptor;
/**
* @author Mark Fisher
* @author Oleg Zhurakousky
*/
public class PollingTransactionTests {
@@ -75,14 +77,14 @@ public class PollingTransactionTests {
PollerMetadata pollerMetedata = TestUtils.getPropertyValue(advicedPoller, "pollerMetadata",PollerMetadata.class);
List<Advice> adviceChain = TestUtils.getPropertyValue(pollerMetedata, "adviceChain",List.class);
assertEquals(2, adviceChain.size());
assertEquals(3, adviceChain.size());
Runnable poller = TestUtils.getPropertyValue(advicedPoller, "poller", Runnable.class);
Callable<?> pollingTask = TestUtils.getPropertyValue(poller, "pollingTask", Callable.class);
assertTrue("Poller is not Advised", pollingTask instanceof Advised);
Advisor[] advisors = ((Advised)pollingTask).getAdvisors();
assertEquals(3, advisors.length);
assertTrue("First advisor is not TX", advisors[0] instanceof TransactionAttributeSourceAdvisor);
assertTrue("First advisor is not TX", ((DefaultPointcutAdvisor)advisors[0]).getAdvice() instanceof TransactionInterceptor);
TestTransactionManager txManager = (TestTransactionManager) context.getBean("txManager");
MessageChannel input = (MessageChannel) context.getBean("goodInputWithAdvice");
PollableChannel output = (PollableChannel) context.getBean("output");
@@ -92,7 +94,6 @@ public class PollingTransactionTests {
txManager.waitForCompletion(10000);
Message<?> message = output.receive(0);
assertNotNull(message);
assertEquals(1, txManager.getCommitCount());
assertEquals(0, txManager.getRollbackCount());
context.stop();
}

View File

@@ -15,17 +15,13 @@
<int:service-activator input-channel="inputChannel" ref="sampleService">
<int:poller max-messages-per-poll="1" fixed-rate="10000">
<int:transactional transaction-manager="txManager" />
<int:advice-chain>
<bean
class="org.springframework.integration.dispatcher.TransactionalPollerWithMixedAopConfig.SampleAdvice" />
</int:advice-chain>
</int:poller>
</int:service-activator>
<bean id="sampleService"
class="org.springframework.integration.dispatcher.TransactionalPollerWithMixedAopConfig.SampleService"/>
class="org.springframework.integration.dispatcher.TransactionalPollerWithMixedAopConfigTests.SampleService"/>
<bean id="foo" class="org.springframework.integration.dispatcher.TransactionalPollerWithMixedAopConfig.Foo">
<bean id="foo" class="org.springframework.integration.dispatcher.TransactionalPollerWithMixedAopConfigTests.Foo">
<constructor-arg value="hello"/>
</bean>

View File

@@ -19,7 +19,7 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
* in the BeanFactory while having <aop:config> resent resulted in
* TX Advisor being applied on all beans in AC
*/
public class TransactionalPollerWithMixedAopConfig {
public class TransactionalPollerWithMixedAopConfigTests {
@Test
public void validateTransactionalProxyIsolationToThePollerOnly(){
@@ -38,9 +38,9 @@ public class TransactionalPollerWithMixedAopConfig {
public Foo(String value){}
}
public static class SampleAdvice implements MethodInterceptor{
public Object invoke(MethodInvocation invocation) throws Throwable {
return invocation.proceed();
}
}
// public static class SampleAdvice implements MethodInterceptor{
// public Object invoke(MethodInvocation invocation) throws Throwable {
// return invocation.proceed();
// }
// }
}

View File

@@ -39,14 +39,25 @@
<service-activator id="advicedSa" input-channel="goodInputWithAdvice" ref="testBean"
method="good" output-channel="output">
<poller max-messages-per-poll="1" fixed-rate="10000">
<transactional transaction-manager="txManager" />
<advice-chain>
<ref bean="txAdvise"/>
<ref bean="adviceA" />
<beans:bean class="org.springframework.integration.dispatcher.PollingTransactionTests.SampleAdvice"/>
</advice-chain>
</poller>
</service-activator>
<beans:bean id="txAdvise" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<beans:property name="transactionManager" ref="txManager"/>
<beans:property name="transactionAttributeSource">
<beans:bean class="org.springframework.transaction.interceptor.MatchAlwaysTransactionAttributeSource">
<beans:property name="transactionAttribute">
<beans:bean class="org.springframework.transaction.interceptor.DefaultTransactionAttribute"/>
</beans:property>
</beans:bean>
</beans:property>
</beans:bean>
<beans:bean id="adviceA" class="org.springframework.integration.dispatcher.PollingTransactionTests.SampleAdvice"/>
<beans:bean id="testBean"