From b9ea75ea98cd10367961335731d85f61bdbaadc8 Mon Sep 17 00:00:00 2001 From: Jonas Partner Date: Thu, 17 Jul 2008 09:06:56 +0000 Subject: [PATCH] Namespace support for XPathRouter INT-300 --- .../IntegrationXmlNamespaceHandler.java | 1 + .../xml/config/XPathRouterParser.java | 86 +++++++++++++ .../xml/config/spring-integration-xml-1.0.xsd | 44 +++++-- .../AbstractXPathChannelNameResolver.java | 5 + .../router/XPathMultiChannelNameResolver.java | 30 +++-- .../XPathSingleChannelNameResolver.java | 18 +-- .../XPathMultiChannelNameResolverTests.java | 25 ++-- .../XPathSingleChannelNameResolverTests.java | 14 ++- .../xml/config/StubMarshaller.java | 5 + .../xml/config/StubUnmarshaller.java | 5 + .../config/XPathRouterParserTests-context.xml | 43 +++++++ .../xml/config/XPathRouterParserTests.java | 116 ++++++++++++++++++ ...tPayloadTransformerParserTests-context.xml | 18 +-- 13 files changed, 354 insertions(+), 56 deletions(-) create mode 100644 org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/config/XPathRouterParser.java create mode 100644 org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/config/XPathRouterParserTests-context.xml create mode 100644 org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/config/XPathRouterParserTests.java diff --git a/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/config/IntegrationXmlNamespaceHandler.java b/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/config/IntegrationXmlNamespaceHandler.java index c886582979..377d1b87d1 100644 --- a/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/config/IntegrationXmlNamespaceHandler.java +++ b/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/config/IntegrationXmlNamespaceHandler.java @@ -29,6 +29,7 @@ public class IntegrationXmlNamespaceHandler extends NamespaceHandlerSupport { registerBeanDefinitionParser("marshalling-transformer", new XmlMarshallingTransformerParser()); registerBeanDefinitionParser("unmarshalling-transformer", new XmlUnmarshallingTransformerParser()); registerBeanDefinitionParser("xslt-transformer", new XsltPayloadTransformerParser()); + registerBeanDefinitionParser("xpath-router", new XPathRouterParser()); } } diff --git a/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/config/XPathRouterParser.java b/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/config/XPathRouterParser.java new file mode 100644 index 0000000000..7c116b7dd8 --- /dev/null +++ b/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/config/XPathRouterParser.java @@ -0,0 +1,86 @@ +/* + * Copyright 2002-2007 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.xml.config; + +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.ConfigurationException; +import org.springframework.integration.router.MultiChannelRouter; +import org.springframework.integration.router.SingleChannelRouter; +import org.springframework.integration.xml.router.XPathMultiChannelNameResolver; +import org.springframework.integration.xml.router.XPathSingleChannelNameResolver; +import org.springframework.util.StringUtils; +import org.springframework.xml.xpath.XPathExpression; +import org.springframework.xml.xpath.XPathExpressionFactory; +import org.w3c.dom.Element; + +/** + * + * @author Jonas Partner + * + */ +public class XPathRouterParser extends AbstractSingleBeanDefinitionParser { + + @Override + protected boolean shouldGenerateId() { + return false; + } + + @Override + protected boolean shouldGenerateIdAsFallback() { + return true; + } + + @Override + protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + + boolean multiChannel = Boolean.parseBoolean(element.getAttribute("multi-channel")); + boolean resolutionRequired = Boolean.parseBoolean(element.getAttribute("resolution-required")); + String xPathExpression = element.getAttribute("xpath-expression"); + String xPathExpressionRef = element.getAttribute("xpath-expression-ref"); + if ((StringUtils.hasText(xPathExpression) && StringUtils.hasText(xPathExpressionRef)) + || (!StringUtils.hasText(xPathExpression) && !StringUtils.hasText(xPathExpressionRef))) { + throw new ConfigurationException("Exactl one of xpath-expression or xpath-expression-ref is required"); + } + + BeanDefinitionBuilder resolverDefinitionBuilder = null; + if (multiChannel) { + builder.getBeanDefinition().setBeanClass(MultiChannelRouter.class); + resolverDefinitionBuilder = BeanDefinitionBuilder + .genericBeanDefinition(XPathMultiChannelNameResolver.class); + } + else { + builder.getBeanDefinition().setBeanClass(SingleChannelRouter.class); + resolverDefinitionBuilder = BeanDefinitionBuilder + .genericBeanDefinition(XPathSingleChannelNameResolver.class); + } + + if (StringUtils.hasText(xPathExpression)) { + XPathExpression expression = XPathExpressionFactory.createXPathExpression(xPathExpression); + resolverDefinitionBuilder.addConstructorArgValue(expression); + } + else { + resolverDefinitionBuilder.addConstructorArgReference(xPathExpressionRef); + } + + builder.getBeanDefinition().getPropertyValues().addPropertyValue("resolutionRequired", resolutionRequired); + builder.getBeanDefinition().getPropertyValues().addPropertyValue("channelNameResolver", + resolverDefinitionBuilder.getBeanDefinition()); + } + +} diff --git a/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/config/spring-integration-xml-1.0.xsd b/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/config/spring-integration-xml-1.0.xsd index 41079bcf8b..1014a9d517 100644 --- a/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/config/spring-integration-xml-1.0.xsd +++ b/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/config/spring-integration-xml-1.0.xsd @@ -38,7 +38,7 @@ - + @@ -51,22 +51,46 @@ use="required" /> - - - + + + - Defines a XML unmarshalling transformer. + Defines a XML XSLT transformer. - - - - + + + + - + + + + + + + Defines a XPath router. + + + + + + + + + + \ No newline at end of file diff --git a/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/router/AbstractXPathChannelNameResolver.java b/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/router/AbstractXPathChannelNameResolver.java index 94fd986c96..4d5ac85121 100644 --- a/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/router/AbstractXPathChannelNameResolver.java +++ b/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/router/AbstractXPathChannelNameResolver.java @@ -20,6 +20,11 @@ import org.springframework.integration.message.Message; import org.springframework.integration.message.MessagingException; import org.w3c.dom.Node; +/** + * + * @author Jonas Partner + * + */ public class AbstractXPathChannelNameResolver { protected Node extractNode(Message message) { diff --git a/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/router/XPathMultiChannelNameResolver.java b/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/router/XPathMultiChannelNameResolver.java index 3f89de077e..3196b481f6 100644 --- a/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/router/XPathMultiChannelNameResolver.java +++ b/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/router/XPathMultiChannelNameResolver.java @@ -26,36 +26,40 @@ import org.springframework.xml.xpath.XPathExpression; import org.w3c.dom.DOMException; import org.w3c.dom.Node; +/** + * + * @author Jonas Partner + * + */ public class XPathMultiChannelNameResolver extends AbstractXPathChannelNameResolver implements MultiChannelNameResolver { private final XPathExpression xPathExpression; - + private NodeMapper nodeMapper = new TextContentNodeMapper(); - - public XPathMultiChannelNameResolver(XPathExpression xPathExpression){ + + public XPathMultiChannelNameResolver(XPathExpression xPathExpression) { Assert.notNull("XPAthExpression must be provided"); this.xPathExpression = xPathExpression; } - - public void setNodeMapper(NodeMapper nodeMapper){ - Assert.notNull(nodeMapper,"NodeMapper can not be null"); + + public void setNodeMapper(NodeMapper nodeMapper) { + Assert.notNull(nodeMapper, "NodeMapper can not be null"); this.nodeMapper = nodeMapper; } - + @SuppressWarnings("unchecked") public String[] resolve(Message message) { - Node node =extractNode(message); + Node node = extractNode(message); List channelNamesList = xPathExpression.evaluate(node, nodeMapper); - return (String[])channelNamesList.toArray(new String[channelNamesList.size()]); + return (String[]) channelNamesList.toArray(new String[channelNamesList.size()]); } - - private static class TextContentNodeMapper implements NodeMapper{ + + private static class TextContentNodeMapper implements NodeMapper { public Object mapNode(Node node, int nodeNum) throws DOMException { return node.getTextContent(); } - + } - } diff --git a/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/router/XPathSingleChannelNameResolver.java b/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/router/XPathSingleChannelNameResolver.java index 58eefb6f0c..29c449e703 100644 --- a/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/router/XPathSingleChannelNameResolver.java +++ b/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/router/XPathSingleChannelNameResolver.java @@ -17,27 +17,29 @@ package org.springframework.integration.xml.router; import org.springframework.integration.message.Message; -import org.springframework.integration.message.MessagingException; import org.springframework.integration.router.ChannelNameResolver; import org.springframework.util.Assert; import org.springframework.xml.xpath.XPathExpression; import org.w3c.dom.Node; +/** + * + * @author Jonas Partner + * + */ public class XPathSingleChannelNameResolver extends AbstractXPathChannelNameResolver implements ChannelNameResolver { private final XPathExpression xPathExpression; - - - public XPathSingleChannelNameResolver(XPathExpression xPathExpression){ - Assert.notNull("XPAthExpression must be provided"); + + public XPathSingleChannelNameResolver(XPathExpression xPathExpression) { + Assert.notNull("XPathExpression must be provided"); this.xPathExpression = xPathExpression; } - public String resolve(Message message) { Node node = extractNode(message); + System.out.println(xPathExpression.evaluateAsString(node)); return xPathExpression.evaluateAsString(node); } - - + } diff --git a/org.springframework.integration.xml/src/test/java/org/springframework/integration/router/XPathMultiChannelNameResolverTests.java b/org.springframework.integration.xml/src/test/java/org/springframework/integration/router/XPathMultiChannelNameResolverTests.java index 1a59697442..220e1a62bc 100644 --- a/org.springframework.integration.xml/src/test/java/org/springframework/integration/router/XPathMultiChannelNameResolverTests.java +++ b/org.springframework.integration.xml/src/test/java/org/springframework/integration/router/XPathMultiChannelNameResolverTests.java @@ -28,38 +28,41 @@ import org.springframework.xml.xpath.XPathExpression; import org.springframework.xml.xpath.XPathExpressionFactory; import org.w3c.dom.Document; +/** + * + * @author Jonas Partner + * + */ public class XPathMultiChannelNameResolverTests { + @SuppressWarnings("unchecked") @Test public void testSimpleSingleeAttribute() throws Exception { Document doc = XmlTestUtil.getDocumentForString(""); XPathExpression expression = XPathExpressionFactory.createXPathExpression("/doc/@type"); XPathMultiChannelNameResolver resolver = new XPathMultiChannelNameResolver(expression); String[] channelNames = resolver.resolve(new GenericMessage(doc)); - assertEquals("Wrong number of channels returend",1,channelNames.length); - assertEquals("Wrong channel name","one",channelNames[0]); + assertEquals("Wrong number of channels returend", 1, channelNames.length); + assertEquals("Wrong channel name", "one", channelNames[0]); } - + + @SuppressWarnings("unchecked") @Test public void testMultipleNodeValues() throws Exception { Document doc = XmlTestUtil.getDocumentForString("bOnebTwo"); XPathExpression expression = XPathExpressionFactory.createXPathExpression("/doc/book"); XPathMultiChannelNameResolver resolver = new XPathMultiChannelNameResolver(expression); String[] channelNames = resolver.resolve(new GenericMessage(doc)); - assertEquals("Wrong number of channels returend",2,channelNames.length); - assertEquals("Wrong channel name","bOne",channelNames[0]); - assertEquals("Wrong channel name","bTwo",channelNames[1]); + assertEquals("Wrong number of channels returend", 2, channelNames.length); + assertEquals("Wrong channel name", "bOne", channelNames[0]); + assertEquals("Wrong channel name", "bTwo", channelNames[1]); } - - @Test(expected=MessagingException.class) + @Test(expected = MessagingException.class) public void testNonNodePayload() throws Exception { XPathExpression expression = XPathExpressionFactory.createXPathExpression("/doc/@type"); XPathMultiChannelNameResolver resolver = new XPathMultiChannelNameResolver(expression); resolver.resolve(new StringMessage("test")); } - - - } diff --git a/org.springframework.integration.xml/src/test/java/org/springframework/integration/router/XPathSingleChannelNameResolverTests.java b/org.springframework.integration.xml/src/test/java/org/springframework/integration/router/XPathSingleChannelNameResolverTests.java index fffd7b9d23..f121d9aaf7 100644 --- a/org.springframework.integration.xml/src/test/java/org/springframework/integration/router/XPathSingleChannelNameResolverTests.java +++ b/org.springframework.integration.xml/src/test/java/org/springframework/integration/router/XPathSingleChannelNameResolverTests.java @@ -28,26 +28,28 @@ import org.springframework.xml.xpath.XPathExpression; import org.springframework.xml.xpath.XPathExpressionFactory; import org.w3c.dom.Document; +/** + * + * @author Jonas Partner + * + */ public class XPathSingleChannelNameResolverTests { + @SuppressWarnings("unchecked") @Test public void testSimpleDocType() throws Exception { Document doc = XmlTestUtil.getDocumentForString(""); XPathExpression expression = XPathExpressionFactory.createXPathExpression("/doc/@type"); XPathSingleChannelNameResolver resolver = new XPathSingleChannelNameResolver(expression); String channelName = resolver.resolve(new GenericMessage(doc)); - assertEquals("Wrong channel name","one",channelName); + assertEquals("Wrong channel name", "one", channelName); } - - @Test(expected=MessagingException.class) + @Test(expected = MessagingException.class) public void testNonNodePayload() throws Exception { XPathExpression expression = XPathExpressionFactory.createXPathExpression("/doc/@type"); XPathSingleChannelNameResolver resolver = new XPathSingleChannelNameResolver(expression); resolver.resolve(new StringMessage("test")); } - - - } diff --git a/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/config/StubMarshaller.java b/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/config/StubMarshaller.java index f6ca049357..580a812c0d 100644 --- a/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/config/StubMarshaller.java +++ b/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/config/StubMarshaller.java @@ -26,6 +26,11 @@ import org.springframework.oxm.Marshaller; import org.springframework.oxm.XmlMappingException; import org.springframework.xml.transform.StringSource; +/** + * + * @author Jonas Partner + * + */ public class StubMarshaller implements Marshaller { public void marshal(Object graph, Result result) throws XmlMappingException, IOException { diff --git a/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/config/StubUnmarshaller.java b/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/config/StubUnmarshaller.java index a51c728280..9a5ee85d1f 100644 --- a/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/config/StubUnmarshaller.java +++ b/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/config/StubUnmarshaller.java @@ -24,6 +24,11 @@ import javax.xml.transform.Source; import org.springframework.oxm.Unmarshaller; import org.springframework.oxm.XmlMappingException; +/** + * + * @author Jonas Partner + * + */ public class StubUnmarshaller implements Unmarshaller { public LinkedList sourcesPassed = new LinkedList(); diff --git a/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/config/XPathRouterParserTests-context.xml b/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/config/XPathRouterParserTests-context.xml new file mode 100644 index 0000000000..a85aacf7bb --- /dev/null +++ b/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/config/XPathRouterParserTests-context.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/config/XPathRouterParserTests.java b/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/config/XPathRouterParserTests.java new file mode 100644 index 0000000000..e2c9bd2faf --- /dev/null +++ b/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/config/XPathRouterParserTests.java @@ -0,0 +1,116 @@ +/* + * Copyright 2002-2007 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.xml.config; + +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.integration.channel.MessageChannel; +import org.springframework.integration.message.GenericMessage; +import org.springframework.integration.xml.util.XmlTestUtil; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests; +import org.w3c.dom.Document; + + +@ContextConfiguration +public class XPathRouterParserTests extends AbstractJUnit4SpringContextTests{ + + @Autowired @Qualifier("inputOne") + MessageChannel inputOne; + + @Autowired @Qualifier("inputTwo") + MessageChannel inputTwo; + + + @Autowired @Qualifier("outputOne") + MessageChannel outputOne; + + + @Autowired @Qualifier("outputTwo") + MessageChannel outputTwo; + + @Autowired @Qualifier("errors") + MessageChannel errors; + + + + + @SuppressWarnings("unchecked") + @Test + public void testOutputOne() throws Exception{ + Document doc =XmlTestUtil.getDocumentForString("outputOne"); + GenericMessage docMessage = new GenericMessage(doc); + inputOne.send(docMessage); + GenericMessage received = (GenericMessage)outputOne.receive(1000); + assertNotNull("Did not recevie message from outputOne", received); + } + + @SuppressWarnings("unchecked") + @Test + public void testOutputTwo() throws Exception{ + Document doc =XmlTestUtil.getDocumentForString("outputTwo"); + GenericMessage docMessage = new GenericMessage(doc); + inputOne.send(docMessage); + GenericMessage received = (GenericMessage)outputTwo.receive(1000); + assertNotNull("Did not recevie message from two", received); + } + @SuppressWarnings("unchecked") + @Test + public void testOutputThree() throws Exception{ + Document doc =XmlTestUtil.getDocumentForString("outputThree"); + GenericMessage docMessage = new GenericMessage(doc); + inputOne.send(docMessage); + GenericMessage received = (GenericMessage)errors.receive(1000); + assertNotNull("Did not recevie message on errors", received); + + } + + @SuppressWarnings("unchecked") + @Test + public void testOutputOneMulti() throws Exception{ + Document doc =XmlTestUtil.getDocumentForString("outputOne"); + GenericMessage docMessage = new GenericMessage(doc); + inputTwo.send(docMessage); + GenericMessage received = (GenericMessage)outputOne.receive(1000); + assertNotNull("Did not recevie message from outputOne", received); + } + + @SuppressWarnings("unchecked") + @Test + public void testOutputOneAndTwoMulti() throws Exception{ + Document doc =XmlTestUtil.getDocumentForString("outputOneoutputTwo"); + GenericMessage docMessage = new GenericMessage(doc); + inputTwo.send(docMessage); + GenericMessage received = (GenericMessage)outputTwo.receive(1000); + assertNotNull("Did not recevie message from two", received); + } + @SuppressWarnings("unchecked") + @Test + public void testOutputThreeMulti() throws Exception{ + Document doc =XmlTestUtil.getDocumentForString("outputThree"); + GenericMessage docMessage = new GenericMessage(doc); + inputTwo.send(docMessage); + GenericMessage received = (GenericMessage) errors.receive(1000); + assertNotNull("Did not recevie message on errors", received); + + } + + +} diff --git a/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/config/XsltPayloadTransformerParserTests-context.xml b/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/config/XsltPayloadTransformerParserTests-context.xml index 925c482e44..35c5d2968c 100644 --- a/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/config/XsltPayloadTransformerParserTests-context.xml +++ b/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/config/XsltPayloadTransformerParserTests-context.xml @@ -5,12 +5,14 @@ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/integration-xml http://www.springframework.org/schema/integration/spring-integration-xml-1.0.xsd"> - - - - - - - + + + + + + +