You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2023/08/30 11:59:45 UTC

[camel] 01/06: CAMEL-19806: camel-jbang - Allow to load OSGi with embedded

This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch blueprint
in repository https://gitbox.apache.org/repos/asf/camel.git

commit e649eba609e6930025a0ca6fcad70cf7988a3b7e
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Wed Aug 30 07:41:14 2023 +0200

    CAMEL-19806: camel-jbang - Allow to load OSGi <blueprint> with embedded <camelContext>
---
 .../apache/camel/model/app/BeansDefinition.java    | 14 +++++
 .../java/org/apache/camel/xml/in/ModelParser.java  | 14 ++++-
 .../java/org/apache/camel/xml/out/ModelWriter.java |  3 +-
 .../java/org/apache/camel/xml/in/BaseParser.java   | 70 ++++++++++++++--------
 .../org/apache/camel/yaml/out/ModelWriter.java     |  3 +-
 .../apache/camel/dsl/jbang/core/commands/Run.java  |  2 +-
 .../java/org/apache/camel/main/KameletMain.java    | 35 +++++++----
 .../apache/camel/dsl/xml/io/XmlModelParser.java    | 20 +++++--
 .../camel/dsl/xml/io/XmlRoutesBuilderLoader.java   | 15 ++++-
 .../camel/dsl/xml/io/XmlBlueprintLoadTest.java     | 39 ++++++++++++
 .../apache/camel/dsl/xml/io/blueprintRoutes.xml    | 50 ++++++++++++++++
 .../packaging/ModelXmlParserGeneratorMojo.java     |  4 +-
 12 files changed, 219 insertions(+), 50 deletions(-)

diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/app/BeansDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/app/BeansDefinition.java
index debe7c0a9f0..a8230ccff11 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/app/BeansDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/app/BeansDefinition.java
@@ -52,6 +52,7 @@ import org.apache.camel.spi.annotations.ExternalSchemaElement;
         "componentScanning",
         "beans",
         "springBeans",
+        "blueprintBeans",
         "restConfigurations",
         "rests",
         "routeConfigurations",
@@ -82,6 +83,11 @@ public class BeansDefinition {
                            documentElement = "beans")
     @XmlAnyElement
     private List<Element> springBeans = new ArrayList<>();
+    @ExternalSchemaElement(names = { "bean" },
+                           namespace = "http://www.osgi.org/xmlns/blueprint/v1.0.0",
+                           documentElement = "blueprint")
+    @XmlAnyElement
+    private List<Element> blueprintBeans = new ArrayList<>();
 
     // the order comes from <camelContext> (org.apache.camel.spring.xml.CamelContextFactoryBean)
     // to make things less confusing, as it's not easy to simply tell JAXB to use <xsd:choice maxOccurs="unbounded">
@@ -127,6 +133,14 @@ public class BeansDefinition {
         this.springBeans = springBeans;
     }
 
+    public List<Element> getBlueprintBeans() {
+        return blueprintBeans;
+    }
+
+    public void setBlueprintBeans(List<Element> blueprintBeans) {
+        this.blueprintBeans = blueprintBeans;
+    }
+
     public List<RestConfigurationDefinition> getRestConfigurations() {
         return restConfigurations;
     }
