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 new file mode 100644 index 0000000000..e5a2d1052d --- /dev/null +++ b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/AbstractInnerDefinitionAwareEndpointParser.java @@ -0,0 +1,68 @@ +/* + * 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/SplitterParser.java b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/SplitterParser.java index e545260727..e6ee0d38cd 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 @@ -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,14 +27,17 @@ import org.springframework.util.StringUtils; * Parser for the <splitter/> element. * * @author Mark Fisher + * @author Oleg Zhurakousky */ -public class SplitterParser extends AbstractConsumerEndpointParser { +public class SplitterParser extends AbstractInnerDefinitionAwareEndpointParser { @Override - protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) { + protected BeanDefinitionBuilder parseEndpoint(Element element, ParserContext parserContext, BeanDefinition innerDefinition) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( IntegrationNamespaceUtils.BASE_PACKAGE + ".config.SplitterFactoryBean"); - if (element.hasAttribute(REF_ATTRIBUTE)) { + if (innerDefinition != null){ + builder.addPropertyValue("targetObject", innerDefinition); + } else if (element.hasAttribute(REF_ATTRIBUTE)) { String ref = element.getAttribute(REF_ATTRIBUTE); builder.addPropertyReference("targetObject", ref); if (StringUtils.hasText(element.getAttribute(METHOD_ATTRIBUTE))) { 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 7758f98299..cc98b85c7b 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 @@ -16,34 +16,41 @@ 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; +import org.w3c.dom.Element; /** * Parser for the <transformer/> element. * * @author Mark Fisher + * @author Oleg Zhurakousky */ -public class TransformerParser extends AbstractConsumerEndpointParser { +public class TransformerParser extends AbstractInnerDefinitionAwareEndpointParser { @Override - protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) { + protected BeanDefinitionBuilder parseEndpoint(Element element, ParserContext parserContext, BeanDefinition innerDefinition) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( IntegrationNamespaceUtils.BASE_PACKAGE + ".config.TransformerFactoryBean"); - String ref = element.getAttribute(REF_ATTRIBUTE); - if (!StringUtils.hasText(ref)) { - parserContext.getReaderContext().error("The 'ref' attribute is required.", element); - return null; - } - builder.addPropertyReference("targetObject", ref); + + if (innerDefinition != null){ + builder.addPropertyValue("targetObject", innerDefinition); + } else { + String ref = element.getAttribute(REF_ATTRIBUTE); + if (!StringUtils.hasText(ref)) { + parserContext.getReaderContext().error("Either \"ref\" attribute or inner bean () definition of concrete implementation of " + + "this Transformer is required.", element); + return null; + } + builder.addPropertyReference("targetObject", ref); + } + String method = element.getAttribute(METHOD_ATTRIBUTE); if (StringUtils.hasText(method)) { builder.addPropertyValue("targetMethodName", method); } return builder; } - } 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 45705168a9..a7367127fa 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 @@ -82,7 +82,8 @@ specified, it will be a bounded queue. - A custom Queue implementation can be + A custom Queue implementation + can be injected using the 'ref' attribute. @@ -776,7 +777,7 @@ - + Defines a Transformer. @@ -843,12 +844,12 @@ - + - - - + + + @@ -873,12 +874,12 @@ - + - - - + + + @@ -900,12 +901,12 @@ - + - + @@ -924,14 +925,15 @@ - - - + + + - + Defines a Splitter. @@ -1010,7 +1012,8 @@ - + @@ -1164,4 +1167,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/config/xml/InnerDefinitionAwareEndpointParserTests-context.xml b/org.springframework.integration/src/test/java/org/springframework/integration/config/xml/InnerDefinitionAwareEndpointParserTests-context.xml new file mode 100644 index 0000000000..2ddb3cd25a --- /dev/null +++ b/org.springframework.integration/src/test/java/org/springframework/integration/config/xml/InnerDefinitionAwareEndpointParserTests-context.xml @@ -0,0 +1,12 @@ + + + + + + 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/InnerDefinitionAwareEndpointParserTests.java new file mode 100644 index 0000000000..d371aceb4d --- /dev/null +++ b/org.springframework.integration/src/test/java/org/springframework/integration/config/xml/InnerDefinitionAwareEndpointParserTests.java @@ -0,0 +1,140 @@ +/* + * 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.io.ByteArrayInputStream; +import java.util.Collection; +import java.util.Properties; + +import junit.framework.Assert; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.BeanDefinitionStoreException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.io.InputStreamResource; +import org.springframework.integration.channel.DirectChannel; +import org.springframework.integration.channel.PollableChannel; +import org.springframework.integration.core.Message; +import org.springframework.integration.endpoint.EventDrivenConsumer; +import org.springframework.integration.message.MessageBuilder; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; +/** + * + * @author Oleg Zhurakousky + * + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +public class InnerDefinitionAwareEndpointParserTests { + + @Autowired + private Properties testConfigurations; + + @Test + public void testInnerSplitterDefinitionSuccess(){ + String configProperty = testConfigurations.getProperty("splitter-inner-success"); + this.testSplitterDefinitionSuccess(configProperty); + } + @Test + public void testRefSplitterDefinitionSuccess(){ + String configProperty = testConfigurations.getProperty("splitter-ref-success"); + this.testSplitterDefinitionSuccess(configProperty); + } + @Test(expected=BeanDefinitionStoreException.class) + public void testInnerSplitterDefinitionFailureRefAndInner(){ + String xmlConfig = testConfigurations.getProperty("splitter-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 testInnerTransformerDefinitionSuccess(){ + String configProperty = testConfigurations.getProperty("transformer-inner-success"); + this.testTransformerDefinitionSuccess(configProperty); + } + @Test + public void testRefTransformerDefinitionSuccess(){ + String configProperty = testConfigurations.getProperty("transformer-ref-success"); + this.testTransformerDefinitionSuccess(configProperty); + } + + @Test(expected=BeanDefinitionStoreException.class) + public void testInnerTransformerDefinitionFailureRefAndInner(){ + String xmlConfig = testConfigurations.getProperty("transformer-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(); + XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(ac); + reader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_XSD); + reader.loadBeanDefinitions(new InputStreamResource(stream)); + EventDrivenConsumer splitter = (EventDrivenConsumer) ac.getBean("testSplitter"); + Assert.assertNotNull(splitter); + MessageBuilder inChannelMessageBuilder = MessageBuilder.withPayload(new String[]{"One","Two"}); + Message inMessage = inChannelMessageBuilder.build(); + DirectChannel inChannel = (DirectChannel) ac.getBean("inChannel"); + inChannel.send(inMessage); + PollableChannel outChannel = (PollableChannel) ac.getBean("outChannel"); + Assert.assertTrue(outChannel.receive().getPayload() instanceof String); + outChannel = (PollableChannel) ac.getBean("outChannel"); + Assert.assertTrue(outChannel.receive().getPayload() instanceof String); + } + + private void testTransformerDefinitionSuccess(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 transformer = (EventDrivenConsumer) ac.getBean("testTransformer"); + Assert.assertNotNull(transformer); + MessageBuilder inChannelMessageBuilder = MessageBuilder.withPayload(new String[]{"One","Two"}); + Message inMessage = inChannelMessageBuilder.build(); + DirectChannel inChannel = (DirectChannel) ac.getBean("inChannel"); + inChannel.send(inMessage); + PollableChannel outChannel = (PollableChannel) ac.getBean("outChannel"); + String payload = (String) outChannel.receive().getPayload(); + Assert.assertTrue(payload.equals("One,Two")); + } + + public static class TestSplitter{ + public Collection split(String[] payload){ + return CollectionUtils.arrayToList(payload); + } + } + + public static class TestTransformer{ + public String split(String[] payload){ + return StringUtils.arrayToDelimitedString(payload, ","); + } + } + +} 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 new file mode 100644 index 0000000000..33009dd3ae --- /dev/null +++ b/org.springframework.integration/src/test/java/org/springframework/integration/config/xml/innerdefaware.properties @@ -0,0 +1,96 @@ +splitter-inner-success=\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + +splitter-ref-success=\ + \ + \ + \ + \ + \ + \ + \ + \ + +splitter-failure-refAndBean=\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + +transformer-inner-success=\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + +transformer-ref-success=\ + \ + \ + \ + \ + \ + \ + \ + \ + +transformer-failure-refAndBean=\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ No newline at end of file