diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/AbstractConsumerEndpointParser.java b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/AbstractConsumerEndpointParser.java index 02dc0ebd10..b8b8d6a354 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/AbstractConsumerEndpointParser.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/AbstractConsumerEndpointParser.java @@ -16,21 +16,28 @@ package org.springframework.integration.config.xml; +import java.util.List; + import org.w3c.dom.Element; import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; 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.AbstractBeanDefinitionParser; +import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate; import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; import org.springframework.util.xml.DomUtils; /** * Base class parser for elements that create Message Endpoints. * * @author Mark Fisher + * @author Oleg Zhurakousky */ public abstract class AbstractConsumerEndpointParser extends AbstractBeanDefinitionParser { @@ -101,5 +108,25 @@ public abstract class AbstractConsumerEndpointParser extends AbstractBeanDefinit IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "auto-startup"); return builder.getBeanDefinition(); } + /** + * + * @return + */ + protected BeanDefinition parseInnerHandlerDefinition(Element element, ParserContext parserContext){ + // parses out inner bean definition for concrete implementation if defined + List childElements = DomUtils.getChildElementsByTagName(element, "bean"); + BeanDefinition innerDefinition = null; + if (childElements != null && childElements.size() == 1){ + Element beanElement = childElements.get(0); + BeanDefinitionParserDelegate delegate = parserContext.getDelegate(); + innerDefinition = delegate.parseBeanDefinitionElement(beanElement).getBeanDefinition(); + } + + String ref = element.getAttribute(REF_ATTRIBUTE); + Assert.isTrue(!(StringUtils.hasText(ref) && innerDefinition != null), "Ambiguous definition. Inner bean " + + (innerDefinition == null ? innerDefinition : innerDefinition.getBeanClassName()) + " declaration and \"ref\" " + ref + + " are not allowed together."); + return innerDefinition; + } } diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/AbstractInnerDefinitionAwareEndpointParser.java b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/AbstractInnerDefinitionAwareEndpointParser.java deleted file mode 100644 index e5a2d1052d..0000000000 --- a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/AbstractInnerDefinitionAwareEndpointParser.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2002-2009 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.config.xml; - -import java.util.List; - -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate; -import org.springframework.beans.factory.xml.ParserContext; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; -import org.springframework.util.xml.DomUtils; -import org.w3c.dom.Element; -/** - * Abstract Parser for any consumers that require capabilities to define - * its handler implementation as inner bean.
- * For example:
- *
- * <transformer id="testTransformer" input-channel="inChannel" output-channel="outChannel">
- *		<beans:bean class="org.bar.TestTransformer"/>
- * </transformer>
- * 
- * - * @author Oleg Zhurakousky - * @since 1.0.3 - */ -public abstract class AbstractInnerDefinitionAwareEndpointParser extends AbstractConsumerEndpointParser { - - @Override - protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) { - // parses out inner bean definition for concrete implementation if defined - List childElements = DomUtils.getChildElementsByTagName(element, "bean"); - BeanDefinition innerDefinition = null; - if (childElements != null && childElements.size() == 1){ - Element beanElement = childElements.get(0); - BeanDefinitionParserDelegate delegate = parserContext.getDelegate(); - innerDefinition = delegate.parseBeanDefinitionElement(beanElement).getBeanDefinition(); - } - - String ref = element.getAttribute(REF_ATTRIBUTE); - Assert.isTrue(!(StringUtils.hasText(ref) && innerDefinition != null), "Ambiguous definition. Inner bean " + - (innerDefinition == null ? innerDefinition : innerDefinition.getBeanClassName()) + " declaration and \"ref\" " + ref + - " are not allowed together."); - return this.parseEndpoint(element, parserContext, innerDefinition); - } - /** - * - * @param element - * @param parserContext - * @param innerDefinition - * @return - */ - protected abstract BeanDefinitionBuilder parseEndpoint(Element element, ParserContext parserContext, BeanDefinition innerDefinition); -} diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/DefaultRouterParser.java b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/DefaultRouterParser.java index 365b9664bc..584fe1a4e2 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/DefaultRouterParser.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/DefaultRouterParser.java @@ -18,6 +18,7 @@ package org.springframework.integration.config.xml; import org.w3c.dom.Element; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.xml.ParserContext; @@ -33,11 +34,17 @@ public class DefaultRouterParser extends AbstractRouterParser { @Override protected void parseRouter(Element element, BeanDefinitionBuilder builder, ParserContext parserContext) { - String ref = element.getAttribute(REF_ATTRIBUTE); - if (!StringUtils.hasText(ref)) { - parserContext.getReaderContext().error("The '" + REF_ATTRIBUTE + "' attribute is required.", element); + BeanDefinition innerDefinition = this.parseInnerHandlerDefinition(element, parserContext); + if (innerDefinition != null){ + builder.addPropertyValue("targetObject", innerDefinition); + } else { + String ref = element.getAttribute(REF_ATTRIBUTE); + if (!StringUtils.hasText(ref)) { + parserContext.getReaderContext().error("The '" + REF_ATTRIBUTE + "' attribute is required.", element); + } + builder.addPropertyReference("targetObject", ref); } - builder.addPropertyReference("targetObject", ref); + if (StringUtils.hasText(element.getAttribute(METHOD_ATTRIBUTE))) { String method = element.getAttribute(METHOD_ATTRIBUTE); builder.addPropertyValue("targetMethodName", method); diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/ServiceActivatorParser.java b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/ServiceActivatorParser.java index 87694bd05d..4511c27626 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/ServiceActivatorParser.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/ServiceActivatorParser.java @@ -18,6 +18,7 @@ package org.springframework.integration.config.xml; import org.w3c.dom.Element; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.util.StringUtils; @@ -26,19 +27,27 @@ import org.springframework.util.StringUtils; * Parser for the <service-activator> element. * * @author Mark Fisher + * @author Oleg Zhurakousky */ public class ServiceActivatorParser extends AbstractConsumerEndpointParser { @Override protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) { + BeanDefinition innerHandlerDefinition = this.parseInnerHandlerDefinition(element, parserContext); + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( IntegrationNamespaceUtils.BASE_PACKAGE + ".handler.ServiceActivatingHandler"); - String ref = element.getAttribute(REF_ATTRIBUTE); - if (!StringUtils.hasText(ref)) { - parserContext.getReaderContext().error("The '" + REF_ATTRIBUTE + "' attribute is required for element " - + IntegrationNamespaceUtils.createElementDescription(element) + ".", element); + if (innerHandlerDefinition != null){ + builder.addConstructorArgValue(innerHandlerDefinition); + } else { + String ref = element.getAttribute(REF_ATTRIBUTE); + if (!StringUtils.hasText(ref)) { + parserContext.getReaderContext().error("The '" + REF_ATTRIBUTE + "' attribute is required for element " + + IntegrationNamespaceUtils.createElementDescription(element) + ".", element); + } + builder.addConstructorArgReference(ref); } - builder.addConstructorArgReference(ref); + if (StringUtils.hasText(element.getAttribute(METHOD_ATTRIBUTE))) { String method = element.getAttribute(METHOD_ATTRIBUTE); builder.getRawBeanDefinition().getConstructorArgumentValues().addGenericArgumentValue(method, "java.lang.String"); diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/SplitterParser.java b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/SplitterParser.java index e6ee0d38cd..203516bdc0 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/SplitterParser.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/SplitterParser.java @@ -29,10 +29,11 @@ import org.springframework.util.StringUtils; * @author Mark Fisher * @author Oleg Zhurakousky */ -public class SplitterParser extends AbstractInnerDefinitionAwareEndpointParser { +public class SplitterParser extends AbstractConsumerEndpointParser { @Override - protected BeanDefinitionBuilder parseEndpoint(Element element, ParserContext parserContext, BeanDefinition innerDefinition) { + protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) { + BeanDefinition innerDefinition = this.parseInnerHandlerDefinition(element, parserContext); BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( IntegrationNamespaceUtils.BASE_PACKAGE + ".config.SplitterFactoryBean"); if (innerDefinition != null){ diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/TransformerParser.java b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/TransformerParser.java index cc98b85c7b..0de4af65cf 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/TransformerParser.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/TransformerParser.java @@ -28,10 +28,11 @@ import org.w3c.dom.Element; * @author Mark Fisher * @author Oleg Zhurakousky */ -public class TransformerParser extends AbstractInnerDefinitionAwareEndpointParser { +public class TransformerParser extends AbstractConsumerEndpointParser { @Override - protected BeanDefinitionBuilder parseEndpoint(Element element, ParserContext parserContext, BeanDefinition innerDefinition) { + protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) { + BeanDefinition innerDefinition = this.parseInnerHandlerDefinition(element, parserContext); BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( IntegrationNamespaceUtils.BASE_PACKAGE + ".config.TransformerFactoryBean"); diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/spring-integration-1.0.xsd b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/spring-integration-1.0.xsd index 63746bcdb2..d815694f17 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/spring-integration-1.0.xsd +++ b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/spring-integration-1.0.xsd @@ -354,7 +354,7 @@ default="true" /> - + Defines an endpoint for exposing any bean @@ -907,9 +907,13 @@ Spring-managed object as specified by the "ref" and "method" attributes. ]]> + + + + @@ -923,6 +927,7 @@ + @@ -1181,13 +1186,13 @@ - - - - - - - + + + + + + + - + \ No newline at end of file diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/config/xml/InnerDefinitionAwareEndpointParserTests.java b/org.springframework.integration/src/test/java/org/springframework/integration/config/xml/InnerDefinitionHandlerAwareEndpointParserTests.java similarity index 59% rename from org.springframework.integration/src/test/java/org/springframework/integration/config/xml/InnerDefinitionAwareEndpointParserTests.java rename to org.springframework.integration/src/test/java/org/springframework/integration/config/xml/InnerDefinitionHandlerAwareEndpointParserTests.java index d371aceb4d..324f91829d 100644 --- a/org.springframework.integration/src/test/java/org/springframework/integration/config/xml/InnerDefinitionAwareEndpointParserTests.java +++ b/org.springframework.integration/src/test/java/org/springframework/integration/config/xml/InnerDefinitionHandlerAwareEndpointParserTests.java @@ -44,7 +44,7 @@ import org.springframework.util.StringUtils; */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration -public class InnerDefinitionAwareEndpointParserTests { +public class InnerDefinitionHandlerAwareEndpointParserTests { @Autowired private Properties testConfigurations; @@ -90,6 +90,46 @@ public class InnerDefinitionAwareEndpointParserTests { reader.loadBeanDefinitions(new InputStreamResource(stream)); } + @Test + public void testInnerRouterDefinitionSuccess(){ + String configProperty = testConfigurations.getProperty("router-inner-success"); + this.testRouterDefinitionSuccess(configProperty); + } + @Test + public void testRefRouterDefinitionSuccess(){ + String configProperty = testConfigurations.getProperty("router-ref-success"); + this.testRouterDefinitionSuccess(configProperty); + } + + @Test(expected=BeanDefinitionStoreException.class) + public void testInnerRouterDefinitionFailureRefAndInner(){ + String xmlConfig = testConfigurations.getProperty("router-failure-refAndBean"); + ByteArrayInputStream stream = new ByteArrayInputStream(xmlConfig.getBytes()); + GenericApplicationContext ac = new GenericApplicationContext(); + XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(ac); + reader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_XSD); + reader.loadBeanDefinitions(new InputStreamResource(stream)); + } + + @Test + public void testInnerSADefinitionSuccess(){ + String configProperty = testConfigurations.getProperty("sa-inner-success"); + this.testSADefinitionSuccess(configProperty); + } + @Test + public void testRefSADefinitionSuccess(){ + String configProperty = testConfigurations.getProperty("sa-ref-success"); + this.testSADefinitionSuccess(configProperty); + } + @Test(expected=BeanDefinitionStoreException.class) + public void testInnerSADefinitionFailureRefAndInner(){ + String xmlConfig = testConfigurations.getProperty("sa-failure-refAndBean"); + ByteArrayInputStream stream = new ByteArrayInputStream(xmlConfig.getBytes()); + GenericApplicationContext ac = new GenericApplicationContext(); + XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(ac); + reader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_XSD); + reader.loadBeanDefinitions(new InputStreamResource(stream)); + } private void testSplitterDefinitionSuccess(String configProperty){ ByteArrayInputStream stream = new ByteArrayInputStream(configProperty.getBytes()); GenericApplicationContext ac = new GenericApplicationContext(); @@ -124,6 +164,41 @@ public class InnerDefinitionAwareEndpointParserTests { String payload = (String) outChannel.receive().getPayload(); Assert.assertTrue(payload.equals("One,Two")); } + private void testRouterDefinitionSuccess(String configProperty){ + ByteArrayInputStream stream = new ByteArrayInputStream(configProperty.getBytes()); + GenericApplicationContext ac = new GenericApplicationContext(); + XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(ac); + reader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_XSD); + reader.loadBeanDefinitions(new InputStreamResource(stream)); + EventDrivenConsumer splitter = (EventDrivenConsumer) ac.getBean("testRouter"); + Assert.assertNotNull(splitter); + MessageBuilder inChannelMessageBuilder = MessageBuilder.withPayload("1"); + Message inMessage = inChannelMessageBuilder.build(); + DirectChannel inChannel = (DirectChannel) ac.getBean("inChannel"); + inChannel.send(inMessage); + PollableChannel channel1 = (PollableChannel) ac.getBean("channel1"); + Assert.assertTrue(channel1.receive().getPayload().equals("1")); + inChannelMessageBuilder = MessageBuilder.withPayload("2"); + inMessage = inChannelMessageBuilder.build(); + inChannel.send(inMessage); + PollableChannel channel2 = (PollableChannel) ac.getBean("channel2"); + Assert.assertTrue(channel2.receive().getPayload().equals("2")); + } + private void testSADefinitionSuccess(String configProperty){ + ByteArrayInputStream stream = new ByteArrayInputStream(configProperty.getBytes()); + GenericApplicationContext ac = new GenericApplicationContext(); + XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(ac); + reader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_XSD); + reader.loadBeanDefinitions(new InputStreamResource(stream)); + EventDrivenConsumer splitter = (EventDrivenConsumer) ac.getBean("testServiceActivator"); + Assert.assertNotNull(splitter); + MessageBuilder inChannelMessageBuilder = MessageBuilder.withPayload("1"); + Message inMessage = inChannelMessageBuilder.build(); + DirectChannel inChannel = (DirectChannel) ac.getBean("inChannel"); + inChannel.send(inMessage); + PollableChannel channel1 = (PollableChannel) ac.getBean("outChannel"); + Assert.assertTrue(channel1.receive().getPayload().equals("1")); + } public static class TestSplitter{ public Collection split(String[] payload){ @@ -136,5 +211,16 @@ public class InnerDefinitionAwareEndpointParserTests { return StringUtils.arrayToDelimitedString(payload, ","); } } + + public static class TestRouter{ + public String route(String value) { + return (value.equals("1")) ? "channel1" : "channel2"; + } + } + public static class TestServiceActivator{ + public String foo(String value) { + return value; + } + } } diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/config/xml/innerdefaware.properties b/org.springframework.integration/src/test/java/org/springframework/integration/config/xml/innerdefaware.properties index 33009dd3ae..054f41f59a 100644 --- a/org.springframework.integration/src/test/java/org/springframework/integration/config/xml/innerdefaware.properties +++ b/org.springframework.integration/src/test/java/org/springframework/integration/config/xml/innerdefaware.properties @@ -93,4 +93,103 @@ transformer-failure-refAndBean=\ \ \ \ + +router-inner-success=\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + +router-ref-success=\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + +router-failure-refAndBean=\ + \ + \ + \ + \ + \ + \ + \ + +sa-inner-success=\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + +sa-ref-success=\ + \ + \ + \ + \ + \ + \ + \ + \ + +sa-failure-refAndBean=\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ \ No newline at end of file