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/10/05 08:05:49 UTC

[camel] branch main updated: CAMEL-19952: camel-core-model - Bean should be able to be created from an inlined script (for advanced use-cases) (#11647)

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 0542da0a4f5 CAMEL-19952: camel-core-model - Bean should be able to be created from an inlined script (for advanced use-cases) (#11647)
0542da0a4f5 is described below

commit 0542da0a4f535eaf841aab3d4c18e6aa2263f3d1
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu Oct 5 10:05:43 2023 +0200

    CAMEL-19952: camel-core-model - Bean should be able to be created from an inlined script (for advanced use-cases) (#11647)
---
 .../apache/camel/catalog/schemas/camel-spring.xsd  |  2 +
 .../camel/model/app/RegistryBeanDefinition.java    | 22 ++++++
 .../java/org/apache/camel/xml/in/ModelParser.java  |  2 +
 .../java/org/apache/camel/xml/out/ModelWriter.java |  2 +
 .../org/apache/camel/xml/LwModelToXMLDumper.java   | 17 ++++-
 .../camel/xml/jaxb/JaxbModelToXMLDumper.java       | 17 ++++-
 .../org/apache/camel/yaml/out/ModelWriter.java     |  2 +
 .../org/apache/camel/yaml/LwModelToYAMLDumper.java |  6 ++
 dsl/camel-xml-io-dsl/pom.xml                       |  5 ++
 .../src/main/docs/java-xml-io-dsl.adoc             | 19 +++++
 .../camel/dsl/xml/io/XmlRoutesBuilderLoader.java   | 81 +++++++++++++++-----
 .../apache/camel/dsl/xml/io/XmlLoadAppTest.java    | 25 +++++++
 .../org/apache/camel/dsl/xml/io/camel-app9.xml     | 39 ++++++++++
 .../dsl/yaml/deserializers/ModelDeserializers.java | 12 +++
 .../dsl/yaml/deserializers/BeansDeserializer.java  | 87 +++++++++++++++++-----
 .../generated/resources/schema/camelYamlDsl.json   |  6 ++
 .../camel-yaml-dsl/src/main/docs/yaml-dsl.adoc     | 20 +++++
 .../org/apache/camel/dsl/yaml/BeansTest.groovy     | 18 +++++
 18 files changed, 336 insertions(+), 46 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 d94d85f3d8d..b4a6bce6207 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
@@ -13600,6 +13600,7 @@ org.apache.camel.builder.RouteBuilder.
     <xs:sequence>
       <xs:element minOccurs="0" name="constructors" type="tns:beanConstructorsDefinition"/>
       <xs:element minOccurs="0" name="properties" type="tns:beanPropertiesDefinition"/>
+      <xs:element minOccurs="0" name="script" type="xs:string"/>
     </xs:sequence>
     <xs:attribute name="name" type="xs:string" use="required"/>
     <xs:attribute name="type" type="xs:string" use="required"/>
@@ -13607,6 +13608,7 @@ org.apache.camel.builder.RouteBuilder.
     <xs:attribute name="destroyMethod" type="xs:string"/>
     <xs:attribute name="factoryMethod" type="xs:string"/>
     <xs:attribute name="factoryBean" type="xs:string"/>
+    <xs:attribute name="scriptLanguage" type="xs:string"/>
   </xs:complexType>
   <xs:complexType name="beanConstructorsDefinition">
     <xs:sequence>
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/app/RegistryBeanDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/app/RegistryBeanDefinition.java
index ad764a8aa71..943d89b0ae3 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/app/RegistryBeanDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/app/RegistryBeanDefinition.java
@@ -54,12 +54,18 @@ public class RegistryBeanDefinition implements ResourceAware {
     private String factoryMethod;
     @XmlAttribute
     private String factoryBean;
+    @XmlAttribute
+    @Metadata(label = "advanced")
+    private String scriptLanguage;
     @XmlElement(name = "constructors")
     @XmlJavaTypeAdapter(BeanConstructorsAdapter.class)
     private Map<Integer, Object> constructors;
     @XmlElement(name = "properties")
     @XmlJavaTypeAdapter(BeanPropertiesAdapter.class)
     private Map<String, Object> properties;
+    @XmlElement(name = "script")
+    @Metadata(label = "advanced")
+    private String script;
 
     public String getName() {
         return name;
@@ -125,6 +131,22 @@ public class RegistryBeanDefinition implements ResourceAware {
         this.properties = properties;
     }
 
+    public String getScriptLanguage() {
+        return scriptLanguage;
+    }
+
+    public void setScriptLanguage(String scriptLanguage) {
+        this.scriptLanguage = scriptLanguage;
+    }
+
+    public void setScript(String script) {
+        this.script = script;
+    }
+
+    public String getScript() {
+        return script;
+    }
+
     @Override
     public Resource getResource() {
         return resource;
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 75abe87c86b..a7483c74b4f 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
@@ -1639,6 +1639,7 @@ public class ModelParser extends BaseParser {
                 case "factoryMethod": def.setFactoryMethod(val); break;
                 case "initMethod": def.setInitMethod(val); break;
                 case "name": def.setName(val); break;
+                case "scriptLanguage": def.setScriptLanguage(val); break;
                 case "type": def.setType(val); break;
                 default: return false;
             }
@@ -1647,6 +1648,7 @@ public class ModelParser extends BaseParser {
             switch (key) {
                 case "constructors": def.setConstructors(new BeanConstructorsAdapter().unmarshal(doParseBeanConstructorsDefinition())); break;
                 case "properties": def.setProperties(new BeanPropertiesAdapter().unmarshal(doParseBeanPropertiesDefinition())); break;
+                case "script": def.setScript(doParseText()); break;
                 default: return false;
             }
             return true;
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 3085681d84a..a88d2559a1c 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
@@ -2568,11 +2568,13 @@ public class ModelWriter extends BaseWriter {
         startElement(name);
         doWriteAttribute("factoryMethod", def.getFactoryMethod());
         doWriteAttribute("initMethod", def.getInitMethod());
+        doWriteAttribute("scriptLanguage", def.getScriptLanguage());
         doWriteAttribute("name", def.getName());
         doWriteAttribute("destroyMethod", def.getDestroyMethod());
         doWriteAttribute("type", def.getType());
         doWriteAttribute("factoryBean", def.getFactoryBean());
         doWriteElement("constructors", new BeanConstructorsAdapter().marshal(def.getConstructors()), this::doWriteBeanConstructorsDefinition);
+        doWriteElement("script", def.getScript(), this::doWriteString);
         doWriteElement("properties", new BeanPropertiesAdapter().marshal(def.getProperties()), this::doWriteBeanPropertiesDefinition);
         endElement(name);
     }
diff --git a/core/camel-xml-io/src/main/java/org/apache/camel/xml/LwModelToXMLDumper.java b/core/camel-xml-io/src/main/java/org/apache/camel/xml/LwModelToXMLDumper.java
index 9ad46e95510..0ca0a23cc76 100644
--- a/core/camel-xml-io/src/main/java/org/apache/camel/xml/LwModelToXMLDumper.java
+++ b/core/camel-xml-io/src/main/java/org/apache/camel/xml/LwModelToXMLDumper.java
@@ -326,16 +326,25 @@ public class LwModelToXMLDumper implements ModelToXMLDumper {
             }
             buffer.write(String.format("    <bean name=\"%s\" type=\"%s\"", b.getName(), type));
             if (b.getFactoryBean() != null) {
-                buffer.write(String.format(" factory-bean=\"%s\"", b.getFactoryBean()));
+                buffer.write(String.format(" factoryBean=\"%s\"", b.getFactoryBean()));
             }
             if (b.getFactoryMethod() != null) {
-                buffer.write(String.format(" factory-method=\"%s\"", b.getFactoryMethod()));
+                buffer.write(String.format(" factoryMethod=\"%s\"", b.getFactoryMethod()));
             }
             if (b.getInitMethod() != null) {
-                buffer.write(String.format(" init-method=\"%s\"", b.getInitMethod()));
+                buffer.write(String.format(" initMethod=\"%s\"", b.getInitMethod()));
             }
             if (b.getDestroyMethod() != null) {
-                buffer.write(String.format(" destroy-method=\"%s\"", b.getDestroyMethod()));
+                buffer.write(String.format(" destroyMethod=\"%s\"", b.getDestroyMethod()));
+            }
+            if (b.getScriptLanguage() != null) {
+                buffer.write(String.format(" scriptLanguage=\"%s\"", b.getScriptLanguage()));
+            }
+            if (b.getScript() != null) {
+                buffer.write(String.format("        <script>%n"));
+                buffer.write(b.getScript());
+                buffer.write("\n");
+                buffer.write(String.format("        </script>%n"));
             }
             buffer.write(">\n");
             if (b.getConstructors() != null && !b.getConstructors().isEmpty()) {
diff --git a/core/camel-xml-jaxb/src/main/java/org/apache/camel/xml/jaxb/JaxbModelToXMLDumper.java b/core/camel-xml-jaxb/src/main/java/org/apache/camel/xml/jaxb/JaxbModelToXMLDumper.java
index 6e6a1abe149..1c6c67f124b 100644
--- a/core/camel-xml-jaxb/src/main/java/org/apache/camel/xml/jaxb/JaxbModelToXMLDumper.java
+++ b/core/camel-xml-jaxb/src/main/java/org/apache/camel/xml/jaxb/JaxbModelToXMLDumper.java
@@ -339,16 +339,25 @@ public class JaxbModelToXMLDumper implements ModelToXMLDumper {
             }
             buffer.write(String.format("    <bean name=\"%s\" type=\"%s\"", b.getName(), type));
             if (b.getFactoryBean() != null) {
-                buffer.write(String.format(" factory-bean=\"%s\"", b.getFactoryBean()));
+                buffer.write(String.format(" factoryBean=\"%s\"", b.getFactoryBean()));
             }
             if (b.getFactoryMethod() != null) {
-                buffer.write(String.format(" factory-method=\"%s\"", b.getFactoryMethod()));
+                buffer.write(String.format(" factoryMethod=\"%s\"", b.getFactoryMethod()));
             }
             if (b.getInitMethod() != null) {
-                buffer.write(String.format(" init-method=\"%s\"", b.getInitMethod()));
+                buffer.write(String.format(" initMethod=\"%s\"", b.getInitMethod()));
             }
             if (b.getDestroyMethod() != null) {
-                buffer.write(String.format(" destroy-method=\"%s\"", b.getDestroyMethod()));
+                buffer.write(String.format(" destroyMethod=\"%s\"", b.getDestroyMethod()));
+            }
+            if (b.getScriptLanguage() != null) {
+                buffer.write(String.format(" scriptLanguage=\"%s\"", b.getScriptLanguage()));
+            }
+            if (b.getScript() != null) {
+                buffer.write(String.format("        <script>%n"));
+                buffer.write(b.getScript());
+                buffer.write("\n");
+                buffer.write(String.format("        </script>%n"));
             }
             buffer.write(">\n");
             if (b.getConstructors() != null && !b.getConstructors().isEmpty()) {
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 82e928bc44c..43aadc53dfb 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
@@ -2568,11 +2568,13 @@ public class ModelWriter extends BaseWriter {
         startElement(name);
         doWriteAttribute("factoryMethod", def.getFactoryMethod());
         doWriteAttribute("initMethod", def.getInitMethod());
+        doWriteAttribute("scriptLanguage", def.getScriptLanguage());
         doWriteAttribute("name", def.getName());
         doWriteAttribute("destroyMethod", def.getDestroyMethod());
         doWriteAttribute("type", def.getType());
         doWriteAttribute("factoryBean", def.getFactoryBean());
         doWriteElement("constructors", new BeanConstructorsAdapter().marshal(def.getConstructors()), this::doWriteBeanConstructorsDefinition);
+        doWriteElement("script", def.getScript(), this::doWriteString);
         doWriteElement("properties", new BeanPropertiesAdapter().marshal(def.getProperties()), this::doWriteBeanPropertiesDefinition);
         endElement(name);
     }
diff --git a/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/LwModelToYAMLDumper.java b/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/LwModelToYAMLDumper.java
index 46decad3e35..5268f4d87e9 100644
--- a/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/LwModelToYAMLDumper.java
+++ b/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/LwModelToYAMLDumper.java
@@ -326,6 +326,12 @@ public class LwModelToYAMLDumper implements ModelToYAMLDumper {
             if (b.getDestroyMethod() != null) {
                 buffer.write(String.format("      destroyMethod: \"%s\"%n", b.getDestroyMethod()));
             }
+            if (b.getScriptLanguage() != null) {
+                buffer.write(String.format("      scriptLanguage: \"%s\"%n", b.getScriptLanguage()));
+            }
+            if (b.getScript() != null) {
+                buffer.write(String.format("      script: \"%s\"%n", b.getScript()));
+            }
             if (b.getConstructors() != null && !b.getConstructors().isEmpty()) {
                 buffer.write(String.format("      constructors:%n"));
                 int counter = 0;
diff --git a/dsl/camel-xml-io-dsl/pom.xml b/dsl/camel-xml-io-dsl/pom.xml
index 814444a0c60..7bf564d9685 100644
--- a/dsl/camel-xml-io-dsl/pom.xml
+++ b/dsl/camel-xml-io-dsl/pom.xml
@@ -106,6 +106,11 @@
             <artifactId>camel-seda</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-groovy</artifactId>
+            <scope>test</scope>
+        </dependency>
 
         <dependency>
             <groupId>org.assertj</groupId>
diff --git a/dsl/camel-xml-io-dsl/src/main/docs/java-xml-io-dsl.adoc b/dsl/camel-xml-io-dsl/src/main/docs/java-xml-io-dsl.adoc
index b985c67c4df..138021c6990 100644
--- a/dsl/camel-xml-io-dsl/src/main/docs/java-xml-io-dsl.adoc
+++ b/dsl/camel-xml-io-dsl/src/main/docs/java-xml-io-dsl.adoc
@@ -222,6 +222,25 @@ public class MyHelper {
 
 NOTE: The factory method must be `public static`.
 
+=== Creating beans using script language
+
+For advanced use-cases then Camel allows to inline a script language, such as groovy, java, javascript, etc, to create the bean.
+This gives flexibility to use a bit of programming to create and configure the bean.
+
+[source,xml]
+----
+	<bean name="myBean" type="com.acme.MyBean" scriptLanguage="groovy">
+        <script>
+      // some groovy script here to create the bean
+      bean = ...
+      ...
+      return bean
+        </script>
+	</bean>
+----
+
+NOTE: When using `script` then constructors and factory bean/method is not in use
+
 === Using init and destroy methods on beans
 
 Sometimes beans need to do some initialization and cleanup work before a bean is ready to be used.
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 9fa17215c3a..2b3d858b849 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
@@ -18,6 +18,7 @@ package org.apache.camel.dsl.xml.io;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -32,6 +33,9 @@ import org.w3c.dom.Document;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.CamelContextAware;
+import org.apache.camel.Exchange;
+import org.apache.camel.Expression;
+import org.apache.camel.NoSuchBeanException;
 import org.apache.camel.api.management.ManagedResource;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.builder.RouteConfigurationBuilder;
@@ -49,11 +53,15 @@ import org.apache.camel.model.app.BeansDefinition;
 import org.apache.camel.model.app.RegistryBeanDefinition;
 import org.apache.camel.model.rest.RestDefinition;
 import org.apache.camel.model.rest.RestsDefinition;
+import org.apache.camel.spi.ExchangeFactory;
+import org.apache.camel.spi.Language;
 import org.apache.camel.spi.Resource;
+import org.apache.camel.spi.ScriptingLanguage;
 import org.apache.camel.spi.annotations.RoutesLoader;
 import org.apache.camel.support.CachedResource;
 import org.apache.camel.support.ObjectHelper;
 import org.apache.camel.support.PropertyBindingSupport;
+import org.apache.camel.support.ScriptHelper;
 import org.apache.camel.support.scan.PackageScanHelper;
 import org.apache.camel.util.KeyValueHolder;
 import org.apache.camel.util.StringHelper;
@@ -339,34 +347,69 @@ public class XmlRoutesBuilderLoader extends RouteBuilderLoaderSupport {
     }
 
     public Object newInstance(RegistryBeanDefinition def, CamelContext context) throws Exception {
+        Object target;
 
         String type = def.getType();
         if (!type.startsWith("#")) {
             type = "#class:" + type;
         }
 
-        // factory bean/method
-        if (def.getFactoryBean() != null && def.getFactoryMethod() != null) {
-            type = type + "#" + def.getFactoryBean() + ":" + def.getFactoryMethod();
-        } else if (def.getFactoryMethod() != null) {
-            type = type + "#" + def.getFactoryMethod();
-        }
-        // property binding support has constructor arguments as part of the type
-        StringJoiner ctr = new StringJoiner(", ");
-        if (def.getConstructors() != null && !def.getConstructors().isEmpty()) {
-            // need to sort constructor args based on index position
-            Map<Integer, Object> sorted = new TreeMap<>(def.getConstructors());
-            for (Object val : sorted.values()) {
-                String text = val.toString();
-                if (!StringHelper.isQuoted(text)) {
-                    text = "\"" + text + "\"";
+        if (def.getScriptLanguage() != null && def.getScript() != null) {
+            // create bean via the script
+            final Language lan = context.resolveLanguage(def.getScriptLanguage());
+            final ScriptingLanguage slan = lan instanceof ScriptingLanguage ? (ScriptingLanguage) lan : null;
+            String fqn = def.getType();
+            if (fqn.startsWith("#class:")) {
+                fqn = fqn.substring(7);
+            }
+            final Class<?> clazz = context.getClassResolver().resolveMandatoryClass(fqn);
+            if (slan != null) {
+                // scripting language should be evaluated with context as binding
+                Map<String, Object> bindings = new HashMap<>();
+                bindings.put("context", context);
+                target = slan.evaluate(def.getScript(), bindings, clazz);
+            } else {
+                // exchange based languages needs a dummy exchange to be evaluated
+                ExchangeFactory ef = context.getCamelContextExtension().getExchangeFactory();
+                Exchange dummy = ef.create(false);
+                try {
+                    String text = ScriptHelper.resolveOptionalExternalScript(context, dummy, def.getScript());
+                    Expression exp = lan.createExpression(text);
+                    target = exp.evaluate(dummy, clazz);
+                } finally {
+                    ef.release(dummy);
                 }
-                ctr.add(text);
             }
-            type = type + "(" + ctr + ")";
-        }
 
-        final Object target = PropertyBindingSupport.resolveBean(context, type);
+            // a bean must be created
+            if (target == null) {
+                throw new NoSuchBeanException(def.getName(), "Creating bean using script returned null");
+            }
+
+        } else {
+            // factory bean/method
+            if (def.getFactoryBean() != null && def.getFactoryMethod() != null) {
+                type = type + "#" + def.getFactoryBean() + ":" + def.getFactoryMethod();
+            } else if (def.getFactoryMethod() != null) {
+                type = type + "#" + def.getFactoryMethod();
+            }
+            // property binding support has constructor arguments as part of the type
+            StringJoiner ctr = new StringJoiner(", ");
+            if (def.getConstructors() != null && !def.getConstructors().isEmpty()) {
+                // need to sort constructor args based on index position
+                Map<Integer, Object> sorted = new TreeMap<>(def.getConstructors());
+                for (Object val : sorted.values()) {
+                    String text = val.toString();
+                    if (!StringHelper.isQuoted(text)) {
+                        text = "\"" + text + "\"";
+                    }
+                    ctr.add(text);
+                }
+                type = type + "(" + ctr + ")";
+            }
+
+            target = PropertyBindingSupport.resolveBean(context, type);
+        }
 
         if (def.getProperties() != null && !def.getProperties().isEmpty()) {
             PropertyBindingSupport.setPropertiesOnTarget(context, target, def.getProperties());
diff --git a/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlLoadAppTest.java b/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlLoadAppTest.java
index d5984129b8d..4039d20dd97 100644
--- a/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlLoadAppTest.java
+++ b/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlLoadAppTest.java
@@ -227,4 +227,29 @@ public class XmlLoadAppTest {
         }
     }
 
+    @Test
+    public void testLoadCamelAppWithBeanScript() throws Exception {
+        try (DefaultCamelContext context = new DefaultCamelContext()) {
+            context.start();
+
+            Resource resource = PluginHelper.getResourceLoader(context).resolveResource(
+                    "/org/apache/camel/dsl/xml/io/camel-app9.xml");
+
+            RoutesLoader routesLoader = PluginHelper.getRoutesLoader(context);
+            routesLoader.preParseRoute(resource, false);
+            routesLoader.loadRoutes(resource);
+
+            assertNotNull(context.getRoute("r9"), "Loaded r9 route should be there");
+            assertEquals(1, context.getRoutes().size());
+
+            // test that loaded route works
+            MockEndpoint y8 = context.getEndpoint("mock:y9", MockEndpoint.class);
+            y8.expectedBodiesReceived("Hi World from groovy Uranus");
+            context.createProducerTemplate().sendBody("direct:x9", "I'm Uranus");
+            y8.assertIsSatisfied();
+
+            context.stop();
+        }
+    }
+
 }
diff --git a/dsl/camel-xml-io-dsl/src/test/resources/org/apache/camel/dsl/xml/io/camel-app9.xml b/dsl/camel-xml-io-dsl/src/test/resources/org/apache/camel/dsl/xml/io/camel-app9.xml
new file mode 100644
index 00000000000..54ce75f60ad
--- /dev/null
+++ b/dsl/camel-xml-io-dsl/src/test/resources/org/apache/camel/dsl/xml/io/camel-app9.xml
@@ -0,0 +1,39 @@
+<?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.
+
+-->
+<camel xmlns="http://camel.apache.org/schema/spring" xmlns:s="http://www.springframework.org/schema/beans">
+
+    <bean name="xml-bean-from-registry" type="org.apache.camel.dsl.xml.io.beans.Greeter"
+          scriptLanguage="groovy">
+        <script>
+            b = new org.apache.camel.dsl.xml.io.beans.Greeter()
+            m = new org.apache.camel.dsl.xml.io.beans.GreeterMessage()
+            m.msg = 'Hi World from groovy'
+            b.message = m
+            return b
+        </script>
+    </bean>
+
+    <route id="r9">
+        <from uri="direct:x9"/>
+        <process ref="xml-bean-from-registry"/>
+        <to uri="mock:y9"/>
+    </route>
+
+</camel>
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java
index c0b4a019284..98270b33ddc 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java
@@ -12361,6 +12361,8 @@ public final class ModelDeserializers extends YamlDeserializerSupport {
                     @YamlProperty(name = "init-method", type = "string"),
                     @YamlProperty(name = "name", type = "string", required = true),
                     @YamlProperty(name = "properties", type = "object"),
+                    @YamlProperty(name = "script", type = "string"),
+                    @YamlProperty(name = "script-language", type = "string"),
                     @YamlProperty(name = "type", type = "string", required = true)
             }
     )
@@ -12413,6 +12415,16 @@ public final class ModelDeserializers extends YamlDeserializerSupport {
                     target.setProperties(val);
                     break;
                 }
+                case "script": {
+                    String val = asText(node);
+                    target.setScript(val);
+                    break;
+                }
+                case "script-language": {
+                    String val = asText(node);
+                    target.setScriptLanguage(val);
+                    break;
+                }
                 case "type": {
                     String val = asText(node);
                     target.setType(val);
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/BeansDeserializer.java b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/BeansDeserializer.java
index 4224881e130..ca2eddc7879 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/BeansDeserializer.java
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/BeansDeserializer.java
@@ -17,6 +17,7 @@
 package org.apache.camel.dsl.yaml.deserializers;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -26,16 +27,23 @@ import java.util.StringJoiner;
 import java.util.TreeMap;
 
 import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+import org.apache.camel.Expression;
+import org.apache.camel.NoSuchBeanException;
 import org.apache.camel.dsl.yaml.common.YamlDeserializationContext;
 import org.apache.camel.dsl.yaml.common.YamlDeserializerResolver;
 import org.apache.camel.dsl.yaml.common.YamlDeserializerSupport;
 import org.apache.camel.model.Model;
 import org.apache.camel.model.app.RegistryBeanDefinition;
 import org.apache.camel.spi.CamelContextCustomizer;
+import org.apache.camel.spi.ExchangeFactory;
+import org.apache.camel.spi.Language;
+import org.apache.camel.spi.ScriptingLanguage;
 import org.apache.camel.spi.annotations.YamlIn;
 import org.apache.camel.spi.annotations.YamlProperty;
 import org.apache.camel.spi.annotations.YamlType;
 import org.apache.camel.support.PropertyBindingSupport;
+import org.apache.camel.support.ScriptHelper;
 import org.apache.camel.util.KeyValueHolder;
 import org.apache.camel.util.ObjectHelper;
 import org.apache.camel.util.StringHelper;
@@ -79,6 +87,10 @@ public class BeansDeserializer extends YamlDeserializerSupport implements Constr
             if (!bean.getType().startsWith("#class:")) {
                 bean.setType("#class:" + bean.getType());
             }
+            if (bean.getScriptLanguage() != null || bean.getScript() != null) {
+                ObjectHelper.notNull(bean.getScriptLanguage(), "The bean script language must be set");
+                ObjectHelper.notNull(bean.getScript(), "The bean script must be set");
+            }
 
             // due to yaml-dsl is pre parsing beans which gets created eager
             // and then later beans can be parsed again such as from Camel K Integration CRD files
@@ -99,32 +111,69 @@ public class BeansDeserializer extends YamlDeserializerSupport implements Constr
     }
 
     public Object newInstance(RegistryBeanDefinition def, CamelContext context) throws Exception {
+        Object target;
 
         String type = def.getType();
 
-        // factory bean/method
-        if (def.getFactoryBean() != null && def.getFactoryMethod() != null) {
-            type = type + "#" + def.getFactoryBean() + ":" + def.getFactoryMethod();
-        } else if (def.getFactoryMethod() != null) {
-            type = type + "#" + def.getFactoryMethod();
-        }
-        // property binding support has constructor arguments as part of the type
-        StringJoiner ctr = new StringJoiner(", ");
-        if (def.getConstructors() != null && !def.getConstructors().isEmpty()) {
-            // need to sort constructor args based on index position
-            Map<Integer, Object> sorted = new TreeMap<>(def.getConstructors());
-            for (Object val : sorted.values()) {
-                String text = val.toString();
-                if (!StringHelper.isQuoted(text)) {
-                    text = "\"" + text + "\"";
+        // script bean
+        if (def.getScriptLanguage() != null && def.getScript() != null) {
+            // create bean via the script
+            final Language lan = context.resolveLanguage(def.getScriptLanguage());
+            final ScriptingLanguage slan = lan instanceof ScriptingLanguage ? (ScriptingLanguage) lan : null;
+            String fqn = def.getType();
+            if (fqn.startsWith("#class:")) {
+                fqn = fqn.substring(7);
+            }
+            final Class<?> clazz = context.getClassResolver().resolveMandatoryClass(fqn);
+            if (slan != null) {
+                // scripting language should be evaluated with context as binding
+                Map<String, Object> bindings = new HashMap<>();
+                bindings.put("context", context);
+                target = slan.evaluate(def.getScript(), bindings, clazz);
+            } else {
+                // exchange based languages needs a dummy exchange to be evaluated
+                ExchangeFactory ef = context.getCamelContextExtension().getExchangeFactory();
+                Exchange dummy = ef.create(false);
+                try {
+                    String text = ScriptHelper.resolveOptionalExternalScript(context, dummy, def.getScript());
+                    Expression exp = lan.createExpression(text);
+                    target = exp.evaluate(dummy, clazz);
+                } finally {
+                    ef.release(dummy);
                 }
-                ctr.add(text);
             }
-            type = type + "(" + ctr + ")";
-        }
 
-        final Object target = PropertyBindingSupport.resolveBean(context, type);
+            // a bean must be created
+            if (target == null) {
+                throw new NoSuchBeanException(def.getName(), "Creating bean using script returned null");
+            }
+
+        } else {
+            // factory bean/method
+            if (def.getFactoryBean() != null && def.getFactoryMethod() != null) {
+                type = type + "#" + def.getFactoryBean() + ":" + def.getFactoryMethod();
+            } else if (def.getFactoryMethod() != null) {
+                type = type + "#" + def.getFactoryMethod();
+            }
+            // property binding support has constructor arguments as part of the type
+            StringJoiner ctr = new StringJoiner(", ");
+            if (def.getConstructors() != null && !def.getConstructors().isEmpty()) {
+                // need to sort constructor args based on index position
+                Map<Integer, Object> sorted = new TreeMap<>(def.getConstructors());
+                for (Object val : sorted.values()) {
+                    String text = val.toString();
+                    if (!StringHelper.isQuoted(text)) {
+                        text = "\"" + text + "\"";
+                    }
+                    ctr.add(text);
+                }
+                type = type + "(" + ctr + ")";
+            }
+
+            target = PropertyBindingSupport.resolveBean(context, type);
+        }
 
+        // set optional properties on created bean
         if (def.getProperties() != null && !def.getProperties().isEmpty()) {
             PropertyBindingSupport.setPropertiesOnTarget(context, target, def.getProperties());
         }
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
index 15d6837cf97..a31f5382127 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
@@ -8044,6 +8044,12 @@
           "properties" : {
             "type" : "object"
           },
+          "script" : {
+            "type" : "string"
+          },
+          "scriptLanguage" : {
+            "type" : "string"
+          },
           "type" : {
             "type" : "string"
           }
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/docs/yaml-dsl.adoc b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/docs/yaml-dsl.adoc
index f52b67e8016..7886335c75d 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/docs/yaml-dsl.adoc
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/docs/yaml-dsl.adoc
@@ -267,6 +267,26 @@ public class MyHelper {
 
 NOTE: The factory method must be `public static`.
 
+=== Creating beans using script language
+
+For advanced use-cases then Camel allows to inline a script language, such as groovy, java, javascript, etc, to create the bean.
+This gives flexibility to use a bit of programming to create and configure the bean.
+
+[source,yaml]
+----
+- beans:
+  - name: myBean
+    type: com.acme.MyBean
+    scriptLanguage: groovy
+    script: >
+      // some groovy script here to create the bean
+      bean = ...
+      ...
+      return bean
+----
+
+NOTE: When using `script` then constructors and factory bean/method is not in use
+
 === Using init and destroy methods on beans
 
 Sometimes beans need to do some initialization and cleanup work before a bean is ready to be used.
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/BeansTest.groovy b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/BeansTest.groovy
index e7e2185ddef..194b6bf9595 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/BeansTest.groovy
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/BeansTest.groovy
@@ -22,6 +22,7 @@ import org.apache.camel.dsl.yaml.support.model.MyCtrBean
 import org.apache.camel.dsl.yaml.support.model.MyDestroyBean
 import org.apache.camel.dsl.yaml.support.model.MyFacBean
 import org.apache.camel.dsl.yaml.support.model.MyFacHelper
+import org.apache.camel.dsl.yaml.support.model.MyUppercaseProcessor
 
 class BeansTest extends YamlTestSupport {
 
@@ -211,4 +212,21 @@ class BeansTest extends YamlTestSupport {
         MyDestroyBean.destroyCalled.get() == true
     }
 
+    def "beans with script"() {
+        when:
+        loadRoutes """
+                - beans:
+                  - name: myBean
+                    type: ${MyBean.class.name}
+                    scriptLanguage: groovy
+                    script: "var b = new ${MyBean.class.name}(); b.field1 = 'script1'; b.field2 = 'script2'; return b"
+            """
+
+        then:
+        with(context.registry.lookupByName('myBean'), MyBean) {
+            it.field1 == 'script1'
+            it.field2 == 'script2'
+        }
+    }
+
 }