INT-1451 move added trasactional advice to the advice chain, made 'advice-chain' and 'transactional' mutually exclusive
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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();
|
||||
// }
|
||||
// }
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user