diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/FilterParser.java b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/FilterParser.java index c63c07a514..b636b85319 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/FilterParser.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/FilterParser.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; @@ -27,6 +28,7 @@ import org.springframework.util.StringUtils; * Parser for the <filter/> element. * * @author Mark Fisher + * @author Oleg Zhurakousky */ public class FilterParser extends AbstractConsumerEndpointParser { @@ -34,16 +36,24 @@ public class FilterParser extends AbstractConsumerEndpointParser { protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( IntegrationNamespaceUtils.BASE_PACKAGE + ".filter.MessageFilter"); - builder.addConstructorArgReference(this.parseSelector(element, parserContext)); + + builder.addConstructorArgReference((String) this.parseSelector(element, parserContext)); + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "discard-channel"); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "throw-exception-on-rejection"); return builder; } private String parseSelector(Element element, ParserContext parserContext) { - String ref = element.getAttribute("ref"); - if (!StringUtils.hasText(ref)) { - parserContext.getReaderContext().error("The 'ref' attribute is required.", element); + BeanDefinition innerHandlerDefinition = this.parseInnerHandlerDefinition(element, parserContext); + String ref = null; + if (innerHandlerDefinition == null){ + ref = element.getAttribute("ref"); + if (!StringUtils.hasText(ref)) { + parserContext.getReaderContext().error("Either \"ref\" attribute or inner bean () definition of concrete implementation of " + + "this MessageFilter is required.", element); + return null; + } } String method = element.getAttribute("method"); if (!StringUtils.hasText(method)) { @@ -51,10 +61,14 @@ public class FilterParser extends AbstractConsumerEndpointParser { } BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( IntegrationNamespaceUtils.BASE_PACKAGE + ".filter.MethodInvokingSelector"); - builder.addConstructorArgReference(ref); + if (innerHandlerDefinition != null){ + builder.addConstructorArgValue(innerHandlerDefinition); + } else { + builder.addConstructorArgReference(ref); + } + builder.getRawBeanDefinition().getConstructorArgumentValues().addGenericArgumentValue(method, "java.lang.String"); - return BeanDefinitionReaderUtils.registerWithGeneratedName( - builder.getBeanDefinition(), parserContext.getRegistry()); + return BeanDefinitionReaderUtils.registerWithGeneratedName(builder.getBeanDefinition(), parserContext.getRegistry()); } } 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 e4a4c39efa..2cee56b77c 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 @@ -843,7 +843,7 @@ - + diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/config/xml/InnerDefinitionHandlerAwareEndpointParserTests.java b/org.springframework.integration/src/test/java/org/springframework/integration/config/xml/InnerDefinitionHandlerAwareEndpointParserTests.java index 0ee28c71b1..34179c1e61 100644 --- a/org.springframework.integration/src/test/java/org/springframework/integration/config/xml/InnerDefinitionHandlerAwareEndpointParserTests.java +++ b/org.springframework.integration/src/test/java/org/springframework/integration/config/xml/InnerDefinitionHandlerAwareEndpointParserTests.java @@ -31,6 +31,7 @@ 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.ApplicationContext; import org.springframework.context.support.GenericApplicationContext; import org.springframework.core.io.InputStreamResource; import org.springframework.integration.channel.DirectChannel; @@ -41,6 +42,7 @@ import org.springframework.integration.core.MessageHeaders; import org.springframework.integration.endpoint.EventDrivenConsumer; import org.springframework.integration.message.GenericMessage; import org.springframework.integration.message.MessageBuilder; +import org.springframework.integration.message.StringMessage; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.util.CollectionUtils; @@ -48,7 +50,7 @@ import org.springframework.util.StringUtils; /** * * @author Oleg Zhurakousky - * + * */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration @@ -70,11 +72,7 @@ public class InnerDefinitionHandlerAwareEndpointParserTests { @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)); + this.bootStrap(xmlConfig); } @Test @@ -91,11 +89,7 @@ public class InnerDefinitionHandlerAwareEndpointParserTests { @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)); + this.bootStrap(xmlConfig); } @Test @@ -112,11 +106,7 @@ public class InnerDefinitionHandlerAwareEndpointParserTests { @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)); + this.bootStrap(xmlConfig); } @Test @@ -132,11 +122,7 @@ public class InnerDefinitionHandlerAwareEndpointParserTests { @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)); + this.bootStrap(xmlConfig); } @Test @@ -157,24 +143,34 @@ public class InnerDefinitionHandlerAwareEndpointParserTests { @Test(expected=BeanDefinitionStoreException.class) public void testInnerAggregatorDefinitionFailureRefAndInner(){ String xmlConfig = testConfigurations.getProperty("aggregator-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)); + this.bootStrap(xmlConfig); + } + + @Test + public void testInnerFilterDefinitionSuccess(){ + String configProperty = testConfigurations.getProperty("filter-inner-success"); + this.testFilterDefinitionSuccess(configProperty); + } + + @Test + public void testRefFilterDefinitionSuccess(){ + String configProperty = testConfigurations.getProperty("filter-ref-success"); + System.out.println(configProperty); + this.testFilterDefinitionSuccess(configProperty); + } + @Test(expected=BeanDefinitionStoreException.class) + public void testInnerFilterDefinitionFailureRefAndInner(){ + String xmlConfig = testConfigurations.getProperty("filter-failure-refAndBean"); + this.bootStrap(xmlConfig); } 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)); + ApplicationContext ac = this.bootStrap(configProperty); 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"); + MessageChannel inChannel = (MessageChannel) ac.getBean("inChannel"); inChannel.send(inMessage); PollableChannel outChannel = (PollableChannel) ac.getBean("outChannel"); Assert.assertTrue(outChannel.receive().getPayload() instanceof String); @@ -183,11 +179,7 @@ public class InnerDefinitionHandlerAwareEndpointParserTests { } 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)); + ApplicationContext ac = this.bootStrap(configProperty); EventDrivenConsumer transformer = (EventDrivenConsumer) ac.getBean("testTransformer"); Assert.assertNotNull(transformer); MessageBuilder inChannelMessageBuilder = MessageBuilder.withPayload(new String[]{"One","Two"}); @@ -199,11 +191,7 @@ public class InnerDefinitionHandlerAwareEndpointParserTests { 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)); + ApplicationContext ac = this.bootStrap(configProperty); EventDrivenConsumer splitter = (EventDrivenConsumer) ac.getBean("testRouter"); Assert.assertNotNull(splitter); MessageBuilder inChannelMessageBuilder = MessageBuilder.withPayload("1"); @@ -219,11 +207,7 @@ public class InnerDefinitionHandlerAwareEndpointParserTests { 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)); + ApplicationContext ac = this.bootStrap(configProperty); EventDrivenConsumer splitter = (EventDrivenConsumer) ac.getBean("testServiceActivator"); Assert.assertNotNull(splitter); MessageBuilder inChannelMessageBuilder = MessageBuilder.withPayload("1"); @@ -234,13 +218,7 @@ public class InnerDefinitionHandlerAwareEndpointParserTests { Assert.assertTrue(channel1.receive().getPayload().equals("1")); } private void testAggregatorDefinitionSuccess(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)); - ac.refresh(); - ac.start(); + ApplicationContext ac = this.bootStrap(configProperty); MessageChannel inChannel = (MessageChannel) ac.getBean("inChannel"); for (int i = 0; i < 5; i++) { Map headers = stubHeaders(i, 5, 1); @@ -249,9 +227,25 @@ public class InnerDefinitionHandlerAwareEndpointParserTests { } PollableChannel output = (PollableChannel) ac.getBean("outChannel"); assertEquals(0 + 1 + 2 + 3 + 4, output.receive().getPayload()); - System.out.println(); + } + private void testFilterDefinitionSuccess(String configProperty){ + ApplicationContext ac = this.bootStrap(configProperty); + MessageChannel input = (MessageChannel) ac.getBean("inChannel"); + PollableChannel output = (PollableChannel) ac.getBean("outChannel"); + input.send(new StringMessage("foo")); + Message reply = output.receive(0); + assertEquals("foo", reply.getPayload()); } + private ApplicationContext bootStrap(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)); + ac.refresh(); + return ac; + } private Map stubHeaders(int sequenceNumber, int sequenceSize, int correllationId) { Map headers = new HashMap(); headers.put(MessageHeaders.SEQUENCE_NUMBER, sequenceNumber); @@ -293,5 +287,11 @@ public class InnerDefinitionHandlerAwareEndpointParserTests { return result; } } + public static class TestMessageFilter{ + public boolean filter(String value) { + System.out.println(">>>> Filtering"); + return value.equals("foo"); + } + } } 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 1dee87ebbe..437eea0f3f 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 @@ -263,4 +263,51 @@ aggregator-failure-refAndBean=\ \ \ \ + +filter-inner-success=\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + +filter-ref-success=\ + \ + \ + \ + \ + \ + \ + \ + \ + +filter-failure-refAndBean=\ + \ + \ + \ + \ + \ + \ + \ + \ + \ No newline at end of file diff --git a/spring-integration-reference/src/filter.xml b/spring-integration-reference/src/filter.xml index 7968f6461f..1e17b8c6bf 100644 --- a/spring-integration-reference/src/filter.xml +++ b/spring-integration-reference/src/filter.xml @@ -52,6 +52,22 @@ alternative to the more proactive approach of using a Message Router with a single point-to-point input channel and multiple output channels. + + Using a "ref" attribute is generally recommended if the custom filter implementation can be reused in other + <filter> definitions. However if the custom filter implementation should be scoped to a + concrete definition of the <filter>, starting with v1.0.3, Spring Integration supports inner + bean definitions for custom filters within the <filter> element: + + +]]> + + + + Using both the "ref" attribute and an inner handler definition in the same <filter> configuration + is not allowed, as it creates an ambiguous condition and will result in Exception being thrown. + + + \ No newline at end of file