diff --git a/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/DefaultXmlPayloadConverter.java b/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/DefaultXmlPayloadConverter.java index ac114f8e76..994ab13537 100644 --- a/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/DefaultXmlPayloadConverter.java +++ b/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/DefaultXmlPayloadConverter.java @@ -23,6 +23,7 @@ import javax.xml.parsers.ParserConfigurationException; import org.springframework.integration.message.MessagingException; import org.w3c.dom.Document; +import org.w3c.dom.Node; import org.xml.sax.InputSource; /** @@ -62,6 +63,17 @@ public class DefaultXmlPayloadConverter implements XmlPayloadConverter { return doc; } + public Node convertToNode(Object object) { + Node node; + if(object instanceof Node){ + node = (Node)object; + } else { + node = convertToDocument(object); + } + return node; + } + + protected synchronized DocumentBuilder getDocumentBuilder() { try { return this.documentBuilderFactory.newDocumentBuilder(); @@ -71,4 +83,5 @@ public class DefaultXmlPayloadConverter implements XmlPayloadConverter { } } + } diff --git a/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/XmlPayloadConverter.java b/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/XmlPayloadConverter.java index 690fc748da..018e066aaf 100644 --- a/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/XmlPayloadConverter.java +++ b/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/XmlPayloadConverter.java @@ -16,6 +16,7 @@ package org.springframework.integration.xml; import org.w3c.dom.Document; +import org.w3c.dom.Node; /** @@ -26,5 +27,7 @@ import org.w3c.dom.Document; public interface XmlPayloadConverter { public Document convertToDocument(Object object); + + public Node convertToNode(Object object); } 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 new file mode 100644 index 0000000000..a240057d43 --- /dev/null +++ b/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/router/AbstractXPathChannelNameResolver.java @@ -0,0 +1,97 @@ +/* + * 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.router; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.integration.router.AbstractChannelNameResolver; +import org.springframework.integration.router.ChannelResolver; +import org.springframework.integration.xml.DefaultXmlPayloadConverter; +import org.springframework.integration.xml.XmlPayloadConverter; +import org.springframework.xml.xpath.XPathExpression; +import org.springframework.xml.xpath.XPathExpressionFactory; + +/** + * Abstract base class for {@link ChannelResolver} classes that use + * {@link XPathExpression} evaluation to determine channel names + * @author Jonas Partner + * + */ +public abstract class AbstractXPathChannelNameResolver extends AbstractChannelNameResolver { + + private final XPathExpression xPathExpression; + + private volatile XmlPayloadConverter converter = new DefaultXmlPayloadConverter(); + + /** + * Creates an channel name resolver using an XPath expression which may + * contain zero or more namespace prefixes + * @param pathExpression + * @param namespaces + */ + public AbstractXPathChannelNameResolver(String pathExpression, Map namespaces) { + this.xPathExpression = XPathExpressionFactory.createXPathExpression(pathExpression, namespaces); + } + + /** + * Create a channel name resolver using an XPath expression with one + * namespace. For example expression '/ns1:one/@type' prefix 'ns1' namespace + * 'www.example.org' + * @param pathExpression + * @param prefix + * @param namespace + */ + public AbstractXPathChannelNameResolver(String pathExpression, String prefix, String namespace) { + Map namespaces = new HashMap(); + namespaces.put(prefix, namespace); + this.xPathExpression = XPathExpressionFactory.createXPathExpression(pathExpression, namespaces); + } + + /** + * Creates a channel name resolver using an XPath expression with no + * namespaces For example '/one/@type' + * @param pathExpression + */ + public AbstractXPathChannelNameResolver(String pathExpression) { + this.xPathExpression = XPathExpressionFactory.createXPathExpression(pathExpression); + } + + /** + * Creates a channel name resolver using the provided XPath expression + * @param pathExpression + */ + public AbstractXPathChannelNameResolver(XPathExpression pathExpression) { + this.xPathExpression = pathExpression; + } + + protected XmlPayloadConverter getConverter() { + return converter; + } + + /** + * Converter used to convert payloads prior to XPAth testing + * @param converter + */ + public void setConverter(XmlPayloadConverter converter) { + this.converter = converter; + } + + protected XPathExpression getXPathExpresion() { + return xPathExpression; + } + +} 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 10cf167a23..066588c3b0 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 @@ -17,39 +17,56 @@ package org.springframework.integration.xml.router; import java.util.List; - -import org.w3c.dom.DOMException; -import org.w3c.dom.Node; +import java.util.Map; import org.springframework.integration.message.Message; -import org.springframework.integration.router.AbstractChannelNameResolver; -import org.springframework.integration.xml.DefaultXmlPayloadConverter; import org.springframework.integration.xml.XmlPayloadConverter; import org.springframework.util.Assert; import org.springframework.xml.xpath.NodeMapper; import org.springframework.xml.xpath.XPathExpression; +import org.w3c.dom.DOMException; +import org.w3c.dom.Node; /** + * Evaluates the XPath expression using + * {@link XPathExpression#evaluateAsNodeList(Node)} which returns zero or more + * nodes in conjunction with an instance of {@link NodeMapper} to produce zero + * or more channel names. An instance of {@link XmlPayloadConverter} is used to + * extract the payload as a {@link Node}. * @author Jonas Partner */ -public class XPathMultiChannelNameResolver extends AbstractChannelNameResolver { - - private final XPathExpression xPathExpression; - - private volatile XmlPayloadConverter payloadConvertor = new DefaultXmlPayloadConverter(); +public class XPathMultiChannelNameResolver extends AbstractXPathChannelNameResolver { private volatile NodeMapper nodeMapper = new TextContentNodeMapper(); - - public XPathMultiChannelNameResolver(XPathExpression xPathExpression) { - Assert.notNull("XPathExpression must not be null"); - this.xPathExpression = xPathExpression; + /** + * @see AbstractXPathChannelNameResolver#AbstractXPathChannelNameResolver(String, + * Map) + */ + public XPathMultiChannelNameResolver(String pathExpression, Map namespaces) { + super(pathExpression, namespaces); } + /** + * @see AbstractXPathChannelNameResolver#AbstractXPathChannelNameResolver(String, + * String, String) + */ + public XPathMultiChannelNameResolver(String pathExpression, String prefix, String namespace) { + super(pathExpression, prefix, namespace); + } - public void setPayloadConvertor(XmlPayloadConverter payloadConvertor) { - Assert.notNull(payloadConvertor, "XmlPayloadConverter must not be null"); - this.payloadConvertor = payloadConvertor; + /** + * @see AbstractXPathChannelNameResolver#AbstractXPathChannelNameResolver(String) + */ + public XPathMultiChannelNameResolver(String pathExpression) { + super(pathExpression); + } + + /** + * @see AbstractXPathChannelNameResolver#AbstractXPathChannelNameResolver(XPathExpression) + */ + public XPathMultiChannelNameResolver(XPathExpression pathExpression) { + super(pathExpression); } public void setNodeMapper(NodeMapper nodeMapper) { @@ -59,12 +76,11 @@ public class XPathMultiChannelNameResolver extends AbstractChannelNameResolver { @SuppressWarnings("unchecked") public String[] resolveChannelNames(Message message) { - Node node = payloadConvertor.convertToDocument(message.getPayload()); - List channelNamesList = this.xPathExpression.evaluate(node, this.nodeMapper); + Node node = getConverter().convertToNode(message.getPayload()); + List channelNamesList = getXPathExpresion().evaluate(node, this.nodeMapper); return (String[]) channelNamesList.toArray(new String[channelNamesList.size()]); } - private static class TextContentNodeMapper implements NodeMapper { public Object mapNode(Node node, int nodeNum) throws DOMException { 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 0e864e5743..4e43bfe1ce 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 @@ -16,35 +16,56 @@ package org.springframework.integration.xml.router; +import java.util.Map; + import org.springframework.integration.message.Message; -import org.springframework.integration.router.AbstractSingleChannelNameResolver; import org.springframework.integration.xml.DefaultXmlPayloadConverter; import org.springframework.integration.xml.XmlPayloadConverter; -import org.springframework.util.Assert; import org.springframework.xml.xpath.XPathExpression; import org.w3c.dom.Node; /** + * Evaluates the payload using {@link XPathExpression#evaluateAsString(Node)} to + * extract a channel name. The payload is extracted as a node using the provided + * {@link XmlPayloadConverter} with {@link DefaultXmlPayloadConverter} being the + * default * @author Jonas Partner */ -public class XPathSingleChannelNameResolver extends AbstractSingleChannelNameResolver { +public class XPathSingleChannelNameResolver extends AbstractXPathChannelNameResolver { - private final XPathExpression xPathExpression; - - private volatile XmlPayloadConverter payloadConvertor = new DefaultXmlPayloadConverter(); - - public XPathSingleChannelNameResolver(XPathExpression xPathExpression) { - Assert.notNull("XPathExpression must be provided"); - this.xPathExpression = xPathExpression; + /** + * @see AbstractXPathChannelNameResolver#AbstractXPathChannelNameResolver(String, + * Map) + */ + public XPathSingleChannelNameResolver(String pathExpression, Map namespaces) { + super(pathExpression, namespaces); } - public String resolveChannelName(Message message) { - Node node = payloadConvertor.convertToDocument(message.getPayload()); - return xPathExpression.evaluateAsString(node); + /** + * @see AbstractXPathChannelNameResolver#AbstractXPathChannelNameResolver(String, + * String, String) + */ + public XPathSingleChannelNameResolver(String pathExpression, String prefix, String namespace) { + super(pathExpression, prefix, namespace); } - public void setPayloadConvertor(XmlPayloadConverter payloadConvertor) { - this.payloadConvertor = payloadConvertor; + /** + * @see AbstractXPathChannelNameResolver#AbstractXPathChannelNameResolver(String) + */ + public XPathSingleChannelNameResolver(String pathExpression) { + super(pathExpression); + } + + /** + * @see AbstractXPathChannelNameResolver#AbstractXPathChannelNameResolver(XPathExpression) + */ + public XPathSingleChannelNameResolver(XPathExpression pathExpression) { + super(pathExpression); + } + + public String[] resolveChannelNames(Message message) { + Node node = getConverter().convertToNode(message.getPayload()); + return new String[] { getXPathExpresion().evaluateAsString(node) }; } } diff --git a/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/selector/BooleanTestXPathMessageSelector.java b/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/selector/BooleanTestXPathMessageSelector.java index d6f970aa7d..1a9abb28ee 100644 --- a/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/selector/BooleanTestXPathMessageSelector.java +++ b/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/selector/BooleanTestXPathMessageSelector.java @@ -21,6 +21,7 @@ import org.springframework.integration.message.Message; import org.springframework.integration.message.selector.MessageSelector; import org.springframework.xml.xpath.XPathExpression; import org.w3c.dom.Document; +import org.w3c.dom.Node; /** * Boolean XPath testing {@link MessageSelector}. Requires an XPathExpression @@ -74,7 +75,7 @@ public class BooleanTestXPathMessageSelector extends AbstractXPathMessageSelecto * return true if the {@link XPathExpression} evaluates to true */ public boolean accept(Message message) { - Document doc = getConverter().convertToDocument(message.getPayload()); - return getXPathExpresion().evaluateAsBoolean(doc); + Node node = getConverter().convertToNode(message.getPayload()); + return getXPathExpresion().evaluateAsBoolean(node); } } diff --git a/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/selector/StringValueTestXPathMessageSelector.java b/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/selector/StringValueTestXPathMessageSelector.java index 85e4bd4237..44831944aa 100644 --- a/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/selector/StringValueTestXPathMessageSelector.java +++ b/org.springframework.integration.xml/src/main/java/org/springframework/integration/xml/selector/StringValueTestXPathMessageSelector.java @@ -21,6 +21,7 @@ import org.springframework.integration.message.Message; import org.springframework.integration.message.selector.MessageSelector; import org.springframework.xml.xpath.XPathExpression; import org.w3c.dom.Document; +import org.w3c.dom.Node; /** * XPath {@link MessageSelector} which tests for a provided value Supports @@ -86,13 +87,13 @@ public class StringValueTestXPathMessageSelector extends AbstractXPathMessageSel * {@link XPathExpression} is equal to the valueToTestFor */ public boolean accept(Message message) { + Node nodeToTest = getConverter().convertToNode(message.getPayload()); + String xPathResult = getXPathExpresion().evaluateAsString(nodeToTest); if (caseSensitive) { - return valueToTestFor.equals(getXPathExpresion().evaluateAsString( - getConverter().convertToDocument(message.getPayload()))); + return valueToTestFor.equals(xPathResult); } else { - return valueToTestFor.equalsIgnoreCase(getXPathExpresion().evaluateAsString( - getConverter().convertToDocument(message.getPayload()))); + return valueToTestFor.equalsIgnoreCase(xPathResult); } } diff --git a/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/DefaultXmlPayloadConverterTests.java b/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/DefaultXmlPayloadConverterTests.java index 682e4babcb..4aefc87563 100644 --- a/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/DefaultXmlPayloadConverterTests.java +++ b/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/DefaultXmlPayloadConverterTests.java @@ -15,6 +15,8 @@ */ package org.springframework.integration.xml; +import static org.junit.Assert.assertTrue; + import java.io.StringReader; import javax.xml.parsers.DocumentBuilderFactory; @@ -25,8 +27,14 @@ import org.custommonkey.xmlunit.XMLAssert; import org.junit.Before; import org.junit.Test; import org.w3c.dom.Document; +import org.w3c.dom.Node; import org.xml.sax.InputSource; +/** + * + * @author Jonas Partner + * + */ public class DefaultXmlPayloadConverterTests { DefaultXmlPayloadConverter converter; @@ -36,22 +44,41 @@ public class DefaultXmlPayloadConverterTests { String testDocumentAsString = "hello"; @Before - public void setUp() throws Exception{ + public void setUp() throws Exception { converter = new DefaultXmlPayloadConverter(); testDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse( new InputSource(new StringReader(testDocumentAsString))); } @Test - public void testWithString() { + public void testGetDocumentWithString() { Document doc = converter.convertToDocument("hello"); XMLAssert.assertXMLEqual(testDocument, doc); } - + @Test - public void testWithDocument() { + public void testGetDocumentWithDocument() { Document doc = converter.convertToDocument(testDocument); Assert.assertTrue(doc == testDocument); } + @Test + public void testGetNodePassingNode() { + Node element = (Node) testDocument.getElementsByTagName("test").item(0); + Node n = converter.convertToNode(element); + assertTrue("Wrong node returned", element == n); + } + + @Test + public void testGetNodePassingString() { + Node n = converter.convertToNode("hello"); + XMLAssert.assertXMLEqual(testDocument, (Document) n); + } + + @Test + public void testGetNodePassingDocument() { + Node n = converter.convertToNode(testDocument); + XMLAssert.assertXMLEqual(testDocument, (Document) n); + } + } diff --git a/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/router/XPathMultiChannelNameResolverTests.java b/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/router/XPathMultiChannelNameResolverTests.java index 9d95e6a3ca..e4531a6c25 100644 --- a/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/router/XPathMultiChannelNameResolverTests.java +++ b/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/router/XPathMultiChannelNameResolverTests.java @@ -26,6 +26,7 @@ import org.springframework.integration.xml.util.XmlTestUtil; import org.springframework.xml.xpath.XPathExpression; import org.springframework.xml.xpath.XPathExpressionFactory; import org.w3c.dom.Document; +import org.w3c.dom.Node; /** * @author Jonas Partner @@ -73,4 +74,13 @@ public class XPathMultiChannelNameResolverTests { resolver.resolveChannelNames(new StringMessage("test")); } + @Test + public void testNodePayload() throws Exception { + XPathMultiChannelNameResolver resolver = new XPathMultiChannelNameResolver("./three/text()"); + Document testDocument = XmlTestUtil.getDocumentForString("bobdave"); + String[] channelNames = resolver.resolveChannelNames(new GenericMessage(testDocument.getElementsByTagName("two").item(0))); + assertEquals("bob",channelNames[0]); + assertEquals("dave",channelNames[1]); + } + } diff --git a/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/router/XPathSingleChannelNameResolverTests.java b/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/router/XPathSingleChannelNameResolverTests.java index bbb433f787..d6f9157e56 100644 --- a/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/router/XPathSingleChannelNameResolverTests.java +++ b/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/router/XPathSingleChannelNameResolverTests.java @@ -19,14 +19,14 @@ package org.springframework.integration.xml.router; import static org.junit.Assert.assertEquals; import org.junit.Test; -import org.w3c.dom.Document; - import org.springframework.integration.message.GenericMessage; import org.springframework.integration.message.MessagingException; import org.springframework.integration.message.StringMessage; import org.springframework.integration.xml.util.XmlTestUtil; import org.springframework.xml.xpath.XPathExpression; import org.springframework.xml.xpath.XPathExpressionFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Node; /** * @author Jonas Partner @@ -34,22 +34,19 @@ import org.springframework.xml.xpath.XPathExpressionFactory; public class XPathSingleChannelNameResolverTests { @Test - @SuppressWarnings("unchecked") public void testSimpleDocType() throws Exception { Document doc = XmlTestUtil.getDocumentForString(""); XPathExpression expression = XPathExpressionFactory.createXPathExpression("/doc/@type"); XPathSingleChannelNameResolver resolver = new XPathSingleChannelNameResolver(expression); - String channelName = resolver.resolveChannelName(new GenericMessage(doc)); + String channelName = resolver.resolveChannelNames(new GenericMessage(doc))[0]; assertEquals("Wrong channel name", "one", channelName); } - - + @Test - @SuppressWarnings("unchecked") public void testSimpleStringDoc() throws Exception { XPathExpression expression = XPathExpressionFactory.createXPathExpression("/doc/@type"); XPathSingleChannelNameResolver resolver = new XPathSingleChannelNameResolver(expression); - String channelName = resolver.resolveChannelName(new GenericMessage("")); + String channelName = resolver.resolveChannelNames(new GenericMessage(""))[0]; assertEquals("Wrong channel name", "one", channelName); } @@ -57,7 +54,15 @@ public class XPathSingleChannelNameResolverTests { public void testNonNodePayload() throws Exception { XPathExpression expression = XPathExpressionFactory.createXPathExpression("/doc/@type"); XPathSingleChannelNameResolver resolver = new XPathSingleChannelNameResolver(expression); - resolver.resolveChannelName(new StringMessage("test")); + resolver.resolveChannelNames(new StringMessage("test")); } + @Test + public void testNodePayload() throws Exception { + XPathSingleChannelNameResolver resolver = new XPathSingleChannelNameResolver("./three/text()"); + Document testDocument = XmlTestUtil.getDocumentForString("bob"); + String[] channelNames = resolver.resolveChannelNames(new GenericMessage(testDocument + .getElementsByTagName("two").item(0))); + assertEquals("bob", channelNames[0]); + } } diff --git a/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/selector/BooleanTestXpathMessageSelectorTests.java b/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/selector/BooleanTestXpathMessageSelectorTests.java index 98d44d57b7..27781caaf6 100644 --- a/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/selector/BooleanTestXpathMessageSelectorTests.java +++ b/org.springframework.integration.xml/src/test/java/org/springframework/integration/xml/selector/BooleanTestXpathMessageSelectorTests.java @@ -25,6 +25,7 @@ import org.springframework.integration.xml.util.XmlTestUtil; import org.springframework.xml.xpath.XPathExpression; import org.springframework.xml.xpath.XPathExpressionFactory; import org.w3c.dom.Document; +import org.w3c.dom.Node; /** * @@ -55,12 +56,22 @@ public class BooleanTestXpathMessageSelectorTests { } @Test - public void testWithXPathExpressionProvided(){ + public void testStringWithXPathExpressionProvided(){ XPathExpression xpathExpression = XPathExpressionFactory.createXPathExpression("boolean(/one/two)"); BooleanTestXPathMessageSelector selector = new BooleanTestXPathMessageSelector(xpathExpression); assertTrue(selector.accept(new StringMessage("")) ) ; assertFalse(selector.accept(new StringMessage("")) ) ; } + @Test + public void testNodeWithXPathExpressionAsString() throws Exception{ + XPathExpression xpathExpression = XPathExpressionFactory.createXPathExpression("boolean(./three)"); + BooleanTestXPathMessageSelector selector = new BooleanTestXPathMessageSelector(xpathExpression); + Document testDocument = XmlTestUtil.getDocumentForString(""); + + assertTrue(selector.accept(new GenericMessage(testDocument.getElementsByTagName("two").item(0)))); + assertFalse(selector.accept(new GenericMessage(testDocument.getElementsByTagName("three").item(0)))); + } + }