diff --git a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
index 074c655829e..d8890ea2ce6 100644
--- a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
+++ b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
@@ -1568,7 +1568,7 @@ public class ModelParser extends BaseParser {
     }
     public Optional<ApplicationDefinition> parseApplicationDefinition()
             throws IOException, XmlPullParserException {
-        String tag = getNextTag("beans", "camel");
+        String tag = getNextTag("beans", "blueprint", "camel");
         if (tag != null) {
             return Optional.of(doParseApplicationDefinition());
         }
@@ -1580,7 +1580,7 @@ public class ModelParser extends BaseParser {
     }
     public Optional<BeansDefinition> parseBeansDefinition()
             throws IOException, XmlPullParserException {
-        String tag = getNextTag("beans", "camel");
+        String tag = getNextTag("beans", "blueprint", "camel");
         if (tag != null) {
             return Optional.of(doParseBeansDefinition());
         }
@@ -1588,7 +1588,15 @@ public class ModelParser extends BaseParser {
     }
     protected <T extends BeansDefinition> ElementHandler<T> beansDefinitionElementHandler() {
         return (def, key) -> {
-            if ("http://www.springframework.org/schema/beans".equals(parser.getNamespace())) {
+            if ("http://www.osgi.org/xmlns/blueprint/v1.0.0".equals(parser.getNamespace())) {
+                Element el = doParseDOMElement("blueprint", "http://www.osgi.org/xmlns/blueprint/v1.0.0", def.getBlueprintBeans());
+                if (el != null) {
+                    doAddElement(el, def.getBlueprintBeans(), def::setBlueprintBeans);
+                    return true;
+                }
+                return false;
+            }
+            else if ("http://www.springframework.org/schema/beans".equals(parser.getNamespace())) {
                 Element el = doParseDOMElement("beans", "http://www.springframework.org/schema/beans", def.getSpringBeans());
                 if (el != null) {
                     doAddElement(el, def.getSpringBeans(), def::setSpringBeans);
diff --git a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
index 3b2487ee6d6..2e89e6409c4 100644
--- a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
+++ b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
@@ -2519,9 +2519,10 @@ public class ModelWriter extends BaseWriter {
             throws IOException {
         doWriteList(null, "route", def.getRoutes(), this::doWriteRouteDefinition);
         domElements(def.getSpringBeans());
-        doWriteList(null, "restConfiguration", def.getRestConfigurations(), this::doWriteRestConfigurationDefinition);
+        domElements(def.getBlueprintBeans());
         doWriteList(null, "component-scan", def.getComponentScanning(), this::doWriteComponentScanDefinition);
         doWriteList(null, "bean", def.getBeans(), this::doWriteRegistryBeanDefinition);
+        doWriteList(null, "restConfiguration", def.getRestConfigurations(), this::doWriteRestConfigurationDefinition);
         doWriteList(null, "rest", def.getRests(), this::doWriteRestDefinition);
         doWriteList(null, "routeConfiguration", def.getRouteConfigurations(), this::doWriteRouteConfigurationDefinition);
         doWriteList(null, "routeTemplate", def.getRouteTemplates(), this::doWriteRouteTemplateDefinition);
diff --git a/core/camel-xml-io/src/main/java/org/apache/camel/xml/in/BaseParser.java b/core/camel-xml-io/src/main/java/org/apache/camel/xml/in/BaseParser.java
index b811d62c339..33caa7cd943 100644
--- a/core/camel-xml-io/src/main/java/org/apache/camel/xml/in/BaseParser.java
+++ b/core/camel-xml-io/src/main/java/org/apache/camel/xml/in/BaseParser.java
@@ -16,12 +16,29 @@
  */
 package org.apache.camel.xml.in;
 
+import org.apache.camel.LineNumberAware;
+import org.apache.camel.model.language.ExpressionDefinition;
+import org.apache.camel.spi.NamespaceAware;
+import org.apache.camel.spi.Resource;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.URISupport;
+import org.apache.camel.xml.io.MXParser;
+import org.apache.camel.xml.io.XmlPullParser;
+import org.apache.camel.xml.io.XmlPullParserException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Text;
+
+import javax.xml.XMLConstants;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.Reader;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Base64;
+import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -31,29 +48,11 @@ import java.util.Set;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
 
-import javax.xml.XMLConstants;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Text;
-
-import org.apache.camel.LineNumberAware;
-import org.apache.camel.model.language.ExpressionDefinition;
-import org.apache.camel.spi.NamespaceAware;
-import org.apache.camel.spi.Resource;
-import org.apache.camel.util.ObjectHelper;
-import org.apache.camel.util.URISupport;
-import org.apache.camel.xml.io.MXParser;
-import org.apache.camel.xml.io.XmlPullParser;
-import org.apache.camel.xml.io.XmlPullParserException;
-
 public class BaseParser {
 
     protected final MXParser parser;
     protected String namespace;
-    protected String secondNamespace = "";
+    protected Set<String> secondaryNamespaces = new HashSet<>();
     protected Resource resource;
 
     public BaseParser(Resource resource) throws IOException, XmlPullParserException {
@@ -88,8 +87,8 @@ public class BaseParser {
         this.namespace = namespace != null ? namespace : "";
     }
 
-    public void addSecondNamespace(String secondNamespace) {
-        this.secondNamespace = secondNamespace;
+    public void addSecondaryNamespace(String namespace) {
+        this.secondaryNamespaces.add(namespace);
     }
 
     protected <T> T doParse(
@@ -368,6 +367,20 @@ public class BaseParser {
         return pn;
     }
 
+    protected String getNextTag(String name, String name2, String name3) throws XmlPullParserException, IOException {
+        if (parser.nextTag() != XmlPullParser.START_TAG) {
+            throw new XmlPullParserException("Expected starting tag");
+        }
+
+        String pn = parser.getName();
+        boolean match = Objects.equals(name, pn) || Objects.equals(name2, pn) || Objects.equals(name3, pn);;
+        if (!match || !matchNamespace(namespace, parser.getNamespace(), null, false)) {
+            return ""; // empty tag
+        }
+
+        return pn;
+    }
+
     protected void handleOtherAttribute(Object definition, String name, String ns, String val) throws XmlPullParserException {
         // Ignore
         if ("http://www.w3.org/2001/XMLSchema-instance".equals(ns)) {
@@ -465,15 +478,22 @@ public class BaseParser {
     }
 
     protected boolean matchNamespace(String ns, boolean optional) {
-        return matchNamespace(ns, namespace, secondNamespace, optional);
+        return matchNamespace(ns, namespace, secondaryNamespaces, optional);
     }
 
-    protected static boolean matchNamespace(String ns, String namespace, String namespace2, boolean optional) {
+    protected static boolean matchNamespace(String ns, String namespace, Set<String> secondaryNamespaces, boolean optional) {
         if (optional && ns.isEmpty()) {
             return true;
         }
-
-        return Objects.equals(ns, namespace) || Objects.equals(ns, namespace2);
+        if (Objects.equals(ns, namespace)) {
+            return true;
+        }
+        for (String second : secondaryNamespaces) {
+            if (Objects.equals(ns, second)) {
+                return true;
+            }
+        }
+        return false;
     }
 
 }
diff --git a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
index 3ab7d56d7d0..3010257af60 100644
--- a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
+++ b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
@@ -2519,9 +2519,10 @@ public class ModelWriter extends BaseWriter {
             throws IOException {
         doWriteList(null, "route", def.getRoutes(), this::doWriteRouteDefinition);
         domElements(def.getSpringBeans());
-        doWriteList(null, "restConfiguration", def.getRestConfigurations(), this::doWriteRestConfigurationDefinition);
+        domElements(def.getBlueprintBeans());
         doWriteList(null, "component-scan", def.getComponentScanning(), this::doWriteComponentScanDefinition);
         doWriteList(null, "bean", def.getBeans(), this::doWriteRegistryBeanDefinition);
+        doWriteList(null, "restConfiguration", def.getRestConfigurations(), this::doWriteRestConfigurationDefinition);
         doWriteList(null, "rest", def.getRests(), this::doWriteRestDefinition);
         doWriteList(null, "routeConfiguration", def.getRouteConfigurations(), this::doWriteRouteConfigurationDefinition);
         doWriteList(null, "routeTemplate", def.getRouteTemplates(), this::doWriteRouteTemplateDefinition);
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
index d4025154fd3..cf96925bb74 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
@@ -93,7 +93,7 @@ public class Run extends CamelCommand {
             "templatedRoute", "templatedRoutes",
             "rest", "rests",
             "routeConfiguration",
-            "beans", "camel"
+            "beans", "blueprint", "camel"
     };
 
     private static final Set<String> ACCEPTED_XML_ROOT_ELEMENTS
diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java
index e437b035c2f..c2f947f032f 100644
--- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java
+++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java
@@ -679,26 +679,41 @@ public class KameletMain extends MainCommandLineSupport {
 
     @Override
     protected void preProcessCamelRegistry(CamelContext camelContext, MainConfigurationProperties config) {
-        // camel-kamelet-main has access to Spring libraries, so we can grab XML documents representing
-        // actual Spring Beans and read them using Spring's BeanFactory to populate Camel registry
-        final Map<String, Document> xmls = new TreeMap<>();
+        final Map<String, Document> springXmls = new TreeMap<>();
+        final Map<String, Document> blueprintXmls = new TreeMap<>();
 
-        Map<String, Document> springBeansDocs = registry.findByTypeWithName(Document.class);
-        if (springBeansDocs != null) {
-            springBeansDocs.forEach((id, doc) -> {
+        Map<String, Document> xmlDocs = registry.findByTypeWithName(Document.class);
+        if (xmlDocs != null) {
+            xmlDocs.forEach((id, doc) -> {
                 if (id.startsWith("camel-xml-io-dsl-spring-xml:")) {
-                    xmls.put(id, doc);
+                    springXmls.put(id, doc);
+                } else if (id.startsWith("camel-xml-io-dsl-blueprint-xml:")) {
+                    blueprintXmls.put(id, doc);
                 }
             });
         }
-
-        if (!xmls.isEmpty()) {
-            processSpringBeans(camelContext, config, xmls);
+        if (!springXmls.isEmpty()) {
+            // camel-kamelet-main has access to Spring libraries, so we can grab XML documents representing
+            // actual Spring Beans and read them using Spring's BeanFactory to populate Camel registry
+            processSpringBeans(camelContext, config, springXmls);
+        }
+        if (!blueprintXmls.isEmpty()) {
+            processBlueprintBeans(camelContext, config, blueprintXmls);
         }
     }
 
+    private void processBlueprintBeans(
+            CamelContext camelContext, MainConfigurationProperties config, final Map<String, Document> xmls) {
+
+        LOG.debug("Loading beans from legacy OSGi <blueprint> XML");
+        // TODO: detect and process beans
+    }
+
     private void processSpringBeans(
             CamelContext camelContext, MainConfigurationProperties config, final Map<String, Document> xmls) {
+
+        LOG.debug("Loading beans from classic Spring <beans> XML");
+
         // we _could_ create something like org.apache.camel.spring.spi.ApplicationContextBeanRepository, but
         // wrapping DefaultListableBeanFactory and use it as one of the
         // org.apache.camel.support.DefaultRegistry.repositories, but for now let's use it to populate
diff --git a/dsl/camel-xml-io-dsl/src/main/java/org/apache/camel/dsl/xml/io/XmlModelParser.java b/dsl/camel-xml-io-dsl/src/main/java/org/apache/camel/dsl/xml/io/XmlModelParser.java
index b440e0a90ce..fde9a848027 100644
--- a/dsl/camel-xml-io-dsl/src/main/java/org/apache/camel/dsl/xml/io/XmlModelParser.java
+++ b/dsl/camel-xml-io-dsl/src/main/java/org/apache/camel/dsl/xml/io/XmlModelParser.java
@@ -23,23 +23,31 @@ import org.apache.camel.xml.in.ModelParser;
 import org.apache.camel.xml.io.XmlPullParserException;
 
 /**
- * XML {@link ModelParser} that supports loading classic Spring XML <beans> with embedded <camelContext>, with limited
- * parsing, to discover <routes> inside <camelContext>.
+ * XML {@link ModelParser} that supports loading:
+ * <ul>
+ * <li>Standard Camel XML DSL</li>
+ * <li>Classic Spring XML <beans> with embedded <camelContext> (limited parsing, to discover <routes> inside
+ * <camelContext>)</li>
+ * <li>Legacy OSGi <blueprint> with embedded <camelContext> (limited parsing, to discover <routes> inside
+ * <camelContext>)</li>
+ * </ul>
  */
 public class XmlModelParser extends ModelParser {
 
     private static final String SPRING_NS = "http://camel.apache.org/schema/spring";
+    private static final String BLUEPRINT_NS = "http://camel.apache.org/schema/blueprint";
 
     public XmlModelParser(Resource input, String namespace) throws IOException, XmlPullParserException {
         super(input, namespace);
-        addSecondNamespace(SPRING_NS);
+        addSecondaryNamespace(SPRING_NS);
+        addSecondaryNamespace(BLUEPRINT_NS);
     }
 
     @Override
     protected boolean handleUnexpectedElement(String namespace, String name) throws XmlPullParserException {
-        // accept embedded <camelContext> inside Spring XML <beans> files, so we can discover
-        // embedded <routes> inside this <camelContext>.
-        if ("camelContext".equals(name) && SPRING_NS.equals(namespace)) {
+        // accept embedded <camelContext> inside Spring XML <beans> files or OSGi <blueprint> files,
+        // so we can discover embedded <routes> inside this <camelContext>.
+        if ("camelContext".equals(name) && (SPRING_NS.equals(namespace) || BLUEPRINT_NS.equals(namespace))) {
             return true;
         }
         return super.handleUnexpectedElement(namespace, name);
diff --git a/dsl/camel-xml-io-dsl/src/main/java/org/apache/camel/dsl/xml/io/XmlRoutesBuilderLoader.java b/dsl/camel-xml-io-dsl/src/main/java/org/apache/camel/dsl/xml/io/XmlRoutesBuilderLoader.java
index bae6983dbac..3dace301432 100644
--- a/dsl/camel-xml-io-dsl/src/main/java/org/apache/camel/dsl/xml/io/XmlRoutesBuilderLoader.java
+++ b/dsl/camel-xml-io-dsl/src/main/java/org/apache/camel/dsl/xml/io/XmlRoutesBuilderLoader.java
@@ -89,7 +89,7 @@ public class XmlRoutesBuilderLoader extends RouteBuilderLoaderSupport {
         XmlStreamInfo xmlInfo = xmlInfo(resource);
         if (xmlInfo.isValid()) {
             String root = xmlInfo.getRootElementName();
-            if ("beans".equals(root) || "camel".equals(root)) {
+            if ("beans".equals(root) || "blueprint".equals(root) || "camel".equals(root)) {
                 new XmlModelParser(resource, xmlInfo.getRootElementNamespace())
                         .parseBeansDefinition()
                         .ifPresent(bd -> {
@@ -116,7 +116,7 @@ public class XmlRoutesBuilderLoader extends RouteBuilderLoaderSupport {
             public void configure() throws Exception {
                 String resourceLocation = input.getLocation();
                 switch (xmlInfo.getRootElementName()) {
-                    case "beans", "camel" -> {
+                    case "beans", "blueprint", "camel" -> {
                         BeansDefinition def = camelAppCache.get(resourceLocation);
                         if (def != null) {
                             configureCamel(def);
@@ -298,6 +298,17 @@ public class XmlRoutesBuilderLoader extends RouteBuilderLoaderSupport {
             String id = String.format("camel-xml-io-dsl-spring-xml:%05d:%s", counter.incrementAndGet(), resource.getLocation());
             getCamelContext().getRegistry().bind(id, doc);
         }
+
+        // <s:bean> elements - all the elements in single BeansDefinition have
+        // one parent org.w3c.dom.Document - and this is what we collect from each resource
+        if (!app.getBlueprintBeans().isEmpty()) {
+            Document doc = app.getBlueprintBeans().get(0).getOwnerDocument();
+            // bind as Document, to be picked up later - bean id allows nice sorting
+            // (can also be single ID - documents will get collected in LinkedHashMap, so we'll be fine)
+            String id = String.format("camel-xml-io-dsl-blueprint-xml:%05d:%s", counter.incrementAndGet(),
+                    resource.getLocation());
+            getCamelContext().getRegistry().bind(id, doc);
+        }
     }
 
     /**
diff --git a/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlBlueprintLoadTest.java b/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlBlueprintLoadTest.java
new file mode 100644
index 00000000000..123f19bff39
--- /dev/null
+++ b/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlBlueprintLoadTest.java
@@ -0,0 +1,39 @@
+/*
+ * 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.dsl.xml.io;
+
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.spi.Resource;
+import org.apache.camel.support.PluginHelper;
+import org.junit.jupiter.api.Test;
+
+public class XmlBlueprintLoadTest {
+
+    @Test
+    public void testLoadRoutesBuilderFromXml() throws Exception {
+        try (DefaultCamelContext context = new DefaultCamelContext()) {
+            // load OSGi blueprint XML <blueprint> with embedded <camelContext>
+            Resource resource = PluginHelper.getResourceLoader(context).resolveResource(
+                    "/org/apache/camel/dsl/xml/io/blueprintRoutes.xml");
+
+            PluginHelper.getRoutesLoader(context).loadRoutes(resource);
+
+            // should be able to parse the file and not fail (camel-jbang supports creating spring beans)
+        }
+    }
+
+}
diff --git a/dsl/camel-xml-io-dsl/src/test/resources/org/apache/camel/dsl/xml/io/blueprintRoutes.xml b/dsl/camel-xml-io-dsl/src/test/resources/org/apache/camel/dsl/xml/io/blueprintRoutes.xml
new file mode 100644
index 00000000000..96515d8c48f
--- /dev/null
+++ b/dsl/camel-xml-io-dsl/src/test/resources/org/apache/camel/dsl/xml/io/blueprintRoutes.xml
@@ -0,0 +1,50 @@
+<?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.
+
+-->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+           xsi:schemaLocation="
+           http://www.osgi.org/xmlns/blueprint/v1.0.0 https://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">
+
+    <!-- spring beans -->
+    <bean id="orderService" class="com.foo.OrderService">
+        <argument index="0" value="true"/>
+        <argument index="1" ref="office"/>
+    </bean>
+    <!-- uses blueprint property placeholder ${xxx} syntax -->
+    <bean id="office" class="com.foo.Address">
+        <property name="zip" value="${zipCode}"/>
+        <property name="street" value="${streetName}"/>
+    </bean>
+
+    <!-- embed Camel with routes -->
+    <camelContext xmlns="http://camel.apache.org/schema/blueprint">
+
+        <route>
+            <from uri="timer:xml?period={{time:1000}}"/>
+            <setBody>
+                <simple>${random(1000)}</simple>
+            </setBody>
+            <bean ref="orderService"/>
+            <log message="${body}"/>
+        </route>
+
+    </camelContext>
+
+</blueprint>
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/ModelXmlParserGeneratorMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/ModelXmlParserGeneratorMojo.java
index 36db5315cf6..c2a5c7cb5ed 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/ModelXmlParserGeneratorMojo.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/ModelXmlParserGeneratorMojo.java
@@ -594,7 +594,9 @@ public class ModelXmlParserGeneratorMojo extends AbstractGeneratorMojo {
                         .setName("parse" + name)
                         .addThrows(IOException.class)
                         .addThrows(XML_PULL_PARSER_EXCEPTION)
-                        .setBody(String.format("String tag = getNextTag(\"%s\", \"%s\");", "beans", "camel"),
+                        .setBody(
+                                String.format("String tag = getNextTag(\"%s\", \"%s\", \"%s\");", "beans", "blueprint",
+                                        "camel"),
                                 "if (tag != null) {",
                                 String.format("    return Optional.of(doParse%s());", name),
                                 "}",