CAMEL-10509: Handle namespaces on XML dump and load operations

Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/20a1b012
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/20a1b012
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/20a1b012

Branch: refs/heads/camel-2.18.x
Commit: 20a1b012bd03c58b702af615dde0b0198284fb52
Parents: 9b43224
Author: James Netherton <jamesnether...@gmail.com>
Authored: Fri Nov 25 15:25:22 2016 +0000
Committer: Claus Ibsen <davscl...@apache.org>
Committed: Tue Nov 29 20:45:15 2016 +0100

----------------------------------------------------------------------
 .../camel/converter/jaxp/XmlConverter.java      |  18 ++
 .../apache/camel/impl/DefaultCamelContext.java  |  28 +--
 .../org/apache/camel/model/ModelHelper.java     | 209 ++++++++++++++-----
 .../org/apache/camel/util/ExchangeHelper.java   |   2 +-
 .../properties/PropertiesRouteFromTest.java     |   2 +-
 .../camel/converter/jaxp/XmlConverterTest.java  |  30 ++-
 .../management/ManagedFromRestGetTest.java      |   8 +-
 .../ManagedFromRestPlaceholderTest.java         |   8 +-
 .../LoadRouteFromXmlWithNamespaceTest.java      |  44 ++++
 .../camel/util/CreateModelFromXmlTest.java      | 123 +++++++++++
 .../util/DumpModelAsXmlAggregateRouteTest.java  |   2 +-
 .../camel/util/DumpModelAsXmlNamespaceTest.java |  26 ++-
 .../util/DumpModelAsXmlPlaceholdersTest.java    |   7 +-
 .../apache/camel/model/routeWithNamespace.xml   |  32 +++
 .../org/apache/camel/util/simpleRoute.xml       |  30 +++
 .../camel/util/simpleRouteWithNamespaces.xml    |  30 +++
 .../spring/DumpModelAsXmlPlaceholdersTest.java  |   4 +-
 17 files changed, 501 insertions(+), 102 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/20a1b012/camel-core/src/main/java/org/apache/camel/converter/jaxp/XmlConverter.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/main/java/org/apache/camel/converter/jaxp/XmlConverter.java 
