diff --git a/README.md b/README.md
index 3b0d765..0e2abf8 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,7 @@ The Spring Integration Extensions project provides extension modules for [Spring
* [SMB][] Support
* [Print][] Support
* [Atmosphere][] Support ([Websockets][])
+* [XQuery][] Support
## Getting support
@@ -123,4 +124,5 @@ The Spring Integration Extensions Framework is released under version 2.0 of the
[SMB]: http://en.wikipedia.org/wiki/Server_Message_Block
[Print]: http://docs.oracle.com/javase/6/docs/technotes/guides/jps/index.html
[Atmosphere]: https://github.com/Atmosphere/atmosphere
-[Websockets]: http://www.html5rocks.com/en/tutorials/websockets/basics/
\ No newline at end of file
+[Websockets]: http://www.html5rocks.com/en/tutorials/websockets/basics/
+[XQuery]: http://en.wikipedia.org/wiki/XQuery
\ No newline at end of file
diff --git a/spring-integration-xquery/README.md b/spring-integration-xquery/README.md
new file mode 100644
index 0000000..63827e8
--- /dev/null
+++ b/spring-integration-xquery/README.md
@@ -0,0 +1,170 @@
+Spring Integration XQuery Support
+=================================
+
+# Overview
+
+**XQuery** is a query and functional programming language that is used to query over a collection of XML data. XQuery is a super-set of **XPath 2.0**. Therefore, if you are looking for *XPath 2.0* functionality, you may consider using the provided components as well. For more information on *XQuery*, please see:
+
+* http://en.wikipedia.org/wiki/XQuery
+* http://en.wikipedia.org/wiki/XPath_2.0
+
+The **XQuery API for Java** ([JSR225][]) is a specification for providing a common *XQuery API* to the Java programming language. The API can be used for *XML databases* as well as pure *XQuery processors*. For more information, please see:
+
+* http://en.wikipedia.org/wiki/XQuery_API_for_Java
+* http://xqj.net/
+* http://xqj.net/javadoc/
+
+Various products and libraries provide *XQuery* support. Below is a selection of available choices:
+
+## XML Databases
+
+* **BaseX** (BSD License) - http://basex.org/
+* **eXist** (GNU LGPL) - http://www.exist-db.org/
+* **MarkLogic** (Commercial License, Free Option) - http://developer.marklogic.com/express
+* **Sedna** (Apache License, Version 2.0) - http://www.sedna.org/
+
+## XQuery Processors
+
+* **Zorba** (Apache License, Version 2.0) - http://www.zorba-xquery.com/
+* **SAXON** (Mozilla Public License) - http://saxon.sourceforge.net/
+
+*Zorba* is written in C++ but provides a Java API ([JSR225][]). *SAXON*, being a pure Java implementation, can be used as a normal Java library. Of all the available XQuery supporting options, *SAXON* is probably the the most widely used choice for Java developers.
+
+# Provided Spring Integration Components
+
+Currently, the *Spring Integration XQuery Module* provides the following components:
+
+* XQuery Router
+* XQuery Transformer
+
+## Java Implementation
+
+The class *o.s.i.xquery.core.XQueryExecutor* is the backbone for all *XQuery* related operations within this Spring Integration module. Its corresponding test class is *o.s.i.xquery.core.AbstractXQueryExecutorTests* with the subclasses:
+
+* *o.s.i.xquery.core.SaxonXQueryExecutorTests*
+* *o.s.i.xquery.core.SednaXQueryExecutorTests*
+
+Each of these sub-classes instantiates the *XQueryExecutor* with the corresponding implementations of the *javax.xml.xquery.[XQDataSource][]* interface. The *XQueryExecutor* provides a setter for specifying the [XQDataSource][] to use. If not explicitly specified, it will default to use *Saxon*, if available on the classpath.
+
+The XQuery Executor is tested using Saxon and Sedna. Furthermore, we provide the [Spring Integration XQuery Samples][] that additionally also cover *BaseX*. *BaseX* is not included in the actual Spring Integration module, as it conflicts with the *Saxon* jars. Both, the *Sedna* and the *BaseX* XML database need to be started and the datasource needs to connect to them. This is even true, if the XML being queried is not present in the database (as we typically provide it with the payload of the Spring Integration *Message*).
+
+See the class comments of *o.s.i.xquery.core.SednaXQueryExecutorTests* for instructions on starting the *Sedna* server and executing the tests using *Sedna*'s implementation. Additional information can also be found at:
+
+* http://xqj.net/
+
+Of course, much more detailed information is also available on the respective product websites.
+
+## XML Namespace Support
+
+The Spring Integration XQuery components also provide XML Namespace support in order to simplify their configuration.
+
+### Routers
+
+#### With **xquery** sub element
+
+
+
+
+
+
+
+
+
+The configuration is pretty simple. The XQuery in this example is specified using the *xquery* sub-element. The query once executed will yield the names of the output channels. XQueries can have *named parameters* specified. In this case we have two named parameters: **name** and **class**.
+
+The value or the expression to find the value for these named parameters is specified using the *xquery-parameter* sub-element. This sub-element provides two possible ways to provide parameters. In the first parameter, the value of the **name** parameter is deduced using a SpEL expression. For the second parameter named **class**, a statically defined value is used.
+
+#### With **xquery** attribute
+
+
+
+
+
+The above config now specifies the *xquery* attribute to specify the query. It additionally has the *converter* and the *xq-datasource* attributes. The *converter* holds a reference to the bean which implements the `org.springframework.integration.xml.DefaultXmlPayloadConverter` interface. By default it uses `org.springframework.integration.xml.DefaultXmlPayloadConverter` Please note that this interface and the implementation is from **spring-integration-xml** project. The *xq-datasource* attribute holds a reference to a bean that implements the `javax.xml.xquery.XQDataSource` interface. By default it will use `net.sf.saxon.xqj.SaxonXQDataSource` implementation.
+
+#### With **xquery-file-resource** attribute
+
+
+
+
+
+
+Instead of the nested *xquery* element or the *xquery* attribute, we provide the path to the resource that contains the xquery, typically a *.xq* file.
+
+Similar to the routers in the core module, the xquery routers accept the mapping subelement to provide an additional level of indirection and mapping from the obtained value(s) from xquery execution to the output channels. Thus, you can have the following subelement in the xquery router definition
+
+
+
+
+### Transformers
+
+Similar to routers, transformers too support accepting the xquery as a child sub element, attribute or an attribute with the resource path to the *.xq* file. Since the definitions and meaning of the common attributes are similar to that of the router, we will not be explaining them again unless we have some attribute specific to transformers.
+
+#### With **xquery** subelement
+
+
+
+
+
+
+
+
+
+#### With **xquery** attribute
+
+
+
+
+An additional attibute *format-output* is mentioned, the value could be *true* or *false*. It informs the transformer to format the output xml generated after transformation.
+
+#### With **xquery-file** attribute.
+
+
+
+
+###Credits
+We would like to thank **Ganesh Shetty** for his suggestion of inclusion of *XQuery* support in *Spring Integration*, giving the initial requirements and use cases for this module. We look forward for more support from the community for evaluating the libraries and provide their feedback.
+
+[JSR225]: http://jcp.org/aboutJava/communityprocess/final/jsr225/index.html
+[xqj.net]: http://xqj.net/
+[XQDataSource]: http://xqj.net/javadoc/javax/xml/xquery/XQDataSource.html
+[Spring Integration XQuery Samples]: https://github.com/SpringSource/spring-integration-samples/tree/master/basic/xquery
diff --git a/spring-integration-xquery/pom.xml b/spring-integration-xquery/pom.xml
new file mode 100644
index 0000000..288518a
--- /dev/null
+++ b/spring-integration-xquery/pom.xml
@@ -0,0 +1,109 @@
+
+
+
+ 4.0.0
+ org.springframework.integration
+ spring-integration-xquery
+ 1.0.0-SNAPSHOT
+
+
+ The Apache Software License, Version 2.0
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+ repo
+
+
+
+
+ 2.2.1
+
+
+
+
+
+ maven-compiler-plugin
+
+ 1.6
+ 1.6
+
+
+
+ maven-surefire-plugin
+
+
+ **/*Tests.java
+
+
+ **/*Abstract*.java
+
+
+ **/SednaXQueryExecutorTests.java
+
+
+
+
+
+
+
+
+ springsource-libs-milestone
+ Spring Framework Maven Milestone Repository
+ https://repo.springsource.org/libs-milestone
+
+
+
+
+
+ org.springframework.integration
+ spring-integration-core
+ 2.2.0.M3
+
+
+
+ org.springframework.integration
+ spring-integration-xml
+ 2.2.0.M3
+
+
+
+ org.springframework.integration
+ spring-integration-test
+ 2.2.0.M3
+
+
+
+ javax.xml.xquery
+ xqj-api
+ 1.0
+
+
+
+ net.sf.saxon
+ Saxon-HE
+ 9.4
+ optional
+
+
+
+ net.xqj.sedna
+ sedna-xqj
+ 1.0.0
+ optional
+
+
+
+ com.xqj2
+ xqj2
+ 0.0.1
+ optional
+
+
+
+ junit
+ junit-dep
+ 4.10
+ test
+
+
+
\ No newline at end of file
diff --git a/spring-integration-xquery/src/main/java/org/springframework/integration/xquery/config/xml/IntegrationXQueryNamespaceHandler.java b/spring-integration-xquery/src/main/java/org/springframework/integration/xquery/config/xml/IntegrationXQueryNamespaceHandler.java
new file mode 100644
index 0000000..6eeaac5
--- /dev/null
+++ b/spring-integration-xquery/src/main/java/org/springframework/integration/xquery/config/xml/IntegrationXQueryNamespaceHandler.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2002-2012 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.xquery.config.xml;
+
+import org.springframework.integration.config.xml.AbstractIntegrationNamespaceHandler;
+
+/**
+ * The namespace handler for the "int-xquery" namespace
+ *
+ * @author Amol Nayak
+ * @since 2.2
+ *
+ */
+public class IntegrationXQueryNamespaceHandler extends
+ AbstractIntegrationNamespaceHandler {
+
+ /* (non-Javadoc)
+ * @see org.springframework.beans.factory.xml.NamespaceHandler#init()
+ */
+ public void init() {
+ registerBeanDefinitionParser("xquery-router", new XQueryRouterParser());
+ registerBeanDefinitionParser("xquery-transformer", new XQueryTransformerParser());
+ }
+}
diff --git a/spring-integration-xquery/src/main/java/org/springframework/integration/xquery/config/xml/XQueryParserUtils.java b/spring-integration-xquery/src/main/java/org/springframework/integration/xquery/config/xml/XQueryParserUtils.java
new file mode 100644
index 0000000..202785b
--- /dev/null
+++ b/spring-integration-xquery/src/main/java/org/springframework/integration/xquery/config/xml/XQueryParserUtils.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2002-2012 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.xquery.config.xml;
+
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.support.ManagedList;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.core.io.Resource;
+import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
+import org.springframework.integration.xquery.core.XQueryExecutor;
+import org.springframework.integration.xquery.support.XQueryParameter;
+import org.springframework.util.Assert;
+import org.springframework.util.ResourceUtils;
+import org.springframework.util.StringUtils;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * The common utility class for the XQuery components that will be performing the
+ * common operations used in the parsers like, creating the XQueryExecutor instance
+ * etc.
+ *
+ * @author Amol Nayak
+ * @since 2.2
+ *
+ */
+public class XQueryParserUtils {
+
+ /**
+ * Create the instance of the {@link XQueryExecutor}
+ * @param element
+ * @return
+ */
+ public static final AbstractBeanDefinition getXQueryExecutor(Element element) {
+ BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(XQueryExecutor.class);
+ IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "converter");
+ IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "xq-datasource","xQDataSource");
+ IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "format-output");
+ NodeList list = element.getElementsByTagNameNS(element.getNamespaceURI(), "xquery");
+ Attr xQueryAttribute = element.getAttributeNode("xquery");
+ Attr xQueryResource = element.getAttributeNode("xquery-file-resource");
+
+ Assert.isTrue(!(xQueryAttribute != null && xQueryResource != null),
+ "Only one of xquery or xquery-file-resource may be specified");
+
+ Assert.isTrue(!(xQueryAttribute != null && list != null && list.getLength() > 0),
+ "At most one of the xquery attribute " +
+ "or the xquery child element should to be provided");
+
+ Assert.isTrue(!(xQueryResource != null && list != null && list.getLength() > 0),
+ "At most one of the xquery-file-resource attribute " +
+ "or the xquery child element should to be provided");
+
+ Assert.isTrue(xQueryResource != null || xQueryAttribute != null || (list != null && list.getLength() > 0),
+ "At least one of xquery, xquery-file-resource attributes or the xquery child element needs to be provided");
+
+ if(xQueryResource != null) {
+ //resource specified
+ String textContent = xQueryResource.getTextContent();
+ Assert.isTrue(StringUtils.hasText(textContent),"Non empty, non null resource path should be provided");
+ Resource resource;
+ if(textContent.startsWith(ResourceUtils.CLASSPATH_URL_PREFIX)) {
+ resource = new ClassPathResource(textContent.substring(ResourceUtils.CLASSPATH_URL_PREFIX.length()));
+ }
+ else if(textContent.startsWith(ResourceUtils.FILE_URL_PREFIX)) {
+ resource = new FileSystemResource(textContent.substring(ResourceUtils.FILE_URL_PREFIX.length()));
+ }
+ else {
+ //assuming its a classpath resource
+ resource = new ClassPathResource(textContent);
+ }
+ builder.addPropertyValue("xQueryFileResource", resource);
+ }
+ else {
+ //child element or attribute defined
+ String textContent;
+ if(xQueryAttribute == null) {
+ //child might be element specified
+ Assert.isTrue(list != null && list.getLength() == 1,"Maximum one xquery child node may be specified");
+ textContent = list.item(0).getTextContent();
+ }
+ else {
+ //xquery specified in the attribute
+ textContent = xQueryAttribute.getTextContent();
+ }
+ if(StringUtils.hasText(textContent)) {
+ builder.addPropertyValue("xQuery", textContent.trim());
+ }
+ }
+
+ //lets get the parameter nodes
+ NodeList parameters = element.getElementsByTagNameNS(element.getNamespaceURI(), "xquery-parameter");
+ if(parameters != null && parameters.getLength() > 0) {
+ ManagedList params = new ManagedList();
+ for(int i = 0;i < parameters.getLength();i++) {
+ Node node = parameters.item(i);
+ NamedNodeMap attrs = node.getAttributes();
+ Assert.isTrue(attrs.getLength() > 1,
+ "One of ref, value or expression should be present with the name attribute");
+ Attr nameAttr = (Attr)attrs.getNamedItem("name");
+
+ //TODO No check for the mutually exclusivity of these attributes, needed?
+
+ //create a new XQueryParameter instance
+ BeanDefinitionBuilder paramBuilder =
+ BeanDefinitionBuilder.genericBeanDefinition(XQueryParameter.class);
+ paramBuilder.addConstructorArgValue(nameAttr.getTextContent());
+ Attr attr;
+ //add the value if present
+ if(attrs.getNamedItem("value") != null) {
+ attr = (Attr)attrs.getNamedItem("value");
+ paramBuilder.addPropertyValue("parameterValue",attr.getTextContent());
+ }
+ else if(attrs.getNamedItem("ref") != null) {
+ attr = (Attr)attrs.getNamedItem("ref");
+ paramBuilder.addPropertyReference("parameterValue", attr.getTextContent());
+ }
+ else if(attrs.getNamedItem("expression") != null) {
+ attr = (Attr)attrs.getNamedItem("expression");
+ paramBuilder.addPropertyValue("expression", attr.getTextContent());
+ }
+ params.add(paramBuilder.getBeanDefinition());
+ }
+ builder.addPropertyValue("xQueryParameters", params);
+ }
+
+
+ return builder.getBeanDefinition();
+ }
+}
diff --git a/spring-integration-xquery/src/main/java/org/springframework/integration/xquery/config/xml/XQueryRouterParser.java b/spring-integration-xquery/src/main/java/org/springframework/integration/xquery/config/xml/XQueryRouterParser.java
new file mode 100644
index 0000000..d1bb16b
--- /dev/null
+++ b/spring-integration-xquery/src/main/java/org/springframework/integration/xquery/config/xml/XQueryRouterParser.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2002-2012 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.xquery.config.xml;
+
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.integration.config.xml.AbstractRouterParser;
+import org.springframework.integration.xquery.router.XQueryRouter;
+import org.w3c.dom.Element;
+
+/**
+ * The parser for the XQuery router
+ *
+ * @author Amol Nayak
+ *
+ * @since 2.2
+ *
+ */
+public class XQueryRouterParser extends AbstractRouterParser {
+
+ @Override
+ protected BeanDefinition doParseRouter(Element element,
+ ParserContext parserContext) {
+ BeanDefinitionBuilder routerBuilder = BeanDefinitionBuilder.genericBeanDefinition(XQueryRouter.class);
+ AbstractBeanDefinition executor = XQueryParserUtils.getXQueryExecutor(element);
+ routerBuilder.addPropertyValue("executor", executor);
+ return routerBuilder.getBeanDefinition();
+ }
+}
diff --git a/spring-integration-xquery/src/main/java/org/springframework/integration/xquery/config/xml/XQueryTransformerParser.java b/spring-integration-xquery/src/main/java/org/springframework/integration/xquery/config/xml/XQueryTransformerParser.java
new file mode 100644
index 0000000..0a67e63
--- /dev/null
+++ b/spring-integration-xquery/src/main/java/org/springframework/integration/xquery/config/xml/XQueryTransformerParser.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2002-2012 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.xquery.config.xml;
+
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.integration.config.xml.AbstractTransformerParser;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+/**
+ * The parser for the XQuery transformer component
+ *
+ * @author Amol Nayak
+ * @since 2.2
+ *
+ */
+public class XQueryTransformerParser extends AbstractTransformerParser {
+
+ /* (non-Javadoc)
+ * @see org.springframework.integration.config.xml.AbstractTransformerParser#getTransformerClassName()
+ */
+ @Override
+ protected String getTransformerClassName() {
+ return "org.springframework.integration.xquery.transformer.XQueryTransformer";
+ }
+
+ /* (non-Javadoc)
+ * @see org.springframework.integration.config.xml.AbstractTransformerParser#parseTransformer(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext, org.springframework.beans.factory.support.BeanDefinitionBuilder)
+ */
+ @Override
+ protected void parseTransformer(Element element,
+ ParserContext parserContext, BeanDefinitionBuilder builder) {
+ AbstractBeanDefinition executor = XQueryParserUtils.getXQueryExecutor(element);
+ builder.addPropertyValue("executor", executor);
+ //Add the result type and the result class attributes
+ String resultType = element.getAttribute("result-type");
+ boolean hasResultType = StringUtils.hasText(resultType);
+ String xqueryResultMapper = element.getAttribute("xquery-result-mapper");
+ boolean hasResultMapper = StringUtils.hasText(xqueryResultMapper);
+ Assert.isTrue(!(hasResultType && hasResultMapper),
+ "Only one of result-type or xquery-result-mapper may be specified");
+ if(hasResultType) {
+ Class> type = null;
+ if("string".equalsIgnoreCase(resultType)) {
+ type = String.class;
+ }
+ else if("boolean".equalsIgnoreCase(resultType)) {
+ type = Boolean.class;
+ }
+ else if("number".equalsIgnoreCase(resultType)) {
+ type = Number.class;
+ }
+ else if("node".equalsIgnoreCase(resultType)) {
+ type = Node.class;
+ }
+ else {
+ try {
+ type = Class.forName(resultType);
+ } catch (ClassNotFoundException e) {
+ new IllegalArgumentException("Class " + resultType + " specified in result-type not found, " +
+ "have you provided the fully qualified name?",e);
+ }
+ }
+ builder.addPropertyValue("resultType", type);
+ }
+ else if(hasResultMapper) {
+ builder.addPropertyReference("resultMapper", xqueryResultMapper);
+ }
+ }
+}
diff --git a/spring-integration-xquery/src/main/java/org/springframework/integration/xquery/core/XQueryExecutor.java b/spring-integration-xquery/src/main/java/org/springframework/integration/xquery/core/XQueryExecutor.java
new file mode 100644
index 0000000..471b21e
--- /dev/null
+++ b/spring-integration-xquery/src/main/java/org/springframework/integration/xquery/core/XQueryExecutor.java
@@ -0,0 +1,491 @@
+/*
+ * Copyright 2002-2012 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.xquery.core;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.namespace.QName;
+import javax.xml.xquery.XQConnection;
+import javax.xml.xquery.XQConstants;
+import javax.xml.xquery.XQDataSource;
+import javax.xml.xquery.XQException;
+import javax.xml.xquery.XQPreparedExpression;
+import javax.xml.xquery.XQResultSequence;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.factory.BeanClassLoaderAware;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.core.io.Resource;
+import org.springframework.integration.Message;
+import org.springframework.integration.MessagingException;
+import org.springframework.integration.xml.DefaultXmlPayloadConverter;
+import org.springframework.integration.xml.XmlPayloadConverter;
+import org.springframework.integration.xquery.support.AbstractXQueryResultMapper;
+import org.springframework.integration.xquery.support.BooleanResultMapper;
+import org.springframework.integration.xquery.support.NodeResultMapper;
+import org.springframework.integration.xquery.support.NumberResultMapper;
+import org.springframework.integration.xquery.support.StringResultMapper;
+import org.springframework.integration.xquery.support.XQueryParameter;
+import org.springframework.integration.xquery.support.XQueryResultMapper;
+import org.springframework.integration.xquery.support.XQueryUtils;
+import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.StringUtils;
+import org.w3c.dom.Node;
+
+/**
+ * The common logic for performing the common xquery operations would reside in this
+ * implementation.
+ * The class would be instantiated with the xquery to be executed and the parameters
+ * that would be used. It would return the result of the execution with the
+ * {@link Message} instance passed to one of its execute methods.
+ * Currently doesn't support advanced mapping techniques of mapping the resulting Node(s)
+ * to a custom object type
+ *
+ * @author Amol Nayak
+ *
+ * @since 2.2
+ *
+ */
+public class XQueryExecutor implements InitializingBean,BeanClassLoaderAware {
+
+
+ private static final String SAXON_XQ_DATASOURCE_CLASS = "net.sf.saxon.xqj.SaxonXQDataSource";
+
+ private final Log logger = LogFactory.getLog(XQueryExecutor.class);
+
+ /**
+ * The payload converter
+ */
+ private XmlPayloadConverter converter = new DefaultXmlPayloadConverter();
+
+ @SuppressWarnings("rawtypes")
+ private volatile Map resultMappers;
+
+ private volatile boolean formatOutput;
+
+ /**
+ * The xQuery that this Executor will execute
+ */
+ private String xQuery;
+
+ private XQDataSource xqDataSource;
+
+ private Map xQueryParameterMap;
+
+ //maintained internally and used for setting the values
+ private List xQueryParameters;
+
+ //The resource to the XQuery's .xq file
+ private Resource xQueryFileResource;
+
+ private String userName;
+
+ private String password;
+
+ private ClassLoader classLoader = ClassUtils.getDefaultClassLoader();
+
+ //TODO: Can we have a static xml resource which will be used always to execute the
+ //given XQuery as against the one sent in the payload. The default is to use the one in the
+ //payload unless one for static xml is provided
+
+ @Override
+ public void afterPropertiesSet() {
+ if(resultMappers == null) {
+ resultMappers = new HashMap,XQueryResultMapper>>();
+ }
+ addDefaultMappers();
+
+ if(xqDataSource == null) {
+ xqDataSource = discoverXQDataSource();//default
+ }
+ if(xQuery == null) {
+ //perhaps resource specified
+ Assert.notNull(xQueryFileResource, "One of XQuery or the XQuery resource is mandatory");
+ xQuery = XQueryUtils.readXQueryFromResource(xQueryFileResource);
+ }
+
+ try {
+ XQConnection conn = getConnection();
+ XQPreparedExpression expression = conn.prepareExpression(xQuery);
+ QName[] extParameters = expression.getAllExternalVariables();
+ if(extParameters != null && extParameters.length > 0) {
+ xQueryParameters = new ArrayList();
+ for(QName qName:extParameters) {
+ xQueryParameters.add(qName.getLocalPart());
+ }
+ }
+ expression.close();
+ conn.close();
+ } catch (XQException e) {
+ throw new MessagingException("Caught Exception while opening a connection to the datasource", e);
+ }
+
+ if(xQueryParameters != null) {
+ if (xQueryParameterMap == null) {
+ throw new MessagingException("Expecting " + xQueryParameters.size() + " parameters in the xquery, " +
+ "but none provided to the router");
+ }
+
+ //now check if all the parameter needed are present in the map
+ List missingParameters = new ArrayList();
+ for(String xQueryParameter:xQueryParameters) {
+ if(!xQueryParameterMap.containsKey(xQueryParameter)) {
+ missingParameters.add(xQueryParameter);
+ }
+ }
+
+ if(missingParameters.size() > 0) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("[").append("$").append(missingParameters.get(0));
+ if(missingParameters.size() > 1) {
+ for(int i = 1;i < missingParameters.size();i++) {
+ builder.append(", ").append("$").append(missingParameters.get(i));
+ }
+ }
+ builder.append("]");
+ throw new MessagingException("Missing parameter(s) " + builder.toString());
+ }
+ }
+
+
+ }
+
+
+
+ public void setBeanClassLoader(ClassLoader classLoader) {
+ Assert.notNull(classLoader, "Non null class loader instance expected");
+ this.classLoader = classLoader;
+
+ }
+
+
+ /**
+ * The method that finds an implementation in the classpath for some well known
+ * providers. This will kick into action only if an {@link XQDataSource} is not
+ * explicitly provided by the user by invoking {@link XQueryExecutor#setXQDataSource(XQDataSource)}
+ * The method currently looks for Saxon's implementation only
+ *
+ * @return the instantiated {@link XQDataSource}
+ */
+ private XQDataSource discoverXQDataSource() {
+ Object xqDataSource = null;
+ try {
+ if(ClassUtils.isPresent(SAXON_XQ_DATASOURCE_CLASS, classLoader)) {
+ xqDataSource = Class.forName(SAXON_XQ_DATASOURCE_CLASS).newInstance();
+ }
+ //For now its just Saxon we will discover, we can add other implementations here later
+ } catch (Exception e) {
+ throw new MessagingException("Unable to discover/instantiate an XQDataSource, " +
+ "see nested exception for details", e);
+ }
+ Assert.notNull(xqDataSource, "No XQDataSource provided nor any known implementation discovered in the classpath");
+ logger.info("Using \"" + xqDataSource.getClass() + "\" as the XQDataSource implementation");
+ return (XQDataSource)xqDataSource;
+ }
+
+ /**
+ * Gets the connection, uses userName and password if one is set
+ * @return
+ * @throws XQException
+ */
+ private XQConnection getConnection() throws XQException {
+ XQConnection conn;
+ if(StringUtils.hasText(userName)) {
+ conn = xqDataSource.getConnection(userName,password);
+ }
+ else {
+ conn = xqDataSource.getConnection();
+ }
+ return conn;
+ }
+
+ /**
+ * There are some default mappers defined, those will be added if the user has not provided
+ * some implementations for them
+ */
+ @SuppressWarnings("unchecked")
+ private void addDefaultMappers() {
+ if(!resultMappers.containsKey(String.class)) {
+ StringResultMapper mapper = new StringResultMapper();
+ mapper.setFormatOutput(formatOutput);
+ resultMappers.put(String.class, mapper);
+ }
+
+ if(!resultMappers.containsKey(Boolean.class)) {
+ BooleanResultMapper mapper = new BooleanResultMapper();
+ mapper.setFormatOutput(formatOutput);
+ resultMappers.put(Boolean.class, mapper);
+ }
+
+ if(!resultMappers.containsKey(Number.class)) {
+ NumberResultMapper mapper = new NumberResultMapper();
+ mapper.setFormatOutput(formatOutput);
+ resultMappers.put(Number.class, mapper);
+ }
+
+ if(!resultMappers.containsKey(Node.class)) {
+ NodeResultMapper mapper = new NodeResultMapper();
+ mapper.setFormatOutput(formatOutput);
+ resultMappers.put(Node.class, mapper);
+ }
+ }
+
+ /**
+ * Executes the XQuery for result and produce the result as a {@link List} of {@link String}
+ * @param message the source message that would be used to derive the values of the parameters
+ *
+ * @return The {@link List} of results
+ */
+ public List executeForString(Message> message) {
+ return execute(message,String.class);
+ }
+
+ /**
+ * Executes the XQuery for result and produce the result as a {@link List} of {@link Boolean}
+ * @param message the source message that would be used to derive the values of the parameters
+ *
+ * @return The {@link List} of results
+ */
+ public List executeForBoolean(Message> message) {
+ return execute(message,Boolean.class);
+ }
+
+
+ /**
+ * Executes the XQuery for result and produce the result as a {@link List} of {@link Number}
+ * @param message the source message that would be used to derive the values of the parameters
+ *
+ * @return The {@link List} of results
+ */
+ public List executeForNumber(Message> message) {
+ return execute(message,Number.class);
+ }
+
+ /**
+ * Executes the XQuery for result and produce the result as a {@link List} of {@link Node}
+ * @param message the source message that would be used to derive the values of the parameters
+ *
+ * @return The {@link List} of results
+ */
+ public List executeForNode(Message> message) {
+ return execute(message,Node.class);
+ }
+
+ /**
+ * Execute the given XQuery and returns a {@link List} of the provided type
+ * @param
+ * @param message
+ * @param returnType
+ * @return
+ */
+ @SuppressWarnings({ "unchecked"})
+ public List execute(Message> message,Class returnType) {
+ Assert.notNull(message,"Non null message expected");
+ Assert.notNull(returnType,"Non null type expected");
+ Assert.isTrue(resultMappers.containsKey(returnType),"No Result mapper found for the type " + returnType.getName());
+ return execute(message, (XQueryResultMapper)resultMappers.get(returnType));
+ }
+
+ /**
+ * The method that executes the actual XQuery and uses the provided mapper
+ * to get the result that is returned.
+ * @param
+ * @param message
+ * @param mapper
+ * @return
+ */
+ public List execute(Message> message,XQueryResultMapper mapper) {
+ Node node = converter.convertToNode(message.getPayload());
+
+ if(node == null) {
+ return null;
+ }
+
+ XQConnection connection = null;
+ XQPreparedExpression expression = null;
+ try {
+ connection = getConnection();
+ expression = connection.prepareExpression(xQuery);
+ expression.bindNode(XQConstants.CONTEXT_ITEM, node, null);
+
+ //bind the parameter values
+ if(xQueryParameters != null && xQueryParameters.size() > 0) {
+ //bind them one by one
+ for(String parameter:xQueryParameters) {
+ XQueryParameter xQueryParam = xQueryParameterMap.get(parameter);
+ //TODO: Check what possible values can be supported to be set here
+ //Accordingly do we need to set the third parameter for XQItemType
+ expression.bindObject(new QName(xQueryParam.getParameterName()),
+ xQueryParam.evaluate(message), null);
+ }
+ }
+
+ XQResultSequence result = expression.executeQuery();
+ return mapper.mapResults(result);
+
+ } catch (XQException e) {
+ throw new MessagingException("Caught Exception while opening a connection to the datasource", e);
+ } finally {
+ try {
+ if(expression != null) {
+ expression.close();
+ }
+ if(connection != null) {
+ connection.close();
+ }
+ } catch (XQException e) {
+ logger.error("Caught Exception while closing the XQ expression.connection", e);
+ }
+ }
+ }
+
+ /**
+ * Sets all the result mappers to be used by this executor.
+ * @param
+ * @param mappers
+ */
+ @SuppressWarnings("rawtypes")
+ public void setResultMappers(Map, XQueryResultMapper> mappers) {
+ Assert.notNull(mappers);
+ this.resultMappers = new HashMap, XQueryResultMapper>(mappers);
+ //not iterate through them and set the format
+ for(Object mapper:resultMappers.values()) {
+ if(mapper instanceof AbstractXQueryResultMapper) {
+ ((AbstractXQueryResultMapper)mapper).setFormatOutput(formatOutput);
+ }
+ }
+ }
+
+ /**
+ *Set the {@link XmlPayloadConverter} that would be used to convert the payload
+ * into the XML {@link Node}
+ *
+ * @param converter
+ */
+ public void setConverter(XmlPayloadConverter converter) {
+ Assert.notNull(converter, "Provide a non null instance of XmlPayloadConverter");
+ this.converter = converter;
+ }
+
+ /**
+ * The XQuery string that would be evaluated to determine the channel names
+ * @param xQuery
+ */
+ public void setXQuery(String xQuery) {
+ Assert.isTrue(xQueryFileResource == null, "Only one of XQuery resource file or XQuery may be specified");
+ Assert.notNull(xQuery, "Provide a non null XQuery");
+ this.xQuery = xQuery;
+ }
+
+ /**
+ * Sets the XQuery's .xq file as the resource. The contents of this file will be read as xQuery
+ *
+ * @param xQueryFileResource
+ */
+ public void setXQueryFileResource(Resource xQueryFileResource) {
+ Assert.isTrue(xQuery == null,"Only one of XQuery resource file or XQuery may be specified");
+ this.xQueryFileResource = xQueryFileResource;
+ }
+
+ /**
+ * Sets the {@link XQDataSource}
+ * @param xqDataSource
+ */
+ public void setXQDataSource(XQDataSource xqDataSource) {
+ Assert.notNull(xqDataSource, "Provide a non null instance of the XQDatasource");
+ this.xqDataSource = xqDataSource;
+ }
+
+
+ /**
+ * Sets the parameter map where the parameter name is the key and the
+ * {@link XQueryParameter} instance is the value
+ *
+ * @param xQueryParameterMap
+ */
+ public void setXQueryParameterMap(
+ Map xQueryParameterMap) {
+ this.xQueryParameterMap = xQueryParameterMap;
+ }
+
+ /**
+ * Convenience method to add a {@link XQueryParameter} to the map
+ * @param param
+ */
+ public void addXQueryParameter(XQueryParameter param) {
+ if(xQueryParameterMap == null) {
+ xQueryParameterMap = new HashMap();
+ }
+ xQueryParameterMap.put(param.getParameterName(), param);
+ }
+
+ /**
+ * Convenience method that would be used to set the parameters in the parameter
+ * map. Any non conflicting parameters would be retained
+ * @param params
+ */
+ public void setXQueryParameters(List params) {
+ if(params != null && params.size() > 0) {
+ if(xQueryParameterMap == null)
+ xQueryParameterMap = new HashMap();
+ for(XQueryParameter param:params) {
+ xQueryParameterMap.put(param.getParameterName(), param);
+ }
+ }
+ }
+
+ /**
+ * If the output result is an xml, the value of this parameter will determine
+ * if the output xml is to be formatted or not. By default, the output will
+ * not be formatted
+ *
+ * @param formatOutput
+ */
+ public void setFormatOutput(boolean formatOutput) {
+ this.formatOutput = formatOutput;
+ }
+
+
+
+ //TODO: Support date, dateTime data types
+
+
+ /**
+ * Sets the user name to be used while obtaining the connection.
+ *
+ */
+ public void setUserName(String userName) {
+ Assert.hasText(userName, "Null or empty string provided as user name");
+ this.userName = userName;
+ }
+
+ /**
+ * Sets the password to be used for obtaining the connection
+ * The value if ignored if the userName is null
+ *
+ * @param password
+ */
+ public void setPassword(String password) {
+ Assert.notNull(password, "Null password provided");
+ //Can password be empty string?
+ this.password = password;
+ }
+}
diff --git a/spring-integration-xquery/src/main/java/org/springframework/integration/xquery/router/XQueryRouter.java b/spring-integration-xquery/src/main/java/org/springframework/integration/xquery/router/XQueryRouter.java
new file mode 100644
index 0000000..d05397a
--- /dev/null
+++ b/spring-integration-xquery/src/main/java/org/springframework/integration/xquery/router/XQueryRouter.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2002-2012 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.xquery.router;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.springframework.integration.Message;
+import org.springframework.integration.router.AbstractMappingMessageRouter;
+import org.springframework.integration.xquery.core.XQueryExecutor;
+import org.springframework.util.Assert;
+
+/**
+ * The message router that will evaluate the provided XQuery on the xml to determine the channel(s)
+ * to which the message will be routed
+ *
+ * @author Amol Nayak
+ * @since 2.2
+ *
+ */
+public class XQueryRouter extends AbstractMappingMessageRouter {
+
+ private XQueryExecutor executor;
+
+ private Class> resultType;
+
+ @Override
+ public void onInit() {
+ super.onInit();
+ Assert.notNull(executor,"No XQueryExecutor instance provided");
+ if(resultType == null) {
+ resultType = String.class;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.springframework.integration.router.AbstractMappingMessageRouter#getChannelKeys(org.springframework.integration.Message)
+ */
+ @Override
+ protected List