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 cc359f6bd4..0d3cb3947d 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 @@ -22,5 +22,10 @@ mapped-request-headers="foo*"/> - + + + + + + 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 6a2e752d6d..e273dbb21f 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 @@ -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,6 +17,7 @@ package org.springframework.integration.amqp.config; import java.lang.reflect.Field; +import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; @@ -33,6 +34,7 @@ import org.springframework.integration.amqp.outbound.AmqpOutboundEndpoint; import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.core.MessageHandler; import org.springframework.integration.endpoint.EventDrivenConsumer; +import org.springframework.integration.handler.MessageHandlerChain; import org.springframework.integration.support.MessageBuilder; import org.springframework.integration.test.util.TestUtils; import org.springframework.test.context.ContextConfiguration; @@ -47,6 +49,7 @@ import static junit.framework.Assert.assertEquals; * @author Mark Fisher * @author Oleg Zhurakousky * @author Gary Russell + * @author Artem Bilan * @since 2.1 */ @ContextConfiguration @@ -99,4 +102,35 @@ public class AmqpOutboundChannelAdapterParserTests { Mockito.verify(amqpTemplate, Mockito.times(1)).send(Mockito.any(String.class), Mockito.any(String.class), Mockito.any(org.springframework.amqp.core.Message.class)); } + @SuppressWarnings("rawtypes") + @Test + public void amqpOutboundChannelAdapterWithinChain() { + Object eventDrivernConsumer = context.getBean("chainWithRabbitOutbound"); + + List chainHandlers = TestUtils.getPropertyValue(eventDrivernConsumer, "handler.handlers", List.class); + + AmqpOutboundEndpoint endpoint = (AmqpOutboundEndpoint) chainHandlers.get(0); + + Field amqpTemplateField = ReflectionUtils.findField(AmqpOutboundEndpoint.class, "amqpTemplate"); + amqpTemplateField.setAccessible(true); + RabbitTemplate amqpTemplate = TestUtils.getPropertyValue(endpoint, "amqpTemplate", RabbitTemplate.class); + amqpTemplate = Mockito.spy(amqpTemplate); + + Mockito.doAnswer(new Answer() { + public Object answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + org.springframework.amqp.core.Message amqpReplyMessage = (org.springframework.amqp.core.Message) args[2]; + assertEquals("hello", new String(amqpReplyMessage.getBody())); + return null; + }}) + .when(amqpTemplate).send(Mockito.any(String.class), Mockito.any(String.class), Mockito.any(org.springframework.amqp.core.Message.class)); + ReflectionUtils.setField(amqpTemplateField, endpoint, amqpTemplate); + + + MessageChannel requestChannel = context.getBean("amqpOutboundChannelAdapterWithinChain", MessageChannel.class); + Message message = MessageBuilder.withPayload("hello").build(); + requestChannel.send(message); + Mockito.verify(amqpTemplate, Mockito.times(1)).send(Mockito.any(String.class), Mockito.any(String.class), Mockito.any(org.springframework.amqp.core.Message.class)); + } + } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractChannelAdapterParser.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractChannelAdapterParser.java index 04c19e58a6..fcb0ab5f76 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractChannelAdapterParser.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractChannelAdapterParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 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. @@ -25,18 +25,28 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.channel.DirectChannel; import org.springframework.util.StringUtils; /** * Base parser for Channel Adapters. - * + * + * Includes logic to determine {@link org.springframework.integration.MessageChannel}: + * if 'channel' attribute is defined - uses its value as 'channelName'; + * if 'id' attribute is defined - creates {@link DirectChannel} at runtime and uses id's value as 'channelName'; + * if current component is defined as nested element inside any other components e.g. <chain> + * 'id' and 'channel' attributes will be ignored and this component will not be parsed as + * {@link org.springframework.integration.endpoint.AbstractEndpoint}. + * * @author Mark Fisher + * @author Artem Bilan */ public abstract class AbstractChannelAdapterParser extends AbstractBeanDefinitionParser { @Override - protected final String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) throws BeanDefinitionStoreException { - String id = element.getAttribute("id"); + protected final String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) + throws BeanDefinitionStoreException { + String id = element.getAttribute(ID_ATTRIBUTE); if (!element.hasAttribute("channel")) { // the created channel will get the 'id', so the adapter's bean name includes a suffix id = id + ".adapter"; @@ -57,13 +67,15 @@ public abstract class AbstractChannelAdapterParser extends AbstractBeanDefinitio } private String createDirectChannel(Element element, ParserContext parserContext) { - String channelId = element.getAttribute("id"); + if (parserContext.isNested()) { + return null; + } + String channelId = element.getAttribute(ID_ATTRIBUTE); if (!StringUtils.hasText(channelId)) { parserContext.getReaderContext().error("The channel-adapter's 'id' attribute is required when no 'channel' " + "reference has been provided, because that 'id' would be used for the created channel.", element); } - BeanDefinitionBuilder channelBuilder = BeanDefinitionBuilder.genericBeanDefinition( - IntegrationNamespaceUtils.BASE_PACKAGE + ".channel.DirectChannel"); + BeanDefinitionBuilder channelBuilder = BeanDefinitionBuilder.genericBeanDefinition(DirectChannel.class); BeanDefinitionHolder holder = new BeanDefinitionHolder(channelBuilder.getBeanDefinition(), channelId); BeanDefinitionReaderUtils.registerBeanDefinition(holder, parserContext.getRegistry()); return channelId; @@ -72,7 +84,7 @@ public abstract class AbstractChannelAdapterParser extends AbstractBeanDefinitio /** * Subclasses must implement this method to parse the adapter element. * The name of the MessageChannel bean is provided. - */ + */ protected abstract AbstractBeanDefinition doParse(Element element, ParserContext parserContext, String channelName); } 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 86b8b5d786..fe23621d39 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 @@ -23,22 +23,31 @@ 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.xml.ParserContext; +import org.springframework.integration.config.ConsumerEndpointFactoryBean; import org.springframework.util.StringUtils; import org.springframework.util.xml.DomUtils; /** * Base class for outbound Channel Adapter parsers. + * + * If this component is defined as the top-level element in the Spring application context it will produce + * 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 */ public abstract class AbstractOutboundChannelAdapterParser extends AbstractChannelAdapterParser { @Override protected AbstractBeanDefinition doParse(Element element, ParserContext parserContext, String channelName) { + if (parserContext.isNested()) { + return this.parseConsumer(element, parserContext); + } + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(ConsumerEndpointFactoryBean.class); Element pollerElement = DomUtils.getChildElementByTagName(element, "poller"); - BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( - IntegrationNamespaceUtils.BASE_PACKAGE + ".config.ConsumerEndpointFactoryBean"); builder.addPropertyReference("handler", this.parseAndRegisterConsumer(element, parserContext)); if (pollerElement != null) { if (!StringUtils.hasText(channelName)) { @@ -60,15 +69,13 @@ public abstract class AbstractOutboundChannelAdapterParser extends AbstractChann protected String parseAndRegisterConsumer(Element element, ParserContext parserContext) { AbstractBeanDefinition definition = this.parseConsumer(element, parserContext); if (definition == null) { - parserContext.getReaderContext().error( - "Consumer parsing must return a BeanComponentDefinition.", element); + parserContext.getReaderContext().error("Consumer parsing must return an AbstractBeanDefinition.", element); } - String order = element.getAttribute("order"); + String order = element.getAttribute(IntegrationNamespaceUtils.ORDER); if (StringUtils.hasText(order)) { - definition.getPropertyValues().addPropertyValue("order", order); + definition.getPropertyValues().addPropertyValue(IntegrationNamespaceUtils.ORDER, order); } - String beanName = BeanDefinitionReaderUtils.generateBeanName( - definition, parserContext.getRegistry()); + String beanName = BeanDefinitionReaderUtils.generateBeanName(definition, parserContext.getRegistry()); parserContext.registerBeanComponent(new BeanComponentDefinition(definition, beanName)); return beanName; } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/DefaultOutboundChannelAdapterParser.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/DefaultOutboundChannelAdapterParser.java index 43fc0feed4..2c3031c391 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/DefaultOutboundChannelAdapterParser.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/DefaultOutboundChannelAdapterParser.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. @@ -18,16 +18,14 @@ package org.springframework.integration.config.xml; import org.w3c.dom.Element; -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.RootBeanDefinition; +import org.springframework.beans.factory.parsing.BeanComponentDefinition; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.ExpressionFactoryBean; import org.springframework.integration.handler.ExpressionEvaluatingMessageHandler; import org.springframework.integration.handler.MethodInvokingMessageHandler; -import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** @@ -39,7 +37,8 @@ import org.springframework.util.StringUtils; */ public class DefaultOutboundChannelAdapterParser extends AbstractOutboundChannelAdapterParser { - protected String parseAndRegisterConsumer(Element element, ParserContext parserContext) { + @Override + protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) { BeanComponentDefinition innerConsumerDefinition = IntegrationNamespaceUtils.parseInnerHandlerDefinition(element, parserContext); String consumerRef = element.getAttribute(IntegrationNamespaceUtils.REF_ATTRIBUTE); @@ -61,42 +60,27 @@ public class DefaultOutboundChannelAdapterParser extends AbstractOutboundChannel "The 'method' attribute cannot be used with the 'expression' attribute.", element); } - if (hasMethod | isExpression) { - BeanDefinitionBuilder consumerBuilder = null; - if (hasMethod) { - consumerBuilder = BeanDefinitionBuilder.genericBeanDefinition(MethodInvokingMessageHandler.class); - if (isRef) { - consumerBuilder.addConstructorArgReference(consumerRef); - } - else { - consumerBuilder.addConstructorArgValue(innerConsumerDefinition); - } - consumerBuilder.addConstructorArgValue(methodName); + BeanDefinitionBuilder consumerBuilder = null; + + if (isExpression) { + consumerBuilder = BeanDefinitionBuilder.genericBeanDefinition(ExpressionEvaluatingMessageHandler.class); + RootBeanDefinition expressionDef = new RootBeanDefinition(ExpressionFactoryBean.class); + expressionDef.getConstructorArgumentValues().addGenericArgumentValue(consumerExpressionString); + consumerBuilder.addConstructorArgValue(expressionDef); + } + else { + consumerBuilder = BeanDefinitionBuilder.genericBeanDefinition(MethodInvokingMessageHandler.class); + if (isRef) { + consumerBuilder.addConstructorArgReference(consumerRef); } else { - consumerBuilder = BeanDefinitionBuilder.genericBeanDefinition(ExpressionEvaluatingMessageHandler.class); - RootBeanDefinition expressionDef = new RootBeanDefinition(ExpressionFactoryBean.class); - expressionDef.getConstructorArgumentValues().addGenericArgumentValue(consumerExpressionString); - consumerBuilder.addConstructorArgValue(expressionDef); + consumerBuilder.addConstructorArgValue(innerConsumerDefinition); } - - consumerBuilder.addPropertyValue("componentType", "outbound-channel-adapter"); - String order = element.getAttribute(IntegrationNamespaceUtils.ORDER); - if (StringUtils.hasText(order)) { - consumerBuilder.addPropertyValue(IntegrationNamespaceUtils.ORDER, order); - } - consumerRef = BeanDefinitionReaderUtils.registerWithGeneratedName(consumerBuilder.getBeanDefinition(), parserContext.getRegistry()); + consumerBuilder.addConstructorArgValue(hasMethod ? methodName : "handleMessage"); } - else if (isInnerConsumer) { - consumerRef = innerConsumerDefinition.getBeanName(); - } - Assert.hasText(consumerRef, "cannot determine consumer for 'outbound-channel-adapter'"); - return consumerRef; - } - @Override - protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) { - throw new UnsupportedOperationException(); + consumerBuilder.addPropertyValue("componentType", "outbound-channel-adapter"); + return consumerBuilder.getBeanDefinition(); } } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/handler/MessageHandlerChain.java b/spring-integration-core/src/main/java/org/springframework/integration/handler/MessageHandlerChain.java index 8b16287230..7f2ee38f91 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/handler/MessageHandlerChain.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/handler/MessageHandlerChain.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,11 +16,9 @@ package org.springframework.integration.handler; -import java.util.HashSet; -import java.util.List; - import org.springframework.aop.framework.Advised; import org.springframework.aop.support.AopUtils; +import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.BeanFactory; import org.springframework.core.Ordered; import org.springframework.integration.Message; @@ -34,6 +32,9 @@ import org.springframework.integration.support.channel.BeanFactoryChannelResolve import org.springframework.integration.support.channel.ChannelResolver; import org.springframework.util.Assert; +import java.util.HashSet; +import java.util.List; + /** * A composite {@link MessageHandler} implementation that invokes a chain of * MessageHandler instances in order. @@ -65,8 +66,9 @@ import org.springframework.util.Assert; * @author Mark Fisher * @author Iwein Fuld * @author Gary Russell + * @author Artem Bilan */ -public class MessageHandlerChain extends AbstractMessageHandler implements MessageProducer, Ordered { +public class MessageHandlerChain extends AbstractMessageHandler implements MessageProducer { private volatile List handlers; @@ -79,8 +81,6 @@ public class MessageHandlerChain extends AbstractMessageHandler implements Messa */ private volatile Long sendTimeout = null; - private volatile int order = Ordered.LOWEST_PRECEDENCE; - private volatile ChannelResolver channelResolver; private volatile boolean initialized; @@ -100,15 +100,6 @@ public class MessageHandlerChain extends AbstractMessageHandler implements Messa this.sendTimeout = sendTimeout; } - public void setOrder(int order) { - this.order = order; - } - - public int getOrder() { - return this.order; - } - - @Override public String getComponentType() { return "chain"; @@ -156,6 +147,13 @@ public class MessageHandlerChain extends AbstractMessageHandler implements Messa } }; ((MessageProducer) handler).setOutputChannel(nextChannel); + + // If this 'handler' is a nested non-last <chain>, it is necessary + // to 'force' re-init it for check its configuration in conjunction with current MessageHandlerChain. + if (handler instanceof MessageHandlerChain) { + new DirectFieldAccessor(handler).setPropertyValue("initialized", false); + ((MessageHandlerChain) handler).afterPropertiesSet(); + } } else if (handler instanceof MessageProducer) { MessageChannel replyChannel = new ReplyForwardingMessageChannel(); 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 7c9a16caa3..39574a0fcd 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 @@ -789,7 +789,7 @@ - + Defines a Channel Adapter that receives from a MessageChannel and passes to @@ -797,89 +797,75 @@ MessageHandler. - - - - - - - Specifies the order for invocation when this endpoint is connected as a -subscriber to a channel. This is particularly relevant when that channel -is using a "failover" dispatching strategy. It has no effect when this -endpoint itself is a Polling Consumer for a channel with a queue. - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - Provide a name for the logger. This is useful when there are multiple logging Channel Adapters configured, - and you would like to differentiate them within the actual log. By default the logger name will be the - fully qualified class name of the LoggingHandler implementation. - - - - - - + + + + + + + + + + + + + + + Provide a name for the logger. This is useful when there are multiple logging Channel Adapters configured, + and you would like to differentiate them within the actual log. By default the logger name will be the + fully qualified class name of the LoggingHandler implementation. + + + + + + - - - - - + + + + - - - - - - Specifies the order for invocation when this endpoint is connected as a - subscriber to a channel. This is particularly relevant when that channel - is using a "failover" dispatching strategy. It has no effect when this - endpoint itself is a Polling Consumer for a channel with a queue. - - - - - - - + + + @@ -945,18 +931,42 @@ endpoint itself is a Polling Consumer for a channel with a queue. - + - + + + + + + + + + + + + + Specifies the order for invocation when this endpoint is connected as a + subscriber to a channel. This is particularly relevant when that channel + is using a "failover" dispatching strategy. It has no effect when this + endpoint itself is a Polling Consumer for a channel with a queue. + + + + + + + + + @@ -1369,38 +1379,44 @@ endpoint itself is a Polling Consumer for a channel with a queue. - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/ChainParserTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/ChainParserTests-context.xml index 5ab6d03d37..09fc426c63 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/ChainParserTests-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/ChainParserTests-context.xml @@ -96,6 +96,17 @@ + + + + + + + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/ChainParserTests.java b/spring-integration-core/src/test/java/org/springframework/integration/config/ChainParserTests.java index 1c684d9b79..5c7860cea5 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/ChainParserTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/ChainParserTests.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,22 +16,17 @@ package org.springframework.integration.config; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; - -import java.util.List; - import org.hamcrest.Factory; import org.hamcrest.Matcher; import org.junit.Test; import org.junit.runner.RunWith; - +import org.springframework.beans.BeansException; import org.springframework.beans.DirectFieldAccessor; +import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; 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; @@ -43,6 +38,15 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.util.StringUtils; +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.List; + +import static org.junit.Assert.*; +import static org.junit.matchers.JUnitMatchers.both; +import static org.junit.matchers.JUnitMatchers.containsString; + /** * @author Mark Fisher * @author Iwein Fuld @@ -94,9 +98,19 @@ public class ChainParserTests { @Autowired private MessageChannel headerValueRouterWithMappingInput; + @Autowired + private MessageChannel loggingChannelAdapterChannel; + + @Autowired + private MessageChannel outboundChannelAdapterChannel; + + @Autowired + private TestConsumer testConsumer; + @Autowired @Qualifier("claimCheckInput") private MessageChannel claimCheckInput; + @Autowired @Qualifier("claimCheckOutput") private PollableChannel claimCheckOutput; @@ -241,6 +255,40 @@ public class ChainParserTests { assertEquals(message.getPayload(), reply.getPayload()); } + @Test //INT-2275 + public void chainWithOutboundChannelAdapter() { + this.outboundChannelAdapterChannel.send(successMessage); + assertSame(successMessage, testConsumer.getLastMessage()); + } + + @Test //INT-2275 + public void chainWithLoggingChannelAdapter() { + Message message = MessageBuilder.withPayload("test").build(); + PrintStream realOut = System.out; + try { + OutputStream out = new ByteArrayOutputStream(); + System.setOut(new PrintStream(out)); + this.loggingChannelAdapterChannel.send(message); + assertEquals("LoggingHandler: TEST", out.toString().trim()); + } + finally { + System.setOut(realOut); + } + } + + @Test(expected = BeanCreationException.class) //INT-2275 + public void invalidNestedChainWithLoggingChannelAdapter() { + try { + new ClassPathXmlApplicationContext("invalidNestedChainWithOutboundChannelAdapter-context.xml", this.getClass()); + fail("BeanCreationException is expected!"); + } + catch (BeansException e) { + assertEquals(IllegalArgumentException.class, e.getCause().getClass()); + assertThat(e.getMessage(), both(containsString("output channel was provided")).and(containsString("does not implement the MessageProducer"))); + throw e; + } + } + public static class StubHandler extends AbstractReplyProducingMessageHandler { @Override protected Object handleRequestMessage(Message requestMessage) { diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/invalidNestedChainWithOutboundChannelAdapter-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/invalidNestedChainWithOutboundChannelAdapter-context.xml new file mode 100644 index 0000000000..8777987fa3 --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/invalidNestedChainWithOutboundChannelAdapter-context.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/MethodInvokingOutboundChannelAdapterParserTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/DefaultOutboundChannelAdapterParserTests-context.xml similarity index 76% rename from spring-integration-core/src/test/java/org/springframework/integration/config/xml/MethodInvokingOutboundChannelAdapterParserTests-context.xml rename to spring-integration-core/src/test/java/org/springframework/integration/config/xml/DefaultOutboundChannelAdapterParserTests-context.xml index 4edcba37be..fa3a430b79 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/MethodInvokingOutboundChannelAdapterParserTests-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/DefaultOutboundChannelAdapterParserTests-context.xml @@ -13,16 +13,22 @@ + + + class="org.springframework.integration.config.xml.DefaultOutboundChannelAdapterParserTests$TestBean"/> - + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/MethodInvokingOutboundChannelAdapterParserTests.java b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/DefaultOutboundChannelAdapterParserTests.java similarity index 71% rename from spring-integration-core/src/test/java/org/springframework/integration/config/xml/MethodInvokingOutboundChannelAdapterParserTests.java rename to spring-integration-core/src/test/java/org/springframework/integration/config/xml/DefaultOutboundChannelAdapterParserTests.java index fdf8b8f7d4..52d82e0b8e 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/MethodInvokingOutboundChannelAdapterParserTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/DefaultOutboundChannelAdapterParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 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. @@ -25,6 +25,7 @@ import org.junit.runner.RunWith; import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; +import org.springframework.integration.config.TestConsumer; import org.springframework.integration.handler.MethodInvokingMessageHandler; import org.springframework.integration.test.util.TestUtils; import org.springframework.test.context.ContextConfiguration; @@ -32,10 +33,11 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * @author Mark Fisher + * @author Artem Bilan */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration -public class MethodInvokingOutboundChannelAdapterParserTests { +public class DefaultOutboundChannelAdapterParserTests { @Autowired private ApplicationContext context; @@ -44,21 +46,29 @@ public class MethodInvokingOutboundChannelAdapterParserTests { @Test public void checkConfig() { Object adapter = context.getBean("adapter"); + assertEquals(Boolean.FALSE, TestUtils.getPropertyValue(adapter, "autoStartup")); Object handler = TestUtils.getPropertyValue(adapter, "handler"); assertEquals(MethodInvokingMessageHandler.class, handler.getClass()); - DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler); - assertEquals(99, handlerAccessor.getPropertyValue("order")); - assertEquals(Boolean.FALSE, TestUtils.getPropertyValue(adapter, "autoStartup")); + assertEquals(99, TestUtils.getPropertyValue(handler, "order")); } @Test public void checkConfigWithInnerBeanAndPoller() { Object adapter = context.getBean("adapterB"); + assertEquals(Boolean.FALSE, TestUtils.getPropertyValue(adapter, "autoStartup")); Object handler = TestUtils.getPropertyValue(adapter, "handler"); assertEquals(MethodInvokingMessageHandler.class, handler.getClass()); - DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler); - assertEquals(99, handlerAccessor.getPropertyValue("order")); - assertEquals(Boolean.FALSE, TestUtils.getPropertyValue(adapter, "autoStartup")); + assertEquals(99, TestUtils.getPropertyValue(handler, "order")); + } + + @Test + public void checkConfigWithInnerMessageHandler() { + Object adapter = context.getBean("adapterC"); + Object handler = TestUtils.getPropertyValue(adapter, "handler"); + assertEquals(MethodInvokingMessageHandler.class, handler.getClass()); + assertEquals(99, TestUtils.getPropertyValue(handler, "order")); + Object targetObject = TestUtils.getPropertyValue(handler, "processor.delegate.targetObject"); + assertEquals(TestConsumer.class, targetObject.getClass()); } @@ -68,4 +78,5 @@ public class MethodInvokingOutboundChannelAdapterParserTests { } } + } 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 b6aaa66efb..88903e44cd 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 @@ -8,7 +8,12 @@ http://www.springframework.org/schema/integration/event http://www.springframework.org/schema/integration/event/spring-integration-event.xsd"> - + + + + + + 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 caa379e49f..5e64e9dfc6 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 @@ -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,14 +16,9 @@ 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; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEvent; @@ -40,8 +35,12 @@ 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 * @since 2.0 */ @RunWith(SpringJUnit4ClassRunner.class) @@ -83,6 +82,25 @@ public class EventOutboundChannelAdapterParserTests { Assert.assertTrue(receivedEvent); } + @Test //INT-2275 + public void testInsideChain() { + 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("foobar")) { + receivedEvent = true; + } + } + } + }; + context.addApplicationListener(listener); + DirectChannel channel = context.getBean("inputChain", DirectChannel.class); + channel.send(new GenericMessage("foo")); + Assert.assertTrue(receivedEvent); + } + @Test(timeout=2000) public void validateUsageWithPollableChannel() throws Exception { ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("EventOutboundChannelAdapterParserTestsWithPollable-context.xml", EventOutboundChannelAdapterParserTests.class); 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 e413ba9fb9..2fcb37f956 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 @@ -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. @@ -54,6 +54,7 @@ import java.nio.charset.Charset; * @author Iwein Fuld * @author Alex Peters * @author Oleg Zhurakousky + * @author Artem Bilan */ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHandler { @@ -71,6 +72,7 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand private volatile Charset charset = Charset.defaultCharset(); + private volatile boolean expectReply = true; public FileWritingMessageHandler(File destinationDirectory) { Assert.notNull(destinationDirectory, "Destination directory must not be null."); @@ -92,7 +94,15 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand public void setTemporaryFileSuffix(String temporaryFileSuffix) { this.temporaryFileSuffix = temporaryFileSuffix; } - + + /** + * Specify whether a reply Message is expected. If not, this handler will simply return null for a + * successful response or throw an Exception for a non-successful response. The default is true. + */ + public void setExpectReply(boolean expectReply) { + this.expectReply = expectReply; + } + protected String getTemporaryFileSuffix() { return temporaryFileSuffix; } @@ -169,6 +179,11 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand catch (Exception e) { throw new MessageHandlingException(requestMessage, "failed to write Message payload to file", e); } + + if (!this.expectReply) { + return null; + } + if (resultFile != null) { if (originalFileFromHeader == null && payload instanceof File) { return MessageBuilder.withPayload(resultFile) 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 db6fac3281..591b7de360 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 @@ -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. @@ -22,8 +22,6 @@ 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.context.IntegrationContextUtils; -import org.springframework.util.StringUtils; /** * Parser for the <outbound-channel-adapter/> element of the 'file' @@ -32,36 +30,14 @@ import org.springframework.util.StringUtils; * @author Mark Fisher * @author Iwein Fuld * @author Oleg Zhurakousky + * @author Artem Bilan */ public class FileOutboundChannelAdapterParser extends AbstractOutboundChannelAdapterParser { @Override protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) { - BeanDefinitionBuilder handlerBuilder = FileWritingMessageHandlerBeanDefinitionBuilder.configure( - element, IntegrationContextUtils.NULL_CHANNEL_BEAN_NAME, parserContext); - if (handlerBuilder != null){ - String remoteFileNameGenerator = element.getAttribute("filename-generator"); - String remoteFileNameGeneratorExpression = element.getAttribute("filename-generator-expression"); - boolean hasRemoteFileNameGenerator = StringUtils.hasText(remoteFileNameGenerator); - boolean hasRemoteFileNameGeneratorExpression = StringUtils.hasText(remoteFileNameGeneratorExpression); - if (hasRemoteFileNameGenerator || hasRemoteFileNameGeneratorExpression) { - if (hasRemoteFileNameGenerator && hasRemoteFileNameGeneratorExpression) { - parserContext.getReaderContext().error("at most one of 'filename-generator-expression' or 'filename-generator' " + - "is allowed on file outbound adapter/gateway", element); - } - if (hasRemoteFileNameGenerator) { - handlerBuilder.addPropertyReference("fileNameGenerator", remoteFileNameGenerator); - } - else { - BeanDefinitionBuilder fileNameGeneratorBuilder = BeanDefinitionBuilder.genericBeanDefinition( - "org.springframework.integration.file.DefaultFileNameGenerator"); - fileNameGeneratorBuilder.addPropertyValue("expression", remoteFileNameGeneratorExpression); - handlerBuilder.addPropertyValue("fileNameGenerator", fileNameGeneratorBuilder.getBeanDefinition()); - } - } - } - - return (handlerBuilder != null ? handlerBuilder.getBeanDefinition() : null); + BeanDefinitionBuilder handlerBuilder = FileWritingMessageHandlerBeanDefinitionBuilder.configure(element, false, parserContext); + return handlerBuilder.getBeanDefinition(); } } diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileOutboundGatewayParser.java b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileOutboundGatewayParser.java index 4166304bdc..c89e28420a 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileOutboundGatewayParser.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileOutboundGatewayParser.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,18 +16,19 @@ package org.springframework.integration.file.config; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; import org.w3c.dom.Element; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.xml.AbstractConsumerEndpointParser; -import org.springframework.util.StringUtils; /** * Parser for the 'outbound-gateway' element of the file namespace. * * @author Mark Fisher * @author Oleg Zhurakousky + * @author Artem Bilan * @since 1.0.3 */ public class FileOutboundGatewayParser extends AbstractConsumerEndpointParser { @@ -39,31 +40,8 @@ public class FileOutboundGatewayParser extends AbstractConsumerEndpointParser { @Override protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) { - String replyChannel = element.getAttribute("reply-channel"); - - BeanDefinitionBuilder handlerBuilder = - FileWritingMessageHandlerBeanDefinitionBuilder.configure(element, replyChannel, parserContext); - - String remoteFileNameGenerator = element.getAttribute("filename-generator"); - String remoteFileNameGeneratorExpression = element.getAttribute("filename-generator-expression"); - boolean hasRemoteFileNameGenerator = StringUtils.hasText(remoteFileNameGenerator); - boolean hasRemoteFileNameGeneratorExpression = StringUtils.hasText(remoteFileNameGeneratorExpression); - if (hasRemoteFileNameGenerator || hasRemoteFileNameGeneratorExpression) { - if (hasRemoteFileNameGenerator && hasRemoteFileNameGeneratorExpression) { - parserContext.getReaderContext().error("at most one of 'filename-generator-expression' or 'filename-generator' " + - "is allowed on file outbound adapter/gateway", element) ; - } - if (hasRemoteFileNameGenerator) { - handlerBuilder.addPropertyReference("fileNameGenerator", remoteFileNameGenerator); - } - else { - BeanDefinitionBuilder fileNameGeneratorBuilder = BeanDefinitionBuilder.genericBeanDefinition( - "org.springframework.integration.file.DefaultFileNameGenerator"); - fileNameGeneratorBuilder.addPropertyValue("expression", remoteFileNameGeneratorExpression); - handlerBuilder.addPropertyValue("fileNameGenerator", fileNameGeneratorBuilder.getBeanDefinition()); - } - } - + BeanDefinitionBuilder handlerBuilder = FileWritingMessageHandlerBeanDefinitionBuilder.configure(element, true, parserContext); + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(handlerBuilder, element, "reply-channel", "outputChannel"); return handlerBuilder; } diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileWritingMessageHandlerBeanDefinitionBuilder.java b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileWritingMessageHandlerBeanDefinitionBuilder.java index 716ea5043e..0efd972686 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileWritingMessageHandlerBeanDefinitionBuilder.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileWritingMessageHandlerBeanDefinitionBuilder.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,6 +16,7 @@ package org.springframework.integration.file.config; +import org.springframework.integration.file.DefaultFileNameGenerator; import org.w3c.dom.Element; import org.springframework.beans.factory.support.BeanDefinitionBuilder; @@ -29,32 +30,42 @@ import org.springframework.util.StringUtils; * {@link org.springframework.integration.file.FileWritingMessageHandler}. * * @author Mark Fisher + * @author Artem Bilan * @since 1.0.3 */ abstract class FileWritingMessageHandlerBeanDefinitionBuilder { - static BeanDefinitionBuilder configure(Element element, String outputChannelBeanName, ParserContext parserContext) { - if (outputChannelBeanName == null) { - parserContext.getReaderContext().error("outputChannelBeanName must not be null", element); - return null; - } + static BeanDefinitionBuilder configure(Element element, boolean expectReply, ParserContext parserContext) { + String directory = element.getAttribute("directory"); if (!StringUtils.hasText(directory)) { parserContext.getReaderContext().error("directory is required", element); } - BeanDefinitionBuilder builder = BeanDefinitionBuilder - .genericBeanDefinition("org.springframework.integration.file.config.FileWritingMessageHandlerFactoryBean"); + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FileWritingMessageHandlerFactoryBean.class); builder.addPropertyValue("directory", directory); - if (StringUtils.hasText(outputChannelBeanName)) { - builder.addPropertyReference("outputChannel", outputChannelBeanName); - } + builder.addPropertyValue("expectReply", expectReply); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "auto-create-directory"); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "delete-source-files"); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "temporary-file-suffix"); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "charset"); - String fileNameGenerator = element.getAttribute("filename-generator"); - if (StringUtils.hasText(fileNameGenerator)) { - builder.addPropertyReference("fileNameGenerator", fileNameGenerator); + String remoteFileNameGenerator = element.getAttribute("filename-generator"); + String remoteFileNameGeneratorExpression = element.getAttribute("filename-generator-expression"); + boolean hasRemoteFileNameGenerator = StringUtils.hasText(remoteFileNameGenerator); + boolean hasRemoteFileNameGeneratorExpression = StringUtils.hasText(remoteFileNameGeneratorExpression); + if (hasRemoteFileNameGenerator || hasRemoteFileNameGeneratorExpression) { + if (hasRemoteFileNameGenerator && hasRemoteFileNameGeneratorExpression) { + parserContext.getReaderContext().error("at most one of 'filename-generator-expression' or 'filename-generator' " + + "is allowed on file outbound adapter/gateway", element); + } + if (hasRemoteFileNameGenerator) { + builder.addPropertyReference("fileNameGenerator", remoteFileNameGenerator); + } + else { + BeanDefinitionBuilder fileNameGeneratorBuilder = BeanDefinitionBuilder + .genericBeanDefinition(DefaultFileNameGenerator.class); + fileNameGeneratorBuilder.addPropertyValue("expression", remoteFileNameGeneratorExpression); + builder.addPropertyValue("fileNameGenerator", fileNameGeneratorBuilder.getBeanDefinition()); + } } return builder; } diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileWritingMessageHandlerFactoryBean.java b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileWritingMessageHandlerFactoryBean.java index 03ac945f39..d4823c62dd 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileWritingMessageHandlerFactoryBean.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/config/FileWritingMessageHandlerFactoryBean.java @@ -29,6 +29,7 @@ import org.springframework.integration.file.FileWritingMessageHandler; * @author Iwein Fuld * @author Oleg Zhurakousky * @author Gary Russell + * @author Artem Bilan * @since 1.0.3 */ public class FileWritingMessageHandlerFactoryBean extends AbstractSimpleMessageHandlerFactoryBean{ @@ -48,7 +49,9 @@ public class FileWritingMessageHandlerFactoryBean extends AbstractSimpleMessageH private volatile Long sendTimeout; private volatile String temporaryFileSuffix; - + + private volatile boolean expectReply = true; + public void setDirectory(File directory) { this.directory = directory; } @@ -80,7 +83,11 @@ public class FileWritingMessageHandlerFactoryBean extends AbstractSimpleMessageH public void setTemporaryFileSuffix(String temporaryFileSuffix) { this.temporaryFileSuffix = temporaryFileSuffix; } - + + public void setExpectReply(boolean expectReply) { + this.expectReply = expectReply; + } + @Override protected FileWritingMessageHandler createHandler() { FileWritingMessageHandler handler = new FileWritingMessageHandler(this.directory); @@ -105,6 +112,7 @@ public class FileWritingMessageHandlerFactoryBean extends AbstractSimpleMessageH if (this.temporaryFileSuffix != null) { handler.setTemporaryFileSuffix(this.temporaryFileSuffix); } + handler.setExpectReply(this.expectReply); return handler; } } diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/FileOutboundChannelAdapterInsideChainTests-context.xml b/spring-integration-file/src/test/java/org/springframework/integration/file/FileOutboundChannelAdapterInsideChainTests-context.xml new file mode 100644 index 0000000000..79aca33f36 --- /dev/null +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/FileOutboundChannelAdapterInsideChainTests-context.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/FileOutboundChannelAdapterInsideChainTests.java b/spring-integration-file/src/test/java/org/springframework/integration/file/FileOutboundChannelAdapterInsideChainTests.java new file mode 100644 index 0000000000..0696d6faba --- /dev/null +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/FileOutboundChannelAdapterInsideChainTests.java @@ -0,0 +1,92 @@ +/* + * 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.file; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Ignore; +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.MessageChannel; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.util.FileCopyUtils; + +import java.io.File; +import java.io.IOException; +import java.util.Properties; + +import static org.junit.Assert.*; + +/** + * //INT-2275 + * + * @author Artem Bilan + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class FileOutboundChannelAdapterInsideChainTests { + + public static final String TEST_FILE_NAME = FileOutboundChannelAdapterInsideChainTests.class.getSimpleName(); + + public static final String WORK_DIR_NAME = System.getProperty("java.io.tmpdir") + "/" + FileOutboundChannelAdapterInsideChainTests.class.getSimpleName() + "Dir"; + + public static final String SAMPLE_CONTENT = "test"; + + public static Properties placeholderProperties = new Properties(); + + static { + placeholderProperties.put("test.file", TEST_FILE_NAME); + placeholderProperties.put("work.dir", WORK_DIR_NAME); + } + + @Autowired + private MessageChannel outboundChainChannel; + + private static File workDir; + + @BeforeClass + public static void setupClass() { + workDir = new File(WORK_DIR_NAME); + workDir.mkdir(); + workDir.deleteOnExit(); + } + + @AfterClass + public static void cleanUp() { + if (workDir != null && workDir.exists()) { + for (File file : workDir.listFiles()) { + file.delete(); + } + } + workDir.delete(); + } + + @Test //INT-2275 + public void testFileOutboundChannelAdapterWithinChain() throws IOException { + Message message = MessageBuilder.withPayload(SAMPLE_CONTENT).build(); + outboundChainChannel.send(message); + File testFile = new File(workDir, TEST_FILE_NAME); + assertTrue(testFile.exists()); + byte[] testFileContent = FileCopyUtils.copyToByteArray(testFile); + assertEquals(new String(testFileContent), SAMPLE_CONTENT); + } + +} diff --git a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/outbound/FtpOutboundChannelAdapterInsideChainTests-context.xml b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/outbound/FtpOutboundChannelAdapterInsideChainTests-context.xml new file mode 100644 index 0000000000..95e2cc9aa5 --- /dev/null +++ b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/outbound/FtpOutboundChannelAdapterInsideChainTests-context.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + diff --git a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/outbound/FtpSendingMessageHandlerTests.java b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/outbound/FtpSendingMessageHandlerTests.java index cd7ef2f395..4845782a53 100644 --- a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/outbound/FtpSendingMessageHandlerTests.java +++ b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/outbound/FtpSendingMessageHandlerTests.java @@ -32,8 +32,11 @@ import org.junit.Test; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.expression.common.LiteralExpression; import org.springframework.integration.Message; +import org.springframework.integration.MessageChannel; import org.springframework.integration.file.FileNameGenerator; import org.springframework.integration.file.remote.handler.FileTransferringMessageHandler; import org.springframework.integration.ftp.session.AbstractFtpSessionFactory; @@ -42,6 +45,7 @@ import org.springframework.util.FileCopyUtils; /** * @author Oleg Zhurakousky + * @author Artem Bilan */ public class FtpSendingMessageHandlerTests { @@ -120,6 +124,25 @@ public class FtpSendingMessageHandlerTests { assertTrue("destination file was not created", destFile.exists()); } + @Test //INT-2275 + public void testFtpOutboundChannelAdapterInsideChain() throws Exception { + File targetDir = new File("remote-target-dir"); + assertTrue("target directory does not exist: " + targetDir.getName(), targetDir.exists()); + + File srcFile = File.createTempFile("testHandleFileMessage", ".tmp"); + srcFile.deleteOnExit(); + + File destFile = new File(targetDir, srcFile.getName()); + destFile.deleteOnExit(); + + ApplicationContext context = new ClassPathXmlApplicationContext("FtpOutboundChannelAdapterInsideChainTests-context.xml", getClass()); + + MessageChannel channel = context.getBean("outboundChainChannel", MessageChannel.class); + + channel.send(new GenericMessage(srcFile)); + assertTrue("destination file was not created", destFile.exists()); + } + public static class TestFtpSessionFactory extends AbstractFtpSessionFactory { diff --git a/spring-integration-gemfire/src/test/java/org/springframework/integration/gemfire/outbound/GemfireOutboundChannelAdapterTests-context.xml b/spring-integration-gemfire/src/test/java/org/springframework/integration/gemfire/outbound/GemfireOutboundChannelAdapterTests-context.xml index 967e2c7858..f671560599 100644 --- a/spring-integration-gemfire/src/test/java/org/springframework/integration/gemfire/outbound/GemfireOutboundChannelAdapterTests-context.xml +++ b/spring-integration-gemfire/src/test/java/org/springframework/integration/gemfire/outbound/GemfireOutboundChannelAdapterTests-context.xml @@ -22,5 +22,9 @@ - + + + + + diff --git a/spring-integration-gemfire/src/test/java/org/springframework/integration/gemfire/outbound/GemfireOutboundChannelAdapterTests.java b/spring-integration-gemfire/src/test/java/org/springframework/integration/gemfire/outbound/GemfireOutboundChannelAdapterTests.java index 5b26ad7b9b..783b19eaee 100644 --- a/spring-integration-gemfire/src/test/java/org/springframework/integration/gemfire/outbound/GemfireOutboundChannelAdapterTests.java +++ b/spring-integration-gemfire/src/test/java/org/springframework/integration/gemfire/outbound/GemfireOutboundChannelAdapterTests.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. You may obtain a copy of the License at @@ -31,6 +31,7 @@ import com.gemstone.gemfire.internal.cache.DistributedRegion; /** * @author David Turanski + * @author Artem Bilan * @since 2.1 */ @@ -40,7 +41,7 @@ public class GemfireOutboundChannelAdapterTests { @Autowired MessageChannel cacheChannel1; - + @Autowired DistributedRegion region1; @@ -49,7 +50,11 @@ public class GemfireOutboundChannelAdapterTests { @Autowired DistributedRegion region2; - + + @Autowired + MessageChannel cacheChainChannel; + + @Before public void setUp() { region1.clear(); @@ -75,4 +80,16 @@ public class GemfireOutboundChannelAdapterTests { assertEquals("hello",region2.get("HELLO")); assertEquals("bar",region2.get("foo")); } + + @Test //INT-2275 + public void testWriteWithinChain() { + Map map = new HashMap(); + map.put("foo","bar"); + + Message message = MessageBuilder.withPayload(map).build(); + cacheChainChannel.send(message); + assertEquals(1,region1.size()); + assertEquals("bar",region1.get("foo")); + } + } diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpOutboundChannelAdapterWithinChainTests-context.xml b/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpOutboundChannelAdapterWithinChainTests-context.xml new file mode 100644 index 0000000000..1ad869df32 --- /dev/null +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpOutboundChannelAdapterWithinChainTests-context.xml @@ -0,0 +1,19 @@ + + + + + + + + + + diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandlerTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandlerTests.java index de03d2ff01..951abb7621 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandlerTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandlerTests.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. @@ -32,13 +32,16 @@ import javax.xml.transform.Source; import org.junit.Test; import org.springframework.beans.DirectFieldAccessor; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.integration.Message; +import org.springframework.integration.MessageChannel; import org.springframework.integration.channel.QueueChannel; -import org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler; import org.springframework.integration.support.MessageBuilder; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestClientException; @@ -47,6 +50,7 @@ import org.springframework.web.client.RestTemplate; /** * @author Mark Fisher * @author Oleg Zhurakousky + * @author Artem Bilan */ public class HttpRequestExecutingMessageHandlerTests { @@ -639,7 +643,15 @@ public class HttpRequestExecutingMessageHandlerTests { assertNull(request.getHeaders().getContentType()); */ } - + + @Test //INT-2275 + public void testOutboundChannelAdapterWithinChain() { + ApplicationContext ctx = new ClassPathXmlApplicationContext("HttpOutboundChannelAdapterWithinChainTests-context.xml", this.getClass()); + MessageChannel channel = ctx.getBean("httpOutboundChannelAdapterWithinChain", MessageChannel.class); + channel.send(MessageBuilder.withPayload("test").build()); +// It's just enough if it was sent successfully from chain without any failures + } + public static class City{ private String name; public City(String name){ @@ -661,4 +673,15 @@ public class HttpRequestExecutingMessageHandlerTests { throw new RuntimeException("intentional"); } } + + private static class MockRestTemplate2 extends RestTemplate { + + @Override + public ResponseEntity exchange(String url, HttpMethod method, HttpEntity requestEntity, + Class responseType, Map uriVariables) throws RestClientException { + return new ResponseEntity(HttpStatus.OK); + } + } + + } diff --git a/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/TcpOutboundChannelAdapterWithinTests-context.xml b/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/TcpOutboundChannelAdapterWithinTests-context.xml new file mode 100644 index 0000000000..abe18b3580 --- /dev/null +++ b/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/TcpOutboundChannelAdapterWithinTests-context.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/TcpSendingMessageHandlerTests.java b/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/TcpSendingMessageHandlerTests.java index aa0eb3e0e4..597292f9fe 100644 --- a/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/TcpSendingMessageHandlerTests.java +++ b/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/TcpSendingMessageHandlerTests.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. @@ -43,10 +43,15 @@ import javax.net.ServerSocketFactory; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.Test; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.core.serializer.DefaultDeserializer; import org.springframework.core.serializer.DefaultSerializer; 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.ip.IpHeaders; import org.springframework.integration.ip.tcp.connection.AbstractConnectionFactory; import org.springframework.integration.ip.tcp.connection.HelloWorldInterceptorFactory; import org.springframework.integration.ip.tcp.connection.TcpConnectionInterceptorFactory; @@ -57,10 +62,12 @@ import org.springframework.integration.ip.tcp.serializer.ByteArrayCrLfSerializer import org.springframework.integration.ip.tcp.serializer.ByteArrayLengthHeaderSerializer; import org.springframework.integration.ip.tcp.serializer.ByteArrayStxEtxSerializer; import org.springframework.integration.ip.util.SocketTestUtils; +import org.springframework.integration.message.GenericMessage; import org.springframework.integration.support.MessageBuilder; /** * @author Gary Russell + * @author Artem Bilan * @since 2.0 */ public class TcpSendingMessageHandlerTests { @@ -1055,4 +1062,20 @@ public class TcpSendingMessageHandlerTests { done.set(true); } + @Test + public void testOutboundChannelAdapterWithinChain() throws Exception { + ApplicationContext ctx = new ClassPathXmlApplicationContext( + "TcpOutboundChannelAdapterWithinTests-context.xml", this.getClass()); + AbstractConnectionFactory ccf = ctx.getBean("ccf", AbstractConnectionFactory.class); +// TODO Lifecycle#start() isn't invoked within chain... + ccf.start(); + MessageChannel channelAdapterWithinChain = ctx.getBean("tcpOutboundChannelAdapterWithinChain", MessageChannel.class); + PollableChannel inbound = ctx.getBean("inbound", PollableChannel.class); + String testPayload = "Hello, world!"; + channelAdapterWithinChain.send(new GenericMessage(testPayload)); + Message m = inbound.receive(1000); + assertNotNull(m); + assertEquals(testPayload, new String((byte[]) m.getPayload())); + } + } diff --git a/spring-integration-ip/src/test/java/org/springframework/integration/ip/udp/UdpUnicastEndToEndTests.java b/spring-integration-ip/src/test/java/org/springframework/integration/ip/udp/UdpUnicastEndToEndTests.java index 4d9f76462f..b296805624 100644 --- a/spring-integration-ip/src/test/java/org/springframework/integration/ip/udp/UdpUnicastEndToEndTests.java +++ b/spring-integration-ip/src/test/java/org/springframework/integration/ip/udp/UdpUnicastEndToEndTests.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. @@ -26,6 +26,7 @@ import java.util.Properties; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import org.junit.Before; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.AbstractApplicationContext; @@ -49,6 +50,7 @@ import org.springframework.integration.test.util.TestUtils; * received in the other context (and written back to the console). * * @author Gary Russell + * @author Artem Bilan * @since 2.0 */ public class UdpUnicastEndToEndTests implements Runnable { @@ -67,7 +69,18 @@ public class UdpUnicastEndToEndTests implements Runnable { private CountDownLatch readyToReceive = new CountDownLatch(1); - private static long hangAroundFor = 0; + private static long hangAroundFor = 10; + + @Before + public void setup() { + this.testingIpText = null; + this.finalMessage = null; + this.sentFirst = new CountDownLatch(1); + this.firstReceived = new CountDownLatch(1); + this.doneProcessing = new CountDownLatch(1); + this.okToRun = true; + this.readyToReceive = new CountDownLatch(1); + } @Test public void runIt() throws Exception { @@ -80,6 +93,17 @@ public class UdpUnicastEndToEndTests implements Runnable { applicationContext.stop(); } + @Test + public void tesUudpOutboundChannelAdapterWithinChain() throws Exception { + UdpUnicastEndToEndTests launcher = new UdpUnicastEndToEndTests(); + Thread t = new Thread(launcher); + t.start(); // launch the receiver + AbstractApplicationContext applicationContext = new ClassPathXmlApplicationContext( + "testIp-out-within-chain-context.xml", UdpUnicastEndToEndTests.class); + launcher.launchSender(applicationContext); + applicationContext.stop(); + } + public void launchSender(ApplicationContext applicationContext) throws Exception { ChannelResolver channelResolver = new BeanFactoryChannelResolver(applicationContext); @@ -159,6 +183,7 @@ public class UdpUnicastEndToEndTests implements Runnable { public static void main(String[] args) throws Exception { hangAroundFor = 120000; new UdpUnicastEndToEndTests().runIt(); + new UdpUnicastEndToEndTests().tesUudpOutboundChannelAdapterWithinChain(); } } diff --git a/spring-integration-ip/src/test/java/org/springframework/integration/ip/udp/testIp-out-within-chain-context.xml b/spring-integration-ip/src/test/java/org/springframework/integration/ip/udp/testIp-out-within-chain-context.xml new file mode 100644 index 0000000000..b2e696b4e0 --- /dev/null +++ b/spring-integration-ip/src/test/java/org/springframework/integration/ip/udp/testIp-out-within-chain-context.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + 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 ef2d80a345..c50026a692 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 @@ -705,17 +705,6 @@ - - - - Indicates whether this procedure's return value - should be included. - - - - - - @@ -1313,4 +1302,4 @@ - \ No newline at end of file + diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcOutboundChannelAdapterWithinChainTests-context.xml b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcOutboundChannelAdapterWithinChainTests-context.xml new file mode 100644 index 0000000000..cf586e4aee --- /dev/null +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcOutboundChannelAdapterWithinChainTests-context.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcOutboundChannelAdapterWithinChainTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcOutboundChannelAdapterWithinChainTests.java new file mode 100644 index 0000000000..b677945480 --- /dev/null +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcOutboundChannelAdapterWithinChainTests.java @@ -0,0 +1,68 @@ +/* + * 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.jdbc; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.support.AbstractApplicationContext; +import org.springframework.integration.Message; +import org.springframework.integration.MessageChannel; +import org.springframework.integration.jdbc.storedproc.User; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.Map; + +import static org.junit.Assert.assertEquals; + +/** + * @author Artem Bilan + * @since 2.2 + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class StoredProcOutboundChannelAdapterWithinChainTests { + + @Autowired + private AbstractApplicationContext context; + + @Autowired + private JdbcTemplate jdbcTemplate; + + @Autowired + private MessageChannel jdbcStoredProcOutboundChannelAdapterWithinChain; + + + @Test + public void test() { + Message message = MessageBuilder.withPayload(new User("username", "password", "email")).build(); + this.jdbcStoredProcOutboundChannelAdapterWithinChain.send(message); + + Map map = this.jdbcTemplate.queryForMap("SELECT * FROM USERS WHERE USERNAME=?", "username"); + + assertEquals("Wrong username", "username", map.get("USERNAME")); + assertEquals("Wrong password", "password", map.get("PASSWORD")); + assertEquals("Wrong email", "email", map.get("EMAIL")); +// embeddedDatabase can be in working state. So other tests with the same embeddedDatabase beanId, type and init scripts +// may be failed with Exception like: object in the DB already exists + this.context.destroy(); + } + +} 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 62e21c04c6..afd4ed0299 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,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. You may obtain a copy of the License at @@ -38,6 +38,7 @@ import org.springframework.jdbc.core.JdbcTemplate; * @author Mark Fisher * @author Oleg Zhurakousky * @author Gary Russell + * @author Artem Bilan * @since 2.0 * */ @@ -115,6 +116,16 @@ public class JdbcMessageHandlerParserTests { assertEquals("Wrong id", "foo", map.get("name")); } + @Test + public void testOutboundChannelAdapterWithinChain(){ + setUp("handlingJdbcOutboundChannelAdapterWithinChainTest.xml", getClass()); + Message message = MessageBuilder.withPayload("foo").setHeader("business.key", "FOO").build(); + channel.send(message); + Map map = this.jdbcTemplate.queryForMap("SELECT * from FOOS"); + assertEquals("Wrong id", "FOO", map.get("ID")); + assertEquals("Wrong id", "foo", map.get("name")); + } + @After public void tearDown(){ if(context != null){ diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/handlingJdbcOutboundChannelAdapterWithinChainTest.xml b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/handlingJdbcOutboundChannelAdapterWithinChainTest.xml new file mode 100644 index 0000000000..318d63fa56 --- /dev/null +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/handlingJdbcOutboundChannelAdapterWithinChainTest.xml @@ -0,0 +1,21 @@ + + + + + + + + + + diff --git a/spring-integration-jms/src/test/java/org/springframework/integration/jms/JmsOutboundInsideChainTests-context.xml b/spring-integration-jms/src/test/java/org/springframework/integration/jms/JmsOutboundInsideChainTests-context.xml new file mode 100644 index 0000000000..27c827d8a0 --- /dev/null +++ b/spring-integration-jms/src/test/java/org/springframework/integration/jms/JmsOutboundInsideChainTests-context.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-jms/src/test/java/org/springframework/integration/jms/JmsOutboundInsideChainTests.java b/spring-integration-jms/src/test/java/org/springframework/integration/jms/JmsOutboundInsideChainTests.java new file mode 100644 index 0000000000..3cbe61cb31 --- /dev/null +++ b/spring-integration-jms/src/test/java/org/springframework/integration/jms/JmsOutboundInsideChainTests.java @@ -0,0 +1,48 @@ +/* + * 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.jms; + +import org.junit.Test; +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.support.MessageBuilder; + +import static org.junit.Assert.*; + +/** + * //INT-2275 + * + * @author Artem Bilan + */ +public class JmsOutboundInsideChainTests { + + @Test + public void testJmsOutboundChannelInsideChain(){ + ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("JmsOutboundInsideChainTests-context.xml", getClass()); + PollableChannel receiveChannel = context.getBean("receiveChannel", PollableChannel.class); + MessageChannel outboundChainChannel = context.getBean("outboundChainChannel", MessageChannel.class); + String testString = "test"; + Message shippedMessage = MessageBuilder.withPayload(testString).build(); + outboundChainChannel.send(shippedMessage); + Message receivedMessage = receiveChannel.receive(); + assertEquals(testString, receivedMessage.getPayload()); + context.close(); + } + +} 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 cddfbbeebb..d8b1f8c8db 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 @@ -24,6 +24,13 @@ object-name="test.publisher:name=publisher" 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 7c6cc8b408..b3dc8b303d 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 @@ -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. @@ -35,6 +35,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * @author Mark Fisher + * @author Artem Bilan * @since 2.0 */ @ContextConfiguration @@ -47,6 +48,9 @@ public class NotificationPublishingChannelAdapterParserTests { @Autowired private TestListener listener; + @Autowired + private MessageChannel publishingWithinChainChannel; + @After public void clearListener() { listener.lastNotification = null; @@ -54,7 +58,7 @@ public class NotificationPublishingChannelAdapterParserTests { @Test - public void publishStringMessag() throws Exception { + public void publishStringMessage() throws Exception { assertNull(listener.lastNotification); Message message = MessageBuilder.withPayload("XYZ") .setHeader(JmxHeaders.NOTIFICATION_TYPE, "test.type").build(); @@ -90,7 +94,18 @@ public class NotificationPublishingChannelAdapterParserTests { assertEquals("default.type", notification.getType()); } - + @Test //INT-2275 + public void publishStringMessageWithinChain() throws Exception { + assertNull(listener.lastNotification); + Message message = MessageBuilder.withPayload("XYZ") + .setHeader(JmxHeaders.NOTIFICATION_TYPE, "test.type").build(); + publishingWithinChainChannel.send(message); + assertNotNull(listener.lastNotification); + Notification notification = listener.lastNotification; + assertEquals("XYZ", notification.getMessage()); + assertEquals("test.type", notification.getType()); + assertNull(notification.getUserData()); + } private static class TestData { } 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 5efd68bbcc..642f15d064 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 @@ -20,6 +20,22 @@ object-name="org.springframework.integration.jmx.config:type=TestBean,name=testBeanAdapter" operation-name="test"/> + + + + + + + + + + diff --git a/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/OperationInvokingChannelAdapterParserTests.java b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/OperationInvokingChannelAdapterParserTests.java index 9f80459618..97c9b5ce46 100644 --- a/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/OperationInvokingChannelAdapterParserTests.java +++ b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/OperationInvokingChannelAdapterParserTests.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. @@ -17,6 +17,8 @@ package org.springframework.integration.jmx.config; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import org.junit.After; import org.junit.Test; @@ -24,6 +26,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.MessagingException; import org.springframework.integration.jmx.JmxHeaders; import org.springframework.integration.message.GenericMessage; import org.springframework.integration.support.MessageBuilder; @@ -33,6 +36,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * @author Mark Fisher * @author Oleg Zhurakousky + * @author Artem Bilan * @since 2.0 */ @ContextConfiguration @@ -42,6 +46,15 @@ public class OperationInvokingChannelAdapterParserTests { @Autowired private MessageChannel input; + @Autowired + private MessageChannel operationWithNonNullReturn; + + @Autowired + private MessageChannel operationInvokingWithinChain; + + @Autowired + private MessageChannel operationWithinChainWithNonNullReturn; + @Autowired private TestBean testBean; @@ -59,7 +72,19 @@ public class OperationInvokingChannelAdapterParserTests { input.send(new GenericMessage("test3")); assertEquals(3, testBean.messages.size()); } - + + @Test + public void testOutboundAdapterWithNonNullReturn() throws Exception { + try { + operationWithNonNullReturn.send(new GenericMessage("test1")); + fail("Expect MessagingException about non-null return"); + } + catch (Exception e) { + assertTrue(e instanceof MessagingException); +// TODO Add check exception's message about 'must have a void return' after refactoring + } + } + @Test // Headers should be ignored public void adapterWitJmxHeaders() throws Exception { @@ -69,7 +94,25 @@ public class OperationInvokingChannelAdapterParserTests { input.send(this.createMessage("3")); assertEquals(3, testBean.messages.size()); } - + + @Test //INT-2275 + public void testInvokeOperationWithinChain() throws Exception { + operationInvokingWithinChain.send(new GenericMessage("test1")); + assertEquals(1, testBean.messages.size()); + } + + @Test //INT-2275 + public void testOperationWithinChainWithNonNullReturn() throws Exception { + try { + operationWithinChainWithNonNullReturn.send(new GenericMessage("test1")); + fail("Expect MessagingException about non-null return"); + } + catch (Exception e) { + assertTrue(e instanceof MessagingException); +// TODO Add check exception's message about 'must have a void return' after refactoring + } + } + private Message createMessage(String payload){ return MessageBuilder.withPayload(payload) .setHeader(JmxHeaders.OBJECT_NAME, "org.springframework.integration.jmx.config:type=TestBean,name=foo") diff --git a/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/TestListener.java b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/TestListener.java index d02745a6cd..1c8b6261b4 100644 --- a/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/TestListener.java +++ b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/TestListener.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. @@ -26,6 +26,7 @@ import org.springframework.jmx.support.ObjectNameManager; /** * @author Mark Fisher + * @author Artem Bilan * @since 2.0 */ public class TestListener implements NotificationListener, BeanFactoryAware { @@ -37,6 +38,8 @@ public class TestListener implements NotificationListener, BeanFactoryAware { try { server.addNotificationListener( ObjectNameManager.getInstance("test.publisher:name=publisher"), this, null, null); + server.addNotificationListener( + ObjectNameManager.getInstance("test.publisher:name=publisher-chain"), this, null, null); } catch (Exception e) { throw new IllegalArgumentException(e); diff --git a/spring-integration-mail/src/test/java/org/springframework/integration/mail/MailSendingMessageHandlerContextTests.java b/spring-integration-mail/src/test/java/org/springframework/integration/mail/MailSendingMessageHandlerContextTests.java index 12bdc273ee..e690b785a9 100644 --- a/spring-integration-mail/src/test/java/org/springframework/integration/mail/MailSendingMessageHandlerContextTests.java +++ b/spring-integration-mail/src/test/java/org/springframework/integration/mail/MailSendingMessageHandlerContextTests.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. @@ -31,6 +31,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.integration.MessageChannel; import org.springframework.integration.mapping.MessageMappingException; import org.springframework.integration.message.GenericMessage; import org.springframework.integration.support.MessageBuilder; @@ -40,6 +41,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * @author Marius Bogoevici + * @author Artem Bilan */ @RunWith(value = SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:/org/springframework/integration/mail/mailSendingMessageHandlerContextTests.xml"}) @@ -51,6 +53,9 @@ public class MailSendingMessageHandlerContextTests { @Autowired private StubJavaMailSender mailSender; + @Autowired + private MessageChannel sendMailOutboundChainChannel; + @Before public void reset() { @@ -99,4 +104,13 @@ public class MailSendingMessageHandlerContextTests { this.handler.handleMessage(new GenericMessage(payload)); } + @Test //INT-2275 + public void mailOutboundChannelAdapterWithinChain() { + this.sendMailOutboundChainChannel.send(MailTestsHelper.createIntegrationMessage()); + SimpleMailMessage mailMessage = MailTestsHelper.createSimpleMailMessage(); + assertEquals("no mime message should have been sent", 0, this.mailSender.getSentMimeMessages().size()); + assertEquals("only one simple message must be sent", 1, this.mailSender.getSentSimpleMailMessages().size()); + assertEquals("message content different from expected", mailMessage, this.mailSender.getSentSimpleMailMessages().get(0)); + } + } diff --git a/spring-integration-mail/src/test/java/org/springframework/integration/mail/mailSendingMessageHandlerContextTests.xml b/spring-integration-mail/src/test/java/org/springframework/integration/mail/mailSendingMessageHandlerContextTests.xml index dc1d8b70e2..391caa3b20 100644 --- a/spring-integration-mail/src/test/java/org/springframework/integration/mail/mailSendingMessageHandlerContextTests.xml +++ b/spring-integration-mail/src/test/java/org/springframework/integration/mail/mailSendingMessageHandlerContextTests.xml @@ -1,16 +1,18 @@ + xmlns:int="http://www.springframework.org/schema/integration" + xmlns:int-mail="http://www.springframework.org/schema/integration/mail" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd + http://www.springframework.org/schema/integration/mail http://www.springframework.org/schema/integration/mail/spring-integration-mail.xsd"> - + + + @@ -19,4 +21,8 @@ - \ No newline at end of file + + + + + diff --git a/spring-integration-redis/src/test/java/org/springframework/integration/redis/config/RedisOutboundChannelAdapterParserTests-context.xml b/spring-integration-redis/src/test/java/org/springframework/integration/redis/config/RedisOutboundChannelAdapterParserTests-context.xml index 4e0b77dd9e..620a792ee6 100644 --- a/spring-integration-redis/src/test/java/org/springframework/integration/redis/config/RedisOutboundChannelAdapterParserTests-context.xml +++ b/spring-integration-redis/src/test/java/org/springframework/integration/redis/config/RedisOutboundChannelAdapterParserTests-context.xml @@ -26,4 +26,8 @@ + + + + diff --git a/spring-integration-redis/src/test/java/org/springframework/integration/redis/config/RedisOutboundChannelAdapterParserTests.java b/spring-integration-redis/src/test/java/org/springframework/integration/redis/config/RedisOutboundChannelAdapterParserTests.java index 7e92fe2f0c..ada5daccf9 100644 --- a/spring-integration-redis/src/test/java/org/springframework/integration/redis/config/RedisOutboundChannelAdapterParserTests.java +++ b/spring-integration-redis/src/test/java/org/springframework/integration/redis/config/RedisOutboundChannelAdapterParserTests.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. @@ -38,6 +38,7 @@ import static junit.framework.Assert.assertEquals; /** * @author Oleg Zhurakousky * @author Mark Fisher + * @author Artem Bilan */ @ContextConfiguration @RunWith(SpringJUnit4ClassRunner.class) @@ -69,6 +70,16 @@ public class RedisOutboundChannelAdapterParserTests extends RedisAvailableTests{ assertEquals("Hello Redis", receiveChannel.receive(1000).getPayload()); } + @Test //INT-2275 + @RedisAvailable + public void testOutboundChannelAdapterWithinChain() throws Exception{ + MessageChannel sendChannel = context.getBean("redisOutboudChain", MessageChannel.class); + sendChannel.send(new GenericMessage("Hello Redis from chain")); + Thread.sleep(1000); + QueueChannel receiveChannel = context.getBean("receiveChannel", QueueChannel.class); + assertEquals("Hello Redis from chain", receiveChannel.receive(1000).getPayload()); + } + @SuppressWarnings("unused") private static class TestMessageConverter extends SimpleMessageConverter { diff --git a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/outbound/SftpOutboundChannelAdapterInsideChainTests-context.xml b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/outbound/SftpOutboundChannelAdapterInsideChainTests-context.xml new file mode 100644 index 0000000000..2a097e15bc --- /dev/null +++ b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/outbound/SftpOutboundChannelAdapterInsideChainTests-context.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + diff --git a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/outbound/SftpSendingMessageHandlerTests.java b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/outbound/SftpSendingMessageHandlerTests.java index 87574469d7..15d7510883 100644 --- a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/outbound/SftpSendingMessageHandlerTests.java +++ b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/outbound/SftpSendingMessageHandlerTests.java @@ -30,7 +30,10 @@ import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.expression.common.LiteralExpression; +import org.springframework.integration.MessageChannel; import org.springframework.integration.file.DefaultFileNameGenerator; import org.springframework.integration.file.remote.handler.FileTransferringMessageHandler; import org.springframework.integration.file.remote.session.Session; @@ -106,6 +109,24 @@ public class SftpSendingMessageHandlerTests { assertTrue(new File("remote-target-dir", "foo.txt").exists()); } + @Test //INT-2275 + public void testSftpOutboundChannelAdapterInsideChain() throws Exception { + File targetDir = new File("remote-target-dir"); + assertTrue("target directory does not exist: " + targetDir.getName(), targetDir.exists()); + + File srcFile = File.createTempFile("testHandleFileMessage", ".tmp"); + srcFile.deleteOnExit(); + + File destFile = new File(targetDir, srcFile.getName()); + destFile.deleteOnExit(); + + ApplicationContext context = new ClassPathXmlApplicationContext("SftpOutboundChannelAdapterInsideChainTests-context.xml", getClass()); + + MessageChannel channel = context.getBean("outboundChainChannel", MessageChannel.class); + + channel.send(new GenericMessage(srcFile)); + assertTrue("destination file was not created", destFile.exists()); + } public static class TestSftpSessionFactory extends DefaultSftpSessionFactory { diff --git a/spring-integration-stream/src/test/java/org/springframework/integration/stream/config/ConsoleOutboundChannelAdapterParserTests.java b/spring-integration-stream/src/test/java/org/springframework/integration/stream/config/ConsoleOutboundChannelAdapterParserTests.java index f8f02d586f..13e17b20e0 100644 --- a/spring-integration-stream/src/test/java/org/springframework/integration/stream/config/ConsoleOutboundChannelAdapterParserTests.java +++ b/spring-integration-stream/src/test/java/org/springframework/integration/stream/config/ConsoleOutboundChannelAdapterParserTests.java @@ -24,20 +24,22 @@ import java.io.OutputStreamWriter; import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.io.Writer; + import java.nio.charset.Charset; import org.junit.Before; import org.junit.Test; - import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.BeanCreationException; import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.message.GenericMessage; import org.springframework.integration.stream.CharacterStreamWritingMessageHandler; /** * @author Mark Fisher * @author Gary Russell + * @author Artem Bilan */ public class ConsoleOutboundChannelAdapterParserTests { @@ -156,4 +158,13 @@ public class ConsoleOutboundChannelAdapterParserTests { assertEquals("foo" + System.getProperty("line.separator"), out.toString()); } + @Test //INT-2275 + public void stdoutInsideNestedChain() { + ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( + "consoleOutboundChannelAdapterParserTests.xml", ConsoleOutboundChannelAdapterParserTests.class); + DirectChannel channel = context.getBean("stdoutInsideNestedChain", DirectChannel.class); + this.resetStreams(); + channel.send(new GenericMessage("foo")); + assertEquals("foobar", out.toString()); + } } diff --git a/spring-integration-stream/src/test/java/org/springframework/integration/stream/config/consoleOutboundChannelAdapterParserTests.xml b/spring-integration-stream/src/test/java/org/springframework/integration/stream/config/consoleOutboundChannelAdapterParserTests.xml index 95492bb297..f70f1c265e 100644 --- a/spring-integration-stream/src/test/java/org/springframework/integration/stream/config/consoleOutboundChannelAdapterParserTests.xml +++ b/spring-integration-stream/src/test/java/org/springframework/integration/stream/config/consoleOutboundChannelAdapterParserTests.xml @@ -20,4 +20,11 @@ - \ No newline at end of file + + + + + + + + diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingDMsUsingNamespace-context.xml b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingDMsUsingNamespace-context.xml index 107348a91f..95b36add49 100644 --- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingDMsUsingNamespace-context.xml +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingDMsUsingNamespace-context.xml @@ -33,6 +33,10 @@ + + + + diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingDMsUsingNamespace.java b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingDMsUsingNamespace.java index c5a0a743d7..13dec1e9dd 100644 --- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingDMsUsingNamespace.java +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingDMsUsingNamespace.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. @@ -31,14 +31,19 @@ import org.springframework.util.StringUtils; /** * @author Josh Long * @author Oleg Zhurakouksy + * @author Artem Bilan */ @ContextConfiguration public class TestSendingDMsUsingNamespace extends AbstractJUnit4SpringContextTests { @Autowired @Qualifier("inputChannel") + private MessageChannel inputChannel; - + @Autowired + @Qualifier("dmOutboundWithinChain") + private MessageChannel dmOutboundWithinChain; + @Test @Ignore public void testSendigRealDirectMessage() throws Throwable { @@ -52,4 +57,16 @@ public class TestSendingDMsUsingNamespace extends AbstractJUnit4SpringContextTes inputChannel.send(mb.build()); } + @Test + @Ignore + public void testSendigDirectMessageFromChain() throws Throwable { + String dmUsr = "z_oleg"; + MessageBuilder mb = MessageBuilder.withPayload("Hello world!"); + + if (StringUtils.hasText(dmUsr)) { + mb.setHeader(TwitterHeaders.DM_TARGET_USER_ID, dmUsr); + } + dmOutboundWithinChain.send(mb.build()); + } + } diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingUpdatesUsingNamespace-context.xml b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingUpdatesUsingNamespace-context.xml index 26465a48c2..a5c4a2b599 100644 --- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingUpdatesUsingNamespace-context.xml +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingUpdatesUsingNamespace-context.xml @@ -32,7 +32,11 @@ - + + + + + diff --git a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingUpdatesUsingNamespace.java b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingUpdatesUsingNamespace.java index d26146f8a7..80d5aa832b 100644 --- a/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingUpdatesUsingNamespace.java +++ b/spring-integration-twitter/src/test/java/org/springframework/integration/twitter/ignored/TestSendingUpdatesUsingNamespace.java @@ -1,5 +1,5 @@ /* - * Copyright 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. @@ -21,6 +21,8 @@ import java.util.Date; import org.junit.Ignore; import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.integration.Message; import org.springframework.integration.MessageChannel; @@ -31,6 +33,7 @@ import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests; /** * @author Josh Long + * @author Artem Bilan */ @ContextConfiguration public class TestSendingUpdatesUsingNamespace extends AbstractJUnit4SpringContextTests { @@ -39,6 +42,10 @@ public class TestSendingUpdatesUsingNamespace extends AbstractJUnit4SpringContex @Value("#{out}") private MessageChannel channel; + @Autowired + @Qualifier("outFromChain") + private MessageChannel outFromChain; + @Test @Ignore public void testSendingATweet() throws Throwable { @@ -48,4 +55,11 @@ public class TestSendingUpdatesUsingNamespace extends AbstractJUnit4SpringContex this.messagingTemplate.send(this.channel, m); } + @Test + @Ignore + public void testSendingATweetFromChain() throws Throwable { + Message m = MessageBuilder.withPayload("Early start today" + new Date(System.currentTimeMillis())).build(); + this.outFromChain.send(m); + } + } 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 31347bce2e..beffa34209 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,4 +43,8 @@ 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 8c05e0a667..73fc340b86 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 @@ -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. @@ -49,6 +49,7 @@ import static org.mockito.Mockito.verify; /** * @author Oleg Zhurakousky * @author Mark Fisher + * @author Artem Bilan */ @ContextConfiguration @RunWith(SpringJUnit4ClassRunner.class) @@ -104,7 +105,6 @@ public class ChatMessageOutboundChannelAdapterParserTests { org.jivesoftware.smack.packet.Message xmppMessage = (org.jivesoftware.smack.packet.Message) args[0]; assertEquals("oleg", xmppMessage.getTo()); assertEquals("foobar", xmppMessage.getProperty("foobar")); - assertEquals("oleg", xmppMessage.getTo()); return null; }}) .when(connection).sendPacket(Mockito.any(org.jivesoftware.smack.packet.Message.class)); @@ -112,6 +112,29 @@ public class ChatMessageOutboundChannelAdapterParserTests { channel.send(message); verify(connection, times(1)).sendPacket(Mockito.any(org.jivesoftware.smack.packet.Message.class)); + Mockito.reset(connection); + } + + @SuppressWarnings("rawtypes") + @Test //INT-2275 + public void testOutboundChannelAdapterInsideChain() throws Exception{ + MessageChannel channel = context.getBean("outboundChainChannel", MessageChannel.class); + Message message = MessageBuilder.withPayload("hello").setHeader(XmppHeaders.TO, "artem").build(); + XMPPConnection connection = context.getBean("testConnection", XMPPConnection.class); + Mockito.doAnswer(new Answer() { + public Object answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + org.jivesoftware.smack.packet.Message xmppMessage = (org.jivesoftware.smack.packet.Message) args[0]; + assertEquals("artem", xmppMessage.getTo()); + assertEquals("hello", xmppMessage.getBody()); + 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); } } diff --git a/src/reference/docbook/chain.xml b/src/reference/docbook/chain.xml index 6e7dd385aa..edc3c7062d 100644 --- a/src/reference/docbook/chain.xml +++ b/src/reference/docbook/chain.xml @@ -57,7 +57,7 @@ The <chain> element provides an input-channel attribute, and if the last element in the chain is capable of producing reply messages (optional), it also supports an output-channel attribute. The sub-elements are then - filters, transformers, splitters, and service-activators. The last element may also be a router. + filters, transformers, splitters, and service-activators. The last element may also be a router or an outbound-channel-adapter. @@ -72,7 +72,18 @@ that touches only header values. You could obtain the same result by implementing a MessageHandler that did the header modifications and wiring that as a bean, but the header-enricher is obviously a simpler option. - + + The <chain> can be configured as the last 'black-box' consumer of the message flow. For this solution it is + enough to put at the end of the <chain> some <outbound-channel-adapter>: + + + + + + + + ]]> + Sometimes you need to make a nested call to another chain from within a chain and then come back and continue execution within the original chain.