b/camel-core/src/main/java/org/apache/camel/converter/jaxp/XmlConverter.java
index 70f4f87..539eee6 100644
--- a/camel-core/src/main/java/org/apache/camel/converter/jaxp/XmlConverter.java
+++ b/camel-core/src/main/java/org/apache/camel/converter/jaxp/XmlConverter.java
@@ -283,6 +283,24 @@ public class XmlConverter {
     }
 
     /**
+     * Converts the given Document to into text
+     * @param document The document to convert
+     * @param outputOptions The {@link OutputKeys} properties to control 
various aspects of the XML output
+     * @return The string representation of the document
+     * @throws TransformerException
+     */
+    public String toStringFromDocument(Document document, Properties 
outputOptions) throws TransformerException {
+        if (document == null) {
+            return null;
+        }
+
+        DOMSource source = new DOMSource(document);
+        StringWriter buffer = new StringWriter();
+        toResult(source, new StreamResult(buffer), outputOptions);
+        return buffer.toString();
+    }
+
+    /**
      * Converts the source instance to a {@link DOMSource} or returns null if 
the conversion is not
      * supported (making it easy to derive from this class to add new kinds of 
conversion).
      * @deprecated will be removed in Camel 3.0. Use the method which has 2 
parameters.

http://git-wip-us.apache.org/repos/asf/camel/blob/20a1b012/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java 
b/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java
index 8cf7a78..8948e6c 100644
--- a/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java
+++ b/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java
@@ -97,6 +97,7 @@ import org.apache.camel.management.ManagementStrategyFactory;
 import org.apache.camel.model.DataFormatDefinition;
 import org.apache.camel.model.FromDefinition;
 import org.apache.camel.model.ModelCamelContext;
+import org.apache.camel.model.ModelHelper;
 import org.apache.camel.model.ProcessorDefinition;
 import org.apache.camel.model.ProcessorDefinitionHelper;
 import org.apache.camel.model.RouteDefinition;
@@ -873,32 +874,7 @@ public class DefaultCamelContext extends ServiceSupport 
implements ModelCamelCon
     }
 
     public synchronized RoutesDefinition loadRoutesDefinition(InputStream is) 
throws Exception {
-        // load routes using JAXB
-        if (jaxbContext == null) {
-            // must use classloader from CamelContext to have JAXB working
-            jaxbContext = getModelJAXBContextFactory().newJAXBContext();
-        }
-
-        Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
-        Object result = unmarshaller.unmarshal(is);
-
-        if (result == null) {
-            throw new IOException("Cannot unmarshal to routes using JAXB from 
input stream: " + is);
-        }
-
-        // can either be routes or a single route
-        RoutesDefinition answer;
-        if (result instanceof RouteDefinition) {
-            RouteDefinition route = (RouteDefinition) result;
-            answer = new RoutesDefinition();
-            answer.getRoutes().add(route);
-        } else if (result instanceof RoutesDefinition) {
-            answer = (RoutesDefinition) result;
-        } else {
-            throw new IllegalArgumentException("Unmarshalled object is an 
unsupported type: " + ObjectHelper.className(result) + " -> " + result);
-        }
-
-        return answer;
+        return ModelHelper.loadRoutesDefinition(this, is);
     }
 
     public synchronized RestsDefinition loadRestsDefinition(InputStream is) 
throws Exception {

http://git-wip-us.apache.org/repos/asf/camel/blob/20a1b012/camel-core/src/main/java/org/apache/camel/model/ModelHelper.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/model/ModelHelper.java 
b/camel-core/src/main/java/org/apache/camel/model/ModelHelper.java
index 3a1aa68..a1d30fd 100644
--- a/camel-core/src/main/java/org/apache/camel/model/ModelHelper.java
+++ b/camel-core/src/main/java/org/apache/camel/model/ModelHelper.java
@@ -17,23 +17,32 @@
 package org.apache.camel.model;
 
 import java.io.InputStream;
-import java.io.StringReader;
 import java.io.StringWriter;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Properties;
+import javax.xml.bind.Binder;
 import javax.xml.bind.JAXBContext;
 import javax.xml.bind.JAXBException;
 import javax.xml.bind.Marshaller;
-import javax.xml.bind.Unmarshaller;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.TransformerException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Expression;
 import org.apache.camel.NamedNode;
+import org.apache.camel.converter.jaxp.XmlConverter;
 import org.apache.camel.model.language.ExpressionDefinition;
 import org.apache.camel.spi.NamespaceAware;
-import org.apache.camel.util.IOHelper;
+import org.apache.camel.spi.TypeConverterRegistry;
+import org.apache.camel.util.ObjectHelper;
 
 import static 
org.apache.camel.model.ProcessorDefinitionHelper.filterTypeInOutputs;
 
@@ -55,13 +64,7 @@ public final class ModelHelper {
      * @throws JAXBException is throw if error marshalling to XML
      */
     public static String dumpModelAsXml(CamelContext context, NamedNode 
definition) throws JAXBException {
-        JAXBContext jaxbContext;
-        if (context == null) {
-            jaxbContext = createJAXBContext();
-        } else {
-            jaxbContext = 
context.getModelJAXBContextFactory().newJAXBContext();
-        }
-
+        JAXBContext jaxbContext = getJAXBContext(context);
         final Map<String, String> namespaces = new LinkedHashMap<>();
 
         // gather all namespaces from the routes or route which is stored on 
the expression nodes
@@ -75,14 +78,32 @@ public final class ModelHelper {
             extractNamespaces(route, namespaces);
         }
 
-        // TODO: add all namespaces to the root node so the xml output 
includes those
-
         Marshaller marshaller = jaxbContext.createMarshaller();
         marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
         StringWriter buffer = new StringWriter();
         marshaller.marshal(definition, buffer);
 
-        return buffer.toString();
+        Document dom = context.getTypeConverter().convertTo(Document.class, 
buffer.toString());
+
+        // Add additional namespaces to the document root element
+        Element documentElement = dom.getDocumentElement();
+        for (String nsPrefix : namespaces.keySet()) {
+            documentElement.setAttribute("xmlns:" + nsPrefix, 
namespaces.get(nsPrefix));
+        }
+
+        // We invoke the type converter directly because we need to pass some 
custom XML output options
+        TypeConverterRegistry registry = context.getTypeConverterRegistry();
+        XmlConverter xmlConverter = 
registry.getInjector().newInstance(XmlConverter.class);
+
+        Properties outputProperties = new Properties();
+        outputProperties.put(OutputKeys.INDENT, "yes");
+        outputProperties.put(OutputKeys.STANDALONE, "yes");
+        try {
+            return xmlConverter.toStringFromDocument(dom, outputProperties);
+        } catch (TransformerException e) {
+            throw new IllegalStateException("Failed converting document object 
to string", e);
+        }
+
     }
 
     /**
@@ -95,28 +116,7 @@ public final class ModelHelper {
      * @throws javax.xml.bind.JAXBException is thrown if error unmarshalling 
from xml to model
      */
     public static <T extends NamedNode> T createModelFromXml(CamelContext 
context, String xml, Class<T> type) throws JAXBException {
-        // TODO: support injecting namespaces into each namespace aware node
-
-        JAXBContext jaxbContext;
-        if (context == null) {
-            jaxbContext = createJAXBContext();
-        } else {
-            jaxbContext = 
context.getModelJAXBContextFactory().newJAXBContext();
-        }
-
-        StringReader reader = new StringReader(xml);
-        Object result;
-        try {
-            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
-            result = unmarshaller.unmarshal(reader);
-        } finally {
-            IOHelper.close(reader);
-        }
-
-        if (result == null) {
-            throw new JAXBException("Cannot unmarshal to " + type + " using 
JAXB from XML: " + xml);
-        }
-        return type.cast(result);
+        return modelToXml(context, xml, type);
     }
 
     /**
@@ -129,18 +129,109 @@ public final class ModelHelper {
      * @throws javax.xml.bind.JAXBException is thrown if error unmarshalling 
from xml to model
      */
     public static <T extends NamedNode> T createModelFromXml(CamelContext 
context, InputStream stream, Class<T> type) throws JAXBException {
-        // TODO: support injecting namespaces into each namespace aware node
+        return modelToXml(context, stream, type);
+    }
+
+    /**
+     * Marshal the xml to the model definition
+     *
+     * @param context the CamelContext, if <tt>null</tt> then {@link 
org.apache.camel.spi.ModelJAXBContextFactory} is not in use
+     * @param inputStream the xml stream
+     * @throws Exception is thrown if an error is encountered unmarshalling 
from xml to model
+     */
+    public static RoutesDefinition loadRoutesDefinition(CamelContext context, 
InputStream inputStream) throws Exception {
+        JAXBContext jaxbContext = getJAXBContext(context);
+
+        Map<String, String> namespaces = new LinkedHashMap<>();
+        Document dom = 
context.getTypeConverter().mandatoryConvertTo(Document.class, inputStream);
+        extractNamespaces(dom, namespaces);
+
+        Binder<Node> binder = jaxbContext.createBinder();
+        Object result = binder.unmarshal(dom);
+
+        if (result == null) {
+            throw new JAXBException("Cannot unmarshal to RoutesDefinition 
using JAXB");
+        }
+
+        // can either be routes or a single route
+        RoutesDefinition answer;
+        if (result instanceof RouteDefinition) {
+            RouteDefinition route = (RouteDefinition) result;
+            answer = new RoutesDefinition();
+            applyNamespaces(route, namespaces);
+            answer.getRoutes().add(route);
+        } else if (result instanceof RoutesDefinition) {
+            answer = (RoutesDefinition) result;
+            for (RouteDefinition route : answer.getRoutes()) {
+                applyNamespaces(route, namespaces);
+            }
+        } else {
+            throw new IllegalArgumentException("Unmarshalled object is an 
unsupported type: " + ObjectHelper.className(result) + " -> " + result);
+        }
 
+        return answer;
+    }
+
+    private static <T extends NamedNode> T modelToXml(CamelContext context, 
Object object, Class<T> type) throws JAXBException {
+        JAXBContext jaxbContext = getJAXBContext(context);
+
+        Map<String, String> namespaces = new LinkedHashMap<>();
+        Document dom = context.getTypeConverter().convertTo(Document.class, 
object);
+        extractNamespaces(dom, namespaces);
+
+        Binder<Node> binder = jaxbContext.createBinder();
+        Object result = binder.unmarshal(dom);
+
+        if (result == null) {
+            throw new JAXBException("Cannot unmarshal to " + type + " using 
JAXB");
+        }
+
+        // Restore namespaces to anything that's NamespaceAware
+        if (result instanceof RoutesDefinition) {
+            List<RouteDefinition> routes = ((RoutesDefinition) 
result).getRoutes();
+            for (RouteDefinition route : routes) {
+                applyNamespaces(route, namespaces);
+            }
+        } else if (result instanceof RouteDefinition) {
+            RouteDefinition route = (RouteDefinition) result;
+            applyNamespaces(route, namespaces);
+        }
+
+        return type.cast(result);
+    }
+
+    private static JAXBContext getJAXBContext(CamelContext context) throws 
JAXBException {
         JAXBContext jaxbContext;
         if (context == null) {
             jaxbContext = createJAXBContext();
         } else {
             jaxbContext = 
context.getModelJAXBContextFactory().newJAXBContext();
         }
+        return jaxbContext;
+    }
 
-        Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
-        Object result = unmarshaller.unmarshal(stream);
-        return type.cast(result);
+    private static void applyNamespaces(RouteDefinition route, Map<String, 
String> namespaces) {
+        Iterator<ExpressionNode> it = filterTypeInOutputs(route.getOutputs(), 
ExpressionNode.class);
+        while (it.hasNext()) {
+            NamespaceAware na = getNamespaceAwareFromExpression(it.next());
+            if (na != null) {
+                na.setNamespaces(namespaces);
+            }
+        }
+    }
+
+    private static NamespaceAware 
getNamespaceAwareFromExpression(ExpressionNode expressionNode) {
+        ExpressionDefinition ed = expressionNode.getExpression();
+
+        NamespaceAware na = null;
+        Expression exp = ed.getExpressionValue();
+        if (exp instanceof NamespaceAware) {
+            na = (NamespaceAware) exp;
+        } else if (ed instanceof NamespaceAware) {
+            na = (NamespaceAware) ed;
+        }
+
+        return na;
     }
 
     private static JAXBContext createJAXBContext() throws JAXBException {
@@ -152,22 +243,12 @@ public final class ModelHelper {
      * Extract all XML namespaces from the expressions in the route
      *
      * @param route       the route
-     * @param namespaces  the map of namespace to add new found XML namespaces
+     * @param namespaces  the map of namespaces to add discovered XML 
namespaces into
      */
     private static void extractNamespaces(RouteDefinition route, Map<String, 
String> namespaces) {
         Iterator<ExpressionNode> it = filterTypeInOutputs(route.getOutputs(), 
ExpressionNode.class);
         while (it.hasNext()) {
-            ExpressionNode en = it.next();
-            ExpressionDefinition ed = en.getExpression();
-
-            // java-dsl sets directly expression so try this first
-            NamespaceAware na = null;
-            Expression exp = ed.getExpressionValue();
-            if (exp != null && exp instanceof NamespaceAware) {
-                na = (NamespaceAware) exp;
-            } else if (ed instanceof NamespaceAware) {
-                na = (NamespaceAware) ed;
-            }
+            NamespaceAware na = getNamespaceAwareFromExpression(it.next());
 
             if (na != null) {
                 Map<String, String> map = na.getNamespaces();
@@ -177,4 +258,30 @@ public final class ModelHelper {
             }
         }
     }
+
+    /**
+     * Extract all XML namespaces from the root element in a DOM Document
+     *
+     * @param document    the DOM document
+     * @param namespaces  the map of namespaces to add new found XML namespaces
+     */
+    private static void extractNamespaces(Document document, Map<String, 
String> namespaces) throws JAXBException {
+        NamedNodeMap attributes = 
document.getDocumentElement().getAttributes();
+        for (int i = 0; i < attributes.getLength(); i++) {
+            Node item = attributes.item(i);
+            String nsPrefix = item.getNodeName();
+            if (nsPrefix != null && nsPrefix.startsWith("xmlns")) {
+                String nsValue = item.getNodeValue();
+                String[] nsParts = nsPrefix.split(":");
+                if (nsParts.length == 1) {
+                    namespaces.put(nsParts[0], nsValue);
+                } else if (nsParts.length == 2) {
+                    namespaces.put(nsParts[1], nsValue);
+                } else {
+                    // Fallback on adding the namespace prefix as we find it
+                    namespaces.put(nsPrefix, nsValue);
+                }
+            }
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/20a1b012/camel-core/src/main/java/org/apache/camel/util/ExchangeHelper.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/util/ExchangeHelper.java 
b/camel-core/src/main/java/org/apache/camel/util/ExchangeHelper.java
index eca7f0f..ce3fdca 100644
--- a/camel-core/src/main/java/org/apache/camel/util/ExchangeHelper.java
+++ b/camel-core/src/main/java/org/apache/camel/util/ExchangeHelper.java
@@ -51,7 +51,7 @@ import org.apache.camel.spi.UnitOfWork;
 /**
  * Some helper methods for working with {@link Exchange} objects
  *
- * @version 
+ * @version
  */
 public final class ExchangeHelper {
 

http://git-wip-us.apache.org/repos/asf/camel/blob/20a1b012/camel-core/src/test/java/org/apache/camel/component/properties/PropertiesRouteFromTest.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/test/java/org/apache/camel/component/properties/PropertiesRouteFromTest.java
 
b/camel-core/src/test/java/org/apache/camel/component/properties/PropertiesRouteFromTest.java
index 10eff3b..fb6296d 100644
--- 
a/camel-core/src/test/java/org/apache/camel/component/properties/PropertiesRouteFromTest.java
+++ 
b/camel-core/src/test/java/org/apache/camel/component/properties/PropertiesRouteFromTest.java
@@ -38,7 +38,7 @@ public class PropertiesRouteFromTest extends 
ContextTestSupport {
         // use a routes definition to dump the routes
         String xml = ModelHelper.dumpModelAsXml(context, 
context.getRouteDefinition("foo"));
         assertTrue(xml.contains("<from uri=\"{{cool.start}}\"/>"));
-        assertTrue(xml.contains("<to uri=\"{{cool.end}}\""));
+        assertTrue(xml.contains("<to id=\"to1\" uri=\"{{cool.end}}\"/>"));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/camel/blob/20a1b012/camel-core/src/test/java/org/apache/camel/converter/jaxp/XmlConverterTest.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/test/java/org/apache/camel/converter/jaxp/XmlConverterTest.java
 
b/camel-core/src/test/java/org/apache/camel/converter/jaxp/XmlConverterTest.java
index e35a3db..4618131 100644
--- 
a/camel-core/src/test/java/org/apache/camel/converter/jaxp/XmlConverterTest.java
+++ 
b/camel-core/src/test/java/org/apache/camel/converter/jaxp/XmlConverterTest.java
@@ -20,6 +20,7 @@ import java.io.File;
 import java.io.InputStream;
 import java.io.Reader;
 import java.nio.ByteBuffer;
+import java.util.Properties;
 import javax.xml.transform.OutputKeys;
 import javax.xml.transform.Source;
 import javax.xml.transform.dom.DOMSource;
@@ -79,6 +80,33 @@ public class XmlConverterTest extends ContextTestSupport {
         assertEquals("<foo>bar</foo>", out);
     }
 
+    public void testToStringWithDocument() throws Exception {
+        XmlConverter conv = new XmlConverter();
+
+        Document document = conv.createDocument();
+        Element foo = document.createElement("foo");
+        foo.setTextContent("bar");
+        document.appendChild(foo);
+
+        String out = conv.toStringFromDocument(document, null);
+        assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\" 
standalone=\"no\"?><foo>bar</foo>", out);
+    }
+
+    public void testToStringWithDocumentSourceOutputProperties() throws 
Exception {
+        XmlConverter conv = new XmlConverter();
+
+        Document document = conv.createDocument();
+        Element foo = document.createElement("foo");
+        foo.setTextContent("bar");
+        document.appendChild(foo);
+
+        Properties properties = new Properties();
+        properties.put(OutputKeys.ENCODING, "ISO-8859-1");
+
+        String out = conv.toStringFromDocument(document, properties);
+        assertEquals("<?xml version=\"1.0\" encoding=\"ISO-8859-1\" 
standalone=\"no\"?><foo>bar</foo>", out);
+    }
+
     public void testToSource() throws Exception {
         XmlConverter conv = new XmlConverter();
 
@@ -497,7 +525,7 @@ public class XmlConverterTest extends ContextTestSupport {
         XmlConverter conv = new XmlConverter();
 
         SAXSource source = conv.toSAXSource("<foo>bar</foo>", exchange);
-        DOMSource out = conv.toDOMSource(source);
+        DOMSource out = conv.toDOMSource(source, exchange);
         assertNotSame(source, out);
 
         assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\" 
standalone=\"no\"?><foo>bar</foo>", conv.toString(out, exchange));

http://git-wip-us.apache.org/repos/asf/camel/blob/20a1b012/camel-core/src/test/java/org/apache/camel/management/ManagedFromRestGetTest.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/test/java/org/apache/camel/management/ManagedFromRestGetTest.java
 
b/camel-core/src/test/java/org/apache/camel/management/ManagedFromRestGetTest.java
index 5aa9ec7..2b69d03 100644
--- 
a/camel-core/src/test/java/org/apache/camel/management/ManagedFromRestGetTest.java
+++ 
b/camel-core/src/test/java/org/apache/camel/management/ManagedFromRestGetTest.java
@@ -62,10 +62,10 @@ public class ManagedFromRestGetTest extends 
ManagementTestSupport {
         assertTrue(xml.contains("application/json"));
         assertTrue(xml.contains("</rests>"));
 
-        assertTrue(xml.contains("<param name=\"header_letter\" type=\"query\" 
description=\"header param description2\""
-                + " defaultValue=\"b\" required=\"false\" 
collectionFormat=\"multi\" dataType=\"string\">"));
-        assertTrue(xml.contains("<param name=\"header_count\" type=\"header\" 
description=\"header param description1\" "
-                + "defaultValue=\"1\" required=\"true\" 
dataType=\"integer\""));
+        assertTrue(xml.contains("<param collectionFormat=\"multi\" 
dataType=\"string\" defaultValue=\"b\" "
+                + "description=\"header param description2\" 
name=\"header_letter\" required=\"false\" type=\"query\">"));
+        assertTrue(xml.contains("<param dataType=\"integer\" 
defaultValue=\"1\" "
+                + "description=\"header param description1\" 
name=\"header_count\" required=\"true\" type=\"header\">"));
         assertTrue(xml.contains("<value>1</value>"));
         assertTrue(xml.contains("<value>a</value>"));
 

http://git-wip-us.apache.org/repos/asf/camel/blob/20a1b012/camel-core/src/test/java/org/apache/camel/management/ManagedFromRestPlaceholderTest.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/test/java/org/apache/camel/management/ManagedFromRestPlaceholderTest.java
 
b/camel-core/src/test/java/org/apache/camel/management/ManagedFromRestPlaceholderTest.java
index 6eec89b..559943a 100644
--- 
a/camel-core/src/test/java/org/apache/camel/management/ManagedFromRestPlaceholderTest.java
+++ 
b/camel-core/src/test/java/org/apache/camel/management/ManagedFromRestPlaceholderTest.java
@@ -69,10 +69,10 @@ public class ManagedFromRestPlaceholderTest extends 
ManagementTestSupport {
         assertTrue(xml.contains("application/json"));
         assertTrue(xml.contains("</rests>"));
 
-        assertTrue(xml.contains("<param name=\"header_letter\" type=\"query\" 
description=\"header param description2\""
-                + " defaultValue=\"b\" required=\"false\" 
collectionFormat=\"multi\" dataType=\"string\">"));
-        assertTrue(xml.contains("<param name=\"header_count\" type=\"header\" 
description=\"header param description1\" "
-                + "defaultValue=\"1\" required=\"true\" 
dataType=\"integer\">"));
+        assertTrue(xml.contains("<param collectionFormat=\"multi\" 
dataType=\"string\" defaultValue=\"b\" description=\"header param 
description2\" "
+                + "name=\"header_letter\" required=\"false\" 
type=\"query\">"));
+        assertTrue(xml.contains("<param dataType=\"integer\" 
defaultValue=\"1\" description=\"header param description1\" "
+                + "name=\"header_count\" required=\"true\" type=\"header\">"));
         assertTrue(xml.contains("<value>1</value>"));
         assertTrue(xml.contains("<value>a</value>"));
 

http://git-wip-us.apache.org/repos/asf/camel/blob/20a1b012/camel-core/src/test/java/org/apache/camel/model/LoadRouteFromXmlWithNamespaceTest.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/test/java/org/apache/camel/model/LoadRouteFromXmlWithNamespaceTest.java
 
b/camel-core/src/test/java/org/apache/camel/model/LoadRouteFromXmlWithNamespaceTest.java
new file mode 100644
index 0000000..aae0265
--- /dev/null
+++ 
b/camel-core/src/test/java/org/apache/camel/model/LoadRouteFromXmlWithNamespaceTest.java
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.model;
+
+import java.io.InputStream;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.Route;
+import org.apache.camel.component.mock.MockEndpoint;
+
+public class LoadRouteFromXmlWithNamespaceTest extends ContextTestSupport {
+
+    public void testLoadRouteWithNamespaceFromXml() throws Exception {
+        InputStream inputStream = 
getClass().getResourceAsStream("routeWithNamespace.xml");
+        RoutesDefinition routes = context.loadRoutesDefinition(inputStream);
+        context.addRouteDefinitions(routes.getRoutes());
+        context.start();
+
+        Route routeWithNamespace = context.getRoute("routeWithNamespace");
+        assertNotNull("Expected to find route with id: routeWithNamespace", 
routeWithNamespace);
+
+        MockEndpoint bar = context.getEndpoint("mock:bar", MockEndpoint.class);
+        bar.expectedBodiesReceived("Hello from foo");
+
+        // Make sure loaded route can process a XML payload with namespaces 
attached
+        context.createProducerTemplate().sendBody("direct:foo", "<?xml 
version='1.0'?><foo xmlns='http://foo'><bar>cheese</bar></foo>");
+
+        bar.assertIsSatisfied();
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/20a1b012/camel-core/src/test/java/org/apache/camel/util/CreateModelFromXmlTest.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/test/java/org/apache/camel/util/CreateModelFromXmlTest.java 
b/camel-core/src/test/java/org/apache/camel/util/CreateModelFromXmlTest.java
new file mode 100644
index 0000000..29bcf9b
--- /dev/null
+++ b/camel-core/src/test/java/org/apache/camel/util/CreateModelFromXmlTest.java
@@ -0,0 +1,123 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.util;
+
+import java.io.InputStream;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.Expression;
+import org.apache.camel.model.ExpressionNode;
+import org.apache.camel.model.ModelHelper;
+import org.apache.camel.model.RouteDefinition;
+import org.apache.camel.model.RoutesDefinition;
+import org.apache.camel.model.language.ExpressionDefinition;
+import org.apache.camel.spi.NamespaceAware;
+import org.junit.Test;
+
+import static 
org.apache.camel.model.ProcessorDefinitionHelper.filterTypeInOutputs;
+
+public class CreateModelFromXmlTest extends ContextTestSupport {
+
+    public static final String NS_CAMEL = 
"http://camel.apache.org/schema/spring";;
+    public static final String NS_FOO = "http://foo";;
+    public static final String NS_BAR = "http://bar";;
+
+    @Test
+    public void testCreateModelFromXmlForInputStreamWithDefaultNamespace() 
throws Exception {
+        RoutesDefinition routesDefinition = 
createModelFromXml("simpleRoute.xml", false);
+        assertNotNull(routesDefinition);
+
+        Map<String, String> expectedNamespaces = new LinkedHashMap<>();
+        expectedNamespaces.put("xmlns", NS_CAMEL);
+
+        assertNamespacesPresent(routesDefinition, expectedNamespaces);
+    }
+
+    @Test
+    public void testCreateModelFromXmlForInputStreamWithAdditionalNamespaces() 
throws Exception {
+        RoutesDefinition routesDefinition = 
createModelFromXml("simpleRouteWithNamespaces.xml", false);
+        assertNotNull(routesDefinition);
+
+        Map<String, String> expectedNamespaces = new LinkedHashMap<>();
+        expectedNamespaces.put("xmlns", NS_CAMEL);
+        expectedNamespaces.put("foo", NS_FOO);
+        expectedNamespaces.put("bar", NS_BAR);
+
+        assertNamespacesPresent(routesDefinition, expectedNamespaces);
+    }
+
+    @Test
+    public void testCreateModelFromXmlForStringWithDefaultNamespace() throws 
Exception {
+        RoutesDefinition routesDefinition = 
createModelFromXml("simpleRoute.xml", true);
+        assertNotNull(routesDefinition);
+
+        Map<String, String> expectedNamespaces = new LinkedHashMap<>();
+        expectedNamespaces.put("xmlns", NS_CAMEL);
+
+        assertNamespacesPresent(routesDefinition, expectedNamespaces);
+    }
+
+    @Test
+    public void testCreateModelFromXmlForStringWithAdditionalNamespaces() 
throws Exception {
+        RoutesDefinition routesDefinition = 
createModelFromXml("simpleRouteWithNamespaces.xml", true);
+        assertNotNull(routesDefinition);
+
+        Map<String, String> expectedNamespaces = new LinkedHashMap<>();
+        expectedNamespaces.put("xmlns", NS_CAMEL);
+        expectedNamespaces.put("foo", NS_FOO);
+        expectedNamespaces.put("bar", NS_BAR);
+
+        assertNamespacesPresent(routesDefinition, expectedNamespaces);
+    }
+
+    private RoutesDefinition createModelFromXml(String camelContextResource, 
boolean fromString) throws Exception {
+        InputStream inputStream = 
getClass().getResourceAsStream(camelContextResource);
+
+        if (fromString) {
+            String xml = context.getTypeConverter().convertTo(String.class, 
inputStream);
+            return ModelHelper.createModelFromXml(context, xml, 
RoutesDefinition.class);
+        }
+
+        return ModelHelper.createModelFromXml(context, inputStream, 
RoutesDefinition.class);
+    }
+
+    private void assertNamespacesPresent(RoutesDefinition routesDefinition, 
Map<String, String> expectedNamespaces) {
+        for (RouteDefinition route : routesDefinition.getRoutes()) {
+            Iterator<ExpressionNode> it = 
filterTypeInOutputs(route.getOutputs(), ExpressionNode.class);
+            if (it.hasNext()) {
+                ExpressionNode en = it.next();
+                ExpressionDefinition ed = en.getExpression();
+
+                NamespaceAware na = null;
+                Expression exp = ed.getExpressionValue();
+                if (exp != null && exp instanceof NamespaceAware) {
+                    na = (NamespaceAware) exp;
+                } else if (ed instanceof NamespaceAware) {
+                    na = (NamespaceAware) ed;
+                }
+
+                assertNotNull(na);
+                assertEquals(expectedNamespaces, na.getNamespaces());
+            } else {
+                fail("Expected to find at least one ExpressionNode in route");
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/20a1b012/camel-core/src/test/java/org/apache/camel/util/DumpModelAsXmlAggregateRouteTest.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/test/java/org/apache/camel/util/DumpModelAsXmlAggregateRouteTest.java
 
b/camel-core/src/test/java/org/apache/camel/util/DumpModelAsXmlAggregateRouteTest.java
index 3369443..f172942 100644
--- 
a/camel-core/src/test/java/org/apache/camel/util/DumpModelAsXmlAggregateRouteTest.java
+++ 
b/camel-core/src/test/java/org/apache/camel/util/DumpModelAsXmlAggregateRouteTest.java
@@ -44,7 +44,7 @@ public class DumpModelAsXmlAggregateRouteTest extends 
ContextTestSupport {
                 from("direct:start").routeId("myRoute")
                     .to("log:input")
                     .aggregate(header("userId"), new 
GroupedExchangeAggregationStrategy()).completionSize(3)
-                        .to("mock:aggregate")
+                    .to("mock:aggregate")
                     .end()
                     .to("mock:result");
             }

http://git-wip-us.apache.org/repos/asf/camel/blob/20a1b012/camel-core/src/test/java/org/apache/camel/util/DumpModelAsXmlNamespaceTest.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/test/java/org/apache/camel/util/DumpModelAsXmlNamespaceTest.java
 
b/camel-core/src/test/java/org/apache/camel/util/DumpModelAsXmlNamespaceTest.java
index 3216da37..2170609 100644
--- 
a/camel-core/src/test/java/org/apache/camel/util/DumpModelAsXmlNamespaceTest.java
+++ 
b/camel-core/src/test/java/org/apache/camel/util/DumpModelAsXmlNamespaceTest.java
@@ -16,19 +16,33 @@
  */
 package org.apache.camel.util;
 
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
 import org.apache.camel.ContextTestSupport;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.model.ModelHelper;
 
-/**
- *
- */
 public class DumpModelAsXmlNamespaceTest extends ContextTestSupport {
 
+    private static final String URL_FOO = "http://foo.com";;
+    private static final String URL_BAR = "http://bar.com";;
+
     public void testDumpModelAsXml() throws Exception {
         String xml = ModelHelper.dumpModelAsXml(context, 
context.getRouteDefinition("myRoute"));
         assertNotNull(xml);
-        log.info(xml);
+
+        Document dom = context.getTypeConverter().convertTo(Document.class, 
xml);
+        Element rootNode = dom.getDocumentElement();
+        assertNotNull(rootNode);
+
+        String attributeFoo = rootNode.getAttribute("xmlns:foo");
+        assertNotNull(attributeFoo);
+        assertEquals(URL_FOO, attributeFoo);
+
+        String attributeBar = rootNode.getAttribute("xmlns:bar");
+        assertNotNull(attributeBar);
+        assertEquals(URL_BAR, attributeBar);
     }
 
     @Override
@@ -38,8 +52,8 @@ public class DumpModelAsXmlNamespaceTest extends 
ContextTestSupport {
             public void configure() throws Exception {
                 from("direct:start").routeId("myRoute")
                     .choice()
-                        .when(xpath("/foo:customer").namespace("foo", 
"http://foo.com";)).to("mock:foo")
-                        .when(xpath("/bar:customer").namespace("bar", 
"http://bar.com";)).to("mock:bar");
+                        .when(xpath("/foo:customer").namespace("foo", 
URL_FOO)).to("mock:foo")
+                        .when(xpath("/bar:customer").namespace("bar", 
URL_BAR)).to("mock:bar");
             }
         };
     }

http://git-wip-us.apache.org/repos/asf/camel/blob/20a1b012/camel-core/src/test/java/org/apache/camel/util/DumpModelAsXmlPlaceholdersTest.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/test/java/org/apache/camel/util/DumpModelAsXmlPlaceholdersTest.java
 
b/camel-core/src/test/java/org/apache/camel/util/DumpModelAsXmlPlaceholdersTest.java
index c0c25d7..91fb906 100644
--- 
a/camel-core/src/test/java/org/apache/camel/util/DumpModelAsXmlPlaceholdersTest.java
+++ 
b/camel-core/src/test/java/org/apache/camel/util/DumpModelAsXmlPlaceholdersTest.java
@@ -22,9 +22,6 @@ import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.properties.PropertiesComponent;
 import org.apache.camel.model.ModelHelper;
 
-/**
- *
- */
 public class DumpModelAsXmlPlaceholdersTest extends ContextTestSupport {
 
     public void testDumpModelAsXml() throws Exception {
@@ -32,9 +29,9 @@ public class DumpModelAsXmlPlaceholdersTest extends 
ContextTestSupport {
         String xml = ModelHelper.dumpModelAsXml(context, 
context.getRouteDefinition("Gouda"));
         assertNotNull(xml);
         log.info(xml);
-        assertTrue(xml.contains("<route customId=\"true\" id=\"Gouda\" 
xmlns=\"http://camel.apache.org/schema/spring\";>"));
+        assertTrue(xml.contains("<route 
xmlns=\"http://camel.apache.org/schema/spring\"; customId=\"true\" 
id=\"Gouda\">"));
         assertTrue(xml.contains("<from 
uri=\"direct:start-{{cheese.type}}\"/>"));
-        assertTrue(xml.contains("<to uri=\"direct:end-{{cheese.type}}\" 
customId=\"true\" id=\"log\"/>"));
+        assertTrue(xml.contains("<to customId=\"true\" id=\"log\" 
uri=\"direct:end-{{cheese.type}}\"/>"));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/camel/blob/20a1b012/camel-core/src/test/resources/org/apache/camel/model/routeWithNamespace.xml
----------------------------------------------------------------------
diff --git 
a/camel-core/src/test/resources/org/apache/camel/model/routeWithNamespace.xml 
b/camel-core/src/test/resources/org/apache/camel/model/routeWithNamespace.xml
new file mode 100644
index 0000000..a58b66d
--- /dev/null
+++ 
b/camel-core/src/test/resources/org/apache/camel/model/routeWithNamespace.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You 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.
+-->
+<routes xmlns="http://camel.apache.org/schema/spring"; xmlns:foo="http://foo";>
+    <route id="routeWithNamespace">
+        <from uri="direct:foo" />
+        <choice>
+            <when>
+                <xpath>/foo:foo/foo:bar = 'cheese'</xpath>
+                <setBody>
+                    <constant>Hello from foo</constant>
+                </setBody>
+                <to uri="mock:bar"/>
+            </when>
+        </choice>
+        <to uri="log:end"/>
+    </route>
+</routes>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/camel/blob/20a1b012/camel-core/src/test/resources/org/apache/camel/util/simpleRoute.xml
----------------------------------------------------------------------
diff --git 
a/camel-core/src/test/resources/org/apache/camel/util/simpleRoute.xml 
b/camel-core/src/test/resources/org/apache/camel/util/simpleRoute.xml
new file mode 100644
index 0000000..49f5077
--- /dev/null
+++ b/camel-core/src/test/resources/org/apache/camel/util/simpleRoute.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You 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.
+-->
+<!-- START SNIPPET: e1 -->
+<routes xmlns="http://camel.apache.org/schema/spring";>
+    <route id="foo">
+        <from uri="direct:start"/>
+        <choice>
+            <when>
+                <xpath>/a/foo = 'bar'</xpath>
+                <to uri="mock:bar"/>
+            </when>
+        </choice>
+    </route>
+</routes>
+<!-- END SNIPPET: e1 -->
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/camel/blob/20a1b012/camel-core/src/test/resources/org/apache/camel/util/simpleRouteWithNamespaces.xml
----------------------------------------------------------------------
diff --git 
a/camel-core/src/test/resources/org/apache/camel/util/simpleRouteWithNamespaces.xml
 
b/camel-core/src/test/resources/org/apache/camel/util/simpleRouteWithNamespaces.xml
new file mode 100644
index 0000000..9a29480
--- /dev/null
+++ 
b/camel-core/src/test/resources/org/apache/camel/util/simpleRouteWithNamespaces.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You 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.
+-->
+<!-- START SNIPPET: e1 -->
+<routes xmlns="http://camel.apache.org/schema/spring"; xmlns:foo="http://foo"; 
xmlns:bar="http://bar";>
+    <route id="foo">
+        <from uri="direct:start"/>
+        <choice>
+            <when>
+                <xpath>/a/foo = 'bar'</xpath>
+                <to uri="mock:bar"/>
+            </when>
+        </choice>
+    </route>
+</routes>
+<!-- END SNIPPET: e1 -->
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/camel/blob/20a1b012/components/camel-spring/src/test/java/org/apache/camel/spring/DumpModelAsXmlPlaceholdersTest.java
----------------------------------------------------------------------
diff --git 
a/components/camel-spring/src/test/java/org/apache/camel/spring/DumpModelAsXmlPlaceholdersTest.java
 
b/components/camel-spring/src/test/java/org/apache/camel/spring/DumpModelAsXmlPlaceholdersTest.java
index 4f533c1..5bf0585 100644
--- 
a/components/camel-spring/src/test/java/org/apache/camel/spring/DumpModelAsXmlPlaceholdersTest.java
+++ 
b/components/camel-spring/src/test/java/org/apache/camel/spring/DumpModelAsXmlPlaceholdersTest.java
@@ -36,9 +36,9 @@ public class DumpModelAsXmlPlaceholdersTest extends 
SpringTestSupport {
         assertNotNull(xml);
         log.info(xml);
 
-        assertTrue(xml.contains("<route customId=\"true\" id=\"Gouda\" 
xmlns=\"http://camel.apache.org/schema/spring\";>"));
+        assertTrue(xml.contains("<route 
xmlns=\"http://camel.apache.org/schema/spring\"; customId=\"true\" 
id=\"Gouda\">"));
         assertTrue(xml.contains("<from 
uri=\"direct:start-{{cheese.type}}\"/>"));
-        assertTrue(xml.contains("<to uri=\"direct:end-{{cheese.type}}\" 
customId=\"true\" id=\"log\"/>"));
+        assertTrue(xml.contains("<to customId=\"true\" id=\"log\" 
uri=\"direct:end-{{cheese.type}}\"/>"));
     }
 
 }

Reply via email to