diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractConsumerEndpointParser.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractConsumerEndpointParser.java index 16343ac985..b0f866b5e5 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractConsumerEndpointParser.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractConsumerEndpointParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 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. @@ -95,12 +95,19 @@ public abstract class AbstractConsumerEndpointParser extends AbstractBeanDefinit String inputChannelAttributeName = this.getInputChannelAttributeName(); boolean hasInputChannelAttribute = element.hasAttribute(inputChannelAttributeName); if (parserContext.isNested()) { + String elementDescription = IntegrationNamespaceUtils.createElementDescription(element); if (hasInputChannelAttribute) { - String elementDescription = IntegrationNamespaceUtils.createElementDescription(element); parserContext.getReaderContext().error("The '" + inputChannelAttributeName + "' attribute isn't allowed for a nested (e.g. inside a ) endpoint element: " + elementDescription + ".", element); } + if (!replyChannelInChainAllowed(element)) { + if (StringUtils.hasText(element.getAttribute("reply-channel"))) { + parserContext.getReaderContext().error("The 'reply-channel' attribute isn't" + + " allowed for a nested (e.g. inside a ) outbound gateway element: " + + elementDescription + ".", element); + } + } return handlerBeanDefinition; } else { @@ -168,4 +175,14 @@ public abstract class AbstractConsumerEndpointParser extends AbstractBeanDefinit return null; } + /** + * Override to allow 'reply-channel' within a chain, for components where it + * makes sense (e.g. enricher). Default is false for outbound gateways, else true. + * @return true to allow a reply channel attribute within a chain. + */ + protected boolean replyChannelInChainAllowed(Element element) { + String localName = element.getLocalName(); + return localName == null || !localName.contains("outbound-gateway"); + } + } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/EnricherParser.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/EnricherParser.java index 0f97644fdb..94543dec93 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/EnricherParser.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/EnricherParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 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. @@ -42,6 +42,7 @@ import org.springframework.util.xml.DomUtils; * @author Artem Bilan * @author Liujiong * @author Kris Jacyna + * @author Gary Russell * @since 2.1 */ public class EnricherParser extends AbstractConsumerEndpointParser { @@ -201,4 +202,9 @@ public class EnricherParser extends AbstractConsumerEndpointParser { return builder; } + @Override + protected boolean replyChannelInChainAllowed(Element element) { + return true; + } + } diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/EnricherParserTests5-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/EnricherParserTests5-context.xml index a6f75c7df6..72bf561dbb 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/EnricherParserTests5-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/EnricherParserTests5-context.xml @@ -11,16 +11,20 @@ + + - - - - + + + + + diff --git a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsOutboundGatewayParserTests.java b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsOutboundGatewayParserTests.java index ac37df71e7..bf36afd0f9 100644 --- a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsOutboundGatewayParserTests.java +++ b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsOutboundGatewayParserTests.java @@ -16,10 +16,13 @@ package org.springframework.integration.jms.config; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; @@ -249,6 +252,12 @@ public class JmsOutboundGatewayParserTests { String result = gateway.echo("hello"); verify(handler, times(1)).handleMessage(Mockito.any(Message.class)); assertEquals("hello", result); + JmsOutboundGateway gw1 = context.getBean("chain1$child.gateway.handler", JmsOutboundGateway.class); + MessageChannel out = TestUtils.getPropertyValue(gw1, "outputChannel", MessageChannel.class); + assertThat(out.getClass().getSimpleName(), equalTo("ReplyForwardingMessageChannel")); + JmsOutboundGateway gw2 = context.getBean("chain2$child.gateway.handler", JmsOutboundGateway.class); + out = TestUtils.getPropertyValue(gw2, "outputChannel", MessageChannel.class); + assertThat(out.getClass().getName(), containsString("MessageHandlerChain$")); context.close(); } diff --git a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/gatewayMaintainsReplyChannel.xml b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/gatewayMaintainsReplyChannel.xml index 3e5cae5a01..9f0af69626 100644 --- a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/gatewayMaintainsReplyChannel.xml +++ b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/gatewayMaintainsReplyChannel.xml @@ -6,7 +6,7 @@ http://www.springframework.org/schema/integration/jms http://www.springframework.org/schema/integration/jms/spring-integration-jms.xsd" xmlns:int="http://www.springframework.org/schema/integration" xmlns:int-jms="http://www.springframework.org/schema/integration/jms"> - + - + - - - - + + + + + + + + + + + @@ -37,7 +67,7 @@ - + @@ -48,11 +78,11 @@ - + - + - + diff --git a/src/reference/asciidoc/whats-new.adoc b/src/reference/asciidoc/whats-new.adoc index 95b3329a9c..29f9625004 100644 --- a/src/reference/asciidoc/whats-new.adoc +++ b/src/reference/asciidoc/whats-new.adoc @@ -13,3 +13,10 @@ development process. [[x4.3-general]] === General Changes +==== Outbound Gateway within Chain + +Previously, it was possible to specify a `reply-channel` on an outbound gateway within a chain. +It was completely ignored; the gateway's reply goes to the next chain element, or to the chain's output channel +if the gateway is the last element. +This condition is now detected and disallowed. +If you have such configuration, simply remove the `reply-channel`.