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 17:57:19 UTC

[camel] branch main updated: Blueprint (#11240)

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

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


The following commit(s) were added to refs/heads/main by this push:
     new c51b05200fe Blueprint (#11240)
c51b05200fe is described below

commit c51b05200feb5bac00cb0d22716062bbe9746208
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Wed Aug 30 19:43:46 2023 +0200

    Blueprint (#11240)
    
    CAMEL-19806: camel-jbang - Allow to load OSGi <blueprint> with embedded <camelContext>
    CAMEL-19807: Move spring XML <beans> logic to its own class
---
 .../apache/camel/catalog/schemas/camel-spring.xsd  |   1 +
 .../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    | 293 ++-----------------
 .../java/org/apache/camel/main/util/XmlHelper.java |  12 +
 .../xml/blueprint/BlueprintXmlBeansHandler.java    | 224 +++++++++++++++
 .../main/xml/spring/SpringXmlBeansHandler.java     | 316 +++++++++++++++++++++
 .../apache/camel/dsl/xml/io/XmlModelParser.java    |  20 +-
 .../camel/dsl/xml/io/XmlRoutesBuilderLoader.java   |  15 +-
 ...eansLoadTest.java => XmlBlueprintLoadTest.java} |  13 +-
 .../camel/dsl/xml/io/XmlSpringBeansLoadTest.java   |   8 +-
 .../apache/camel/dsl/xml/io/blueprintRoutes.xml    |  50 ++++
 .../packaging/ModelXmlParserGeneratorMojo.java     |   4 +-
 17 files changed, 742 insertions(+), 320 deletions(-)

diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
index b4bdabc4b57..4572bd2431d 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
@@ -13583,6 +13583,7 @@ org.apache.camel.builder.RouteBuilder.
       </xs:element>
       <xs:element maxOccurs="unbounded" minOccurs="0" name="bean" type="tns:registryBeanDefinition"/>
       <xs:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="skip"/>
+      <xs:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="skip"/>
       <xs:element maxOccurs="unbounded" minOccurs="0" ref="tns:restConfiguration"/>
       <xs:element maxOccurs="unbounded" minOccurs="0" ref="tns:rest"/>
       <xs:element maxOccurs="unbounded" minOccurs="0" ref="tns:routeConfiguration"/>
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..5d1a2a7bf55 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 39d8b128c1f..44e39f5de38 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 7cc52d1045f..c436be5703f 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,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.model.Model;
-import org.apache.camel.model.app.RegistryBeanDefinition;
+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;
@@ -87,29 +76,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 +88,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 +100,8 @@ 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();
+    private final BlueprintXmlBeansHandler blueprintXmlBeansHandler = new BlueprintXmlBeansHandler();
 
     public KameletMain() {
         configureInitialProperties(DEFAULT_KAMELETS_LOCATION);
@@ -693,250 +659,33 @@ 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
+            springXmlBeansHandler.processSpringBeans(camelContext, config, springXmls);
+        }
+        if (!blueprintXmls.isEmpty()) {
+            blueprintXmlBeansHandler.processBlueprintBeans(camelContext, config, blueprintXmls);
         }
-    }
-
-    private void processSpringBeans(
-            CamelContext camelContext, MainConfigurationProperties config, final Map<String, Document> xmls) {
-        // 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);
+        blueprintXmlBeansHandler.createAndRegisterBeans(camelContext);
     }
 
     @Override
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
new file mode 100644
index 00000000000..bcdf6ab97fd
--- /dev/null
+++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/blueprint/BlueprintXmlBeansHandler.java
@@ -0,0 +1,224 @@
+/*
+ * 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.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+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.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;
+
+/**
+ * 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(BlueprintXmlBeansHandler.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<>();
+    private final List<RegistryBeanDefinition> delayedRegistrations = new ArrayList<>();
+
+    /**
+     * 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");
+
+        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 def = createBeanModel(camelContext, id, n);
+            LOG.info("Creating bean: {}", def);
+            registerBeanDefinition(camelContext, def, true);
+        }
+
+        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) {
+        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 replace = "{{" + matcher.group(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;
+    }
+
+    /**
+     * 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);
+                }
+            }
+        }
+    }
+
+}
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
new file mode 100644
index 00000000000..f6c4d7eba43
--- /dev/null
+++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/spring/SpringXmlBeansHandler.java
@@ -0,0 +1,316 @@
+/*
+ * 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.spring;
+
+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;
+
+/**
+ * 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);
+    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<>();
+    // 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
+    private final Set<String> infraBeanNames = Set.of("CamelContext", "MainConfiguration");
+
+    /**
+     * 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) {
+
+        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);
+
+        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);
+    }
+
+    /**
+     * 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<>();
+
+        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));
+            }
+
+            addBeanToCamelModel(camelContext, name, def);
+        }
+    }
+
+    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));
+                    }
+                }
+            }
+            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:" + extractValue(camelContext, br.getBeanName(), false) + "'");
+                }
+            }
+            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:" + extractValue(camelContext, br.getBeanName(), false));
+                    } 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:" + extractValue(camelContext, br.getBeanName(), false));
+                            }
+                            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:" + extractValue(camelContext, br.getBeanName(), false));
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    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 replace = "{{" + matcher.group(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-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/XmlSpringBeansLoadTest.java b/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlBlueprintLoadTest.java
similarity index 72%
copy from dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlSpringBeansLoadTest.java
copy to dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlBlueprintLoadTest.java
index a28258f7d62..ce3f61d0629 100644
--- a/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlSpringBeansLoadTest.java
+++ b/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlBlueprintLoadTest.java
@@ -19,20 +19,23 @@ 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.Assertions;
 import org.junit.jupiter.api.Test;
 
-public class XmlSpringBeansLoadTest {
+public class XmlBlueprintLoadTest {
 
     @Test
     public void testLoadRoutesBuilderFromXml() throws Exception {
         try (DefaultCamelContext context = new DefaultCamelContext()) {
-            // load spring XML <beans> with embedded <camelContext>
+            // load OSGi blueprint XML <blueprint> with embedded <camelContext>
             Resource resource = PluginHelper.getResourceLoader(context).resolveResource(
-                    "/org/apache/camel/dsl/xml/io/springBeans.xml");
+                    "/org/apache/camel/dsl/xml/io/blueprintRoutes.xml");
 
-            PluginHelper.getRoutesLoader(context).loadRoutes(resource);
+            Assertions.assertDoesNotThrow(() -> {
+                // should be able to parse the file and not fail (camel-jbang supports creating spring beans)
+                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/java/org/apache/camel/dsl/xml/io/XmlSpringBeansLoadTest.java b/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlSpringBeansLoadTest.java
index a28258f7d62..c5f7ba6b226 100644
--- a/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlSpringBeansLoadTest.java
+++ b/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlSpringBeansLoadTest.java
@@ -19,6 +19,7 @@ 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.Assertions;
 import org.junit.jupiter.api.Test;
 
 public class XmlSpringBeansLoadTest {
@@ -30,9 +31,10 @@ public class XmlSpringBeansLoadTest {
             Resource resource = PluginHelper.getResourceLoader(context).resolveResource(
                     "/org/apache/camel/dsl/xml/io/springBeans.xml");
 
-            PluginHelper.getRoutesLoader(context).loadRoutes(resource);
-
-            // should be able to parse the file and not fail (camel-jbang supports creating spring beans)
+            Assertions.assertDoesNotThrow(() -> {
+                // should be able to parse the file and not fail (camel-jbang supports creating spring beans)
+                PluginHelper.getRoutesLoader(context).loadRoutes(resource);
+            });
         }
     }
 
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),
                                 "}",