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:44 UTC

[camel] branch blueprint created (now cee4cdd2fc4)

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

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


      at cee4cdd2fc4 camel-jbang - Allow to load OSGi <blueprint> with embedded <camelContext>

This branch includes the following new commits:

     new e649eba609e CAMEL-19806: camel-jbang - Allow to load OSGi <blueprint> with embedded <camelContext>
     new 38ceae15f83 CAMEL-19807: Move spring XML <beans> logic to its own class
     new 3a357a3d582 CAMEL-19807: Move spring XML <beans> logic to its own class
     new a1f0ac8aac0 CAMEL-19807: Move spring XML <beans> logic to its own class
     new c3813a64eb5 camel-jbang - Allow to load OSGi <blueprint> with embedded <camelContext>
     new cee4cdd2fc4 camel-jbang - Allow to load OSGi <blueprint> with embedded <camelContext>

The 6 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



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

Posted by da...@apache.org.
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 cee4cdd2fc41d716cc14e31f3c1a23518379a7ac
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Wed Aug 30 13:58:33 2023 +0200

    camel-jbang - Allow to load OSGi <blueprint> with embedded <camelContext>
---
 .../xml/blueprint/BlueprintXmlBeansHandler.java    | 60 +++++++++++++++++++---
 1 file changed, 52 insertions(+), 8 deletions(-)

diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/blueprint/BlueprintXmlBeansHandler.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/blueprint/BlueprintXmlBeansHandler.java
index 9316a5df26a..7fb611d959c 100644
--- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/blueprint/BlueprintXmlBeansHandler.java
+++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/blueprint/BlueprintXmlBeansHandler.java
@@ -16,7 +16,9 @@
  */
 package org.apache.camel.main.xml.blueprint;
 
+import java.util.ArrayList;
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.StringJoiner;
 import java.util.regex.Matcher;
@@ -34,6 +36,7 @@ import org.apache.camel.model.Model;
 import org.apache.camel.model.app.RegistryBeanDefinition;
 import org.apache.camel.spi.Resource;
 import org.apache.camel.spi.ResourceLoader;
+import org.apache.camel.support.PropertyBindingSupport;
 import org.apache.camel.util.StringHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -51,6 +54,7 @@ public class BlueprintXmlBeansHandler {
     // that's why some beans should be processed later
     private final Map<String, Node> delayedBeans = new LinkedHashMap<>();
     private final Map<String, Resource> resources = new LinkedHashMap<>();
+    private final List<RegistryBeanDefinition> delayedRegistrations = new ArrayList<>();
 
     /**
      * Parses the XML documents and discovers blueprint beans, which will be created manually via Camel.
@@ -78,16 +82,20 @@ public class BlueprintXmlBeansHandler {
         for (Map.Entry<String, Node> entry : delayedBeans.entrySet()) {
             String id = entry.getKey();
             Node n = entry.getValue();
-            RegistryBeanDefinition bean = createBeanModel(camelContext, id, n);
-            // create bean and register to camel
-
-            LOG.info("Creating bean: {}", bean);
-            // TODO: create bean from the model
+            RegistryBeanDefinition def = createBeanModel(camelContext, id, n);
+            LOG.info("Creating bean: {}", def);
+            registerBeanDefinition(camelContext, def, true);
+        }
 
-            // register bean into model (as a BeanRegistry that allows Camel DSL to know about these beans)
-            Model model = camelContext.getCamelContextExtension().getContextPlugin(Model.class);
-            model.addRegistryBean(bean);
+        if (!delayedRegistrations.isEmpty()) {
+            // some of the beans were not available yet, so we have to try register them now
+            for (RegistryBeanDefinition def : delayedRegistrations) {
+                LOG.info("Creating bean (2nd-try): {}", def);
+                registerBeanDefinition(camelContext, def, false);
+            }
+            delayedRegistrations.clear();
         }
+
     }
 
     private RegistryBeanDefinition createBeanModel(CamelContext camelContext, String name, Node node) {
@@ -179,4 +187,40 @@ public class BlueprintXmlBeansHandler {
         return val;
     }
 
+    /**
+     * Try to instantiate bean from the definition.
+     */
+    private void registerBeanDefinition(CamelContext camelContext, RegistryBeanDefinition def, boolean delayIfFailed) {
+        String type = def.getType();
+        String name = def.getName();
+        if (name == null || name.trim().isEmpty()) {
+            name = type;
+        }
+        if (type != null) {
+            if (!type.startsWith("#")) {
+                type = "#class:" + type;
+            }
+            try {
+                final Object target = PropertyBindingSupport.resolveBean(camelContext, type);
+
+                if (def.getProperties() != null && !def.getProperties().isEmpty()) {
+                    PropertyBindingSupport.setPropertiesOnTarget(camelContext, target, def.getProperties());
+                }
+                camelContext.getRegistry().unbind(name);
+                camelContext.getRegistry().bind(name, target);
+
+                // register bean in model
+                Model model = camelContext.getCamelContextExtension().getContextPlugin(Model.class);
+                model.addRegistryBean(def);
+
+            } catch (Exception e) {
+                if (delayIfFailed) {
+                    delayedRegistrations.add(def);
+                } else {
+                    LOG.warn("Error creating bean: {} due to: {}. This exception is ignored.", type, e.getMessage(), e);
+                }
+            }
+        }
+    }
+
 }


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

Posted by da...@apache.org.
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),
                                 "}",


[camel] 03/06: CAMEL-19807: Move spring XML logic to its own class

Posted by da...@apache.org.
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 3a357a3d582e9d07be9379c52db8a84eefee58ac
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Wed Aug 30 10:07:17 2023 +0200

    CAMEL-19807: Move spring XML <beans> logic to its own class
---
 .../camel/main/xml/SpringXmlBeansHandler.java      | 184 +++++++++++----------
 1 file changed, 97 insertions(+), 87 deletions(-)

diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/SpringXmlBeansHandler.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/SpringXmlBeansHandler.java
index fba5be91821..6e813560e07 100644
--- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/SpringXmlBeansHandler.java
+++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/SpringXmlBeansHandler.java
@@ -70,29 +70,9 @@ public class SpringXmlBeansHandler {
     private final List<String> delayedBeans = new LinkedList<>();
     private Set<String> infraBeanNames;
 
-    public void createAndRegisterBeans(CamelContext camelContext) {
-        if (delayedBeans.isEmpty()) {
-            return;
-        }
-
-        DefaultListableBeanFactory beanFactory
-                = camelContext.getRegistry().lookupByNameAndType("SpringBeanFactory", DefaultListableBeanFactory.class);
-
-        // we have some beans with classes that we couldn't load before. now, after loading the routes
-        // we may have the needed class definitions
-        for (String beanName : delayedBeans) {
-            BeanDefinition bd = beanFactory.getMergedBeanDefinition(beanName);
-            if (bd instanceof AbstractBeanDefinition abd) {
-                if (!abd.hasBeanClass()) {
-                    Class<?> c = camelContext.getClassResolver().resolveClass(abd.getBeanClassName());
-                    abd.setBeanClass(c);
-                }
-            }
-        }
-
-        instantiateAndRegisterBeans(camelContext, beanFactory, delayedBeans);
-    }
-
+    /**
+     * Parses the XML documents and discovers spring beans, which will be created by Spring {@link BeanFactory}.
+     */
     public void processSpringBeans(
             CamelContext camelContext, MainConfigurationProperties config, final Map<String, Document> xmls) {
 
@@ -153,6 +133,32 @@ public class SpringXmlBeansHandler {
         instantiateAndRegisterBeans(camelContext, beanFactory, beanNames);
     }
 
+    /**
+     * Invoked at later stage to create and register spring beans into Camel {@link org.apache.camel.spi.Registry}.
+     */
+    public void createAndRegisterBeans(CamelContext camelContext) {
+        if (delayedBeans.isEmpty()) {
+            return;
+        }
+
+        DefaultListableBeanFactory beanFactory
+                = camelContext.getRegistry().lookupByNameAndType("SpringBeanFactory", DefaultListableBeanFactory.class);
+
+        // we have some beans with classes that we couldn't load before. now, after loading the routes
+        // we may have the needed class definitions
+        for (String beanName : delayedBeans) {
+            BeanDefinition bd = beanFactory.getMergedBeanDefinition(beanName);
+            if (bd instanceof AbstractBeanDefinition abd) {
+                if (!abd.hasBeanClass()) {
+                    Class<?> c = camelContext.getClassResolver().resolveClass(abd.getBeanClassName());
+                    abd.setBeanClass(c);
+                }
+            }
+        }
+
+        instantiateAndRegisterBeans(camelContext, beanFactory, delayedBeans);
+    }
+
     private void instantiateAndRegisterBeans(
             CamelContext camelContext, DefaultListableBeanFactory beanFactory, List<String> beanNames) {
         List<String> instantiatedBeanNames = new LinkedList<>();
@@ -203,76 +209,80 @@ public class SpringXmlBeansHandler {
                 camelContext.getRegistry().bind(name, (Supplier<Object>) () -> beanFactory.getBean(name));
             }
 
-            // register bean into model (as a BeanRegistry that allows Camel DSL to know about these beans)
-            Model model = camelContext.getCamelContextExtension().getContextPlugin(Model.class);
-            if (model != null) {
-                RegistryBeanDefinition rrd = new RegistryBeanDefinition();
-                if (def instanceof GenericBeanDefinition gbd) {
-                    // set camel resource to refer to the source file
-                    Resource res = gbd.getResource();
-                    if (res != null) {
-                        String fn = res.getFilename();
-                        if (fn != null) {
-                            rrd.setResource(camelContext.getCamelContextExtension().getContextPlugin(ResourceLoader.class)
-                                    .resolveResource("file:" + fn));
-                        }
-                    }
-                }
-                rrd.setType(def.getBeanClassName());
-                rrd.setName(name);
-                model.addRegistryBean(rrd);
+            addBeanToCamelModel(camelContext, name, def);
+        }
+    }
 
-                // constructor arguments
-                ConstructorArgumentValues ctr = def.getConstructorArgumentValues();
-                StringJoiner sj = new StringJoiner(", ");
-                for (ConstructorArgumentValues.ValueHolder v : ctr.getIndexedArgumentValues().values()) {
-                    Object val = v.getValue();
-                    if (val instanceof TypedStringValue tsv) {
-                        sj.add("'" + extractValue(camelContext, tsv.getValue(), false) + "'");
-                    } else if (val instanceof BeanReference br) {
-                        sj.add("'#bean:" + br.getBeanName() + "'");
+    private void addBeanToCamelModel(CamelContext camelContext, String name, BeanDefinition def) {
+        // register bean into model (as a BeanRegistry that allows Camel DSL to know about these beans)
+        Model model = camelContext.getCamelContextExtension().getContextPlugin(Model.class);
+        if (model != null) {
+            RegistryBeanDefinition rrd = new RegistryBeanDefinition();
+            if (def instanceof GenericBeanDefinition gbd) {
+                // set camel resource to refer to the source file
+                Resource res = gbd.getResource();
+                if (res != null) {
+                    String fn = res.getFilename();
+                    if (fn != null) {
+                        rrd.setResource(camelContext.getCamelContextExtension().getContextPlugin(ResourceLoader.class)
+                                .resolveResource("file:" + fn));
                     }
                 }
-                if (sj.length() > 0) {
-                    rrd.setType("#class:" + def.getBeanClassName() + "(" + sj + ")");
+            }
+            rrd.setType(def.getBeanClassName());
+            rrd.setName(name);
+            model.addRegistryBean(rrd);
+
+            // constructor arguments
+            ConstructorArgumentValues ctr = def.getConstructorArgumentValues();
+            StringJoiner sj = new StringJoiner(", ");
+            for (ConstructorArgumentValues.ValueHolder v : ctr.getIndexedArgumentValues().values()) {
+                Object val = v.getValue();
+                if (val instanceof TypedStringValue tsv) {
+                    sj.add("'" + extractValue(camelContext, tsv.getValue(), false) + "'");
+                } else if (val instanceof BeanReference br) {
+                    sj.add("'#bean:" + br.getBeanName() + "'");
                 }
-                // property values
-                if (def.hasPropertyValues()) {
-                    Map<String, Object> properties = new LinkedHashMap<>();
-                    rrd.setProperties(properties);
+            }
+            if (sj.length() > 0) {
+                rrd.setType("#class:" + def.getBeanClassName() + "(" + sj + ")");
+            }
+            // property values
+            if (def.hasPropertyValues()) {
+                Map<String, Object> properties = new LinkedHashMap<>();
+                rrd.setProperties(properties);
 
-                    MutablePropertyValues values = def.getPropertyValues();
-                    for (PropertyValue v : values) {
-                        String key = v.getName();
-                        PropertyValue src = v.getOriginalPropertyValue();
-                        Object val = src.getValue();
-                        if (val instanceof TypedStringValue tsv) {
-                            properties.put(key, extractValue(camelContext, tsv.getValue(), false));
-                        } else if (val instanceof BeanReference br) {
-                            properties.put(key, "#bean:" + br.getBeanName());
-                        } else if (val instanceof List) {
-                            int i = 0;
-                            Iterator<?> it = ObjectHelper.createIterator(val);
-                            while (it.hasNext()) {
-                                String k = key + "[" + i + "]";
-                                val = it.next();
-                                if (val instanceof TypedStringValue tsv) {
-                                    properties.put(k, extractValue(camelContext, tsv.getValue(), false));
-                                } else if (val instanceof BeanReference br) {
-                                    properties.put(k, "#bean:" + br.getBeanName());
-                                }
-                                i++;
+                MutablePropertyValues values = def.getPropertyValues();
+                for (PropertyValue v : values) {
+                    String key = v.getName();
+                    PropertyValue src = v.getOriginalPropertyValue();
+                    Object val = src.getValue();
+                    if (val instanceof TypedStringValue tsv) {
+                        properties.put(key, extractValue(camelContext, tsv.getValue(), false));
+                    } else if (val instanceof BeanReference br) {
+                        properties.put(key, "#bean:" + br.getBeanName());
+                    } else if (val instanceof List) {
+                        int i = 0;
+                        Iterator<?> it = ObjectHelper.createIterator(val);
+                        while (it.hasNext()) {
+                            String k = key + "[" + i + "]";
+                            val = it.next();
+                            if (val instanceof TypedStringValue tsv) {
+                                properties.put(k, extractValue(camelContext, tsv.getValue(), false));
+                            } else if (val instanceof BeanReference br) {
+                                properties.put(k, "#bean:" + br.getBeanName());
                             }
-                        } else if (val instanceof Map) {
-                            Map<TypedStringValue, Object> map = (Map) val;
-                            for (Map.Entry<TypedStringValue, Object> entry : map.entrySet()) {
-                                String k = key + "[" + entry.getKey().getValue() + "]";
-                                val = entry.getValue();
-                                if (val instanceof TypedStringValue tsv) {
-                                    properties.put(k, extractValue(camelContext, tsv.getValue(), false));
-                                } else if (val instanceof BeanReference br) {
-                                    properties.put(k, "#bean:" + br.getBeanName());
-                                }
+                            i++;
+                        }
+                    } else if (val instanceof Map) {
+                        Map<TypedStringValue, Object> map = (Map) val;
+                        for (Map.Entry<TypedStringValue, Object> entry : map.entrySet()) {
+                            String k = key + "[" + entry.getKey().getValue() + "]";
+                            val = entry.getValue();
+                            if (val instanceof TypedStringValue tsv) {
+                                properties.put(k, extractValue(camelContext, tsv.getValue(), false));
+                            } else if (val instanceof BeanReference br) {
+                                properties.put(k, "#bean:" + br.getBeanName());
                             }
                         }
                     }


[camel] 04/06: CAMEL-19807: Move spring XML logic to its own class

Posted by da...@apache.org.
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 a1f0ac8aac06e42e1f1c008feada1f52ca3169b3
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Wed Aug 30 12:55:47 2023 +0200

    CAMEL-19807: Move spring XML <beans> logic to its own class
---
 .../java/org/apache/camel/main/KameletMain.java    |  8 ++--
 .../xml/blueprint/BlueprintXmlBeansHandler.java    | 53 ++++++++++++++++++++++
 .../xml/{ => spring}/SpringXmlBeansHandler.java    |  8 +++-
 3 files changed, 64 insertions(+), 5 deletions(-)

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 aaea4d2631d..800dd4ecb5f 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
@@ -59,7 +59,8 @@ import org.apache.camel.main.download.PackageNameSourceLoader;
 import org.apache.camel.main.download.TypeConverterLoaderDownloadListener;
 import org.apache.camel.main.injection.AnnotationDependencyInjection;
 import org.apache.camel.main.util.ExtraFilesClassLoader;
-import org.apache.camel.main.xml.SpringXmlBeansHandler;
+import org.apache.camel.main.xml.blueprint.BlueprintXmlBeansHandler;
+import org.apache.camel.main.xml.spring.SpringXmlBeansHandler;
 import org.apache.camel.spi.ClassResolver;
 import org.apache.camel.spi.CliConnector;
 import org.apache.camel.spi.CliConnectorFactory;
@@ -100,6 +101,7 @@ public class KameletMain extends MainCommandLineSupport {
     private DependencyDownloaderClassLoader classLoader;
 
     private final SpringXmlBeansHandler springXmlBeansHandler = new SpringXmlBeansHandler();
+    private final BlueprintXmlBeansHandler blueprintXmlBeansHandler = new BlueprintXmlBeansHandler();
 
     public KameletMain() {
         configureInitialProperties(DEFAULT_KAMELETS_LOCATION);
@@ -662,14 +664,14 @@ public class KameletMain extends MainCommandLineSupport {
             springXmlBeansHandler.processSpringBeans(camelContext, config, springXmls);
         }
         if (!blueprintXmls.isEmpty()) {
-            // TODO: blueprint
+            blueprintXmlBeansHandler.processBlueprintBeans(camelContext, config, blueprintXmls);
         }
     }
 
     @Override
     protected void postProcessCamelRegistry(CamelContext camelContext, MainConfigurationProperties config) {
         springXmlBeansHandler.createAndRegisterBeans(camelContext);
-        // TODO: blueprint
+        blueprintXmlBeansHandler.createAndRegisterBeans(camelContext);
     }
 
     @Override
diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/blueprint/BlueprintXmlBeansHandler.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/blueprint/BlueprintXmlBeansHandler.java
new file mode 100644
index 00000000000..959dd09a610
--- /dev/null
+++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/blueprint/BlueprintXmlBeansHandler.java
@@ -0,0 +1,53 @@
+/*
+ * 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.main.xml.blueprint;
+
+import java.util.Map;
+
+import org.w3c.dom.Document;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.main.MainConfigurationProperties;
+import org.apache.camel.main.xml.spring.SpringXmlBeansHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Used for parsing and discovering legacy OSGi <blueprint> XML to make it runnable on camel-jbang, and for tooling to
+ * migrate this to modern Camel DSL in plain Camel XML or YAML DSL.
+ */
+public class BlueprintXmlBeansHandler {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SpringXmlBeansHandler.class);
+
+    /**
+     * Parses the XML documents and discovers blueprint beans, which will be created manually via Camel.
+     */
+    public void processBlueprintBeans(
+            CamelContext camelContext, MainConfigurationProperties config, final Map<String, Document> xmls) {
+
+        LOG.debug("Loading beans from classic OSGi <blueprint> XML");
+    }
+
+    /**
+     * Invoked at later stage to create and register Blueprint beans into Camel {@link org.apache.camel.spi.Registry}.
+     */
+    public void createAndRegisterBeans(CamelContext camelContext) {
+
+    }
+
+}
diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/SpringXmlBeansHandler.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/spring/SpringXmlBeansHandler.java
similarity index 98%
rename from dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/SpringXmlBeansHandler.java
rename to dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/spring/SpringXmlBeansHandler.java
index 6e813560e07..f84ae377082 100644
--- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/SpringXmlBeansHandler.java
+++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/spring/SpringXmlBeansHandler.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.main.xml;
+package org.apache.camel.main.xml.spring;
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
@@ -60,6 +60,10 @@ import org.springframework.core.io.AbstractResource;
 import org.springframework.core.io.Resource;
 import org.springframework.core.metrics.StartupStep;
 
+/**
+ * Used for parsing and discovering legacy Spring XML <beans> to make it runnable on camel-jbang, and for tooling to
+ * migrate this to modern Camel DSL in plain Camel XML or YAML DSL.
+ */
 public class SpringXmlBeansHandler {
 
     private static final Logger LOG = LoggerFactory.getLogger(SpringXmlBeansHandler.class);
@@ -134,7 +138,7 @@ public class SpringXmlBeansHandler {
     }
 
     /**
-     * Invoked at later stage to create and register spring beans into Camel {@link org.apache.camel.spi.Registry}.
+     * Invoked at later stage to create and register Spring beans into Camel {@link org.apache.camel.spi.Registry}.
      */
     public void createAndRegisterBeans(CamelContext camelContext) {
         if (delayedBeans.isEmpty()) {


[camel] 05/06: camel-jbang - Allow to load OSGi with embedded

Posted by da...@apache.org.
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 c3813a64eb575cff7cbaca0abbcb5c568f09f152
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Wed Aug 30 13:31:57 2023 +0200

    camel-jbang - Allow to load OSGi <blueprint> with embedded <camelContext>
---
 .../java/org/apache/camel/main/util/XmlHelper.java |  12 ++
 .../xml/blueprint/BlueprintXmlBeansHandler.java    | 129 +++++++++++++++++++++
 .../main/xml/spring/SpringXmlBeansHandler.java     |   8 +-
 3 files changed, 145 insertions(+), 4 deletions(-)

diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/util/XmlHelper.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/util/XmlHelper.java
index 52b843abfcf..0a362f18334 100644
--- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/util/XmlHelper.java
+++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/util/XmlHelper.java
@@ -20,6 +20,8 @@ import javax.xml.XMLConstants;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
 
+import org.w3c.dom.Node;
+
 import org.apache.camel.util.ObjectHelper;
 
 public final class XmlHelper {
@@ -60,4 +62,14 @@ public final class XmlHelper {
         return factory;
     }
 
+    public static String getAttribute(Node node, String key) {
+        if (node != null && node.hasAttributes()) {
+            Node attr = node.getAttributes().getNamedItem(key);
+            if (attr != null) {
+                return attr.getNodeValue();
+            }
+        }
+        return null;
+    }
+
 }
diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/blueprint/BlueprintXmlBeansHandler.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/blueprint/BlueprintXmlBeansHandler.java
index 959dd09a610..9316a5df26a 100644
--- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/blueprint/BlueprintXmlBeansHandler.java
+++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/blueprint/BlueprintXmlBeansHandler.java
@@ -16,13 +16,25 @@
  */
 package org.apache.camel.main.xml.blueprint;
 
+import java.util.LinkedHashMap;
 import java.util.Map;
+import java.util.StringJoiner;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.main.MainConfigurationProperties;
+import org.apache.camel.main.util.XmlHelper;
 import org.apache.camel.main.xml.spring.SpringXmlBeansHandler;
+import org.apache.camel.model.Model;
+import org.apache.camel.model.app.RegistryBeanDefinition;
+import org.apache.camel.spi.Resource;
+import org.apache.camel.spi.ResourceLoader;
+import org.apache.camel.util.StringHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -33,6 +45,12 @@ import org.slf4j.LoggerFactory;
 public class BlueprintXmlBeansHandler {
 
     private static final Logger LOG = LoggerFactory.getLogger(SpringXmlBeansHandler.class);
+    private static final Pattern BLUEPRINT_PATTERN = Pattern.compile("(\\$\\{.*?})"); // non-greedy mode
+
+    // when preparing blueprint-based beans, we may have problems loading classes which are provided with Java DSL
+    // that's why some beans should be processed later
+    private final Map<String, Node> delayedBeans = new LinkedHashMap<>();
+    private final Map<String, Resource> resources = new LinkedHashMap<>();
 
     /**
      * Parses the XML documents and discovers blueprint beans, which will be created manually via Camel.
@@ -41,13 +59,124 @@ public class BlueprintXmlBeansHandler {
             CamelContext camelContext, MainConfigurationProperties config, final Map<String, Document> xmls) {
 
         LOG.debug("Loading beans from classic OSGi <blueprint> XML");
+
+        xmls.forEach((id, doc) -> {
+            if (id.startsWith("camel-xml-io-dsl-blueprint-xml:")) {
+                // this is a camel bean via camel-xml-io-dsl
+                String fileName = StringHelper.afterLast(id, ":");
+                discoverBeans(camelContext, fileName, doc);
+            }
+        });
     }
 
     /**
      * Invoked at later stage to create and register Blueprint beans into Camel {@link org.apache.camel.spi.Registry}.
      */
     public void createAndRegisterBeans(CamelContext camelContext) {
+        LOG.info("Discovered {} OSGi <blueprint> XML beans", delayedBeans.size());
+
+        for (Map.Entry<String, Node> entry : delayedBeans.entrySet()) {
+            String id = entry.getKey();
+            Node n = entry.getValue();
+            RegistryBeanDefinition bean = createBeanModel(camelContext, id, n);
+            // create bean and register to camel
+
+            LOG.info("Creating bean: {}", bean);
+            // TODO: create bean from the model
+
+            // register bean into model (as a BeanRegistry that allows Camel DSL to know about these beans)
+            Model model = camelContext.getCamelContextExtension().getContextPlugin(Model.class);
+            model.addRegistryBean(bean);
+        }
+    }
+
+    private RegistryBeanDefinition createBeanModel(CamelContext camelContext, String name, Node node) {
+        RegistryBeanDefinition rrd = new RegistryBeanDefinition();
+        rrd.setResource(resources.get(name));
+        rrd.setType(XmlHelper.getAttribute(node, "class"));
+        rrd.setName(name);
+
+        // constructor arguments
+        StringJoiner sj = new StringJoiner(", ");
+        NodeList props = node.getChildNodes();
+        for (int i = 0; i < props.getLength(); i++) {
+            Node child = props.item(i);
+            // assume the args are in order (1, 2)
+            if ("argument".equals(child.getNodeName())) {
+                String val = XmlHelper.getAttribute(child, "value");
+                String ref = XmlHelper.getAttribute(child, "ref");
+                if (val != null) {
+                    sj.add("'" + extractValue(camelContext, val, false) + "'");
+                } else if (ref != null) {
+                    sj.add("'#bean:" + extractValue(camelContext, ref, false) + "'");
+                }
+            }
+        }
+        if (sj.length() > 0) {
+            rrd.setType("#class:" + rrd.getType() + "(" + sj + ")");
+        }
+
+        // property values
+        Map<String, Object> properties = new LinkedHashMap<>();
+        props = node.getChildNodes();
+        for (int i = 0; i < props.getLength(); i++) {
+            Node child = props.item(i);
+            // assume the args are in order (1, 2)
+            if ("property".equals(child.getNodeName())) {
+                String key = XmlHelper.getAttribute(child, "name");
+                String val = XmlHelper.getAttribute(child, "value");
+                String ref = XmlHelper.getAttribute(child, "ref");
+
+                // TODO: List/Map properties
+                if (key != null && val != null) {
+                    properties.put(key, extractValue(camelContext, val, false));
+                } else if (key != null && ref != null) {
+                    properties.put(key, extractValue(camelContext, "#bean:" + ref, false));
+                }
+            }
+        }
+        if (!properties.isEmpty()) {
+            rrd.setProperties(properties);
+        }
+
+        return rrd;
+    }
+
+    private void discoverBeans(CamelContext camelContext, String fileName, Document dom) {
+        Resource resource = camelContext.getCamelContextExtension().getContextPlugin(ResourceLoader.class)
+                .resolveResource("file:" + fileName);
+
+        NodeList beans = dom.getElementsByTagName("bean");
+        for (int i = 0; i < beans.getLength(); i++) {
+            Node n = beans.item(i);
+            if (n.hasAttributes()) {
+                String id = XmlHelper.getAttribute(n, "id");
+                if (id != null) {
+                    delayedBeans.put(id, n);
+                    resources.put(id, resource);
+                }
+            }
+        }
+    }
+
+    protected String extractValue(CamelContext camelContext, String val, boolean resolve) {
+        // blueprint placeholder prefix
+        if (val != null && val.contains("${")) {
+            Matcher matcher = BLUEPRINT_PATTERN.matcher(val);
+            while (matcher.find()) {
+                String group = matcher.group(1);
+                String replace = "{{" + group.substring(2, group.length() - 1) + "}}";
+                val = matcher.replaceFirst(replace);
+                // we changed so reset matcher so it can find more
+                matcher.reset(val);
+            }
+        }
 
+        if (resolve && camelContext != null) {
+            // if running camel then resolve property placeholders from beans
+            val = camelContext.resolvePropertyPlaceholders(val);
+        }
+        return val;
     }
 
 }
diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/spring/SpringXmlBeansHandler.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/spring/SpringXmlBeansHandler.java
index f84ae377082..4473c26a98a 100644
--- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/spring/SpringXmlBeansHandler.java
+++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/spring/SpringXmlBeansHandler.java
@@ -245,7 +245,7 @@ public class SpringXmlBeansHandler {
                 if (val instanceof TypedStringValue tsv) {
                     sj.add("'" + extractValue(camelContext, tsv.getValue(), false) + "'");
                 } else if (val instanceof BeanReference br) {
-                    sj.add("'#bean:" + br.getBeanName() + "'");
+                    sj.add("'#bean:" + extractValue(camelContext, br.getBeanName(), false) + "'");
                 }
             }
             if (sj.length() > 0) {
@@ -264,7 +264,7 @@ public class SpringXmlBeansHandler {
                     if (val instanceof TypedStringValue tsv) {
                         properties.put(key, extractValue(camelContext, tsv.getValue(), false));
                     } else if (val instanceof BeanReference br) {
-                        properties.put(key, "#bean:" + br.getBeanName());
+                        properties.put(key, "#bean:" + extractValue(camelContext, br.getBeanName(), false));
                     } else if (val instanceof List) {
                         int i = 0;
                         Iterator<?> it = ObjectHelper.createIterator(val);
@@ -274,7 +274,7 @@ public class SpringXmlBeansHandler {
                             if (val instanceof TypedStringValue tsv) {
                                 properties.put(k, extractValue(camelContext, tsv.getValue(), false));
                             } else if (val instanceof BeanReference br) {
-                                properties.put(k, "#bean:" + br.getBeanName());
+                                properties.put(k, "#bean:" + extractValue(camelContext, br.getBeanName(), false));
                             }
                             i++;
                         }
@@ -286,7 +286,7 @@ public class SpringXmlBeansHandler {
                             if (val instanceof TypedStringValue tsv) {
                                 properties.put(k, extractValue(camelContext, tsv.getValue(), false));
                             } else if (val instanceof BeanReference br) {
-                                properties.put(k, "#bean:" + br.getBeanName());
+                                properties.put(k, "#bean:" + extractValue(camelContext, br.getBeanName(), false));
                             }
                         }
                     }


[camel] 02/06: CAMEL-19807: Move spring XML logic to its own class

Posted by da...@apache.org.
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 38ceae15f834149eae948e5cc694a59b5ecb8b98
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Wed Aug 30 09:54:09 2023 +0200

    CAMEL-19807: Move spring XML <beans> logic to its own class
---
 .../java/org/apache/camel/main/KameletMain.java    | 280 +------------------
 .../camel/main/xml/SpringXmlBeansHandler.java      | 304 +++++++++++++++++++++
 2 files changed, 310 insertions(+), 274 deletions(-)

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 c2f947f032f..aaea4d2631d 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
@@ -16,24 +16,13 @@
  */
 package org.apache.camel.main;
 
-import java.io.ByteArrayInputStream;
 import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import java.util.Set;
-import java.util.StringJoiner;
 import java.util.TreeMap;
-import java.util.function.Supplier;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 import org.w3c.dom.Document;
 
@@ -70,8 +59,7 @@ import org.apache.camel.main.download.PackageNameSourceLoader;
 import org.apache.camel.main.download.TypeConverterLoaderDownloadListener;
 import org.apache.camel.main.injection.AnnotationDependencyInjection;
 import org.apache.camel.main.util.ExtraFilesClassLoader;
-import org.apache.camel.model.Model;
-import org.apache.camel.model.app.RegistryBeanDefinition;
+import org.apache.camel.main.xml.SpringXmlBeansHandler;
 import org.apache.camel.spi.ClassResolver;
 import org.apache.camel.spi.CliConnector;
 import org.apache.camel.spi.CliConnectorFactory;
@@ -87,29 +75,10 @@ import org.apache.camel.spi.RoutesLoader;
 import org.apache.camel.spi.UriFactoryResolver;
 import org.apache.camel.startup.jfr.FlightRecorderStartupStepRecorder;
 import org.apache.camel.support.DefaultContextReloadStrategy;
-import org.apache.camel.support.ObjectHelper;
 import org.apache.camel.support.PluginHelper;
 import org.apache.camel.support.RouteOnDemandReloadStrategy;
 import org.apache.camel.support.service.ServiceHelper;
 import org.apache.camel.tooling.maven.MavenGav;
-import org.apache.camel.util.StringHelper;
-import org.springframework.beans.MutablePropertyValues;
-import org.springframework.beans.PropertyValue;
-import org.springframework.beans.factory.BeanFactory;
-import org.springframework.beans.factory.CannotLoadBeanClassException;
-import org.springframework.beans.factory.SmartFactoryBean;
-import org.springframework.beans.factory.SmartInitializingSingleton;
-import org.springframework.beans.factory.config.BeanDefinition;
-import org.springframework.beans.factory.config.BeanReference;
-import org.springframework.beans.factory.config.ConstructorArgumentValues;
-import org.springframework.beans.factory.config.TypedStringValue;
-import org.springframework.beans.factory.support.AbstractBeanDefinition;
-import org.springframework.beans.factory.support.DefaultListableBeanFactory;
-import org.springframework.beans.factory.support.GenericBeanDefinition;
-import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
-import org.springframework.core.io.AbstractResource;
-import org.springframework.core.io.Resource;
-import org.springframework.core.metrics.StartupStep;
 
 /**
  * A Main class for booting up Camel with Kamelet in standalone mode.
@@ -118,8 +87,6 @@ public class KameletMain extends MainCommandLineSupport {
 
     public static final String DEFAULT_KAMELETS_LOCATION = "classpath:/kamelets,github:apache:camel-kamelets/kamelets";
 
-    private static final Pattern SPRING_PATTERN = Pattern.compile("(\\$\\{.*?})"); // non-greedy mode
-
     protected final MainRegistry registry = new MainRegistry();
     private boolean download = true;
     private String repos;
@@ -132,10 +99,7 @@ public class KameletMain extends MainCommandLineSupport {
     private DownloadListener downloadListener;
     private DependencyDownloaderClassLoader classLoader;
 
-    // when preparing spring-based beans, we may have problems loading classes which are provided with Java DSL
-    // that's why some beans should be processed later
-    private final List<String> delayedBeans = new LinkedList<>();
-    private Set<String> infraBeanNames;
+    private final SpringXmlBeansHandler springXmlBeansHandler = new SpringXmlBeansHandler();
 
     public KameletMain() {
         configureInitialProperties(DEFAULT_KAMELETS_LOCATION);
@@ -695,249 +659,17 @@ public class KameletMain extends MainCommandLineSupport {
         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);
+            springXmlBeansHandler.processSpringBeans(camelContext, config, springXmls);
         }
         if (!blueprintXmls.isEmpty()) {
-            processBlueprintBeans(camelContext, config, blueprintXmls);
+            // TODO: blueprint
         }
     }
 
-    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
-        // Spring registry and then copy the beans (whether the scope is)
-        final DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
-        beanFactory.setAllowCircularReferences(true); // for now
-        beanFactory.setBeanClassLoader(classLoader);
-        beanFactory.setBeanExpressionResolver((value, beanExpressionContext) -> extractValue(value, true));
-        registry.bind("SpringBeanFactory", beanFactory);
-
-        // register some existing beans (the list may change)
-        // would be nice to keep the documentation up to date: docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
-        infraBeanNames = Set.of("CamelContext", "MainConfiguration");
-        beanFactory.registerSingleton("CamelContext", camelContext);
-        beanFactory.registerSingleton("MainConfiguration", config);
-        // ...
-
-        // instead of generating an MX parser for spring-beans.xsd and use it to read the docs, we can simply
-        // pass w3c Documents directly to Spring
-        final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
-        xmls.forEach((id, doc) -> {
-            reader.registerBeanDefinitions(doc, new AbstractResource() {
-                @Override
-                public String getFilename() {
-                    if (id.startsWith("camel-xml-io-dsl-spring-xml:")) {
-                        // this is a camel bean via camel-xml-io-dsl
-                        return StringHelper.afterLast(id, ":");
-                    }
-                    return null;
-                }
-
-                @Override
-                public String getDescription() {
-                    return id;
-                }
-
-                @Override
-                public InputStream getInputStream() throws IOException {
-                    return new ByteArrayInputStream(new byte[0]);
-                }
-            });
-        });
-
-        // for full interaction between Spring ApplicationContext and its BeanFactory see
-        // org.springframework.context.support.AbstractApplicationContext.refresh()
-        // see org.springframework.context.support.AbstractApplicationContext.prepareBeanFactory() to check
-        // which extra/infra beans are added
-        beanFactory.freezeConfiguration();
-
-        List<String> beanNames = Arrays.asList(beanFactory.getBeanDefinitionNames());
-
-        // Trigger initialization of all non-lazy singleton beans...
-        instantiateAndRegisterBeans(beanFactory, beanNames);
-    }
-
     @Override
     protected void postProcessCamelRegistry(CamelContext camelContext, MainConfigurationProperties config) {
-        if (delayedBeans.isEmpty()) {
-            return;
-        }
-
-        DefaultListableBeanFactory beanFactory
-                = registry.lookupByNameAndType("SpringBeanFactory", DefaultListableBeanFactory.class);
-
-        // we have some beans with classes that we couldn't load before. now, after loading the routes
-        // we may have the needed class definitions
-        for (String beanName : delayedBeans) {
-            BeanDefinition bd = beanFactory.getMergedBeanDefinition(beanName);
-            if (bd instanceof AbstractBeanDefinition abd) {
-                if (!abd.hasBeanClass()) {
-                    Class<?> c = camelContext.getClassResolver().resolveClass(abd.getBeanClassName());
-                    abd.setBeanClass(c);
-                }
-            }
-        }
-
-        instantiateAndRegisterBeans(beanFactory, delayedBeans);
-    }
-
-    private void instantiateAndRegisterBeans(DefaultListableBeanFactory beanFactory, List<String> beanNames) {
-        List<String> instantiatedBeanNames = new LinkedList<>();
-
-        for (String beanName : beanNames) {
-            BeanDefinition bd = beanFactory.getMergedBeanDefinition(beanName);
-            if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
-                try {
-                    if (beanFactory.isFactoryBean(beanName)) {
-                        Object bean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
-                        if (bean instanceof SmartFactoryBean<?> smartFactoryBean && smartFactoryBean.isEagerInit()) {
-                            beanFactory.getBean(beanName);
-                            instantiatedBeanNames.add(beanName);
-                        }
-                    } else {
-                        beanFactory.getBean(beanName);
-                        instantiatedBeanNames.add(beanName);
-                    }
-                } catch (CannotLoadBeanClassException ignored) {
-                    // we'll try to resolve later
-                    delayedBeans.add(beanName);
-                }
-            }
-        }
-
-        // Trigger post-initialization callback for all applicable beans...
-        for (String beanName : instantiatedBeanNames) {
-            Object singletonInstance = beanFactory.getSingleton(beanName);
-            if (singletonInstance instanceof SmartInitializingSingleton smartSingleton) {
-                StartupStep smartInitialize = beanFactory.getApplicationStartup()
-                        .start("spring.beans.smart-initialize")
-                        .tag("beanName", beanName);
-                smartSingleton.afterSingletonsInstantiated();
-                smartInitialize.end();
-            }
-        }
-
-        for (String name : instantiatedBeanNames) {
-            if (infraBeanNames.contains(name)) {
-                continue;
-            }
-            BeanDefinition def = beanFactory.getBeanDefinition(name);
-            if (def.isSingleton()) {
-                // just grab the singleton and put into registry
-                registry.bind(name, beanFactory.getBean(name));
-            } else {
-                // rely on the bean factory to implement prototype scope
-                registry.bind(name, (Supplier<Object>) () -> beanFactory.getBean(name));
-            }
-
-            // register bean into model (as a BeanRegistry that allows Camel DSL to know about these beans)
-            Model model = camelContext.getCamelContextExtension().getContextPlugin(Model.class);
-            if (model != null) {
-                RegistryBeanDefinition rrd = new RegistryBeanDefinition();
-                if (def instanceof GenericBeanDefinition gbd) {
-                    // set camel resource to refer to the source file
-                    Resource res = gbd.getResource();
-                    if (res != null) {
-                        String fn = res.getFilename();
-                        if (fn != null) {
-                            rrd.setResource(camelContext.getCamelContextExtension().getContextPlugin(ResourceLoader.class)
-                                    .resolveResource("file:" + fn));
-                        }
-                    }
-                }
-                rrd.setType(def.getBeanClassName());
-                rrd.setName(name);
-                model.addRegistryBean(rrd);
-
-                // constructor arguments
-                ConstructorArgumentValues ctr = def.getConstructorArgumentValues();
-                StringJoiner sj = new StringJoiner(", ");
-                for (ConstructorArgumentValues.ValueHolder v : ctr.getIndexedArgumentValues().values()) {
-                    Object val = v.getValue();
-                    if (val instanceof TypedStringValue tsv) {
-                        sj.add("'" + extractValue(tsv.getValue(), false) + "'");
-                    } else if (val instanceof BeanReference br) {
-                        sj.add("'#bean:" + br.getBeanName() + "'");
-                    }
-                }
-                if (sj.length() > 0) {
-                    rrd.setType("#class:" + def.getBeanClassName() + "(" + sj + ")");
-                }
-                // property values
-                if (def.hasPropertyValues()) {
-                    Map<String, Object> properties = new LinkedHashMap<>();
-                    rrd.setProperties(properties);
-
-                    MutablePropertyValues values = def.getPropertyValues();
-                    for (PropertyValue v : values) {
-                        String key = v.getName();
-                        PropertyValue src = v.getOriginalPropertyValue();
-                        Object val = src.getValue();
-                        if (val instanceof TypedStringValue tsv) {
-                            properties.put(key, extractValue(tsv.getValue(), false));
-                        } else if (val instanceof BeanReference br) {
-                            properties.put(key, "#bean:" + br.getBeanName());
-                        } else if (val instanceof List) {
-                            int i = 0;
-                            Iterator<?> it = ObjectHelper.createIterator(val);
-                            while (it.hasNext()) {
-                                String k = key + "[" + i + "]";
-                                val = it.next();
-                                if (val instanceof TypedStringValue tsv) {
-                                    properties.put(k, extractValue(tsv.getValue(), false));
-                                } else if (val instanceof BeanReference br) {
-                                    properties.put(k, "#bean:" + br.getBeanName());
-                                }
-                                i++;
-                            }
-                        } else if (val instanceof Map) {
-                            Map<TypedStringValue, Object> map = (Map) val;
-                            for (Map.Entry<TypedStringValue, Object> entry : map.entrySet()) {
-                                String k = key + "[" + entry.getKey().getValue() + "]";
-                                val = entry.getValue();
-                                if (val instanceof TypedStringValue tsv) {
-                                    properties.put(k, extractValue(tsv.getValue(), false));
-                                } else if (val instanceof BeanReference br) {
-                                    properties.put(k, "#bean:" + br.getBeanName());
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    protected String extractValue(String val, boolean resolve) {
-        // spring placeholder prefix
-        if (val != null && val.contains("${")) {
-            Matcher matcher = SPRING_PATTERN.matcher(val);
-            while (matcher.find()) {
-                String group = matcher.group(1);
-                String replace = "{{" + group.substring(2, group.length() - 1) + "}}";
-                val = matcher.replaceFirst(replace);
-                // we changed so reset matcher so it can find more
-                matcher.reset(val);
-            }
-        }
-
-        if (resolve && camelContext != null) {
-            // if running camel then resolve property placeholders from beans
-            val = camelContext.resolvePropertyPlaceholders(val);
-        }
-        return val;
+        springXmlBeansHandler.createAndRegisterBeans(camelContext);
+        // TODO: blueprint
     }
 
     @Override
diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/SpringXmlBeansHandler.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/SpringXmlBeansHandler.java
new file mode 100644
index 00000000000..fba5be91821
--- /dev/null
+++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/SpringXmlBeansHandler.java
@@ -0,0 +1,304 @@
+/*
+ * 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.main.xml;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringJoiner;
+import java.util.function.Supplier;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.w3c.dom.Document;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.main.MainConfigurationProperties;
+import org.apache.camel.model.Model;
+import org.apache.camel.model.app.RegistryBeanDefinition;
+import org.apache.camel.spi.ResourceLoader;
+import org.apache.camel.support.ObjectHelper;
+import org.apache.camel.util.StringHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.MutablePropertyValues;
+import org.springframework.beans.PropertyValue;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.CannotLoadBeanClassException;
+import org.springframework.beans.factory.SmartFactoryBean;
+import org.springframework.beans.factory.SmartInitializingSingleton;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanReference;
+import org.springframework.beans.factory.config.ConstructorArgumentValues;
+import org.springframework.beans.factory.config.TypedStringValue;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.beans.factory.support.GenericBeanDefinition;
+import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
+import org.springframework.core.io.AbstractResource;
+import org.springframework.core.io.Resource;
+import org.springframework.core.metrics.StartupStep;
+
+public class SpringXmlBeansHandler {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SpringXmlBeansHandler.class);
+    private static final Pattern SPRING_PATTERN = Pattern.compile("(\\$\\{.*?})"); // non-greedy mode
+
+    // when preparing spring-based beans, we may have problems loading classes which are provided with Java DSL
+    // that's why some beans should be processed later
+    private final List<String> delayedBeans = new LinkedList<>();
+    private Set<String> infraBeanNames;
+
+    public void createAndRegisterBeans(CamelContext camelContext) {
+        if (delayedBeans.isEmpty()) {
+            return;
+        }
+
+        DefaultListableBeanFactory beanFactory
+                = camelContext.getRegistry().lookupByNameAndType("SpringBeanFactory", DefaultListableBeanFactory.class);
+
+        // we have some beans with classes that we couldn't load before. now, after loading the routes
+        // we may have the needed class definitions
+        for (String beanName : delayedBeans) {
+            BeanDefinition bd = beanFactory.getMergedBeanDefinition(beanName);
+            if (bd instanceof AbstractBeanDefinition abd) {
+                if (!abd.hasBeanClass()) {
+                    Class<?> c = camelContext.getClassResolver().resolveClass(abd.getBeanClassName());
+                    abd.setBeanClass(c);
+                }
+            }
+        }
+
+        instantiateAndRegisterBeans(camelContext, beanFactory, delayedBeans);
+    }
+
+    public 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
+        // Spring registry and then copy the beans (whether the scope is)
+        final DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
+        beanFactory.setAllowCircularReferences(true); // for now
+        beanFactory.setBeanClassLoader(camelContext.getApplicationContextClassLoader());
+        beanFactory.setBeanExpressionResolver((value, beanExpressionContext) -> extractValue(camelContext, value, true));
+        camelContext.getRegistry().bind("SpringBeanFactory", beanFactory);
+
+        // register some existing beans (the list may change)
+        // would be nice to keep the documentation up to date: docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
+        infraBeanNames = Set.of("CamelContext", "MainConfiguration");
+        beanFactory.registerSingleton("CamelContext", camelContext);
+        beanFactory.registerSingleton("MainConfiguration", config);
+        // ...
+
+        // instead of generating an MX parser for spring-beans.xsd and use it to read the docs, we can simply
+        // pass w3c Documents directly to Spring
+        final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
+        xmls.forEach((id, doc) -> {
+            reader.registerBeanDefinitions(doc, new AbstractResource() {
+                @Override
+                public String getFilename() {
+                    if (id.startsWith("camel-xml-io-dsl-spring-xml:")) {
+                        // this is a camel bean via camel-xml-io-dsl
+                        return StringHelper.afterLast(id, ":");
+                    }
+                    return null;
+                }
+
+                @Override
+                public String getDescription() {
+                    return id;
+                }
+
+                @Override
+                public InputStream getInputStream() throws IOException {
+                    return new ByteArrayInputStream(new byte[0]);
+                }
+            });
+        });
+
+        // for full interaction between Spring ApplicationContext and its BeanFactory see
+        // org.springframework.context.support.AbstractApplicationContext.refresh()
+        // see org.springframework.context.support.AbstractApplicationContext.prepareBeanFactory() to check
+        // which extra/infra beans are added
+        beanFactory.freezeConfiguration();
+
+        List<String> beanNames = Arrays.asList(beanFactory.getBeanDefinitionNames());
+
+        // Trigger initialization of all non-lazy singleton beans...
+        instantiateAndRegisterBeans(camelContext, beanFactory, beanNames);
+    }
+
+    private void instantiateAndRegisterBeans(
+            CamelContext camelContext, DefaultListableBeanFactory beanFactory, List<String> beanNames) {
+        List<String> instantiatedBeanNames = new LinkedList<>();
+
+        for (String beanName : beanNames) {
+            BeanDefinition bd = beanFactory.getMergedBeanDefinition(beanName);
+            if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
+                try {
+                    if (beanFactory.isFactoryBean(beanName)) {
+                        Object bean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
+                        if (bean instanceof SmartFactoryBean<?> smartFactoryBean && smartFactoryBean.isEagerInit()) {
+                            beanFactory.getBean(beanName);
+                            instantiatedBeanNames.add(beanName);
+                        }
+                    } else {
+                        beanFactory.getBean(beanName);
+                        instantiatedBeanNames.add(beanName);
+                    }
+                } catch (CannotLoadBeanClassException ignored) {
+                    // we'll try to resolve later
+                    delayedBeans.add(beanName);
+                }
+            }
+        }
+
+        // Trigger post-initialization callback for all applicable beans...
+        for (String beanName : instantiatedBeanNames) {
+            Object singletonInstance = beanFactory.getSingleton(beanName);
+            if (singletonInstance instanceof SmartInitializingSingleton smartSingleton) {
+                StartupStep smartInitialize = beanFactory.getApplicationStartup()
+                        .start("spring.beans.smart-initialize")
+                        .tag("beanName", beanName);
+                smartSingleton.afterSingletonsInstantiated();
+                smartInitialize.end();
+            }
+        }
+
+        for (String name : instantiatedBeanNames) {
+            if (infraBeanNames.contains(name)) {
+                continue;
+            }
+            BeanDefinition def = beanFactory.getBeanDefinition(name);
+            if (def.isSingleton()) {
+                // just grab the singleton and put into registry
+                camelContext.getRegistry().bind(name, beanFactory.getBean(name));
+            } else {
+                // rely on the bean factory to implement prototype scope
+                camelContext.getRegistry().bind(name, (Supplier<Object>) () -> beanFactory.getBean(name));
+            }
+
+            // register bean into model (as a BeanRegistry that allows Camel DSL to know about these beans)
+            Model model = camelContext.getCamelContextExtension().getContextPlugin(Model.class);
+            if (model != null) {
+                RegistryBeanDefinition rrd = new RegistryBeanDefinition();
+                if (def instanceof GenericBeanDefinition gbd) {
+                    // set camel resource to refer to the source file
+                    Resource res = gbd.getResource();
+                    if (res != null) {
+                        String fn = res.getFilename();
+                        if (fn != null) {
+                            rrd.setResource(camelContext.getCamelContextExtension().getContextPlugin(ResourceLoader.class)
+                                    .resolveResource("file:" + fn));
+                        }
+                    }
+                }
+                rrd.setType(def.getBeanClassName());
+                rrd.setName(name);
+                model.addRegistryBean(rrd);
+
+                // constructor arguments
+                ConstructorArgumentValues ctr = def.getConstructorArgumentValues();
+                StringJoiner sj = new StringJoiner(", ");
+                for (ConstructorArgumentValues.ValueHolder v : ctr.getIndexedArgumentValues().values()) {
+                    Object val = v.getValue();
+                    if (val instanceof TypedStringValue tsv) {
+                        sj.add("'" + extractValue(camelContext, tsv.getValue(), false) + "'");
+                    } else if (val instanceof BeanReference br) {
+                        sj.add("'#bean:" + br.getBeanName() + "'");
+                    }
+                }
+                if (sj.length() > 0) {
+                    rrd.setType("#class:" + def.getBeanClassName() + "(" + sj + ")");
+                }
+                // property values
+                if (def.hasPropertyValues()) {
+                    Map<String, Object> properties = new LinkedHashMap<>();
+                    rrd.setProperties(properties);
+
+                    MutablePropertyValues values = def.getPropertyValues();
+                    for (PropertyValue v : values) {
+                        String key = v.getName();
+                        PropertyValue src = v.getOriginalPropertyValue();
+                        Object val = src.getValue();
+                        if (val instanceof TypedStringValue tsv) {
+                            properties.put(key, extractValue(camelContext, tsv.getValue(), false));
+                        } else if (val instanceof BeanReference br) {
+                            properties.put(key, "#bean:" + br.getBeanName());
+                        } else if (val instanceof List) {
+                            int i = 0;
+                            Iterator<?> it = ObjectHelper.createIterator(val);
+                            while (it.hasNext()) {
+                                String k = key + "[" + i + "]";
+                                val = it.next();
+                                if (val instanceof TypedStringValue tsv) {
+                                    properties.put(k, extractValue(camelContext, tsv.getValue(), false));
+                                } else if (val instanceof BeanReference br) {
+                                    properties.put(k, "#bean:" + br.getBeanName());
+                                }
+                                i++;
+                            }
+                        } else if (val instanceof Map) {
+                            Map<TypedStringValue, Object> map = (Map) val;
+                            for (Map.Entry<TypedStringValue, Object> entry : map.entrySet()) {
+                                String k = key + "[" + entry.getKey().getValue() + "]";
+                                val = entry.getValue();
+                                if (val instanceof TypedStringValue tsv) {
+                                    properties.put(k, extractValue(camelContext, tsv.getValue(), false));
+                                } else if (val instanceof BeanReference br) {
+                                    properties.put(k, "#bean:" + br.getBeanName());
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    protected String extractValue(CamelContext camelContext, String val, boolean resolve) {
+        // spring placeholder prefix
+        if (val != null && val.contains("${")) {
+            Matcher matcher = SPRING_PATTERN.matcher(val);
+            while (matcher.find()) {
+                String group = matcher.group(1);
+                String replace = "{{" + group.substring(2, group.length() - 1) + "}}";
+                val = matcher.replaceFirst(replace);
+                // we changed so reset matcher so it can find more
+                matcher.reset(val);
+            }
+        }
+
+        if (resolve && camelContext != null) {
+            // if running camel then resolve property placeholders from beans
+            val = camelContext.resolvePropertyPlaceholders(val);
+        }
+        return val;
+    }
+
+}