diff --git a/build.gradle b/build.gradle index 79e81a0f62..6b6e68e5fa 100644 --- a/build.gradle +++ b/build.gradle @@ -57,6 +57,7 @@ subprojects { subproject -> springSecurityVersion = '3.1.0.RELEASE' springSocialTwitterVersion = '1.0.1.RELEASE' springWsVersion = '2.1.0.RELEASE' + springRetryVersion = '1.0.2.RELEASE' } eclipse { @@ -176,6 +177,7 @@ project('spring-integration-core') { compile "org.springframework:spring-aop:$springVersion" compile "org.springframework:spring-context:$springVersion" compile "org.springframework:spring-tx:$springVersion" + compile "org.springframework.retry:spring-retry:$springRetryVersion" compile("org.codehaus.jackson:jackson-mapper-asl:$jacksonVersion", optional) testCompile "org.aspectj:aspectjrt:$aspectjVersion" testCompile "org.aspectj:aspectjweaver:$aspectjVersion" @@ -185,6 +187,7 @@ project('spring-integration-core') { importTemplate += [ 'org.springframework.*;version="[3.1.1, 4.0.0)"', 'org.springframework.transaction;version="[3.1.1, 4.0.0)";resolution:=optional', + 'org.springframework.retry;version="[1.0.2, 2.0.0)"', 'org.apache.commons.logging;version="[1.1.1, 2.0.0)"', 'org.aopalliance.*;version="[1.0.0, 2.0.0)"', 'org.codehaus.jackson.*;version="[1.0.0, 2.0.0)";resolution:=optional', diff --git a/spring-integration-amqp/src/main/resources/org/springframework/integration/amqp/config/spring-integration-amqp-2.2.xsd b/spring-integration-amqp/src/main/resources/org/springframework/integration/amqp/config/spring-integration-amqp-2.2.xsd index 9871a16ea4..67612af0e8 100644 --- a/spring-integration-amqp/src/main/resources/org/springframework/integration/amqp/config/spring-integration-amqp-2.2.xsd +++ b/spring-integration-amqp/src/main/resources/org/springframework/integration/amqp/config/spring-integration-amqp-2.2.xsd @@ -368,6 +368,9 @@ Base type for the 'outbound-channel-adapter' and 'outbound-gateway' elements. + + + diff --git a/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/AmqpOutboundChannelAdapterParserTests-context.xml b/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/AmqpOutboundChannelAdapterParserTests-context.xml index 056f6c7bdd..ceaa5e15f4 100644 --- a/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/AmqpOutboundChannelAdapterParserTests-context.xml +++ b/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/AmqpOutboundChannelAdapterParserTests-context.xml @@ -9,7 +9,11 @@ http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> - + + + + + diff --git a/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/AmqpOutboundChannelAdapterParserTests.java b/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/AmqpOutboundChannelAdapterParserTests.java index c9673f071d..9cad51a2c6 100644 --- a/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/AmqpOutboundChannelAdapterParserTests.java +++ b/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/AmqpOutboundChannelAdapterParserTests.java @@ -20,12 +20,13 @@ import static junit.framework.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.lang.reflect.Field; -import java.util.List; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.junit.Test; @@ -48,10 +49,12 @@ import org.springframework.integration.amqp.AmqpHeaders; import org.springframework.integration.amqp.outbound.AmqpOutboundEndpoint; import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.channel.NullChannel; +import org.springframework.integration.context.NamedComponent; import org.springframework.integration.core.MessageHandler; import org.springframework.integration.core.PollableChannel; import org.springframework.integration.endpoint.EventDrivenConsumer; -import org.springframework.integration.handler.MessageHandlerChain; +import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; +import org.springframework.integration.message.GenericMessage; import org.springframework.integration.support.MessageBuilder; import org.springframework.integration.test.util.TestUtils; import org.springframework.test.context.ContextConfiguration; @@ -72,6 +75,8 @@ import com.rabbitmq.client.Channel; @RunWith(SpringJUnit4ClassRunner.class) public class AmqpOutboundChannelAdapterParserTests { + private static volatile int adviceCalled; + @Autowired private ApplicationContext context; @@ -82,16 +87,18 @@ public class AmqpOutboundChannelAdapterParserTests { assertEquals(DirectChannel.class, channel.getClass()); assertEquals(EventDrivenConsumer.class, adapter.getClass()); MessageHandler handler = TestUtils.getPropertyValue(adapter, "handler", MessageHandler.class); - assertEquals(AmqpOutboundEndpoint.class, handler.getClass()); - assertEquals("amqp:outbound-channel-adapter", ((AmqpOutboundEndpoint) handler).getComponentType()); + assertTrue(handler instanceof NamedComponent); + assertEquals("amqp:outbound-channel-adapter", ((NamedComponent) handler).getComponentType()); + handler.handleMessage(new GenericMessage("foo")); + assertEquals(1, adviceCalled); } - + @Test public void withHeaderMapperCustomHeaders() { Object eventDrivenConsumer = context.getBean("withHeaderMapperCustomHeaders"); - + AmqpOutboundEndpoint endpoint = TestUtils.getPropertyValue(eventDrivenConsumer, "handler", AmqpOutboundEndpoint.class); - + Field amqpTemplateField = ReflectionUtils.findField(AmqpOutboundEndpoint.class, "amqpTemplate"); amqpTemplateField.setAccessible(true); RabbitTemplate amqpTemplate = TestUtils.getPropertyValue(endpoint, "amqpTemplate", RabbitTemplate.class); @@ -110,8 +117,8 @@ public class AmqpOutboundChannelAdapterParserTests { .when(amqpTemplate).send(Mockito.any(String.class), Mockito.any(String.class), Mockito.any(org.springframework.amqp.core.Message.class), Mockito.any(CorrelationData.class)); ReflectionUtils.setField(amqpTemplateField, endpoint, amqpTemplate); - - + + MessageChannel requestChannel = context.getBean("requestChannel", MessageChannel.class); Message message = MessageBuilder.withPayload("hello").setHeader("foo", "foo").setHeader("bar", "bar").setHeader("foobar", "foobar").build(); requestChannel.send(message); @@ -214,4 +221,14 @@ public class AmqpOutboundChannelAdapterParserTests { assertEquals("bar", returned.getHeaders().get(AmqpHeaders.RETURN_ROUTING_KEY)); assertEquals("hello", returned.getPayload()); } + + public static class FooAdvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return null; + } + + } } diff --git a/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/AmqpOutboundGatewayParserTests-context.xml b/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/AmqpOutboundGatewayParserTests-context.xml index db2ed30102..7b1e716385 100644 --- a/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/AmqpOutboundGatewayParserTests-context.xml +++ b/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/AmqpOutboundGatewayParserTests-context.xml @@ -55,7 +55,11 @@ amqp-template="amqpTemplate" order="5" mapped-request-headers="" - mapped-reply-headers=""/> + mapped-reply-headers=""> + + + + diff --git a/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/AmqpOutboundGatewayParserTests.java b/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/AmqpOutboundGatewayParserTests.java index d029f26bf2..ba8d596f60 100644 --- a/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/AmqpOutboundGatewayParserTests.java +++ b/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/AmqpOutboundGatewayParserTests.java @@ -15,6 +15,12 @@ */ package org.springframework.integration.amqp.config; +import static junit.framework.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + import java.lang.reflect.Field; import java.util.List; @@ -31,17 +37,11 @@ import org.springframework.integration.MessageChannel; import org.springframework.integration.amqp.AmqpHeaders; import org.springframework.integration.amqp.outbound.AmqpOutboundEndpoint; import org.springframework.integration.channel.QueueChannel; +import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; import org.springframework.integration.support.MessageBuilder; import org.springframework.integration.test.util.TestUtils; import org.springframework.util.ReflectionUtils; -import static junit.framework.Assert.assertEquals; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; - /** * @author Oleg Zhurakousky * @author Gary Russell @@ -53,6 +53,8 @@ import static org.junit.Assert.assertTrue; */ public class AmqpOutboundGatewayParserTests { + private static volatile int adviceCalled; + @Test public void testGatewayConfig(){ ApplicationContext context = new ClassPathXmlApplicationContext("AmqpOutboundGatewayParserTests-context.xml", this.getClass()); @@ -209,6 +211,7 @@ public class AmqpOutboundGatewayParserTests { assertNull(replyMessage.getHeaders().get(AmqpHeaders.DELIVERY_MODE)); assertNull(replyMessage.getHeaders().get(AmqpHeaders.CONTENT_TYPE)); assertNull(replyMessage.getHeaders().get(AmqpHeaders.APP_ID)); + assertEquals(1, adviceCalled); } @Test //INT-1029 @@ -261,4 +264,13 @@ public class AmqpOutboundGatewayParserTests { } + public static class FooAdvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return callback.execute(); + } + + } } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/MessageHeaders.java b/spring-integration-core/src/main/java/org/springframework/integration/MessageHeaders.java index 170df014d8..d6d38e18de 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/MessageHeaders.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/MessageHeaders.java @@ -91,6 +91,8 @@ public final class MessageHeaders implements Map, Serializable { public static final String DISPOSITION_RESULT = "dispositionResult"; + public static final String POSTPROCESS_RESULT = "postProcessResult"; + private final Map headers; @@ -164,12 +166,12 @@ public final class MessageHeaders implements Map, Serializable { } @Override - public boolean equals(Object obj) { - if (this == obj) { + public boolean equals(Object object) { + if (this == object) { return true; } - if (obj != null && obj instanceof MessageHeaders) { - MessageHeaders other = (MessageHeaders) obj; + if (object != null && object instanceof MessageHeaders) { + MessageHeaders other = (MessageHeaders) object; return this.headers.equals(other.headers); } return false; diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/AbstractSimpleMessageHandlerFactoryBean.java b/spring-integration-core/src/main/java/org/springframework/integration/config/AbstractSimpleMessageHandlerFactoryBean.java index 3012642196..92e6bba117 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/AbstractSimpleMessageHandlerFactoryBean.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/AbstractSimpleMessageHandlerFactoryBean.java @@ -1,11 +1,11 @@ /* - * Copyright 2002-2011 the original author or authors. - * + * Copyright 2002-2012 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. @@ -13,6 +13,9 @@ package org.springframework.integration.config; +import java.util.List; + +import org.aopalliance.aop.Advice; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; @@ -23,11 +26,14 @@ import org.springframework.integration.MessageChannel; import org.springframework.integration.context.Orderable; import org.springframework.integration.core.MessageHandler; import org.springframework.integration.core.MessageProducer; +import org.springframework.integration.handler.AbstractReplyProducingMessageHandler; import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; /** * @author Dave Syer * @author Oleg Zhurakousky + * @author Gary Russell */ public abstract class AbstractSimpleMessageHandlerFactoryBean implements FactoryBean, BeanFactoryAware { @@ -43,6 +49,8 @@ public abstract class AbstractSimpleMessageHandlerFactoryBean adviceChain; + public AbstractSimpleMessageHandlerFactoryBean() { super(); @@ -64,6 +72,10 @@ public abstract class AbstractSimpleMessageHandlerFactoryBean adviceChain) { + this.adviceChain = adviceChain; + } + public H getObject() throws Exception { if (this.handler == null) { this.handler = this.createHandlerInternal(); @@ -71,9 +83,6 @@ public abstract class AbstractSimpleMessageHandlerFactoryBean adviceChain; + public void setHandler(MessageHandler handler) { Assert.notNull(handler, "handler must not be null"); synchronized (this.handlerMonitor) { @@ -94,7 +103,7 @@ public class ConsumerEndpointFactoryBean public void setPollerMetadata(PollerMetadata pollerMetadata) { this.pollerMetadata = pollerMetadata; } - + public void setBeanClassLoader(ClassLoader classLoader) { this.beanClassLoader = classLoader; } @@ -112,6 +121,11 @@ public class ConsumerEndpointFactoryBean this.beanFactory = (ConfigurableBeanFactory) beanFactory; } + public void setAdviceChain(List adviceChain) { + Assert.notNull(adviceChain, "adviceChain must not be null"); + this.adviceChain = adviceChain; + } + public void afterPropertiesSet() throws Exception { try { if (!this.beanName.startsWith("org.springframework")) { @@ -132,6 +146,32 @@ public class ConsumerEndpointFactoryBean + this.handler + " for " + this.beanName + " :" + e.getMessage()); } } + if (!CollectionUtils.isEmpty(this.adviceChain)) { + /* + * ARPMHs advise the handleRequesMessage method internally and already have the advice chain injected. + * So we only advise handlers that are not reply-producing. If the handler is already advised, + * add the configured advices to its chain, otherwise create a proxy. + */ + if (!(this.handler instanceof AbstractReplyProducingMessageHandler)) { + if (AopUtils.isAopProxy(this.handler) && this.handler instanceof Advised) { + Class targetClass = AopUtils.getTargetClass(this.handler); + for (Advice advice : this.adviceChain) { + NameMatchMethodPointcutAdvisor handlerAdvice = new NameMatchMethodPointcutAdvisor(advice); + handlerAdvice.addMethodName("handleMessage"); + if (AopUtils.canApply(handlerAdvice.getPointcut(), targetClass)) { + ((Advised) this.handler).addAdvice(advice); + } + } + } + else { + ProxyFactory proxyFactory = new ProxyFactory(this.handler); + for (Advice advice : this.adviceChain) { + proxyFactory.addAdvice(advice); + } + this.handler = (MessageHandler) proxyFactory.getProxy(this.beanClassLoader); + } + } + } this.initializeEndpoint(); } @@ -184,9 +224,9 @@ public class ConsumerEndpointFactoryBean pollingConsumer.setTrigger(this.pollerMetadata.getTrigger()); pollingConsumer.setAdviceChain(this.pollerMetadata.getAdviceChain()); pollingConsumer.setMaxMessagesPerPoll(this.pollerMetadata.getMaxMessagesPerPoll()); - + pollingConsumer.setErrorHandler(this.pollerMetadata.getErrorHandler()); - + pollingConsumer.setReceiveTimeout(this.pollerMetadata.getReceiveTimeout()); pollingConsumer.setBeanClassLoader(beanClassLoader); pollingConsumer.setBeanFactory(beanFactory); diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractConsumerEndpointParser.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractConsumerEndpointParser.java index 88995538c8..4629ff3874 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractConsumerEndpointParser.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractConsumerEndpointParser.java @@ -19,8 +19,7 @@ package org.springframework.integration.config.xml; import java.util.Collection; import java.util.List; -import org.w3c.dom.Element; - +import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConstructorArgumentValues; import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder; @@ -32,10 +31,10 @@ import org.springframework.beans.factory.support.ManagedSet; import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.ConsumerEndpointFactoryBean; -import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import org.springframework.util.xml.DomUtils; +import org.w3c.dom.Element; /** * Base class parser for elements that create Message Endpoints. @@ -92,6 +91,11 @@ public abstract class AbstractConsumerEndpointParser extends AbstractBeanDefinit return handlerBeanDefinition; } + Element adviceChainElement = DomUtils.getChildElementByTagName(element, + IntegrationNamespaceUtils.REQUEST_HANDLER_ADVICE_CHAIN); + IntegrationNamespaceUtils.configureAndSetAdviceChainIfPresent(adviceChainElement, null, + handlerBuilder, parserContext); + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(ConsumerEndpointFactoryBean.class); String handlerBeanName = BeanDefinitionReaderUtils.generateBeanName(handlerBeanDefinition, parserContext.getRegistry()); @@ -99,6 +103,7 @@ public abstract class AbstractConsumerEndpointParser extends AbstractBeanDefinit parserContext.registerBeanComponent(new BeanComponentDefinition(handlerBeanDefinition, handlerBeanName, handlerAlias)); builder.addPropertyReference("handler", handlerBeanName); + String inputChannelName = element.getAttribute(inputChannelAttributeName); if (!parserContext.getRegistry().containsBeanDefinition(inputChannelName)){ @@ -136,4 +141,5 @@ public abstract class AbstractConsumerEndpointParser extends AbstractBeanDefinit parserContext.registerBeanComponent(new BeanComponentDefinition(beanDefinition, beanName)); return null; } + } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractOutboundChannelAdapterParser.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractOutboundChannelAdapterParser.java index 00386f3b7d..6d77f21953 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractOutboundChannelAdapterParser.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractOutboundChannelAdapterParser.java @@ -16,16 +16,18 @@ package org.springframework.integration.config.xml; -import org.w3c.dom.Element; - +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.parsing.BeanComponentDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; +import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.ConsumerEndpointFactoryBean; +import org.springframework.integration.handler.AbstractReplyProducingMessageHandler; import org.springframework.util.StringUtils; import org.springframework.util.xml.DomUtils; +import org.w3c.dom.Element; /** * Base class for outbound Channel Adapter parsers. @@ -34,7 +36,7 @@ import org.springframework.util.xml.DomUtils; * an {@link org.springframework.integration.endpoint.AbstractEndpoint} depending on the channel type. * If this component is defined as nested element (e.g., inside of the chain) it will produce * a {@link org.springframework.integration.core.MessageHandler}. - * + * * @author Mark Fisher * @author Gary Russell * @author Artem Bilan @@ -48,7 +50,8 @@ public abstract class AbstractOutboundChannelAdapterParser extends AbstractChann } BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(ConsumerEndpointFactoryBean.class); Element pollerElement = DomUtils.getChildElementByTagName(element, "poller"); - builder.addPropertyReference("handler", this.parseAndRegisterConsumer(element, parserContext)); + BeanComponentDefinition handlerBeanComponentDefinition = this.doParseAndRegisterConsumer(element, parserContext); + builder.addPropertyReference("handler", handlerBeanComponentDefinition.getBeanName()); if (pollerElement != null) { if (!StringUtils.hasText(channelName)) { parserContext.getReaderContext().error( @@ -58,6 +61,40 @@ public abstract class AbstractOutboundChannelAdapterParser extends AbstractChann } builder.addPropertyValue("inputChannelName", channelName); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "auto-startup"); + + Element adviceChainElement = DomUtils.getChildElementByTagName(element, + IntegrationNamespaceUtils.REQUEST_HANDLER_ADVICE_CHAIN); + @SuppressWarnings("rawtypes") + ManagedList adviceChain = IntegrationNamespaceUtils.configureAdviceChain(adviceChainElement, null, + builder, parserContext); + if (adviceChain != null) { + BeanDefinition handlerBeanDefinition = handlerBeanComponentDefinition.getBeanDefinition(); + /* + * For ARPMH, the advice chain is injected so just the handleRequestMessage method is advised. + * Sometime ARPMHs do double duty as a gateway and a channel adapter. The parser subclass + * can indicate this by overriding isUsingReplyProducer(), or we can try to determine it from + * the bean class. + */ + boolean isReplyProducer = this.isUsingReplyProducer(); + if (!isReplyProducer) { + Class beanClass = null; + if (handlerBeanDefinition instanceof AbstractBeanDefinition) { + AbstractBeanDefinition abstractBeanDefinition = (AbstractBeanDefinition) handlerBeanDefinition; + if (abstractBeanDefinition.hasBeanClass()) { + beanClass = abstractBeanDefinition.getBeanClass(); + } + } + isReplyProducer = beanClass != null && AbstractReplyProducingMessageHandler.class.isAssignableFrom(beanClass); + } + + if (isReplyProducer) { + handlerBeanDefinition.getPropertyValues().add("adviceChain", adviceChain); + } + else { + builder.addPropertyValue("adviceChain", adviceChain); + } + } + return builder.getBeanDefinition(); } @@ -65,8 +102,19 @@ public abstract class AbstractOutboundChannelAdapterParser extends AbstractChann * Override this method to control the registration process and return the bean name. * If parsing a bean definition whose name can be auto-generated, consider using * {@link #parseConsumer(Element, ParserContext)} instead. + * @deprecated Use {@link #doParseAndRegisterConsumer(Element, ParserContext)} */ + @Deprecated protected String parseAndRegisterConsumer(Element element, ParserContext parserContext) { + return doParseAndRegisterConsumer(element, parserContext).getBeanName(); + } + + /** + * Override this method to control the registration process and return the bean name. + * If parsing a bean definition whose name can be auto-generated, consider using + * {@link #parseConsumer(Element, ParserContext)} instead. + */ + protected BeanComponentDefinition doParseAndRegisterConsumer(Element element, ParserContext parserContext) { AbstractBeanDefinition definition = this.parseConsumer(element, parserContext); if (definition == null) { parserContext.getReaderContext().error("Consumer parsing must return an AbstractBeanDefinition.", element); @@ -77,8 +125,9 @@ public abstract class AbstractOutboundChannelAdapterParser extends AbstractChann } String beanName = BeanDefinitionReaderUtils.generateBeanName(definition, parserContext.getRegistry()); String[] handlerAlias = IntegrationNamespaceUtils.generateAlias(element); - parserContext.registerBeanComponent(new BeanComponentDefinition(definition, beanName, handlerAlias)); - return beanName; + BeanComponentDefinition beanComponentDefinition = new BeanComponentDefinition(definition, beanName, handlerAlias); + parserContext.registerBeanComponent(beanComponentDefinition); + return beanComponentDefinition; } /** @@ -87,4 +136,13 @@ public abstract class AbstractOutboundChannelAdapterParser extends AbstractChann */ protected abstract AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext); + /** + * Override this to signal that this channel adapter is actually using a AbstractReplyProducingMessageHandler + * while it is not possible for this parser to determine that because, say, a FactoryBean is being used. + * @return false, unless overridden. + */ + protected boolean isUsingReplyProducer() { + return false; + } + } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceUtils.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceUtils.java index eb86a327ad..2c2fe14fe9 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceUtils.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceUtils.java @@ -19,9 +19,11 @@ import java.util.List; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; +import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.config.TypedStringValue; import org.springframework.beans.factory.parsing.BeanComponentDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.core.Conventions; @@ -33,6 +35,8 @@ import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.springframework.util.xml.DomUtils; import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; /** * Shared utility methods for integration namespace parsers. @@ -54,6 +58,7 @@ public abstract class IntegrationNamespaceUtils { static final String ORDER = "order"; static final String EXPRESSION_ATTRIBUTE = "expression"; public static final String HANDLER_ALIAS_SUFFIX = ".handler"; + public static final String REQUEST_HANDLER_ADVICE_CHAIN = "request-handler-advice-chain"; /** * Property name on ChannelInitializer used to configure the default max subscribers for @@ -308,4 +313,56 @@ public abstract class IntegrationNamespaceUtils { } return handlerAlias; } + + @SuppressWarnings({ "rawtypes" }) + public static void configureAndSetAdviceChainIfPresent(Element adviceChainElement, Element txElement, + BeanDefinitionBuilder parentBuilder, ParserContext parserContext) { + ManagedList adviceChain = configureAdviceChain(adviceChainElement, txElement, parentBuilder, parserContext); + if (adviceChain != null) { + parentBuilder.addPropertyValue("adviceChain", adviceChain); + } + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static ManagedList configureAdviceChain(Element adviceChainElement, Element txElement, + BeanDefinitionBuilder parentBuilder, ParserContext parserContext) { + ManagedList adviceChain = null; + // Schema validation ensures txElement and adviceChainElement are mutually exclusive + if (txElement != null) { + adviceChain = new ManagedList(); + adviceChain.add(IntegrationNamespaceUtils.configureTransactionAttributes(txElement)); + } + if (adviceChainElement != null) { + 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, parentBuilder.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, parentBuilder.getBeanDefinition()); + if (customBeanDefinition == null) { + parserContext.getReaderContext().error( + "failed to parse custom element '" + localName + "'", childElement); + } + adviceChain.add(customBeanDefinition); + } + } + } + } + return adviceChain; + } + } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/PollerParser.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/PollerParser.java index 19bc9d56a9..9d609eada3 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/PollerParser.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/PollerParser.java @@ -20,14 +20,9 @@ import java.util.ArrayList; import java.util.List; import org.springframework.beans.factory.BeanDefinitionStoreException; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.BeanDefinitionHolder; -import org.springframework.beans.factory.config.RuntimeBeanReference; -import org.springframework.beans.factory.parsing.BeanComponentDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; -import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; @@ -40,8 +35,6 @@ import org.springframework.scheduling.support.PeriodicTrigger; import org.springframework.util.StringUtils; import org.springframework.util.xml.DomUtils; import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; /** * Parser for the <poller> element. @@ -94,7 +87,8 @@ public class PollerParser extends AbstractBeanDefinitionParser { Element txElement = DomUtils.getChildElementByTagName(element, "transactional"); Element adviceChainElement = DomUtils.getChildElementByTagName(element, "advice-chain"); - configureAdviceChain(adviceChainElement, txElement, metadataBuilder, parserContext); + IntegrationNamespaceUtils.configureAndSetAdviceChainIfPresent(adviceChainElement, txElement, + metadataBuilder, parserContext); Element pseudoTxElement = DomUtils.getChildElementByTagName(element, "psuedo-transactional"); if (pseudoTxElement != null && txElement != null) { @@ -174,48 +168,6 @@ public class PollerParser extends AbstractBeanDefinitionParser { targetBuilder.addPropertyReference("trigger", triggerBeanNames.get(0)); } - /** - * Parses the 'advice-chain' element's sub-elements. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - private void configureAdviceChain(Element adviceChainElement, Element txElement, BeanDefinitionBuilder targetBuilder, ParserContext parserContext) { - ManagedList adviceChain = new ManagedList(); - // Schema validation ensures txElement and adviceChainElement are mutually exclusive - if (txElement != null) { - adviceChain.add(IntegrationNamespaceUtils.configureTransactionAttributes(txElement)); - } - 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); - } - adviceChain.add(customBeanDefinition); - } - } - } - } - targetBuilder.addPropertyValue("adviceChain", adviceChain); - } - private void configureTransactionSync(Element element, BeanDefinitionBuilder metadataBuilder, ParserContext parserContext) { if (element != null) { diff --git a/spring-integration-core/src/main/java/org/springframework/integration/handler/AbstractReplyProducingMessageHandler.java b/spring-integration-core/src/main/java/org/springframework/integration/handler/AbstractReplyProducingMessageHandler.java index 75307c73cd..80a304a448 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/handler/AbstractReplyProducingMessageHandler.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/handler/AbstractReplyProducingMessageHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -16,6 +16,11 @@ package org.springframework.integration.handler; +import java.util.List; + +import org.aopalliance.aop.Advice; +import org.springframework.aop.framework.ProxyFactory; +import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.integration.Message; import org.springframework.integration.MessageChannel; import org.springframework.integration.MessageDeliveryException; @@ -26,6 +31,8 @@ import org.springframework.integration.support.MessageBuilder; import org.springframework.integration.support.channel.ChannelResolutionException; import org.springframework.integration.support.channel.ChannelResolver; import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; +import org.springframework.util.CollectionUtils; /** * Base class for MessageHandlers that are capable of producing replies. @@ -33,8 +40,10 @@ import org.springframework.util.Assert; * @author Mark Fisher * @author Iwein Fuld * @author Oleg Zhurakousky + * @author Gary Russell */ -public abstract class AbstractReplyProducingMessageHandler extends AbstractMessageHandler implements MessageProducer { +public abstract class AbstractReplyProducingMessageHandler extends AbstractMessageHandler + implements MessageProducer, BeanClassLoaderAware { private MessageChannel outputChannel; @@ -42,6 +51,13 @@ public abstract class AbstractReplyProducingMessageHandler extends AbstractMessa private final MessagingTemplate messagingTemplate; + private volatile RequestHandler advisedRequestHandler; + + private volatile List adviceChain; + + private volatile ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader(); + + public AbstractReplyProducingMessageHandler() { this.messagingTemplate = new MessagingTemplate(); @@ -82,11 +98,30 @@ public abstract class AbstractReplyProducingMessageHandler extends AbstractMessa return this.messagingTemplate; } + + public void setAdviceChain(List adviceChain) { + Assert.notNull(adviceChain, "adviceChain cannot be null"); + this.adviceChain = adviceChain; + } + + + public void setBeanClassLoader(ClassLoader beanClassLoader) { + this.beanClassLoader = beanClassLoader; + } + + @Override protected void onInit() { if (this.getBeanFactory() != null) { this.messagingTemplate.setBeanFactory(getBeanFactory()); } + if (!CollectionUtils.isEmpty(this.adviceChain)) { + ProxyFactory proxyFactory = new ProxyFactory(new AdvisedRequestHandler()); + for (Advice advice : this.adviceChain) { + proxyFactory.addAdvice(advice); + } + this.advisedRequestHandler = (RequestHandler) proxyFactory.getProxy(this.beanClassLoader); + } } /** @@ -94,7 +129,13 @@ public abstract class AbstractReplyProducingMessageHandler extends AbstractMessa */ @Override protected final void handleMessageInternal(Message message) { - Object result = this.handleRequestMessage(message); + Object result; + if (this.advisedRequestHandler == null) { + result = this.handleRequestMessage(message); + } + else { + result = this.advisedRequestHandler.handleRequestMessage(message); + } if (result != null) { MessageHeaders requestHeaders = message.getHeaders(); this.handleResult(result, requestHeaders); @@ -149,7 +190,7 @@ public abstract class AbstractReplyProducingMessageHandler extends AbstractMessa * 'outputChannel' is null. In that case, the header value must not also be * null, and it must be an instance of either String or {@link MessageChannel}. * @param replyMessage the reply Message to send - * @param replyChannelHeaderValue the 'replyChannel' header value from the original request + * @param replyChannelHeaderValue the 'replyChannel' header value from the original request */ private final void sendReplyMessage(Message replyMessage, final Object replyChannelHeaderValue) { if (logger.isDebugEnabled()) { @@ -207,4 +248,26 @@ public abstract class AbstractReplyProducingMessageHandler extends AbstractMessa */ protected abstract Object handleRequestMessage(Message requestMessage); + + private interface RequestHandler { + + Object handleRequestMessage(Message requestMessage); + + String toString(); + } + + private class AdvisedRequestHandler implements RequestHandler { + + public Object handleRequestMessage(Message requestMessage) { + return AbstractReplyProducingMessageHandler.this.handleRequestMessage(requestMessage); + } + + @Override + public String toString() { + return AbstractReplyProducingMessageHandler.this.toString(); + } + + + } + } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/handler/RetryStateGenerator.java b/spring-integration-core/src/main/java/org/springframework/integration/handler/RetryStateGenerator.java new file mode 100644 index 0000000000..e8430d8d62 --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/handler/RetryStateGenerator.java @@ -0,0 +1,31 @@ +/* + * Copyright 2002-2012 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.integration.handler; + +import org.springframework.integration.Message; +import org.springframework.retry.RetryState; + +/** + * Strategy interface for generating a {@link RetryState} instance + * based on a message. + * @author Gary Russell + * @since 2.2 + * + */ +public interface RetryStateGenerator { + + RetryState determineRetryState(Message message); +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/handler/advice/AbstractRequestHandlerAdvice.java b/spring-integration-core/src/main/java/org/springframework/integration/handler/advice/AbstractRequestHandlerAdvice.java new file mode 100644 index 0000000000..4f17c89d7f --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/handler/advice/AbstractRequestHandlerAdvice.java @@ -0,0 +1,87 @@ +/* + * Copyright 2002-2012 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.integration.handler.advice; + +import java.lang.reflect.Method; + +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.integration.Message; +import org.springframework.integration.core.MessageHandler; + +/** + * Base class for {@link MessageHandler} advice classes. + * @author Gary Russell + * @since 2.2 + * + */ +public abstract class AbstractRequestHandlerAdvice implements MethodInterceptor { + + protected final Log logger = LogFactory.getLog(this.getClass()); + + public final Object invoke(final MethodInvocation invocation) throws Throwable { + + Method method = invocation.getMethod(); + Object[] arguments = invocation.getArguments(); + boolean isMessageMethod = (method.getName().equals("handleRequestMessage") || method.getName().equals("handleMessage")) + && (arguments.length == 1 && arguments[0] instanceof Message); + + if (!isMessageMethod) { + return invocation.proceed(); + } + else { + Message message = (Message) arguments[0]; + try { + return doInvoke(new ExecutionCallback(){ + + public Object execute() throws Exception { + try { + return invocation.proceed(); + } + catch (Throwable e) { + throw new ThrowableHolderException(e); + } + } + }, invocation.getThis(), message); + } + catch (Exception e) { + if (e instanceof ThrowableHolderException) { + throw e.getCause(); + } + else { + throw e; + } + } + } + } + + protected abstract Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception; + + protected interface ExecutionCallback { + + Object execute() throws Exception; + } + + @SuppressWarnings("serial") + private class ThrowableHolderException extends RuntimeException { + + public ThrowableHolderException(Throwable cause) { + super(cause); + } + } +} \ No newline at end of file diff --git a/spring-integration-core/src/main/java/org/springframework/integration/handler/advice/ExpressionEvaluatingRequestHandlerAdvice.java b/spring-integration-core/src/main/java/org/springframework/integration/handler/advice/ExpressionEvaluatingRequestHandlerAdvice.java new file mode 100644 index 0000000000..72a56825af --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/handler/advice/ExpressionEvaluatingRequestHandlerAdvice.java @@ -0,0 +1,166 @@ +/* + * Copyright 2002-2012 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.integration.handler.advice; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.expression.Expression; +import org.springframework.integration.Message; +import org.springframework.integration.MessageChannel; +import org.springframework.integration.MessageHeaders; +import org.springframework.integration.core.MessageHandler; +import org.springframework.integration.core.MessagingTemplate; +import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor; +import org.springframework.integration.support.MessageBuilder; + +/** + * Used to advise {@link MessageHandler}s. + * Two expressions 'onSuccessExpression' and 'onFailureExpression' are evaluated when + * appropriate. If the evaluation returns a result, a message is sent to the onSuccessChannel + * or onFailureChannel as appropriate; the message is the input message with a header + * {@link MessageHeaders#POSTPROCESS_RESULT} containing the evaluation result. + * The failure expression is NOT evaluated if the success expression throws an exception. + * @author Gary Russell + * @since 2.2 + * + */ +public class ExpressionEvaluatingRequestHandlerAdvice extends AbstractRequestHandlerAdvice + implements BeanFactoryAware { + + private final ExpressionEvaluatingMessageProcessor onSuccessMessageProcessor; + + private final MessageChannel successChannel; + + private final ExpressionEvaluatingMessageProcessor onFailureMessageProcessor; + + private final MessageChannel failureChannel; + + private final MessagingTemplate messagingTemplate = new MessagingTemplate(); + + private volatile boolean trapException = false; + + private volatile boolean returnFailureExpressionResult = false; + + private volatile BeanFactory beanFactory; + + private volatile boolean propagateOnSuccessEvaluationFailures; + + /** + * @param onSuccessExpression + * @param successChannel + * @param onFailureExpression + * @param failureChannel + */ + public ExpressionEvaluatingRequestHandlerAdvice(Expression onSuccessExpression, MessageChannel successChannel, + Expression onFailureExpression, MessageChannel failureChannel) { + if (onSuccessExpression != null) { + this.onSuccessMessageProcessor = new ExpressionEvaluatingMessageProcessor(onSuccessExpression); + this.onSuccessMessageProcessor.setBeanFactory(this.beanFactory); + } + else { + this.onSuccessMessageProcessor = null; + } + this.successChannel = successChannel; + if (onFailureExpression != null) { + this.onFailureMessageProcessor = new ExpressionEvaluatingMessageProcessor(onFailureExpression); + onFailureMessageProcessor.setBeanFactory(this.beanFactory); + } + else { + this.onFailureMessageProcessor = null; + } + this.failureChannel = failureChannel; + + } + + /** + * If true, any exception will be caught and null returned. + * Default false. + * @param trapException + */ + public void setTrapException(boolean trapException) { + this.trapException = trapException; + } + + /** + * If true, the result of evaluating the onFailureExpression will + * be returned as the result of AbstractReplyProducingMessageHandler.handleRequestMessage(Message). + * @param returnFailureExpressionResult + */ + public void setReturnFailureExpressionResult(boolean returnFailureExpressionResult) { + this.returnFailureExpressionResult = returnFailureExpressionResult; + } + + /** + * If true and an onSuccess expression evaluation fails with an exception, the exception will be thrown to the + * caller. If false, the exception is caught. Default false. Ignored for onFailure expression evaluation - the + * original exception will be propagated (unless trapException is true). + * @param propagateOnSuccessEvaluationFailures + */ + public void setPropagateEvaluationFailures(boolean propagateOnSuccessEvaluationFailures) { + this.propagateOnSuccessEvaluationFailures = propagateOnSuccessEvaluationFailures; + } + + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = beanFactory; + } + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + try { + Object result = callback.execute(); + if (onSuccessMessageProcessor != null) { + evaluateExpression(message, this.onSuccessMessageProcessor, this.successChannel, this.propagateOnSuccessEvaluationFailures); + } + return result; + } + catch (Exception e) { + Object evalResult = evaluateExpression(message, this.onFailureMessageProcessor, this.failureChannel, false); + if (this.returnFailureExpressionResult) { + return evalResult; + } + if (!this.trapException) { + throw e; + } + return null; + } + } + + private Object evaluateExpression(Message message, + ExpressionEvaluatingMessageProcessor expressionEvaluatingMessageProcessor, + MessageChannel resultChannel, boolean propagateEvaluationFailure) throws Exception { + Object evalResult; + boolean evaluationFailed = false; + try { + evalResult = expressionEvaluatingMessageProcessor.processMessage(message); + } + catch (Exception e) { + evalResult = e; + evaluationFailed = true; + } + if (evalResult != null && resultChannel != null) { + message = MessageBuilder.fromMessage(message) + .setHeader(MessageHeaders.POSTPROCESS_RESULT, evalResult) + .build(); + this.messagingTemplate.send(resultChannel, message); + } + if (evaluationFailed && propagateEvaluationFailure) { + throw (Exception) evalResult; + } + return evalResult; + } + +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/handler/advice/RequestHandlerCircuitBreakerAdvice.java b/spring-integration-core/src/main/java/org/springframework/integration/handler/advice/RequestHandlerCircuitBreakerAdvice.java new file mode 100644 index 0000000000..1def2a8aa1 --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/handler/advice/RequestHandlerCircuitBreakerAdvice.java @@ -0,0 +1,95 @@ +/* + * Copyright 2002-2012 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.integration.handler.advice; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicInteger; + +import org.springframework.integration.Message; +import org.springframework.integration.MessagingException; + +/** + * A circuit breaker that stops calling a failing service after threshold + * failures, until halfOpenAfter milliseconds has elapsed. A successful + * call resets the failure counter. + * + * @author Gary Russell + * @since 2.2 + * + */ +public class RequestHandlerCircuitBreakerAdvice extends AbstractRequestHandlerAdvice { + + private volatile int threshold = 5; + + private volatile long halfOpenAfter = 1000; + + private final ConcurrentMap metadataMap = new ConcurrentHashMap(); + + public void setThreshold(int threshold) { + this.threshold = threshold; + } + + public void setHalfOpenAfter(long halfOpenAfter) { + this.halfOpenAfter = halfOpenAfter; + } + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + AdvisedMetadata metadata = this.metadataMap.get(target); + if (metadata == null) { + this.metadataMap.putIfAbsent(target, new AdvisedMetadata()); + metadata = this.metadataMap.get(target); + } + if (metadata.getFailures().get() >= this.threshold && + System.currentTimeMillis() - metadata.getLastFailure() < this.halfOpenAfter) { + throw new MessagingException("Circuit Breaker is Open for " + target); + } + try { + Object result = callback.execute(); + if (logger.isDebugEnabled() && metadata.getFailures().get() > 0) { + logger.debug("Closing Circuit Breaker for " + target); + } + metadata.getFailures().set(0); + return result; + } + catch (Exception e) { + metadata.getFailures().incrementAndGet(); + metadata.setLastFailure(System.currentTimeMillis()); + throw e; + } + } + + private class AdvisedMetadata { + + private final AtomicInteger failures = new AtomicInteger(); + + private volatile long lastFailure; + + private long getLastFailure() { + return lastFailure; + } + + private void setLastFailure(long lastFailure) { + this.lastFailure = lastFailure; + } + + private AtomicInteger getFailures() { + return failures; + } + } + +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/handler/advice/RequestHandlerRetryAdvice.java b/spring-integration-core/src/main/java/org/springframework/integration/handler/advice/RequestHandlerRetryAdvice.java new file mode 100644 index 0000000000..9a7eb59590 --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/handler/advice/RequestHandlerRetryAdvice.java @@ -0,0 +1,87 @@ +/* + * Copyright 2002-2012 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.integration.handler.advice; + +import org.springframework.integration.Message; +import org.springframework.integration.MessagingException; +import org.springframework.integration.handler.RetryStateGenerator; +import org.springframework.retry.RecoveryCallback; +import org.springframework.retry.RetryCallback; +import org.springframework.retry.RetryContext; +import org.springframework.retry.RetryState; +import org.springframework.retry.support.RetryTemplate; + +/** + * Uses spring-retry to perform stateless or stateful retry. + * Stateless retry means the retries are performed internally + * by the {@link RetryTemplate}; stateful retry means the + * exception is thrown but state is maintained to support + * the retry policies. Stateful retry requires a + * {@link RetryStateGenerator}. + * @author Gary Russell + * @since 2.2 + * + */ +public class RequestHandlerRetryAdvice extends AbstractRequestHandlerAdvice { + + private volatile RetryTemplate retryTemplate = new RetryTemplate(); + + private volatile RecoveryCallback recoveryCallback; + + // Stateless unless a state generator is provided + private volatile RetryStateGenerator retryStateGenerator = + new RetryStateGenerator() { + public RetryState determineRetryState(Message message) { + return null; + } + }; + + public void setRetryTemplate(RetryTemplate retryTemplate) { + this.retryTemplate = retryTemplate; + } + + public void setRecoveryCallback(RecoveryCallback recoveryCallback) { + this.recoveryCallback = recoveryCallback; + } + + public void setRetryStateGenerator(RetryStateGenerator retryStateGenerator) { + this.retryStateGenerator = retryStateGenerator; + } + + @Override + protected Object doInvoke(final ExecutionCallback callback, Object target, final Message message) throws Exception { + RetryState retryState = null; + retryState = this.retryStateGenerator.determineRetryState(message); + + return retryTemplate.execute(new RetryCallback(){ + public Object doWithRetry(RetryContext context) throws Exception { + try { + return callback.execute(); + } + catch (MessagingException e) { + if (e.getFailedMessage() == null) { + e.setFailedMessage(message); + } + throw e; + } + catch (Exception e) { + throw new MessagingException(message, "Failed to invoke handler", e); + } + } + }, this.recoveryCallback, retryState); + } + +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/handler/advice/package-info.java b/spring-integration-core/src/main/java/org/springframework/integration/handler/advice/package-info.java new file mode 100644 index 0000000000..3c3dbb01fd --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/handler/advice/package-info.java @@ -0,0 +1,6 @@ +/** + * Provides classes that are used to advise + * {@link org.springframework.integration.core.MessageHandler}s with + * cross-cutting concerns. + */ +package org.springframework.integration.handler.advice; \ No newline at end of file diff --git a/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.2.xsd b/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.2.xsd index 6e4f27d390..951bc7a012 100644 --- a/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.2.xsd +++ b/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.2.xsd @@ -1124,6 +1124,7 @@ + @@ -1481,28 +1482,7 @@ - - - - - - - - - - - - - - - - - - - - - - + @@ -3422,12 +3402,34 @@ is provided, the return value is expected to match a channel name exactly. + + + + + + + + + + + + + + + + + + + + + - + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/FilterParserTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/FilterParserTests-context.xml index 79fa610dfc..90b06b9931 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/FilterParserTests-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/FilterParserTests-context.xml @@ -23,7 +23,11 @@ - + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/FilterParserTests.java b/spring-integration-core/src/test/java/org/springframework/integration/config/FilterParserTests.java index 95b95ced8b..6ac12485c8 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/FilterParserTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/FilterParserTests.java @@ -22,7 +22,6 @@ import static org.junit.Assert.assertNull; import org.junit.Test; import org.junit.runner.RunWith; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.integration.Message; @@ -30,6 +29,7 @@ import org.springframework.integration.MessageChannel; import org.springframework.integration.MessageRejectedException; import org.springframework.integration.core.MessageSelector; import org.springframework.integration.core.PollableChannel; +import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; import org.springframework.integration.message.GenericMessage; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -69,13 +69,16 @@ public class FilterParserTests { @Autowired @Qualifier("discardAndExceptionOutput") PollableChannel discardAndExceptionOutput; + private static volatile int adviceCalled; @Test public void filterWithSelectorAdapterAccepts() { + adviceCalled = 0; adapterInput.send(new GenericMessage("test")); Message reply = adapterOutput.receive(0); assertNotNull(reply); assertEquals("test", reply.getPayload()); + assertEquals(1, adviceCalled); } @Test @@ -156,4 +159,13 @@ public class FilterParserTests { } } + public static class FooFilter extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return callback.execute(); + } + + } } diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/EnricherParserTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/EnricherParserTests-context.xml index 20b6dfb392..5b2a3f9a66 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/EnricherParserTests-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/EnricherParserTests-context.xml @@ -22,6 +22,10 @@ + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/EnricherParserTests.java b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/EnricherParserTests.java index 87fa08bb9c..e70ef0739e 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/EnricherParserTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/EnricherParserTests.java @@ -35,6 +35,7 @@ import org.springframework.integration.core.PollableChannel; import org.springframework.integration.core.SubscribableChannel; import org.springframework.integration.endpoint.EventDrivenConsumer; import org.springframework.integration.handler.AbstractReplyProducingMessageHandler; +import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; import org.springframework.integration.support.MessageBuilder; import org.springframework.integration.test.util.TestUtils; import org.springframework.integration.transformer.ContentEnricher; @@ -44,7 +45,8 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * @author Mark Fisher * @author Gunnar Hillert - * + * @author Gary Russell + * * @since 2.1 */ @RunWith(SpringJUnit4ClassRunner.class) @@ -54,6 +56,7 @@ public class EnricherParserTests { @Autowired private ApplicationContext context; + private static volatile int adviceCalled; @Test @SuppressWarnings("unchecked") @@ -84,13 +87,14 @@ public class EnricherParserTests { throw new IllegalStateException("expected 'name', 'age', and 'gender' only, not: " + e.getKey().getExpressionString()); } } + } @Test public void configurationCheckTimeoutParameters() { - + Object endpoint = context.getBean("enricher"); - + Long requestTimeout = TestUtils.getPropertyValue(endpoint, "handler.requestTimeout", Long.class); Long replyTimeout = TestUtils.getPropertyValue(endpoint, "handler.replyTimeout", Long.class); @@ -98,18 +102,18 @@ public class EnricherParserTests { assertEquals(Long.valueOf(9876L), replyTimeout); } - + @Test public void configurationCheckRequiresReply() { - + Object endpoint = context.getBean("enricher"); - + boolean requiresReply = TestUtils.getPropertyValue(endpoint, "handler.requiresReply", Boolean.class); assertTrue("Was expecting requiresReply to be 'false'", requiresReply); } - + @Test public void integrationTest() { SubscribableChannel requests = context.getBean("requests", SubscribableChannel.class); @@ -128,6 +132,7 @@ public class EnricherParserTests { assertEquals(42, enriched.getAge()); assertEquals("male", enriched.getGender()); assertNotSame(original, enriched); + assertEquals(1, adviceCalled); } private static class Source { @@ -176,6 +181,7 @@ public class EnricherParserTests { this.gender = gender; } + @Override public Object clone() { Target copy = new Target(); copy.setName(this.name); @@ -184,4 +190,13 @@ public class EnricherParserTests { } } + public static class FooAdvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return callback.execute(); + } + + } } diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ServiceActivatorParserTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ServiceActivatorParserTests-context.xml index 16950067e9..f0b46f7961 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ServiceActivatorParserTests-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ServiceActivatorParserTests-context.xml @@ -21,4 +21,9 @@ + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ServiceActivatorParserTests.java b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ServiceActivatorParserTests.java index d87b9a045c..6dca7a109f 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ServiceActivatorParserTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ServiceActivatorParserTests.java @@ -20,12 +20,13 @@ import static org.junit.Assert.assertEquals; import org.junit.Test; import org.junit.runner.RunWith; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.integration.Message; import org.springframework.integration.MessageChannel; import org.springframework.integration.core.MessagingTemplate; import org.springframework.integration.handler.ServiceActivatingHandler; +import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -56,6 +57,9 @@ public class ServiceActivatorParserTests { @Autowired private MessageChannel multipleArgsFromPayloadInput; + @Autowired + private MessageChannel advisedInput; + @SuppressWarnings("unused") // testing auto wiring only @Autowired @Qualifier("org.springframework.integration.config.ServiceActivatorFactoryBean#0") @@ -102,6 +106,11 @@ public class ServiceActivatorParserTests { assertEquals("JohnDoe", result); } + @Test + public void advised() { + Object result = this.sendAndReceive(advisedInput, "hello"); + assertEquals("bar", result); + } private Object sendAndReceive(MessageChannel channel, Object payload) { MessagingTemplate template = new MessagingTemplate(channel); @@ -152,4 +161,13 @@ public class ServiceActivatorParserTests { } } + public static class BarAdvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + callback.execute(); + return "bar"; + } + + } } diff --git a/spring-integration-core/src/test/java/org/springframework/integration/handler/AdvisedMessageHandlerTests.java b/spring-integration-core/src/test/java/org/springframework/integration/handler/AdvisedMessageHandlerTests.java new file mode 100644 index 0000000000..6e187893b0 --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/handler/AdvisedMessageHandlerTests.java @@ -0,0 +1,477 @@ +/* + * Copyright 2002-2012 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.integration.handler; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import org.aopalliance.aop.Advice; +import org.junit.Test; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.integration.Message; +import org.springframework.integration.MessageHandlingException; +import org.springframework.integration.MessageHeaders; +import org.springframework.integration.channel.QueueChannel; +import org.springframework.integration.core.PollableChannel; +import org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice; +import org.springframework.integration.handler.advice.RequestHandlerCircuitBreakerAdvice; +import org.springframework.integration.handler.advice.RequestHandlerRetryAdvice; +import org.springframework.integration.message.GenericMessage; +import org.springframework.retry.RecoveryCallback; +import org.springframework.retry.RetryContext; +import org.springframework.retry.RetryState; +import org.springframework.retry.support.DefaultRetryState; + +/** + * @author Gary Russell + * @since 2.2 + * + */ +public class AdvisedMessageHandlerTests { + + @Test + public void successFailureAdvice() { + final AtomicBoolean doFail = new AtomicBoolean(); + AbstractReplyProducingMessageHandler handler = new AbstractReplyProducingMessageHandler() { + @Override + protected Object handleRequestMessage(Message requestMessage) { + if (doFail.get()) { + throw new RuntimeException("qux"); + } + return "baz"; + } + }; + QueueChannel replies = new QueueChannel(); + handler.setOutputChannel(replies); + Message message = new GenericMessage("Hello, world!"); + + // no advice + handler.handleMessage(message); + Message reply = replies.receive(1000); + assertNotNull(reply); + assertEquals("baz", reply.getPayload()); + + PollableChannel successChannel = new QueueChannel(); + PollableChannel failureChannel = new QueueChannel(); + ExpressionEvaluatingRequestHandlerAdvice advice = new ExpressionEvaluatingRequestHandlerAdvice( + new SpelExpressionParser().parseExpression("'foo'"), successChannel, + new SpelExpressionParser().parseExpression("'bar'"), failureChannel); + + List adviceChain = new ArrayList(); + adviceChain.add(advice); + handler.setAdviceChain(adviceChain); + handler.afterPropertiesSet(); + + // advice with success + handler.handleMessage(message); + reply = replies.receive(1000); + assertNotNull(reply); + assertEquals("baz", reply.getPayload()); + + Message success = successChannel.receive(1000); + assertNotNull(success); + assertEquals("Hello, world!", success.getPayload()); + assertEquals("foo", success.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT)); + + // advice with failure, not trapped + doFail.set(true); + try { + handler.handleMessage(message); + fail("Expected exception"); + } + catch (Exception e) { + assertEquals("qux", e.getCause().getMessage()); + } + + Message failure = failureChannel.receive(1000); + assertNotNull(failure); + assertEquals("Hello, world!", failure.getPayload()); + assertEquals("bar", failure.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT)); + + // advice with failure, trapped + advice.setTrapException(true); + handler.handleMessage(message); + failure = failureChannel.receive(1000); + assertNotNull(failure); + assertEquals("Hello, world!", failure.getPayload()); + assertEquals("bar", failure.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT)); + assertNull(replies.receive(1)); + + // advice with failure, eval is result + advice.setReturnFailureExpressionResult(true); + handler.handleMessage(message); + failure = failureChannel.receive(1000); + assertNotNull(failure); + assertEquals("Hello, world!", failure.getPayload()); + assertEquals("bar", failure.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT)); + + reply = replies.receive(1000); + assertNotNull(reply); + assertEquals("bar", reply.getPayload()); + + } + + @Test + public void propagateOnSuccessExpressionFailures() { + final AtomicBoolean doFail = new AtomicBoolean(); + AbstractReplyProducingMessageHandler handler = new AbstractReplyProducingMessageHandler() { + @Override + protected Object handleRequestMessage(Message requestMessage) { + if (doFail.get()) { + throw new RuntimeException("qux"); + } + return "baz"; + } + }; + QueueChannel replies = new QueueChannel(); + handler.setOutputChannel(replies); + Message message = new GenericMessage("Hello, world!"); + + PollableChannel successChannel = new QueueChannel(); + PollableChannel failureChannel = new QueueChannel(); + ExpressionEvaluatingRequestHandlerAdvice advice = new ExpressionEvaluatingRequestHandlerAdvice( + new SpelExpressionParser().parseExpression("1/0"), successChannel, + new SpelExpressionParser().parseExpression("1/0"), failureChannel); + + List adviceChain = new ArrayList(); + adviceChain.add(advice); + handler.setAdviceChain(adviceChain); + handler.afterPropertiesSet(); + + // failing advice with success + handler.handleMessage(message); + Message reply = replies.receive(1000); + assertNotNull(reply); + assertEquals("baz", reply.getPayload()); + + Message success = successChannel.receive(1000); + assertNotNull(success); + assertEquals("Hello, world!", success.getPayload()); + assertEquals(MessageHandlingException.class, success.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT).getClass()); + assertEquals("Expression evaluation failed: 1/0", ((Exception) success.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT)).getMessage()); + + // propagate failing advice with success + advice.setPropagateEvaluationFailures(true); + try { + handler.handleMessage(message); + fail("Expected Exception"); + } + catch (MessageHandlingException e) { + assertEquals("Expression evaluation failed: 1/0", e.getMessage()); + } + reply = replies.receive(1); + assertNull(reply); + + success = successChannel.receive(1000); + assertNotNull(success); + assertEquals("Hello, world!", success.getPayload()); + assertEquals(MessageHandlingException.class, success.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT).getClass()); + assertEquals("Expression evaluation failed: 1/0", ((Exception) success.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT)).getMessage()); + + } + + @Test + public void propagateOnFailureExpressionFailures() { + final AtomicBoolean doFail = new AtomicBoolean(true); + AbstractReplyProducingMessageHandler handler = new AbstractReplyProducingMessageHandler() { + @Override + protected Object handleRequestMessage(Message requestMessage) { + if (doFail.get()) { + throw new RuntimeException("qux"); + } + return "baz"; + } + }; + QueueChannel replies = new QueueChannel(); + handler.setOutputChannel(replies); + Message message = new GenericMessage("Hello, world!"); + + PollableChannel successChannel = new QueueChannel(); + PollableChannel failureChannel = new QueueChannel(); + ExpressionEvaluatingRequestHandlerAdvice advice = new ExpressionEvaluatingRequestHandlerAdvice( + new SpelExpressionParser().parseExpression("1/0"), successChannel, + new SpelExpressionParser().parseExpression("1/0"), failureChannel); + + List adviceChain = new ArrayList(); + adviceChain.add(advice); + handler.setAdviceChain(adviceChain); + handler.afterPropertiesSet(); + + // failing advice with failure + try { + handler.handleMessage(message); + fail("Expected exception"); + } + catch (Exception e) { + assertEquals("qux", e.getCause().getMessage()); + } + Message reply = replies.receive(1); + assertNull(reply); + + Message failure = failureChannel.receive(1000); + assertNotNull(failure); + assertEquals("Hello, world!", failure.getPayload()); + assertEquals(MessageHandlingException.class, failure.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT).getClass()); + assertEquals("Expression evaluation failed: 1/0", ((Exception) failure.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT)).getMessage()); + + // propagate failing advice with failure; expect original exception + advice.setPropagateEvaluationFailures(true); + try { + handler.handleMessage(message); + fail("Expected Exception"); + } + catch (MessageHandlingException e) { + assertEquals("qux", e.getCause().getMessage()); + } + reply = replies.receive(1); + assertNull(reply); + + failure = failureChannel.receive(1000); + assertNotNull(failure); + assertEquals("Hello, world!", failure.getPayload()); + assertEquals(MessageHandlingException.class, failure.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT).getClass()); + assertEquals("Expression evaluation failed: 1/0", ((Exception) failure.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT)).getMessage()); + + } + + @Test + public void circuitBreakerTests() throws Exception { + final AtomicBoolean doFail = new AtomicBoolean(); + AbstractReplyProducingMessageHandler handler = new AbstractReplyProducingMessageHandler() { + + @Override + protected Object handleRequestMessage(Message requestMessage) { + if (doFail.get()) { + throw new RuntimeException("foo"); + } + return "bar"; + } + + }; + handler.setBeanName("baz"); + handler.setOutputChannel(new QueueChannel()); + RequestHandlerCircuitBreakerAdvice advice = new RequestHandlerCircuitBreakerAdvice(); + /* + * Circuit breaker opens after 2 failures; allows a new attempt after 100ms and + * immediately opens again if that attempt fails. After a successful attempt, + * we reset the failure counter. + */ + advice.setThreshold(2); + advice.setHalfOpenAfter(100); + + List adviceChain = new ArrayList(); + adviceChain.add(advice); + handler.setAdviceChain(adviceChain); + handler.afterPropertiesSet(); + + doFail.set(true); + Message message = new GenericMessage("Hello, world!"); + try { + handler.handleMessage(message); + fail("Expected failure"); + } + catch (Exception e) { + assertEquals("foo", e.getCause().getMessage()); + } + try { + handler.handleMessage(message); + fail("Expected failure"); + } + catch (Exception e) { + assertEquals("foo", e.getCause().getMessage()); + } + try { + handler.handleMessage(message); + fail("Expected failure"); + } + catch (Exception e) { + assertEquals("Circuit Breaker is Open for baz", e.getMessage()); + } + Thread.sleep(100); + try { + handler.handleMessage(message); + fail("Expected failure"); + } + catch (Exception e) { + assertEquals("foo", e.getCause().getMessage()); + } + try { + handler.handleMessage(message); + fail("Expected failure"); + } + catch (Exception e) { + assertEquals("Circuit Breaker is Open for baz", e.getMessage()); + } + Thread.sleep(100); + doFail.set(false); + handler.handleMessage(message); + doFail.set(true); + try { + handler.handleMessage(message); + fail("Expected failure"); + } + catch (Exception e) { + assertEquals("foo", e.getCause().getMessage()); + } + try { + handler.handleMessage(message); + fail("Expected failure"); + } + catch (Exception e) { + assertEquals("foo", e.getCause().getMessage()); + } + try { + handler.handleMessage(message); + fail("Expected failure"); + } + catch (Exception e) { + assertEquals("Circuit Breaker is Open for baz", e.getMessage()); + } + } + + @Test + public void defaultRetrySucceedonThirdTry() { + final AtomicInteger counter = new AtomicInteger(2); + AbstractReplyProducingMessageHandler handler = new AbstractReplyProducingMessageHandler() { + + @Override + protected Object handleRequestMessage(Message requestMessage) { + if (counter.getAndDecrement() > 0) { + throw new RuntimeException("foo"); + } + return "bar"; + } + }; + QueueChannel replies = new QueueChannel(); + handler.setOutputChannel(replies); + RequestHandlerRetryAdvice advice = new RequestHandlerRetryAdvice(); + + List adviceChain = new ArrayList(); + adviceChain.add(advice); + handler.setAdviceChain(adviceChain); + handler.afterPropertiesSet(); + + Message message = new GenericMessage("Hello, world!"); + handler.handleMessage(message); + assertTrue(counter.get() == -1); + Message reply = replies.receive(1000); + assertNotNull(reply); + assertEquals("bar", reply.getPayload()); + + } + + @Test + public void defaultStatefulRetrySucceedonThirdTry() { + final AtomicInteger counter = new AtomicInteger(2); + AbstractReplyProducingMessageHandler handler = new AbstractReplyProducingMessageHandler() { + + @Override + protected Object handleRequestMessage(Message requestMessage) { + if (counter.getAndDecrement() > 0) { + throw new RuntimeException("foo"); + } + return "bar"; + } + }; + QueueChannel replies = new QueueChannel(); + handler.setOutputChannel(replies); + RequestHandlerRetryAdvice advice = new RequestHandlerRetryAdvice(); + + advice.setRetryStateGenerator(new RetryStateGenerator() { + public RetryState determineRetryState(Message message) { + return new DefaultRetryState(message.getHeaders().getId()); + } + }); + + List adviceChain = new ArrayList(); + adviceChain.add(advice); + handler.setAdviceChain(adviceChain); + handler.afterPropertiesSet(); + + Message message = new GenericMessage("Hello, world!"); + for (int i = 0; i < 3; i++) { + try { + handler.handleMessage(message); + } + catch (Exception e) { + assertTrue(i < 2); + } + } + assertTrue(counter.get() == -1); + Message reply = replies.receive(1000); + assertNotNull(reply); + assertEquals("bar", reply.getPayload()); + + } + + @Test + public void defaultStatefulRetryRecoverAfterThirdTry() { + final AtomicInteger counter = new AtomicInteger(3); + AbstractReplyProducingMessageHandler handler = new AbstractReplyProducingMessageHandler() { + + @Override + protected Object handleRequestMessage(Message requestMessage) { + if (counter.getAndDecrement() > 0) { + throw new RuntimeException("foo"); + } + return "bar"; + } + }; + QueueChannel replies = new QueueChannel(); + handler.setOutputChannel(replies); + RequestHandlerRetryAdvice advice = new RequestHandlerRetryAdvice(); + + advice.setRetryStateGenerator(new RetryStateGenerator() { + public RetryState determineRetryState(Message message) { + return new DefaultRetryState(message.getHeaders().getId()); + } + }); + + advice.setRecoveryCallback(new RecoveryCallback() { + + public Object recover(RetryContext context) throws Exception { + return "baz"; + } + }); + + List adviceChain = new ArrayList(); + adviceChain.add(advice); + handler.setAdviceChain(adviceChain); + handler.afterPropertiesSet(); + + Message message = new GenericMessage("Hello, world!"); + for (int i = 0; i < 4; i++) { + try { + handler.handleMessage(message); + } + catch (Exception e) { + } + } + assertTrue(counter.get() == 0); + Message reply = replies.receive(1000); + assertNotNull(reply); + assertEquals("baz", reply.getPayload()); + + } +} diff --git a/spring-integration-core/src/test/java/org/springframework/integration/transformer/TransformerContextTests.java b/spring-integration-core/src/test/java/org/springframework/integration/transformer/TransformerContextTests.java index 7bc55cccf4..2280203f14 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/transformer/TransformerContextTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/transformer/TransformerContextTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2012 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. @@ -19,19 +19,22 @@ package org.springframework.integration.transformer; import static org.junit.Assert.assertEquals; import org.junit.Test; - import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.integration.Message; import org.springframework.integration.MessageChannel; import org.springframework.integration.core.PollableChannel; +import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; import org.springframework.integration.message.GenericMessage; /** * @author Mark Fisher + * @author Gary Russell */ public class TransformerContextTests { + private static volatile int adviceCalled; + @Test public void methodInvokingTransformer() { ApplicationContext context = new ClassPathXmlApplicationContext( @@ -41,6 +44,16 @@ public class TransformerContextTests { input.send(new GenericMessage("foo")); Message reply = output.receive(0); assertEquals("FOO", reply.getPayload()); + assertEquals(1, adviceCalled); } + public static class FooAdvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return callback.execute(); + } + + } } \ No newline at end of file diff --git a/spring-integration-core/src/test/java/org/springframework/integration/transformer/transformerContextTests.xml b/spring-integration-core/src/test/java/org/springframework/integration/transformer/transformerContextTests.xml index 5679865023..2fbcdc1dd9 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/transformer/transformerContextTests.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/transformer/transformerContextTests.xml @@ -13,7 +13,11 @@ - + + + + + diff --git a/spring-integration-event/src/main/resources/org/springframework/integration/event/config/spring-integration-event-2.2.xsd b/spring-integration-event/src/main/resources/org/springframework/integration/event/config/spring-integration-event-2.2.xsd index 91a174aa06..1658e38d8d 100644 --- a/spring-integration-event/src/main/resources/org/springframework/integration/event/config/spring-integration-event-2.2.xsd +++ b/spring-integration-event/src/main/resources/org/springframework/integration/event/config/spring-integration-event-2.2.xsd @@ -81,9 +81,10 @@ - + - + + diff --git a/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParserTests-context.xml b/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParserTests-context.xml index 88903e44cd..b160d943f5 100644 --- a/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParserTests-context.xml +++ b/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParserTests-context.xml @@ -11,6 +11,14 @@ + + + + + + + + diff --git a/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParserTests.java b/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParserTests.java index 5e64e9dfc6..675e9de37e 100644 --- a/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParserTests.java +++ b/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParserTests.java @@ -16,7 +16,11 @@ package org.springframework.integration.event.config; +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CyclicBarrier; + import junit.framework.Assert; + import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.DirectFieldAccessor; @@ -31,16 +35,15 @@ import org.springframework.integration.channel.QueueChannel; import org.springframework.integration.core.MessageHandler; import org.springframework.integration.endpoint.EventDrivenConsumer; import org.springframework.integration.event.outbound.ApplicationEventPublishingMessageHandler; +import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; import org.springframework.integration.message.GenericMessage; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import java.util.concurrent.BrokenBarrierException; -import java.util.concurrent.CyclicBarrier; - /** * @author Oleg Zhurakousky * @author Artem Bilan + * @author Gary Russell * @since 2.0 */ @RunWith(SpringJUnit4ClassRunner.class) @@ -52,6 +55,7 @@ public class EventOutboundChannelAdapterParserTests { private volatile boolean receivedEvent; + private static volatile int adviceCalled; @Test public void validateEventParser() { @@ -82,8 +86,30 @@ public class EventOutboundChannelAdapterParserTests { Assert.assertTrue(receivedEvent); } + @Test + public void withAdvice() { + receivedEvent = false; + ApplicationListener listener = new ApplicationListener() { + public void onApplicationEvent(ApplicationEvent event) { + Object source = event.getSource(); + if (source instanceof Message){ + String payload = (String) ((Message) source).getPayload(); + if (payload.equals("hello")) { + receivedEvent = true; + } + } + } + }; + context.addApplicationListener(listener); + DirectChannel channel = context.getBean("inputAdvice", DirectChannel.class); + channel.send(new GenericMessage("hello")); + Assert.assertTrue(receivedEvent); + Assert.assertEquals(1, adviceCalled); + } + @Test //INT-2275 public void testInsideChain() { + receivedEvent = false; ApplicationListener listener = new ApplicationListener() { public void onApplicationEvent(ApplicationEvent event) { Object source = event.getSource(); @@ -103,6 +129,7 @@ public class EventOutboundChannelAdapterParserTests { @Test(timeout=2000) public void validateUsageWithPollableChannel() throws Exception { + receivedEvent = false; ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("EventOutboundChannelAdapterParserTestsWithPollable-context.xml", EventOutboundChannelAdapterParserTests.class); final CyclicBarrier barier = new CyclicBarrier(2); ApplicationListener listener = new ApplicationListener() { @@ -132,4 +159,13 @@ public class EventOutboundChannelAdapterParserTests { Assert.assertTrue(receivedEvent); } + public static class FooAdvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return callback.execute(); + } + + } } diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/FileWritingMessageHandler.java b/spring-integration-file/src/main/java/org/springframework/integration/file/FileWritingMessageHandler.java index 3bac5a81ed..052318d5c0 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/FileWritingMessageHandler.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/FileWritingMessageHandler.java @@ -215,6 +215,8 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand @Override public final void onInit() { + super.onInit(); + this.evaluationContext.addPropertyAccessor(new MapAccessor()); final BeanFactory beanFactory = this.getBeanFactory(); diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileOutboundChannelAdapterParser.java b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileOutboundChannelAdapterParser.java index 591b7de360..3815df9bd1 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileOutboundChannelAdapterParser.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileOutboundChannelAdapterParser.java @@ -16,17 +16,16 @@ package org.springframework.integration.file.config; -import org.w3c.dom.Element; - import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser; +import org.w3c.dom.Element; /** * Parser for the <outbound-channel-adapter/> element of the 'file' * namespace. - * + * * @author Mark Fisher * @author Iwein Fuld * @author Oleg Zhurakousky @@ -40,4 +39,10 @@ public class FileOutboundChannelAdapterParser extends AbstractOutboundChannelAda return handlerBuilder.getBeanDefinition(); } + @Override + protected boolean isUsingReplyProducer() { + // cannot be automatically determined by superclass because we are using a factory bean. + return true; + } + } diff --git a/spring-integration-file/src/main/resources/org/springframework/integration/file/config/spring-integration-file-2.2.xsd b/spring-integration-file/src/main/resources/org/springframework/integration/file/config/spring-integration-file-2.2.xsd index 4fbca60514..524a4ded55 100644 --- a/spring-integration-file/src/main/resources/org/springframework/integration/file/config/spring-integration-file-2.2.xsd +++ b/spring-integration-file/src/main/resources/org/springframework/integration/file/config/spring-integration-file-2.2.xsd @@ -255,9 +255,10 @@ Only files matching this regular expression will be picked up by this adapter. - + - + + diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileOutboundChannelAdapterParserTests-context.xml b/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileOutboundChannelAdapterParserTests-context.xml index f4821a043f..6fed5a9bf8 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileOutboundChannelAdapterParserTests-context.xml +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileOutboundChannelAdapterParserTests-context.xml @@ -47,7 +47,11 @@ + directory="test"> + + + + message) throws Exception { + adviceCalled++; + return callback.execute(); + } } } diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileOutboundGatewayParserTests-context.xml b/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileOutboundGatewayParserTests-context.xml index 2afba0e123..b105a54cb0 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileOutboundGatewayParserTests-context.xml +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileOutboundGatewayParserTests-context.xml @@ -19,7 +19,11 @@ + auto-startup="false" order="777" filename-generator-expression="'foo.txt'"> + + + + ("foo")); + assertEquals(1, adviceCalled); } /** @@ -282,4 +287,13 @@ public class FileOutboundGatewayParserTests { } + public static class FooAdvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return null; + } + + } } diff --git a/spring-integration-ftp/src/main/resources/org/springframework/integration/ftp/config/spring-integration-ftp-2.2.xsd b/spring-integration-ftp/src/main/resources/org/springframework/integration/ftp/config/spring-integration-ftp-2.2.xsd index 24e064c1d7..5ac4c3e054 100644 --- a/spring-integration-ftp/src/main/resources/org/springframework/integration/ftp/config/spring-integration-ftp-2.2.xsd +++ b/spring-integration-ftp/src/main/resources/org/springframework/integration/ftp/config/spring-integration-ftp-2.2.xsd @@ -20,6 +20,9 @@ + + + @@ -232,6 +235,9 @@ + + + diff --git a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundChannelAdapterParserTests-context.xml b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundChannelAdapterParserTests-context.xml index 2adbd5ada3..ed5f351c6e 100644 --- a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundChannelAdapterParserTests-context.xml +++ b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundChannelAdapterParserTests-context.xml @@ -59,7 +59,16 @@ channel="ftpChannel" session-factory="cachingSessionFactory" remote-directory="foo/bar"/> - + + + + + + + diff --git a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundChannelAdapterParserTests.java b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundChannelAdapterParserTests.java index 2cf4585dba..38fbce2e88 100644 --- a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundChannelAdapterParserTests.java +++ b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundChannelAdapterParserTests.java @@ -26,16 +26,18 @@ import java.util.Iterator; import java.util.Set; import org.junit.Test; - import org.springframework.beans.factory.BeanCreationException; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.Message; import org.springframework.integration.channel.PublishSubscribeChannel; import org.springframework.integration.core.MessageHandler; import org.springframework.integration.endpoint.EventDrivenConsumer; import org.springframework.integration.file.remote.handler.FileTransferringMessageHandler; import org.springframework.integration.file.remote.session.CachingSessionFactory; import org.springframework.integration.ftp.session.DefaultFtpSessionFactory; +import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; +import org.springframework.integration.message.GenericMessage; import org.springframework.integration.test.util.TestUtils; /** @@ -45,6 +47,8 @@ import org.springframework.integration.test.util.TestUtils; */ public class FtpOutboundChannelAdapterParserTests { + private static volatile int adviceCalled; + @Test public void testFtpOutboundChannelAdapterComplete() throws Exception{ ApplicationContext ac = @@ -97,6 +101,15 @@ public class FtpOutboundChannelAdapterParserTests { assertEquals(DefaultFtpSessionFactory.class, innerSfProperty.getClass()); } + @Test + public void adviceChain() { + ApplicationContext ac = new ClassPathXmlApplicationContext( + "FtpOutboundChannelAdapterParserTests-context.xml", this.getClass()); + Object adapter = ac.getBean("advisedAdapter"); + MessageHandler handler = TestUtils.getPropertyValue(adapter, "handler", MessageHandler.class); + handler.handleMessage(new GenericMessage("foo")); + assertEquals(1, adviceCalled); + } @Test public void testTemporaryFileSuffix() { @@ -107,4 +120,13 @@ public class FtpOutboundChannelAdapterParserTests { assertFalse((Boolean)TestUtils.getPropertyValue(handler,"useTemporaryFileName")); } + public static class FooAdvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return null; + } + + } } diff --git a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundGatewayParserTests-context.xml b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundGatewayParserTests-context.xml index b5dd3c5fae..60145b3ced 100644 --- a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundGatewayParserTests-context.xml +++ b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundGatewayParserTests-context.xml @@ -41,7 +41,11 @@ command-options="-P" expression="payload" order="2" - /> + > + + + + diff --git a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundGatewayParserTests.java b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundGatewayParserTests.java index 33f67bcf1d..78ffb5cbc6 100644 --- a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundGatewayParserTests.java +++ b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundGatewayParserTests.java @@ -26,9 +26,12 @@ import java.util.Set; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.integration.Message; import org.springframework.integration.endpoint.AbstractEndpoint; import org.springframework.integration.file.remote.session.CachingSessionFactory; import org.springframework.integration.ftp.gateway.FtpOutboundGateway; +import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; +import org.springframework.integration.message.GenericMessage; import org.springframework.integration.test.util.TestUtils; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -50,6 +53,8 @@ public class FtpOutboundGatewayParserTests { @Autowired AbstractEndpoint gateway2; + private static volatile int adviceCalled; + @Test public void testGateway1() { FtpOutboundGateway gateway = TestUtils.getPropertyValue(gateway1, @@ -84,5 +89,17 @@ public class FtpOutboundGatewayParserTests { @SuppressWarnings("unchecked") Set options = TestUtils.getPropertyValue(gateway, "options", Set.class); assertTrue(options.contains("-P")); + gateway.handleMessage(new GenericMessage("foo")); + assertEquals(1, adviceCalled); + } + + public static class FooAdvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return null; + } + } } diff --git a/spring-integration-gemfire/src/main/resources/org/springframework/integration/gemfire/config/xml/spring-integration-gemfire-2.2.xsd b/spring-integration-gemfire/src/main/resources/org/springframework/integration/gemfire/config/xml/spring-integration-gemfire-2.2.xsd index d5ceae6963..db4fb05c4e 100644 --- a/spring-integration-gemfire/src/main/resources/org/springframework/integration/gemfire/config/xml/spring-integration-gemfire-2.2.xsd +++ b/spring-integration-gemfire/src/main/resources/org/springframework/integration/gemfire/config/xml/spring-integration-gemfire-2.2.xsd @@ -132,7 +132,7 @@ writes Message to a Gemfire cache - + @@ -142,7 +142,8 @@ - + + diff --git a/spring-integration-gemfire/src/test/java/org/springframework/integration/gemfire/config/xml/GemfireOutboundChannelAdapterParserTests-context.xml b/spring-integration-gemfire/src/test/java/org/springframework/integration/gemfire/config/xml/GemfireOutboundChannelAdapterParserTests-context.xml new file mode 100644 index 0000000000..713a1a335e --- /dev/null +++ b/spring-integration-gemfire/src/test/java/org/springframework/integration/gemfire/config/xml/GemfireOutboundChannelAdapterParserTests-context.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + diff --git a/spring-integration-gemfire/src/test/java/org/springframework/integration/gemfire/config/xml/GemfireOutboundChannelAdapterParserTests.java b/spring-integration-gemfire/src/test/java/org/springframework/integration/gemfire/config/xml/GemfireOutboundChannelAdapterParserTests.java index 0a10899d35..d1304b339d 100644 --- a/spring-integration-gemfire/src/test/java/org/springframework/integration/gemfire/config/xml/GemfireOutboundChannelAdapterParserTests.java +++ b/spring-integration-gemfire/src/test/java/org/springframework/integration/gemfire/config/xml/GemfireOutboundChannelAdapterParserTests.java @@ -13,24 +13,51 @@ package org.springframework.integration.gemfire.config.xml; -import org.junit.Test; -import org.springframework.beans.factory.parsing.BeanDefinitionParsingException; -import org.w3c.dom.Element; - +import static org.junit.Assert.assertEquals; import static org.springframework.integration.gemfire.config.xml.ParserTestUtil.createFakeParserContext; import static org.springframework.integration.gemfire.config.xml.ParserTestUtil.loadXMLFrom; +import org.junit.Test; +import org.springframework.beans.factory.parsing.BeanDefinitionParsingException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.Message; +import org.springframework.integration.MessageChannel; +import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; +import org.springframework.integration.message.GenericMessage; +import org.w3c.dom.Element; + /** * @author Dan Oxlade */ public class GemfireOutboundChannelAdapterParserTests { - private GemfireOutboundChannelAdapterParser underTest = new GemfireOutboundChannelAdapterParser(); + private GemfireOutboundChannelAdapterParser underTest = new GemfireOutboundChannelAdapterParser(); - @Test(expected = BeanDefinitionParsingException.class) - public void regionIsARequiredAttribute() throws Exception { - String xml = ""; - Element element = loadXMLFrom(xml).getDocumentElement(); - underTest.parseConsumer(element, createFakeParserContext()); - } + private volatile static int adviceCalled; + + @Test(expected = BeanDefinitionParsingException.class) + public void regionIsARequiredAttribute() throws Exception { + String xml = ""; + Element element = loadXMLFrom(xml).getDocumentElement(); + underTest.parseConsumer(element, createFakeParserContext()); + } + + @Test + public void withAdvice() { + ApplicationContext ctx = new ClassPathXmlApplicationContext(this.getClass().getSimpleName() + "-context.xml", this.getClass()); + MessageChannel channel = ctx.getBean("input", MessageChannel.class); + channel.send(new GenericMessage("foo")); + assertEquals(1, adviceCalled); + } + + public static class FooAdvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return null; + } + + } } diff --git a/spring-integration-groovy/src/main/resources/org/springframework/integration/groovy/config/spring-integration-groovy-2.2.xsd b/spring-integration-groovy/src/main/resources/org/springframework/integration/groovy/config/spring-integration-groovy-2.2.xsd index 627fee4e21..dc60b9a20e 100644 --- a/spring-integration-groovy/src/main/resources/org/springframework/integration/groovy/config/spring-integration-groovy-2.2.xsd +++ b/spring-integration-groovy/src/main/resources/org/springframework/integration/groovy/config/spring-integration-groovy-2.2.xsd @@ -49,10 +49,11 @@ ]]> - + - + + diff --git a/spring-integration-groovy/src/test/java/org/springframework/integration/groovy/config/GroovyControlBusTests-context.xml b/spring-integration-groovy/src/test/java/org/springframework/integration/groovy/config/GroovyControlBusTests-context.xml index cc92b14d75..3272ca7af6 100644 --- a/spring-integration-groovy/src/test/java/org/springframework/integration/groovy/config/GroovyControlBusTests-context.xml +++ b/spring-integration-groovy/src/test/java/org/springframework/integration/groovy/config/GroovyControlBusTests-context.xml @@ -21,7 +21,11 @@ + customizer="groovyCustomizer"> + + + + diff --git a/spring-integration-groovy/src/test/java/org/springframework/integration/groovy/config/GroovyControlBusTests.java b/spring-integration-groovy/src/test/java/org/springframework/integration/groovy/config/GroovyControlBusTests.java index dc197d238d..531846b50b 100644 --- a/spring-integration-groovy/src/test/java/org/springframework/integration/groovy/config/GroovyControlBusTests.java +++ b/spring-integration-groovy/src/test/java/org/springframework/integration/groovy/config/GroovyControlBusTests.java @@ -20,12 +20,11 @@ import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; +import groovy.lang.GroovyObject; import java.util.HashMap; import java.util.Map; -import groovy.lang.GroovyObject; - import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.BeanCreationException; @@ -36,6 +35,7 @@ import org.springframework.integration.Message; import org.springframework.integration.MessageChannel; import org.springframework.integration.MessageHandlingException; import org.springframework.integration.core.PollableChannel; +import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; import org.springframework.integration.support.MessageBuilder; import org.springframework.jmx.export.annotation.ManagedOperation; import org.springframework.jmx.export.annotation.ManagedResource; @@ -49,6 +49,7 @@ import org.springframework.web.context.request.RequestContextHolder; /** * @author Dave Syer * @author Artem Bilan + * @author Gary Russell * @since 2.0 */ @ContextConfiguration @@ -64,6 +65,8 @@ public class GroovyControlBusTests { @Autowired private MyGroovyCustomizer groovyCustomizer; + private static volatile int adviceCalled; + @Test public void testOperationOfControlBus() { // long is > 3 this.groovyCustomizer.executed = false; @@ -72,6 +75,7 @@ public class GroovyControlBusTests { assertEquals("catbar", output.receive(0).getPayload()); assertNull(output.receive(0)); assertTrue(this.groovyCustomizer.executed); + assertEquals(1, adviceCalled); } @Test //INT-2567 @@ -201,4 +205,13 @@ public class GroovyControlBusTests { } + public static class FooAdvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return callback.execute(); + } + + } } diff --git a/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http-2.2.xsd b/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http-2.2.xsd index 75902c0f34..63abaae351 100644 --- a/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http-2.2.xsd +++ b/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http-2.2.xsd @@ -1,11 +1,13 @@ + - + - + + @@ -509,9 +512,10 @@ The String "HTTP_REQUEST_HEADERS" will match against any of the standard HTTP Re - + - + + diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParserTests-context.xml b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParserTests-context.xml index 77d0a29fbe..08e59d432d 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParserTests-context.xml +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParserTests-context.xml @@ -40,6 +40,12 @@ + + + + + + diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParserTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParserTests.java index 0b93513d47..3535b4e885 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParserTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundChannelAdapterParserTests.java @@ -28,7 +28,6 @@ import java.util.Map; import org.junit.Test; import org.junit.runner.RunWith; - import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -41,8 +40,12 @@ import org.springframework.http.HttpMethod; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.ClientHttpResponse; import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.integration.Message; +import org.springframework.integration.core.MessageHandler; import org.springframework.integration.endpoint.AbstractEndpoint; +import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; import org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler; +import org.springframework.integration.message.GenericMessage; import org.springframework.integration.test.util.TestUtils; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -52,6 +55,7 @@ import org.springframework.web.client.RestTemplate; /** * @author Mark Fisher + * @author Gary Russell */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration @@ -75,12 +79,16 @@ public class HttpOutboundChannelAdapterParserTests { @Autowired @Qualifier("withUrlExpression") private AbstractEndpoint withUrlExpression; + @Autowired @Qualifier("withAdvice") + private AbstractEndpoint withAdvice; + @Autowired @Qualifier("withUrlExpressionAndTemplate") private AbstractEndpoint withUrlExpressionAndTemplate; @Autowired private ApplicationContext applicationContext; + private static volatile int adviceCalled; @Test public void minimalConfig() { @@ -201,6 +209,13 @@ public class HttpOutboundChannelAdapterParserTests { assertEquals(true, handlerAccessor.getPropertyValue("extractPayload")); } + @Test + public void withAdvice() { + MessageHandler handler = TestUtils.getPropertyValue(this.withAdvice, "handler", MessageHandler.class); + handler.handleMessage(new GenericMessage("foo")); + assertEquals(1, adviceCalled); + } + @Test public void withUrlExpressionAndTemplate() { DirectFieldAccessor endpointAccessor = new DirectFieldAccessor(this.withUrlExpressionAndTemplate); @@ -239,4 +254,13 @@ public class HttpOutboundChannelAdapterParserTests { } } + public static class FooAdvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return null; + } + + } } diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundGatewayParserTests-context.xml b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundGatewayParserTests-context.xml index b86162eb4a..57fbdd3f8b 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundGatewayParserTests-context.xml +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundGatewayParserTests-context.xml @@ -42,6 +42,12 @@ + + + + + + diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundGatewayParserTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundGatewayParserTests.java index f70be245bb..37e15b8400 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundGatewayParserTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpOutboundGatewayParserTests.java @@ -26,7 +26,6 @@ import java.util.Map; import org.junit.Test; import org.junit.runner.RunWith; - import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -37,9 +36,12 @@ import org.springframework.http.HttpMethod; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.ClientHttpResponse; import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.integration.Message; import org.springframework.integration.MessageChannel; import org.springframework.integration.endpoint.AbstractEndpoint; +import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; import org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler; +import org.springframework.integration.message.GenericMessage; import org.springframework.integration.test.util.TestUtils; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -48,6 +50,7 @@ import org.springframework.web.client.ResponseErrorHandler; /** * @author Mark Fisher + * @author Gary Russell */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration @@ -62,9 +65,13 @@ public class HttpOutboundGatewayParserTests { @Autowired @Qualifier("withUrlExpression") private AbstractEndpoint withUrlExpressionEndpoint; + @Autowired @Qualifier("withAdvice") + private AbstractEndpoint withAdvice; + @Autowired private ApplicationContext applicationContext; + private static volatile int adviceCalled; @Test public void minimalConfig() { @@ -160,6 +167,13 @@ public class HttpOutboundGatewayParserTests { assertEquals(false, handlerAccessor.getPropertyValue("transferCookies")); } + @Test + public void withAdvice() { + HttpRequestExecutingMessageHandler handler = (HttpRequestExecutingMessageHandler) new DirectFieldAccessor( + this.withAdvice).getPropertyValue("handler"); + handler.handleMessage(new GenericMessage("foo")); + assertEquals(1, adviceCalled); + } public static class StubErrorHandler implements ResponseErrorHandler { @@ -171,4 +185,13 @@ public class HttpOutboundGatewayParserTests { } } + public static class FooAdvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return null; + } + + } } diff --git a/spring-integration-ip/src/main/resources/org/springframework/integration/ip/config/spring-integration-ip-2.2.xsd b/spring-integration-ip/src/main/resources/org/springframework/integration/ip/config/spring-integration-ip-2.2.xsd index 16df61d304..5745e91552 100644 --- a/spring-integration-ip/src/main/resources/org/springframework/integration/ip/config/spring-integration-ip-2.2.xsd +++ b/spring-integration-ip/src/main/resources/org/springframework/integration/ip/config/spring-integration-ip-2.2.xsd @@ -84,6 +84,9 @@ message headers (ip_hostName). Default "true". + + + @@ -176,6 +179,9 @@ task executors such as a WorkManagerTaskExecutor. + + + @@ -298,6 +304,9 @@ task executors such as a WorkManagerTaskExecutor. + + + diff --git a/spring-integration-ip/src/test/java/org/springframework/integration/ip/config/ParserUnitTests-context.xml b/spring-integration-ip/src/test/java/org/springframework/integration/ip/config/ParserUnitTests-context.xml index e3bfc95b9d..cbfd3564aa 100644 --- a/spring-integration-ip/src/test/java/org/springframework/integration/ip/config/ParserUnitTests-context.xml +++ b/spring-integration-ip/src/test/java/org/springframework/integration/ip/config/ParserUnitTests-context.xml @@ -138,6 +138,15 @@ order="12" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ("foo")); + assertEquals(1, adviceCalled); + } + + @Test + public void tcpAdvice() { + adviceCalled = 0; + this.tcpAdviceChannel.send(new GenericMessage("foo")); + assertEquals(1, adviceCalled); + } + + @Test + public void tcpGatewayAdvice() { + adviceCalled = 0; + this.tcpAdviceGateChannel.send(new GenericMessage("foo")); + assertEquals(1, adviceCalled); + } + @Test public void testOutTcp() { DirectFieldAccessor dfa = new DirectFieldAccessor(tcpOut); @@ -592,4 +631,14 @@ public class ParserUnitTests { assertSame(socketFactorySupport, dfa.getPropertyValue("tcpSocketFactorySupport")); assertSame(socketSupport, dfa.getPropertyValue("tcpSocketSupport")); } + + public static class FooAdvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return null; + } + + } } diff --git a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/JdbcOutboundGateway.java b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/JdbcOutboundGateway.java index 53474cbfdb..1a0639fb08 100644 --- a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/JdbcOutboundGateway.java +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/JdbcOutboundGateway.java @@ -106,6 +106,7 @@ public class JdbcOutboundGateway extends AbstractReplyProducingMessageHandler im @Override protected void onInit() { + super.onInit(); if (this.maxRowsPerPoll != null) { Assert.notNull(poller, "If you want to set 'maxRowsPerPoll', then you must provide a 'selectQuery'."); diff --git a/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/config/spring-integration-jdbc-2.2.xsd b/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/config/spring-integration-jdbc-2.2.xsd index 46198b45a6..ba12b7ef3f 100644 --- a/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/config/spring-integration-jdbc-2.2.xsd +++ b/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/config/spring-integration-jdbc-2.2.xsd @@ -245,9 +245,10 @@ - + - + + @@ -334,7 +335,7 @@ - + @@ -357,7 +358,8 @@ - + + @@ -652,6 +654,7 @@ + @@ -787,6 +790,7 @@ + diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcMessageHandlerParserTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcMessageHandlerParserTests.java index afd4ed0299..e460214d4a 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcMessageHandlerParserTests.java +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcMessageHandlerParserTests.java @@ -1,11 +1,11 @@ /* * Copyright 2002-2012 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. @@ -28,6 +28,7 @@ import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.integration.Message; import org.springframework.integration.MessageChannel; +import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; import org.springframework.integration.jdbc.JdbcMessageHandler; import org.springframework.integration.support.MessageBuilder; import org.springframework.integration.test.util.TestUtils; @@ -45,11 +46,13 @@ import org.springframework.jdbc.core.JdbcTemplate; public class JdbcMessageHandlerParserTests { private JdbcTemplate jdbcTemplate; - + private MessageChannel channel; - + private ConfigurableApplicationContext context; - + + private static volatile int adviceCalled; + @Test public void testSimpleOutboundChannelAdapter(){ setUp("handlingWithJdbcOperationsJdbcOutboundChannelAdapterTest.xml", getClass()); @@ -60,6 +63,7 @@ public class JdbcMessageHandlerParserTests { assertEquals("Wrong id", "foo", map.get("name")); JdbcMessageHandler handler = context.getBean(JdbcMessageHandler.class); assertEquals(23, TestUtils.getPropertyValue(handler, "order")); + assertEquals(1, adviceCalled); } @Test @@ -103,7 +107,7 @@ public class JdbcMessageHandlerParserTests { assertEquals("Wrong id", message.getHeaders().getId().toString(), map.get("ID")); assertEquals("Wrong name", "bar", map.get("name")); } - + @Test public void testOutboundAdapterWithPoller() throws Exception{ ApplicationContext ac = new ClassPathXmlApplicationContext("JdbcOutboundAdapterWithPollerTest-context.xml", this.getClass()); @@ -132,11 +136,20 @@ public class JdbcMessageHandlerParserTests { context.close(); } } - + public void setUp(String name, Class cls){ context = new ClassPathXmlApplicationContext(name, cls); jdbcTemplate = new JdbcTemplate(this.context.getBean("dataSource",DataSource.class)); channel = this.context.getBean("target", MessageChannel.class); } - + + public static class FooAdvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return callback.execute(); + } + + } } diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcOutboundGatewayParserTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcOutboundGatewayParserTests.java index 75b4f0a40f..79a1bf233d 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcOutboundGatewayParserTests.java +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcOutboundGatewayParserTests.java @@ -32,6 +32,7 @@ import org.springframework.integration.MessageChannel; import org.springframework.integration.core.MessagingTemplate; import org.springframework.integration.core.PollableChannel; import org.springframework.integration.endpoint.PollingConsumer; +import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; import org.springframework.integration.jdbc.JdbcOutboundGateway; import org.springframework.integration.support.MessageBuilder; import org.springframework.integration.test.util.TestUtils; @@ -55,6 +56,8 @@ public class JdbcOutboundGatewayParserTests { private MessagingTemplate messagingTemplate; + private static volatile int adviceCalled; + @Test public void testMapPayloadMapReply() { setUp("handlingMapPayloadJdbcOutboundGatewayTest.xml", getClass()); @@ -71,6 +74,8 @@ public class JdbcOutboundGatewayParserTests { assertEquals("bar", payload.get("name")); JdbcOutboundGateway gateway = context.getBean(JdbcOutboundGateway.class); assertEquals(23, TestUtils.getPropertyValue(gateway, "order")); + Object gw = context.getBean("jdbcGateway"); + assertEquals(1, adviceCalled); } @Test @@ -230,4 +235,13 @@ public class JdbcOutboundGatewayParserTests { setupMessagingTemplate(); } + public static class FooAdvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return callback.execute(); + } + + } } diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/StoredProcMessageHandlerParserTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/StoredProcMessageHandlerParserTests.java index 5ef56e90a3..13edd19973 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/StoredProcMessageHandlerParserTests.java +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/StoredProcMessageHandlerParserTests.java @@ -27,14 +27,20 @@ import org.springframework.beans.DirectFieldAccessor; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.expression.Expression; +import org.springframework.integration.Message; +import org.springframework.integration.core.MessageHandler; import org.springframework.integration.endpoint.EventDrivenConsumer; +import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; import org.springframework.integration.jdbc.storedproc.ProcedureParameter; +import org.springframework.integration.message.GenericMessage; +import org.springframework.integration.test.util.TestUtils; import org.springframework.jdbc.core.SqlInOutParameter; import org.springframework.jdbc.core.SqlOutParameter; import org.springframework.jdbc.core.SqlParameter; /** * @author Gunnar Hillert + * @author Gary Russell * @since 2.1 * */ @@ -44,6 +50,8 @@ public class StoredProcMessageHandlerParserTests { private EventDrivenConsumer consumer; + private static volatile int adviceCalled; + @Test public void testProcedureNameIsSet() throws Exception { setUp("basicStoredProcOutboundChannelAdapterTest.xml", getClass()); @@ -149,6 +157,15 @@ public class StoredProcMessageHandlerParserTests { } + @Test + public void adviceCalled() throws Exception { + setUp("advisedStoredProcOutboundChannelAdapterTest.xml", getClass()); + + MessageHandler handler = TestUtils.getPropertyValue(this.consumer, "handler", MessageHandler.class); + handler.handleMessage(new GenericMessage("foo")); + assertEquals(1, adviceCalled); + } + @After public void tearDown(){ if(context != null){ @@ -161,4 +178,13 @@ public class StoredProcMessageHandlerParserTests { consumer = this.context.getBean("storedProcedureOutboundChannelAdapter", EventDrivenConsumer.class); } + public static class FooAdvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return null; + } + + } } diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/StoredProcOutboundGatewayParserTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/StoredProcOutboundGatewayParserTests.java index 027be53dbe..cfe9a4dfa0 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/StoredProcOutboundGatewayParserTests.java +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/StoredProcOutboundGatewayParserTests.java @@ -30,10 +30,15 @@ import org.springframework.beans.DirectFieldAccessor; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.expression.Expression; +import org.springframework.integration.Message; +import org.springframework.integration.core.MessageHandler; import org.springframework.integration.core.MessagingTemplate; import org.springframework.integration.endpoint.EventDrivenConsumer; +import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; import org.springframework.integration.jdbc.storedproc.PrimeMapper; import org.springframework.integration.jdbc.storedproc.ProcedureParameter; +import org.springframework.integration.message.GenericMessage; +import org.springframework.integration.test.util.TestUtils; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.SqlInOutParameter; import org.springframework.jdbc.core.SqlOutParameter; @@ -41,6 +46,7 @@ import org.springframework.jdbc.core.SqlParameter; /** * @author Gunnar Hillert + * @author Gary Russell * @since 2.1 * */ @@ -50,6 +56,8 @@ public class StoredProcOutboundGatewayParserTests { private EventDrivenConsumer outboundGateway; + private static volatile int adviceCalled; + @Test public void testProcedureNameIsSet() throws Exception { setUp("storedProcOutboundGatewayParserTest.xml", getClass()); @@ -206,6 +214,15 @@ public class StoredProcOutboundGatewayParserTests { } + @Test + public void advised() throws Exception { + setUp("advisedStoredProcOutboundGatewayParserTest.xml", getClass()); + + MessageHandler handler = TestUtils.getPropertyValue(this.outboundGateway, "handler", MessageHandler.class); + handler.handleMessage(new GenericMessage("foo")); + assertEquals(1, adviceCalled); + } + @After public void tearDown(){ if(context != null){ @@ -218,4 +235,13 @@ public class StoredProcOutboundGatewayParserTests { this.outboundGateway = this.context.getBean("storedProcedureOutboundGateway", EventDrivenConsumer.class); } + public static class FooAdvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return null; + } + + } } diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/advisedStoredProcOutboundChannelAdapterTest.xml b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/advisedStoredProcOutboundChannelAdapterTest.xml new file mode 100644 index 0000000000..bf0dc8f6e6 --- /dev/null +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/advisedStoredProcOutboundChannelAdapterTest.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/advisedStoredProcOutboundGatewayParserTest.xml b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/advisedStoredProcOutboundGatewayParserTest.xml new file mode 100644 index 0000000000..c9477ef1d1 --- /dev/null +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/advisedStoredProcOutboundGatewayParserTest.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/handlingMapPayloadJdbcOutboundGatewayTest.xml b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/handlingMapPayloadJdbcOutboundGatewayTest.xml index d081186d5e..e23878060a 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/handlingMapPayloadJdbcOutboundGatewayTest.xml +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/handlingMapPayloadJdbcOutboundGatewayTest.xml @@ -17,7 +17,11 @@ + request-channel="target" reply-channel="output" data-source="dataSource" order="23"> + + + + + channel="target" jdbc-operations="jdbcTemplate" order="23"> + + + + diff --git a/spring-integration-jms/src/main/java/org/springframework/integration/jms/JmsOutboundGateway.java b/spring-integration-jms/src/main/java/org/springframework/integration/jms/JmsOutboundGateway.java index e97363b404..91de46dbe3 100644 --- a/spring-integration-jms/src/main/java/org/springframework/integration/jms/JmsOutboundGateway.java +++ b/spring-integration-jms/src/main/java/org/springframework/integration/jms/JmsOutboundGateway.java @@ -50,7 +50,7 @@ import org.springframework.util.Assert; /** * An outbound Messaging Gateway for request/reply JMS. - * + * * @author Mark Fisher * @author Arjen Poutsma * @author Juergen Hoeller @@ -182,7 +182,7 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler { * Specify whether the request destination is a Topic. This value is * necessary when providing a destination name for a Topic rather than * a destination reference. - * + * * @param requestPubSubDomain true if the request destination is a Topic */ public void setRequestPubSubDomain(boolean requestPubSubDomain) { @@ -193,7 +193,7 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler { * Specify whether the reply destination is a Topic. This value is * necessary when providing a destination name for a Topic rather than * a destination reference. - * + * * @param replyPubSubDomain true if the reply destination is a Topic */ public void setReplyPubSubDomain(boolean replyPubSubDomain) { @@ -275,8 +275,8 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler { } /** - * This property describes how a JMS Message should be generated from the - * Spring Integration Message. If set to 'true', the body of the JMS Message will be + * This property describes how a JMS Message should be generated from the + * Spring Integration Message. If set to 'true', the body of the JMS Message will be * created from the Spring Integration Message's payload (via the MessageConverter). * If set to 'false', then the entire Spring Integration Message will serve as * the base for JMS Message creation. Since the JMS Message is created by the @@ -284,7 +284,7 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler { * the entire Spring Integration Message or only its payload. *
* Default is 'true' - * + * * @param extractRequestPayload */ public void setExtractRequestPayload(boolean extractRequestPayload) { @@ -297,7 +297,7 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler { * created from the JMS Reply Message's body (via MessageConverter). * Otherwise, the entire JMS Message will become the payload of the * Spring Integration Message. - * + * * @param extractReplyPayload */ public void setExtractReplyPayload(boolean extractReplyPayload) { @@ -312,6 +312,7 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler { this.setOutputChannel(replyChannel); } + @Override public String getComponentType() { return "jms:outbound-gateway"; } @@ -369,6 +370,7 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler { ^ this.requestDestinationName != null ^ this.requestDestinationExpressionProcessor != null, "Exactly one of 'requestDestination', 'requestDestinationName', or 'requestDestinationExpression' is required."); + super.onInit(); if (this.requestDestinationExpressionProcessor != null) { this.requestDestinationExpressionProcessor.setBeanFactory(getBeanFactory()); this.requestDestinationExpressionProcessor.setConversionService(getConversionService()); @@ -516,7 +518,7 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler { "because that ID can only be provided to a MessageSelector after the reuqest Message has been sent thereby " + "creating a race condition where a fast response might be sent before the MessageConsumer has been created. " + "Consider providing a value to the 'correlationKey' property of this gateway instead. Then the MessageConsumer " + - "will be created before the request Message is sent."); + "will be created before the request Message is sent."); } MessageProducer messageProducer = null; MessageConsumer messageConsumer = null; @@ -554,7 +556,7 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler { */ private void deleteDestinationIfTemporary(Destination destination) { try { - if (destination instanceof TemporaryQueue) { + if (destination instanceof TemporaryQueue) { ((TemporaryQueue) destination).delete(); } else if (destination instanceof TemporaryTopic) { diff --git a/spring-integration-jms/src/main/resources/org/springframework/integration/jms/config/spring-integration-jms-2.2.xsd b/spring-integration-jms/src/main/resources/org/springframework/integration/jms/config/spring-integration-jms-2.2.xsd index 62ae297da3..5c53c83dfe 100644 --- a/spring-integration-jms/src/main/resources/org/springframework/integration/jms/config/spring-integration-jms-2.2.xsd +++ b/spring-integration-jms/src/main/resources/org/springframework/integration/jms/config/spring-integration-jms-2.2.xsd @@ -731,9 +731,10 @@
- + - + + @@ -967,9 +968,10 @@ - + - + + diff --git a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsOutboundChannelAdapterParserTests.java b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsOutboundChannelAdapterParserTests.java index 370e302313..ac97178ecf 100644 --- a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsOutboundChannelAdapterParserTests.java +++ b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsOutboundChannelAdapterParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2012 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. @@ -23,20 +23,27 @@ import static org.junit.Assert.assertTrue; import javax.jms.DeliveryMode; import org.junit.Test; - import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.Message; +import org.springframework.integration.core.MessageHandler; import org.springframework.integration.endpoint.EventDrivenConsumer; +import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; import org.springframework.integration.jms.JmsHeaderMapper; +import org.springframework.integration.message.GenericMessage; +import org.springframework.integration.test.util.TestUtils; import org.springframework.jms.core.JmsTemplate; import org.springframework.jms.support.converter.MessageConverter; /** * @author Mark Fisher + * @author Gary Russell */ public class JmsOutboundChannelAdapterParserTests { + private static volatile int adviceCalled; + @Test public void adapterWithConnectionFactoryAndDestination() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( @@ -47,6 +54,16 @@ public class JmsOutboundChannelAdapterParserTests { assertNotNull(accessor.getPropertyValue("jmsTemplate")); } + @Test + public void advisedAdapter() { + ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( + "jmsOutboundWithConnectionFactoryAndDestination.xml", this.getClass()); + EventDrivenConsumer endpoint = (EventDrivenConsumer) context.getBean("advised"); + MessageHandler handler = TestUtils.getPropertyValue(endpoint, "handler", MessageHandler.class); + handler.handleMessage(new GenericMessage("foo")); + assertEquals(1, adviceCalled); + } + @Test public void adapterWithConnectionFactoryAndDestinationName() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( @@ -164,4 +181,13 @@ public class JmsOutboundChannelAdapterParserTests { assertEquals(false, accessor.getPropertyValue("explicitQosEnabled")); } + public static class FooAdvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return null; + } + + } } diff --git a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsOutboundGatewayParserTests.java b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsOutboundGatewayParserTests.java index 3702c1af56..4d4ca31b1c 100644 --- a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsOutboundGatewayParserTests.java +++ b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsOutboundGatewayParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -30,7 +30,6 @@ import javax.jms.DeliveryMode; import org.junit.Test; import org.mockito.Mockito; - import org.springframework.beans.DirectFieldAccessor; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.integration.Message; @@ -41,9 +40,11 @@ import org.springframework.integration.core.MessagingTemplate; import org.springframework.integration.core.SubscribableChannel; import org.springframework.integration.endpoint.EventDrivenConsumer; import org.springframework.integration.endpoint.PollingConsumer; +import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; import org.springframework.integration.history.MessageHistory; import org.springframework.integration.jms.JmsOutboundGateway; import org.springframework.integration.jms.StubMessageConverter; +import org.springframework.integration.message.GenericMessage; import org.springframework.integration.test.util.TestUtils; import org.springframework.jms.support.converter.MessageConverter; @@ -51,9 +52,12 @@ import org.springframework.jms.support.converter.MessageConverter; * @author Jonas Partner * @author Oleg Zhurakousky * @author Mark Fisher + * @author Gary Russell */ public class JmsOutboundGatewayParserTests { - + + private static volatile int adviceCalled; + @Test public void testWithDeliveryPersistentAttribute(){ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( @@ -66,6 +70,16 @@ public class JmsOutboundGatewayParserTests { assertEquals(DeliveryMode.PERSISTENT, deliveryMode); } + @Test + public void testAdvised(){ + ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( + "jmsOutboundGatewayWithDeliveryPersistent.xml", this.getClass()); + EventDrivenConsumer endpoint = (EventDrivenConsumer) context.getBean("advised"); + JmsOutboundGateway gateway = TestUtils.getPropertyValue(endpoint, "handler", JmsOutboundGateway.class); + gateway.handleMessage(new GenericMessage("foo")); + assertEquals(1, adviceCalled); + } + @Test public void testDefault(){ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( @@ -88,7 +102,7 @@ public class JmsOutboundGatewayParserTests { Object order = accessor.getPropertyValue("order"); assertEquals(99, order); } - + @Test public void gatewayMaintainsReplyChannelAndInboundHistory() { ActiveMqTestUtils.prepare(); @@ -96,7 +110,7 @@ public class JmsOutboundGatewayParserTests { "gatewayMaintainsReplyChannel.xml", this.getClass()); SampleGateway gateway = context.getBean("gateway", SampleGateway.class); SubscribableChannel jmsInput = context.getBean("jmsInput", SubscribableChannel.class); - MessageHandler handler = new MessageHandler() { + MessageHandler handler = new MessageHandler() { public void handleMessage(Message message) throws MessagingException { MessageHistory history = MessageHistory.read(message); assertNotNull(history); @@ -139,11 +153,20 @@ public class JmsOutboundGatewayParserTests { public static interface SampleGateway{ public String echo(String value); } - + public static class SampleService{ public String echo(String value){ return value.toUpperCase(); } } - + + public static class FooAdvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return null; + } + + } } diff --git a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/jmsOutboundGatewayWithDeliveryPersistent.xml b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/jmsOutboundGatewayWithDeliveryPersistent.xml index 9b9b65c1b4..b6f8e6074e 100644 --- a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/jmsOutboundGatewayWithDeliveryPersistent.xml +++ b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/jmsOutboundGatewayWithDeliveryPersistent.xml @@ -24,5 +24,14 @@ - + + + + + + + diff --git a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/jmsOutboundWithConnectionFactoryAndDestination.xml b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/jmsOutboundWithConnectionFactoryAndDestination.xml index 1daa93aac5..7681480200 100644 --- a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/jmsOutboundWithConnectionFactoryAndDestination.xml +++ b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/jmsOutboundWithConnectionFactoryAndDestination.xml @@ -27,4 +27,13 @@ + + + + + + diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/OperationInvokingMessageHandler.java b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/OperationInvokingMessageHandler.java index 363f304c80..5a47131102 100644 --- a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/OperationInvokingMessageHandler.java +++ b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/OperationInvokingMessageHandler.java @@ -103,6 +103,7 @@ public class OperationInvokingMessageHandler extends AbstractReplyProducingMessa @Override public final void onInit() { Assert.notNull(this.server, "MBeanServer is required."); + super.onInit(); } @Override diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/NotificationPublishingChannelAdapterParser.java b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/NotificationPublishingChannelAdapterParser.java index 501fbb80d0..113e3c69f3 100644 --- a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/NotificationPublishingChannelAdapterParser.java +++ b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/NotificationPublishingChannelAdapterParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2012 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. @@ -16,16 +16,17 @@ package org.springframework.integration.jmx.config; -import org.w3c.dom.Element; - import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser; import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.integration.jmx.NotificationPublishingMessageHandler; +import org.w3c.dom.Element; /** * @author Mark Fisher + * @author Gary Russell * @since 2.0 */ public class NotificationPublishingChannelAdapterParser extends AbstractOutboundChannelAdapterParser { @@ -37,8 +38,8 @@ public class NotificationPublishingChannelAdapterParser extends AbstractOutbound @Override protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) { - BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition( - "org.springframework.integration.jmx.NotificationPublishingMessageHandler"); + BeanDefinitionBuilder builder = BeanDefinitionBuilder + .rootBeanDefinition(NotificationPublishingMessageHandler.class); builder.addConstructorArgValue(element.getAttribute("object-name")); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "default-notification-type"); return builder.getBeanDefinition(); diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/OperationInvokingChannelAdapterParser.java b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/OperationInvokingChannelAdapterParser.java index 55c22940e3..2319aca529 100644 --- a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/OperationInvokingChannelAdapterParser.java +++ b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/OperationInvokingChannelAdapterParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2012 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. @@ -16,16 +16,17 @@ package org.springframework.integration.jmx.config; -import org.w3c.dom.Element; - import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser; import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.integration.jmx.OperationInvokingMessageHandler; +import org.w3c.dom.Element; /** * @author Mark Fisher + * @author Gary Russell * @since 2.0 */ public class OperationInvokingChannelAdapterParser extends AbstractOutboundChannelAdapterParser { @@ -37,8 +38,7 @@ public class OperationInvokingChannelAdapterParser extends AbstractOutboundChann @Override protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) { - BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition( - "org.springframework.integration.jmx.OperationInvokingMessageHandler"); + BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(OperationInvokingMessageHandler.class); IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "server"); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "object-name"); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "operation-name"); diff --git a/spring-integration-jmx/src/main/resources/org/springframework/integration/jmx/config/spring-integration-jmx-2.2.xsd b/spring-integration-jmx/src/main/resources/org/springframework/integration/jmx/config/spring-integration-jmx-2.2.xsd index 7f0a939f52..05cd858d68 100644 --- a/spring-integration-jmx/src/main/resources/org/springframework/integration/jmx/config/spring-integration-jmx-2.2.xsd +++ b/spring-integration-jmx/src/main/resources/org/springframework/integration/jmx/config/spring-integration-jmx-2.2.xsd @@ -42,6 +42,9 @@ + + + @@ -58,6 +61,9 @@ + + + @@ -90,6 +96,9 @@ + + + diff --git a/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/NotificationPublishingChannelAdapterParserTests-context.xml b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/NotificationPublishingChannelAdapterParserTests-context.xml index d8b1f8c8db..58be1e19da 100644 --- a/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/NotificationPublishingChannelAdapterParserTests-context.xml +++ b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/NotificationPublishingChannelAdapterParserTests-context.xml @@ -22,7 +22,11 @@ + default-notification-type="default.type"> + + + + diff --git a/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/NotificationPublishingChannelAdapterParserTests.java b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/NotificationPublishingChannelAdapterParserTests.java index b3dc8b303d..015c7025fa 100644 --- a/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/NotificationPublishingChannelAdapterParserTests.java +++ b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/NotificationPublishingChannelAdapterParserTests.java @@ -28,6 +28,7 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.integration.Message; import org.springframework.integration.MessageChannel; +import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; import org.springframework.integration.jmx.JmxHeaders; import org.springframework.integration.support.MessageBuilder; import org.springframework.test.context.ContextConfiguration; @@ -36,6 +37,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * @author Mark Fisher * @author Artem Bilan + * @author Gary Russell * @since 2.0 */ @ContextConfiguration @@ -51,6 +53,8 @@ public class NotificationPublishingChannelAdapterParserTests { @Autowired private MessageChannel publishingWithinChainChannel; + private static volatile int adviceCalled; + @After public void clearListener() { listener.lastNotification = null; @@ -59,6 +63,7 @@ public class NotificationPublishingChannelAdapterParserTests { @Test public void publishStringMessage() throws Exception { + adviceCalled = 0; assertNull(listener.lastNotification); Message message = MessageBuilder.withPayload("XYZ") .setHeader(JmxHeaders.NOTIFICATION_TYPE, "test.type").build(); @@ -68,6 +73,7 @@ public class NotificationPublishingChannelAdapterParserTests { assertEquals("XYZ", notification.getMessage()); assertEquals("test.type", notification.getType()); assertNull(notification.getUserData()); + assertEquals(1, adviceCalled); } @Test @@ -110,4 +116,15 @@ public class NotificationPublishingChannelAdapterParserTests { private static class TestData { } + public static class FooADvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + System.out.println("foo"); + new RuntimeException("foo").printStackTrace(); + return callback.execute(); + } + + } } diff --git a/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/OperationInvokingChannelAdapterParserTests-context.xml b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/OperationInvokingChannelAdapterParserTests-context.xml index 642f15d064..4c6ec94578 100644 --- a/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/OperationInvokingChannelAdapterParserTests-context.xml +++ b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/OperationInvokingChannelAdapterParserTests-context.xml @@ -18,7 +18,11 @@ + operation-name="test"> + + + + ("test2")); input.send(new GenericMessage("test3")); assertEquals(3, testBean.messages.size()); + assertEquals(3, adviceCalled); } @Test @@ -118,4 +122,14 @@ public class OperationInvokingChannelAdapterParserTests { .setHeader(JmxHeaders.OBJECT_NAME, "org.springframework.integration.jmx.config:type=TestBean,name=foo") .setHeader(JmxHeaders.OPERATION_NAME, "blah").build(); } + + public static class FooADvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return callback.execute(); + } + + } } diff --git a/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/OperationInvokingOutboundGatewayTests-context.xml b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/OperationInvokingOutboundGatewayTests-context.xml index f9742c7275..64482bbe5d 100644 --- a/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/OperationInvokingOutboundGatewayTests-context.xml +++ b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/OperationInvokingOutboundGatewayTests-context.xml @@ -27,7 +27,11 @@ + operation-name="testWithReturn"> + + + + ) withReplyChannelOutput.receive().getPayload()).size()); withReplyChannel.send(new GenericMessage("3")); assertEquals(3, ((List) withReplyChannelOutput.receive().getPayload()).size()); + assertEquals(3, adviceCalled); } @Test @@ -94,4 +99,13 @@ public class OperationInvokingOutboundGatewayTests { assertEquals(3, ((List) withReplyChannelOutput.receive().getPayload()).size()); } + public static class FooADvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return callback.execute(); + } + + } } diff --git a/spring-integration-jpa/src/main/java/org/springframework/integration/jpa/config/xml/JpaOutboundChannelAdapterParser.java b/spring-integration-jpa/src/main/java/org/springframework/integration/jpa/config/xml/JpaOutboundChannelAdapterParser.java index bc6b40d662..22e7b698ae 100644 --- a/spring-integration-jpa/src/main/java/org/springframework/integration/jpa/config/xml/JpaOutboundChannelAdapterParser.java +++ b/spring-integration-jpa/src/main/java/org/springframework/integration/jpa/config/xml/JpaOutboundChannelAdapterParser.java @@ -73,11 +73,16 @@ public class JpaOutboundChannelAdapterParser extends AbstractOutboundChannelAdap BeanDefinition txAdviceDefinition = IntegrationNamespaceUtils.configureTransactionAttributes(transactionalElement); ManagedList adviceChain = new ManagedList(); adviceChain.add(txAdviceDefinition); - jpaOutboundChannelAdapterBuilder.addPropertyValue("adviceChain", adviceChain); + jpaOutboundChannelAdapterBuilder.addPropertyValue("txAdviceChain", adviceChain); } return jpaOutboundChannelAdapterBuilder.getBeanDefinition(); } + @Override + protected boolean isUsingReplyProducer() { + return true; + } + } diff --git a/spring-integration-jpa/src/main/java/org/springframework/integration/jpa/outbound/JpaOutboundGatewayFactoryBean.java b/spring-integration-jpa/src/main/java/org/springframework/integration/jpa/outbound/JpaOutboundGatewayFactoryBean.java index 439027b6c3..3914d23c83 100644 --- a/spring-integration-jpa/src/main/java/org/springframework/integration/jpa/outbound/JpaOutboundGatewayFactoryBean.java +++ b/spring-integration-jpa/src/main/java/org/springframework/integration/jpa/outbound/JpaOutboundGatewayFactoryBean.java @@ -39,19 +39,34 @@ import org.springframework.util.CollectionUtils; * * @author Amol Nayak * @author Gunnar Hillert + * @author Gary Russell * @since 2.2 * */ public class JpaOutboundGatewayFactoryBean extends AbstractFactoryBean { - private final JpaExecutor jpaExecutor; + private final JpaExecutor jpaExecutor; + private OutboundGatewayType gatewayType = OutboundGatewayType.UPDATING; + /** + * <transactional /> element applies to entire flow from this point + */ + private volatile List txAdviceChain; + + /** + * <request-handler-advice-chain /> only applies to the handleRequestMessage. + */ private volatile List adviceChain; + private volatile ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader(); + private boolean producesReply = true; + private MessageChannel outputChannel; + private int order; + private long replyTimeout; /** @@ -69,34 +84,8 @@ public class JpaOutboundGatewayFactoryBean extends AbstractFactoryBean getObjectType() { - return MessageHandler.class; - } - - @Override - protected MessageHandler createInstance() { - - JpaOutboundGateway jpaOutboundGateway = new JpaOutboundGateway(jpaExecutor); - jpaOutboundGateway.setGatewayType(this.gatewayType); - jpaOutboundGateway.setProducesReply(this.producesReply); - jpaOutboundGateway.setOutputChannel(this.outputChannel); - jpaOutboundGateway.setOrder(this.order); - jpaOutboundGateway.setSendTimeout(replyTimeout); - - if (!CollectionUtils.isEmpty(this.adviceChain)) { - - ProxyFactory proxyFactory = new ProxyFactory(jpaOutboundGateway); - if (!CollectionUtils.isEmpty(adviceChain)) { - for (Advice advice : adviceChain) { - proxyFactory.addAdvice(advice); - } - } - - return (MessageHandler) proxyFactory.getProxy(this.beanClassLoader); - } - - return jpaOutboundGateway; + public void setTxAdviceChain(List txAdviceChain) { + this.txAdviceChain = txAdviceChain; } public void setAdviceChain(List adviceChain) { @@ -125,7 +114,38 @@ public class JpaOutboundGatewayFactoryBean extends AbstractFactoryBean getObjectType() { + return MessageHandler.class; + } + @Override + protected MessageHandler createInstance() { + JpaOutboundGateway jpaOutboundGateway = new JpaOutboundGateway(jpaExecutor); + jpaOutboundGateway.setGatewayType(this.gatewayType); + jpaOutboundGateway.setProducesReply(this.producesReply); + jpaOutboundGateway.setOutputChannel(this.outputChannel); + jpaOutboundGateway.setOrder(this.order); + jpaOutboundGateway.setSendTimeout(replyTimeout); + if (this.adviceChain != null) { + jpaOutboundGateway.setAdviceChain(this.adviceChain); + } + jpaOutboundGateway.afterPropertiesSet(); + + if (!CollectionUtils.isEmpty(this.txAdviceChain)) { + + ProxyFactory proxyFactory = new ProxyFactory(jpaOutboundGateway); + if (!CollectionUtils.isEmpty(txAdviceChain)) { + for (Advice advice : txAdviceChain) { + proxyFactory.addAdvice(advice); + } + } + + return (MessageHandler) proxyFactory.getProxy(this.beanClassLoader); + } + + return jpaOutboundGateway; + } } diff --git a/spring-integration-jpa/src/main/resources/org/springframework/integration/jpa/config/xml/spring-integration-jpa-2.2.xsd b/spring-integration-jpa/src/main/resources/org/springframework/integration/jpa/config/xml/spring-integration-jpa-2.2.xsd index a3b3fcb6b3..33a0068d92 100644 --- a/spring-integration-jpa/src/main/resources/org/springframework/integration/jpa/config/xml/spring-integration-jpa-2.2.xsd +++ b/spring-integration-jpa/src/main/resources/org/springframework/integration/jpa/config/xml/spring-integration-jpa-2.2.xsd @@ -9,7 +9,7 @@ + schemaLocation="http://www.springframework.org/schema/integration/spring-integration-2.2.xsd" /> + @@ -187,6 +188,7 @@ + @@ -218,6 +220,7 @@
+ diff --git a/spring-integration-jpa/src/test/java/org/springframework/integration/jpa/config/xml/JpaMessageHandlerParserTests.java b/spring-integration-jpa/src/test/java/org/springframework/integration/jpa/config/xml/JpaMessageHandlerParserTests.java index 39f3421cd5..78e7df421f 100644 --- a/spring-integration-jpa/src/test/java/org/springframework/integration/jpa/config/xml/JpaMessageHandlerParserTests.java +++ b/spring-integration-jpa/src/test/java/org/springframework/integration/jpa/config/xml/JpaMessageHandlerParserTests.java @@ -24,12 +24,16 @@ import org.junit.After; import org.junit.Test; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.Message; import org.springframework.integration.channel.AbstractMessageChannel; +import org.springframework.integration.core.MessageHandler; import org.springframework.integration.endpoint.EventDrivenConsumer; +import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; import org.springframework.integration.jpa.core.JpaExecutor; import org.springframework.integration.jpa.core.JpaOperations; import org.springframework.integration.jpa.support.JpaParameter; import org.springframework.integration.jpa.support.PersistMode; +import org.springframework.integration.message.GenericMessage; import org.springframework.integration.test.util.TestUtils; /** @@ -44,6 +48,8 @@ public class JpaMessageHandlerParserTests { private EventDrivenConsumer consumer; + private static volatile int adviceCalled; + @Test public void testJpaMessageHandlerParser() throws Exception { setUp("JpaMessageHandlerParserTests.xml", getClass()); @@ -77,6 +83,47 @@ public class JpaMessageHandlerParserTests { } + @Test + public void advised() throws Exception { + setUp("JpaMessageHandlerParserTests.xml", getClass()); + + EventDrivenConsumer consumer = this.context.getBean("advised", EventDrivenConsumer.class); + + final AbstractMessageChannel inputChannel = TestUtils.getPropertyValue(consumer, "inputChannel", AbstractMessageChannel.class); + + assertEquals("target", inputChannel.getComponentName()); + + final MessageHandler handler = TestUtils.getPropertyValue(consumer, "handler", MessageHandler.class); + + adviceCalled = 0; + + handler.handleMessage(new GenericMessage("foo")); + + assertEquals(1, adviceCalled); + } + + /* + * Tests that an already advised handler (tx) gets the request handler advice added to its chain. + */ + @Test + public void advisedAndTransactional() throws Exception { + setUp("JpaMessageHandlerParserTests.xml", getClass()); + + EventDrivenConsumer consumer = this.context.getBean("advisedAndTransactional", EventDrivenConsumer.class); + + final AbstractMessageChannel inputChannel = TestUtils.getPropertyValue(consumer, "inputChannel", AbstractMessageChannel.class); + + assertEquals("target", inputChannel.getComponentName()); + + final MessageHandler handler = TestUtils.getPropertyValue(consumer, "handler", MessageHandler.class); + + adviceCalled = 0; + + handler.handleMessage(new GenericMessage("foo")); + + assertEquals(1, adviceCalled); + } + @Test public void testJpaMessageHandlerParserWithEntityManagerFactory() throws Exception { setUp("JpaMessageHandlerParserTestsWithEmFactory.xml", getClass()); @@ -156,7 +203,7 @@ public class JpaMessageHandlerParserTests { assertNotNull(context.getBean("jpaOutboundChannelAdapter.jpaExecutor", JpaExecutor.class)); } - + @After public void tearDown(){ if(context != null){ @@ -169,4 +216,13 @@ public class JpaMessageHandlerParserTests { consumer = this.context.getBean("jpaOutboundChannelAdapter", EventDrivenConsumer.class); } + public static class FooAdvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return null; + } + + } } diff --git a/spring-integration-jpa/src/test/java/org/springframework/integration/jpa/config/xml/JpaMessageHandlerParserTests.xml b/spring-integration-jpa/src/test/java/org/springframework/integration/jpa/config/xml/JpaMessageHandlerParserTests.xml index 93e455e3fa..588dbad2b1 100644 --- a/spring-integration-jpa/src/test/java/org/springframework/integration/jpa/config/xml/JpaMessageHandlerParserTests.xml +++ b/spring-integration-jpa/src/test/java/org/springframework/integration/jpa/config/xml/JpaMessageHandlerParserTests.xml @@ -26,4 +26,37 @@ + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-jpa/src/test/java/org/springframework/integration/jpa/config/xml/JpaOutboundGatewayParserTests.java b/spring-integration-jpa/src/test/java/org/springframework/integration/jpa/config/xml/JpaOutboundGatewayParserTests.java index 22413e1140..6bd1758a64 100644 --- a/spring-integration-jpa/src/test/java/org/springframework/integration/jpa/config/xml/JpaOutboundGatewayParserTests.java +++ b/spring-integration-jpa/src/test/java/org/springframework/integration/jpa/config/xml/JpaOutboundGatewayParserTests.java @@ -20,18 +20,22 @@ import org.junit.After; import org.junit.Test; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.Message; import org.springframework.integration.channel.AbstractMessageChannel; import org.springframework.integration.endpoint.EventDrivenConsumer; +import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; import org.springframework.integration.jpa.core.JpaExecutor; import org.springframework.integration.jpa.core.JpaOperations; import org.springframework.integration.jpa.outbound.JpaOutboundGateway; import org.springframework.integration.jpa.support.OutboundGatewayType; import org.springframework.integration.jpa.support.PersistMode; +import org.springframework.integration.message.GenericMessage; import org.springframework.integration.test.util.TestUtils; /** * @author Gunnar Hillert * @author Amol Nayak + * @author Gary Russell * @since 2.2 * */ @@ -41,6 +45,8 @@ public class JpaOutboundGatewayParserTests { private EventDrivenConsumer consumer; + private static volatile int adviceCalled; + @Test public void testRetrievingJpaOutboundGatewayParser() throws Exception { setUp("JpaOutboundGatewayParserTests.xml", getClass(), "retrievingJpaOutboundGateway"); @@ -126,6 +132,16 @@ public class JpaOutboundGatewayParserTests { } + @Test + public void advised() throws Exception { + setUp("JpaOutboundGatewayParserTests.xml", getClass(), "updatingJpaOutboundGateway"); + + EventDrivenConsumer consumer = this.context.getBean("advised", EventDrivenConsumer.class); + final JpaOutboundGateway jpaOutboundGateway = TestUtils.getPropertyValue(consumer, "handler", JpaOutboundGateway.class); + jpaOutboundGateway.handleMessage(new GenericMessage("foo")); + assertEquals(1, adviceCalled); + } + @Test public void testJpaExecutorBeanIdNaming() throws Exception { @@ -148,4 +164,13 @@ public class JpaOutboundGatewayParserTests { consumer = this.context.getBean(gatewayId, EventDrivenConsumer.class); } + public static class FooAdvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return null; + } + + } } diff --git a/spring-integration-jpa/src/test/java/org/springframework/integration/jpa/config/xml/JpaOutboundGatewayParserTests.xml b/spring-integration-jpa/src/test/java/org/springframework/integration/jpa/config/xml/JpaOutboundGatewayParserTests.xml index e081b85db5..9cffc8b0cc 100644 --- a/spring-integration-jpa/src/test/java/org/springframework/integration/jpa/config/xml/JpaOutboundGatewayParserTests.xml +++ b/spring-integration-jpa/src/test/java/org/springframework/integration/jpa/config/xml/JpaOutboundGatewayParserTests.xml @@ -36,4 +36,19 @@ reply-channel="out" reply-timeout="100"/> + + + + + + diff --git a/spring-integration-mail/src/main/resources/org/springframework/integration/mail/config/spring-integration-mail-2.2.xsd b/spring-integration-mail/src/main/resources/org/springframework/integration/mail/config/spring-integration-mail-2.2.xsd index 63006186a5..ea240f4b1e 100644 --- a/spring-integration-mail/src/main/resources/org/springframework/integration/mail/config/spring-integration-mail-2.2.xsd +++ b/spring-integration-mail/src/main/resources/org/springframework/integration/mail/config/spring-integration-mail-2.2.xsd @@ -26,9 +26,10 @@ Defines an outbound mail-sending Channel Adapter. - + - + + diff --git a/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/MailOutboundChannelAdapterParserTests.java b/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/MailOutboundChannelAdapterParserTests.java index 7ae2580037..5c650d77ff 100644 --- a/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/MailOutboundChannelAdapterParserTests.java +++ b/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/MailOutboundChannelAdapterParserTests.java @@ -16,27 +16,33 @@ package org.springframework.integration.mail.config; +import static junit.framework.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + import java.util.Properties; import org.junit.Test; import org.springframework.beans.DirectFieldAccessor; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.Message; import org.springframework.integration.channel.QueueChannel; +import org.springframework.integration.core.MessageHandler; import org.springframework.integration.endpoint.PollingConsumer; +import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; import org.springframework.integration.mail.MailSendingMessageHandler; +import org.springframework.integration.message.GenericMessage; import org.springframework.integration.test.util.TestUtils; import org.springframework.mail.MailSender; -import static junit.framework.Assert.assertEquals; - -import static org.junit.Assert.assertNotNull; - /** * @author Mark Fisher + * @author Gary Russell */ public class MailOutboundChannelAdapterParserTests { + public static volatile int adviceCalled; + @Test public void adapterWithMailSenderReference() { ApplicationContext context = new ClassPathXmlApplicationContext( @@ -50,6 +56,17 @@ public class MailOutboundChannelAdapterParserTests { assertEquals(23, fieldAccessor.getPropertyValue("order")); } + @Test + public void advised() { + ApplicationContext context = new ClassPathXmlApplicationContext( + "mailOutboundChannelAdapterParserTests.xml", this.getClass()); + Object adapter = context.getBean("advised.adapter"); + MessageHandler handler = (MessageHandler) + new DirectFieldAccessor(adapter).getPropertyValue("handler"); + handler.handleMessage(new GenericMessage("foo")); + assertEquals(1, adviceCalled); + } + @Test public void adapterWithHostProperty() { ApplicationContext context = new ClassPathXmlApplicationContext( @@ -61,7 +78,7 @@ public class MailOutboundChannelAdapterParserTests { MailSender mailSender = (MailSender) fieldAccessor.getPropertyValue("mailSender"); assertNotNull(mailSender); } - + @Test public void adapterWithPollableChannel() { ApplicationContext context = new ClassPathXmlApplicationContext( @@ -70,7 +87,7 @@ public class MailOutboundChannelAdapterParserTests { QueueChannel pollableChannel = TestUtils.getPropertyValue(pc, "inputChannel", QueueChannel.class); assertEquals("pollableChannel", pollableChannel.getComponentName()); } - + @Test public void adapterWithJavaMailProperties() { ApplicationContext context = new ClassPathXmlApplicationContext( @@ -87,4 +104,13 @@ public class MailOutboundChannelAdapterParserTests { assertEquals("true", javaMailProperties.get("mail.smtps.auth")); } + public static class FooAdvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return null; + } + + } } diff --git a/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/mailOutboundChannelAdapterParserTests.xml b/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/mailOutboundChannelAdapterParserTests.xml index 55b32ce0c8..b8e242e922 100644 --- a/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/mailOutboundChannelAdapterParserTests.xml +++ b/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/mailOutboundChannelAdapterParserTests.xml @@ -32,4 +32,11 @@ + + + + + + diff --git a/spring-integration-rmi/src/main/resources/org/springframework/integration/rmi/config/spring-integration-rmi-2.2.xsd b/spring-integration-rmi/src/main/resources/org/springframework/integration/rmi/config/spring-integration-rmi-2.2.xsd index 5ed498f799..ff7d8d0135 100644 --- a/spring-integration-rmi/src/main/resources/org/springframework/integration/rmi/config/spring-integration-rmi-2.2.xsd +++ b/spring-integration-rmi/src/main/resources/org/springframework/integration/rmi/config/spring-integration-rmi-2.2.xsd @@ -63,9 +63,10 @@ - + - + + diff --git a/spring-integration-rmi/src/test/java/org/springframework/integration/rmi/config/RmiOutboundGatewayParserTests.java b/spring-integration-rmi/src/test/java/org/springframework/integration/rmi/config/RmiOutboundGatewayParserTests.java index d219fcefea..d6043ce61b 100644 --- a/spring-integration-rmi/src/test/java/org/springframework/integration/rmi/config/RmiOutboundGatewayParserTests.java +++ b/spring-integration-rmi/src/test/java/org/springframework/integration/rmi/config/RmiOutboundGatewayParserTests.java @@ -27,6 +27,7 @@ import org.springframework.integration.Message; import org.springframework.integration.MessageChannel; import org.springframework.integration.channel.QueueChannel; import org.springframework.integration.core.PollableChannel; +import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; import org.springframework.integration.message.GenericMessage; import org.springframework.integration.rmi.RmiInboundGateway; import org.springframework.integration.rmi.RmiOutboundGateway; @@ -42,6 +43,8 @@ public class RmiOutboundGatewayParserTests { private final QueueChannel testChannel = new QueueChannel(); + private static volatile int adviceCalled; + @Before public void setupTestInboundGateway() throws Exception { testChannel.setBeanName("testChannel"); @@ -55,7 +58,7 @@ public class RmiOutboundGatewayParserTests { public void testOrder() { ApplicationContext context = new ClassPathXmlApplicationContext( "rmiOutboundGatewayParserTests.xml", this.getClass()); - RmiOutboundGateway gateway = context.getBean(RmiOutboundGateway.class); + RmiOutboundGateway gateway = context.getBean("gateway.handler", RmiOutboundGateway.class); assertEquals(23, TestUtils.getPropertyValue(gateway, "order")); } @@ -63,11 +66,12 @@ public class RmiOutboundGatewayParserTests { public void directInvocation() { ApplicationContext context = new ClassPathXmlApplicationContext( "rmiOutboundGatewayParserTests.xml", this.getClass()); - MessageChannel localChannel = (MessageChannel) context.getBean("localChannel"); + MessageChannel localChannel = (MessageChannel) context.getBean("advisedChannel"); localChannel.send(new GenericMessage("test")); Message result = testChannel.receive(1000); assertNotNull(result); assertEquals("test", result.getPayload()); + assertEquals(1, adviceCalled); } @Test //INT-1029 @@ -93,4 +97,13 @@ public class RmiOutboundGatewayParserTests { assertEquals("TEST", result.getPayload()); } + public static class FooAdvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return callback.execute(); + } + + } } diff --git a/spring-integration-rmi/src/test/java/org/springframework/integration/rmi/config/rmiOutboundGatewayParserTests.xml b/spring-integration-rmi/src/test/java/org/springframework/integration/rmi/config/rmiOutboundGatewayParserTests.xml index 1e3c4b80ed..8254ef1ccb 100644 --- a/spring-integration-rmi/src/test/java/org/springframework/integration/rmi/config/rmiOutboundGatewayParserTests.xml +++ b/spring-integration-rmi/src/test/java/org/springframework/integration/rmi/config/rmiOutboundGatewayParserTests.xml @@ -18,6 +18,17 @@ remote-channel="testChannel" host="localhost"/> + + + + + + + + diff --git a/spring-integration-sftp/src/main/resources/org/springframework/integration/sftp/config/spring-integration-sftp-2.2.xsd b/spring-integration-sftp/src/main/resources/org/springframework/integration/sftp/config/spring-integration-sftp-2.2.xsd index ebccdbca7a..1170d41246 100644 --- a/spring-integration-sftp/src/main/resources/org/springframework/integration/sftp/config/spring-integration-sftp-2.2.xsd +++ b/spring-integration-sftp/src/main/resources/org/springframework/integration/sftp/config/spring-integration-sftp-2.2.xsd @@ -21,7 +21,9 @@ - + + + @@ -235,7 +237,9 @@ - + + + diff --git a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/config/OutboundChannelAdapterParserTests-context.xml b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/config/OutboundChannelAdapterParserTests-context.xml index f0ce6448e3..04c984ca6f 100644 --- a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/config/OutboundChannelAdapterParserTests-context.xml +++ b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/config/OutboundChannelAdapterParserTests-context.xml @@ -50,6 +50,17 @@ charset="UTF-8" use-temporary-file-name="false" remote-directory="foo/bar"/> + + + + + + diff --git a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/config/OutboundChannelAdapterParserTests.java b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/config/OutboundChannelAdapterParserTests.java index 7d9931708d..c00137efef 100644 --- a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/config/OutboundChannelAdapterParserTests.java +++ b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/config/OutboundChannelAdapterParserTests.java @@ -27,19 +27,21 @@ import java.util.Iterator; import java.util.Set; import org.junit.Test; - import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.expression.Expression; import org.springframework.expression.common.LiteralExpression; import org.springframework.expression.spel.standard.SpelExpression; +import org.springframework.integration.Message; import org.springframework.integration.channel.PublishSubscribeChannel; import org.springframework.integration.core.MessageHandler; import org.springframework.integration.endpoint.EventDrivenConsumer; import org.springframework.integration.file.FileNameGenerator; import org.springframework.integration.file.remote.handler.FileTransferringMessageHandler; import org.springframework.integration.file.remote.session.CachingSessionFactory; +import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; +import org.springframework.integration.message.GenericMessage; import org.springframework.integration.sftp.session.DefaultSftpSessionFactory; import org.springframework.integration.test.util.TestUtils; @@ -50,6 +52,8 @@ import org.springframework.integration.test.util.TestUtils; */ public class OutboundChannelAdapterParserTests { + private static volatile int adviceCalled; + @Test public void testOutboundChannelAdapterWithId(){ ApplicationContext context = @@ -117,6 +121,16 @@ public class OutboundChannelAdapterParserTests { assertFalse((Boolean)TestUtils.getPropertyValue(handler,"useTemporaryFileName")); } + @Test + public void advised(){ + ApplicationContext context = + new ClassPathXmlApplicationContext("OutboundChannelAdapterParserTests-context.xml", this.getClass()); + Object consumer = context.getBean("advised"); + MessageHandler handler = TestUtils.getPropertyValue(consumer, "handler", MessageHandler.class); + handler.handleMessage(new GenericMessage("foo")); + assertEquals(1, adviceCalled); + } + @Test(expected=BeanDefinitionStoreException.class) public void testFailWithRemoteDirAndExpression(){ new ClassPathXmlApplicationContext("OutboundChannelAdapterParserTests-context-fail.xml", this.getClass()); @@ -128,4 +142,14 @@ public class OutboundChannelAdapterParserTests { new ClassPathXmlApplicationContext("OutboundChannelAdapterParserTests-context-fail-fileFileGen.xml", this.getClass()); } + + public static class FooAdvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return null; + } + + } } diff --git a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/config/SftpOutboundGatewayParserTests-context.xml b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/config/SftpOutboundGatewayParserTests-context.xml index 32baa477af..95484539ce 100644 --- a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/config/SftpOutboundGatewayParserTests-context.xml +++ b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/config/SftpOutboundGatewayParserTests-context.xml @@ -43,6 +43,24 @@ order="2" /> + + + + + + diff --git a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/config/SftpOutboundGatewayParserTests.java b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/config/SftpOutboundGatewayParserTests.java index 60b246ae02..dd459a44c6 100644 --- a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/config/SftpOutboundGatewayParserTests.java +++ b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/config/SftpOutboundGatewayParserTests.java @@ -26,8 +26,11 @@ import java.util.Set; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.integration.Message; import org.springframework.integration.endpoint.AbstractEndpoint; import org.springframework.integration.file.remote.session.CachingSessionFactory; +import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; +import org.springframework.integration.message.GenericMessage; import org.springframework.integration.sftp.gateway.SftpOutboundGateway; import org.springframework.integration.test.util.TestUtils; import org.springframework.test.context.ContextConfiguration; @@ -50,6 +53,11 @@ public class SftpOutboundGatewayParserTests { @Autowired AbstractEndpoint gateway2; + @Autowired + AbstractEndpoint advised; + + private static volatile int adviceCalled; + @Test public void testGateway1() { SftpOutboundGateway gateway = TestUtils.getPropertyValue(gateway1, @@ -85,4 +93,22 @@ public class SftpOutboundGatewayParserTests { Set options = TestUtils.getPropertyValue(gateway, "options", Set.class); assertTrue(options.contains("-P")); } + + @Test + public void advised() { + SftpOutboundGateway gateway = TestUtils.getPropertyValue(advised, + "handler", SftpOutboundGateway.class); + gateway.handleMessage(new GenericMessage("foo")); + assertEquals(1, adviceCalled); + } + + public static class FooAdvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return null; + } + + } } diff --git a/spring-integration-twitter/src/main/resources/org/springframework/integration/twitter/config/spring-integration-twitter-2.2.xsd b/spring-integration-twitter/src/main/resources/org/springframework/integration/twitter/config/spring-integration-twitter-2.2.xsd index 4d969ce032..1f3269d07d 100644 --- a/spring-integration-twitter/src/main/resources/org/springframework/integration/twitter/config/spring-integration-twitter-2.2.xsd +++ b/spring-integration-twitter/src/main/resources/org/springframework/integration/twitter/config/spring-integration-twitter-2.2.xsd @@ -159,6 +159,9 @@ + + + diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingMessageHandlerParser-context.xml b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingMessageHandlerParser-context.xml index 2a14b52895..e3c6e36120 100644 --- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingMessageHandlerParser-context.xml +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingMessageHandlerParser-context.xml @@ -29,6 +29,20 @@ + + + + + + + + + + + + diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingMessageHandlerParserTests.java b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingMessageHandlerParserTests.java index 9a56a8280f..d8a50ea46d 100644 --- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingMessageHandlerParserTests.java +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/config/TestSendingMessageHandlerParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -17,12 +17,16 @@ package org.springframework.integration.twitter.config; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; import org.junit.Test; - import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.Message; +import org.springframework.integration.core.MessageHandler; import org.springframework.integration.endpoint.EventDrivenConsumer; +import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; +import org.springframework.integration.message.GenericMessage; import org.springframework.integration.test.util.TestUtils; import org.springframework.integration.twitter.outbound.DirectMessageSendingMessageHandler; @@ -33,13 +37,32 @@ import org.springframework.integration.twitter.outbound.DirectMessageSendingMess */ public class TestSendingMessageHandlerParserTests { + private static volatile int adviceCalled; + @Test public void testSendingMessageHandlerSuccessfulBootstrap(){ ApplicationContext ac = new ClassPathXmlApplicationContext("TestSendingMessageHandlerParser-context.xml", this.getClass()); EventDrivenConsumer dmAdapter = ac.getBean("dmAdapter", EventDrivenConsumer.class); - Object handler = TestUtils.getPropertyValue(dmAdapter, "handler"); + MessageHandler handler = TestUtils.getPropertyValue(dmAdapter, "handler", MessageHandler.class); assertEquals(DirectMessageSendingMessageHandler.class, handler.getClass()); assertEquals(23, TestUtils.getPropertyValue(handler, "order")); + dmAdapter = ac.getBean("dmAdvised", EventDrivenConsumer.class); + handler = TestUtils.getPropertyValue(dmAdapter, "handler", MessageHandler.class); + handler.handleMessage(new GenericMessage("foo")); + assertEquals(1, adviceCalled); + MessageHandler handler2 = TestUtils.getPropertyValue(ac.getBean("advised"), "handler", MessageHandler.class); + assertNotSame(handler, handler2); + handler2.handleMessage(new GenericMessage("foo")); + assertEquals(2, adviceCalled); } + public static class FooAdvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return null; + } + + } } diff --git a/spring-integration-ws/src/main/resources/org/springframework/integration/ws/config/spring-integration-ws-2.2.xsd b/spring-integration-ws/src/main/resources/org/springframework/integration/ws/config/spring-integration-ws-2.2.xsd index 1e20f1bf66..ce308ea07e 100644 --- a/spring-integration-ws/src/main/resources/org/springframework/integration/ws/config/spring-integration-ws-2.2.xsd +++ b/spring-integration-ws/src/main/resources/org/springframework/integration/ws/config/spring-integration-ws-2.2.xsd @@ -29,6 +29,7 @@ + diff --git a/spring-integration-ws/src/test/java/org/springframework/integration/ws/config/WebServiceOutboundGatewayParserTests.java b/spring-integration-ws/src/test/java/org/springframework/integration/ws/config/WebServiceOutboundGatewayParserTests.java index 472a98075e..849f9f9db8 100644 --- a/spring-integration-ws/src/test/java/org/springframework/integration/ws/config/WebServiceOutboundGatewayParserTests.java +++ b/spring-integration-ws/src/test/java/org/springframework/integration/ws/config/WebServiceOutboundGatewayParserTests.java @@ -16,17 +16,24 @@ package org.springframework.integration.ws.config; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; +import static org.junit.Assert.assertNull; + import java.util.List; import org.junit.Test; - import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.parsing.BeanDefinitionParsingException; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.Message; +import org.springframework.integration.core.MessageHandler; import org.springframework.integration.endpoint.AbstractEndpoint; import org.springframework.integration.endpoint.EventDrivenConsumer; import org.springframework.integration.endpoint.PollingConsumer; +import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; +import org.springframework.integration.message.GenericMessage; import org.springframework.integration.test.util.TestUtils; import org.springframework.integration.ws.MarshallingWebServiceOutboundGateway; import org.springframework.integration.ws.SimpleWebServiceOutboundGateway; @@ -40,18 +47,16 @@ import org.springframework.ws.client.core.WebServiceMessageCallback; import org.springframework.ws.client.support.interceptor.ClientInterceptor; import org.springframework.ws.transport.WebServiceMessageSender; -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertTrue; - -import static org.junit.Assert.assertNull; - /** * @author Mark Fisher * @author Oleg Zhurakousky * @author Gunnar Hillert + * @author Gary Russell */ public class WebServiceOutboundGatewayParserTests { + private static volatile int adviceCalled; + @Test public void simpleGatewayWithReplyChannel() { ApplicationContext context = new ClassPathXmlApplicationContext( @@ -360,6 +365,17 @@ public class WebServiceOutboundGatewayParserTests { assertEquals("Wrong DestinationProvider", stubProvider,destinationProviderObject); } + @Test + public void advised() { + ApplicationContext context = new ClassPathXmlApplicationContext( + "simpleWebServiceOutboundGatewayParserTests.xml", this.getClass()); + AbstractEndpoint endpoint = (AbstractEndpoint) context.getBean("gatewayWithAdvice"); + assertEquals(EventDrivenConsumer.class, endpoint.getClass()); + MessageHandler handler = TestUtils.getPropertyValue(endpoint, "handler", MessageHandler.class); + handler.handleMessage(new GenericMessage("foo")); + assertEquals(1, adviceCalled); + } + @Test(expected = BeanDefinitionParsingException.class) public void invalidGatewayWithBothUriAndDestinationProvider() { new ClassPathXmlApplicationContext("invalidGatewayWithBothUriAndDestinationProvider.xml", this.getClass()); @@ -370,4 +386,13 @@ public class WebServiceOutboundGatewayParserTests { new ClassPathXmlApplicationContext("invalidGatewayWithNeitherUriNorDestinationProvider.xml", this.getClass()); } + public static class FooAdvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return null; + } + + } } diff --git a/spring-integration-ws/src/test/java/org/springframework/integration/ws/config/simpleWebServiceOutboundGatewayParserTests.xml b/spring-integration-ws/src/test/java/org/springframework/integration/ws/config/simpleWebServiceOutboundGatewayParserTests.xml index 3d49c505ec..d991f24cf9 100644 --- a/spring-integration-ws/src/test/java/org/springframework/integration/ws/config/simpleWebServiceOutboundGatewayParserTests.xml +++ b/spring-integration-ws/src/test/java/org/springframework/integration/ws/config/simpleWebServiceOutboundGatewayParserTests.xml @@ -102,6 +102,14 @@ request-channel="inputChannel" destination-provider="destinationProvider" /> + + + + + + diff --git a/spring-integration-xmpp/src/main/resources/org/springframework/integration/xmpp/config/spring-integration-xmpp-2.2.xsd b/spring-integration-xmpp/src/main/resources/org/springframework/integration/xmpp/config/spring-integration-xmpp-2.2.xsd index 763a44aa6d..483316d8d4 100644 --- a/spring-integration-xmpp/src/main/resources/org/springframework/integration/xmpp/config/spring-integration-xmpp-2.2.xsd +++ b/spring-integration-xmpp/src/main/resources/org/springframework/integration/xmpp/config/spring-integration-xmpp-2.2.xsd @@ -214,9 +214,10 @@ - + - + + diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/ChatMessageOutboundChannelAdapterParserTests-context.xml b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/ChatMessageOutboundChannelAdapterParserTests-context.xml index beffa34209..6c05125532 100644 --- a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/ChatMessageOutboundChannelAdapterParserTests-context.xml +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/ChatMessageOutboundChannelAdapterParserTests-context.xml @@ -43,6 +43,13 @@ xmpp-connection="testConnection"> + + + + + + diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/ChatMessageOutboundChannelAdapterParserTests.java b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/ChatMessageOutboundChannelAdapterParserTests.java index 73fc340b86..02356947f9 100644 --- a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/ChatMessageOutboundChannelAdapterParserTests.java +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/ChatMessageOutboundChannelAdapterParserTests.java @@ -16,6 +16,11 @@ package org.springframework.integration.xmpp.config; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + import java.util.List; import org.jivesoftware.smack.XMPPConnection; @@ -24,15 +29,17 @@ import org.junit.runner.RunWith; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.integration.Message; import org.springframework.integration.MessageChannel; import org.springframework.integration.channel.QueueChannel; +import org.springframework.integration.core.MessageHandler; import org.springframework.integration.core.SubscribableChannel; import org.springframework.integration.endpoint.EventDrivenConsumer; import org.springframework.integration.endpoint.PollingConsumer; +import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; +import org.springframework.integration.message.GenericMessage; import org.springframework.integration.support.MessageBuilder; import org.springframework.integration.test.util.TestUtils; import org.springframework.integration.xmpp.XmppHeaders; @@ -41,11 +48,6 @@ import org.springframework.integration.xmpp.support.XmppHeaderMapper; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertTrue; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - /** * @author Oleg Zhurakousky * @author Mark Fisher @@ -57,10 +59,12 @@ public class ChatMessageOutboundChannelAdapterParserTests { @Autowired private ApplicationContext context; - + @Autowired private XmppHeaderMapper headerMapper; + private static volatile int adviceCalled; + @Test public void testPollingConsumer() { Object pollingConsumer = context.getBean("withHeaderMapper"); @@ -75,11 +79,19 @@ public class ChatMessageOutboundChannelAdapterParserTests { assertTrue(eventConsumer instanceof SubscribableChannel); } + @Test + public void advised() { + MessageHandler handler = TestUtils.getPropertyValue(context.getBean("advised"), + "handler", MessageHandler.class); + handler.handleMessage(new GenericMessage("foo")); + assertEquals(1, adviceCalled); + } + @SuppressWarnings("unchecked") @Test public void testEventConsumer() { Object eventConsumer = context.getBean("outboundEventAdapter"); - DefaultXmppHeaderMapper headerMapper = + DefaultXmppHeaderMapper headerMapper = TestUtils.getPropertyValue(eventConsumer, "handler.headerMapper", DefaultXmppHeaderMapper.class); List requestHeaderNames = TestUtils.getPropertyValue(headerMapper, "requestHeaderNames", List.class); assertEquals(2, requestHeaderNames.size()); @@ -98,7 +110,7 @@ public class ChatMessageOutboundChannelAdapterParserTests { Message message = MessageBuilder.withPayload("hello").setHeader(XmppHeaders.TO, "oleg"). setHeader("foobar", "foobar").build(); XMPPConnection connection = context.getBean("testConnection", XMPPConnection.class); - + Mockito.doAnswer(new Answer() { public Object answer(InvocationOnMock invocation) { Object[] args = invocation.getArguments(); @@ -108,9 +120,9 @@ public class ChatMessageOutboundChannelAdapterParserTests { return null; }}) .when(connection).sendPacket(Mockito.any(org.jivesoftware.smack.packet.Message.class)); - + channel.send(message); - + verify(connection, times(1)).sendPacket(Mockito.any(org.jivesoftware.smack.packet.Message.class)); Mockito.reset(connection); } @@ -137,4 +149,13 @@ public class ChatMessageOutboundChannelAdapterParserTests { Mockito.reset(connection); } + public static class FooAdvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return null; + } + + } } diff --git a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/PresenceOutboundChannelAdapterParserTests-context.xml b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/PresenceOutboundChannelAdapterParserTests-context.xml index b9529edfae..63be35a8fa 100644 --- a/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/PresenceOutboundChannelAdapterParserTests-context.xml +++ b/spring-integration-xmpp/src/test/java/org/springframework/integration/xmpp/config/PresenceOutboundChannelAdapterParserTests-context.xml @@ -29,6 +29,16 @@ xmpp-connection="testConnection" channel="eventChannel" order="34"/> + + + + + + + ("foo")); + assertEquals(1, adviceCalled); + } + @Test public void testRosterEventOutboundChannel(){ Object channel = context.getBean("eventOutboundRosterChannel"); @@ -78,4 +91,13 @@ public class PresenceOutboundChannelAdapterParserTests { assertEquals(45, TestUtils.getPropertyValue(handlers.toArray()[0], "order")); } + public static class FooAdvice extends AbstractRequestHandlerAdvice { + + @Override + protected Object doInvoke(ExecutionCallback callback, Object target, Message message) throws Exception { + adviceCalled++; + return null; + } + + } }