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 d20428ae23a CAMEL-20930: camel-core - Bean DSL dev console (#14692)
d20428ae23a is described below

commit d20428ae23a0dd363a4c4ae3678410dde8253598
Author: Claus Ibsen <claus.ib...@gmail.com>
AuthorDate: Mon Jul 1 21:42:01 2024 +0200

    CAMEL-20930: camel-core - Bean DSL dev console (#14692)
    
    CAMEL-20930: camel-core - Bean DSL dev console
    Regen
---
 .../apache/camel/catalog/dev-consoles.properties   |   1 +
 .../camel/catalog/dev-consoles/bean-model.json     |  15 ++
 components/camel-spring-xml/pom.xml                |   2 +-
 .../apache/camel/impl/console/BeanDevConsole.java  | 132 ++++++++++-
 .../camel/impl/console/VariablesDevConsole.java    |   4 +-
 .../org/apache/camel/dev-console/bean-model.json   |  15 ++
 .../org/apache/camel/dev-console/bean-model        |   2 +
 .../org/apache/camel/dev-consoles.properties       |   7 +
 .../camel/model/console/BeanModelDevConsole.java   | 217 +++++++++++++++++++
 .../java/org/apache/camel/util/ObjectHelper.java   |   2 +-
 .../camel/cli/connector/LocalCliConnector.java     |  34 ++-
 .../dsl/jbang/core/commands/CamelJBangMain.java    |   1 +
 .../jbang/core/commands/action/CamelBeanDump.java  | 241 +++++++++++++++++++++
 .../jbang/core/commands/process/ListVariable.java  |   6 +-
 14 files changed, 661 insertions(+), 18 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles.properties
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles.properties
index 68e2314bd3f..ecd2d10d672 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles.properties
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles.properties
@@ -2,6 +2,7 @@ aws-secrets
 aws2-s3
 azure-secrets
 bean
+bean-model
 blocked
 circuit-breaker
 consumer
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/bean-model.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/bean-model.json
new file mode 100644
index 00000000000..76e6cae37bf
--- /dev/null
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/bean-model.json
@@ -0,0 +1,15 @@
+{
+  "console": {
+    "kind": "console",
+    "group": "camel",
+    "name": "bean-model",
+    "title": "Bean Model",
+    "description": "Displays beans from the DSL model",
+    "deprecated": false,
+    "javaType": "org.apache.camel.model.console.BeanModelDevConsole",
+    "groupId": "org.apache.camel",
+    "artifactId": "camel-core-model",
+    "version": "4.7.0-SNAPSHOT"
+  }
+}
+
diff --git a/components/camel-spring-xml/pom.xml 
b/components/camel-spring-xml/pom.xml
index 0ae78b19c16..bd5ceaabad1 100644
--- a/components/camel-spring-xml/pom.xml
+++ b/components/camel-spring-xml/pom.xml
@@ -323,7 +323,7 @@
                                     <classifier>sources</classifier>
                                     <overWrite>true</overWrite>
                                     <includes>**/*</includes>
-                                    <excludes>**/*Configurer.java</excludes>
+                                    
<excludes>**/*Configurer.java,**/console/*</excludes>
                                     
<outputDirectory>target/sources/camel-core-model</outputDirectory>
                                 </artifactItem>
                                 <artifactItem>
diff --git 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/BeanDevConsole.java
 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/BeanDevConsole.java
index ad74cb0c332..1c0ec76b95f 100644
--- 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/BeanDevConsole.java
+++ 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/BeanDevConsole.java
@@ -17,11 +17,17 @@
 package org.apache.camel.impl.console;
 
 import java.util.Map;
+import java.util.TreeMap;
 import java.util.stream.Stream;
 
+import org.apache.camel.spi.BeanIntrospection;
 import org.apache.camel.spi.annotations.DevConsole;
+import org.apache.camel.support.PatternHelper;
+import org.apache.camel.support.PluginHelper;
 import org.apache.camel.support.console.AbstractDevConsole;
+import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
 
 @DevConsole(name = "bean", description = "Displays Java beans from the 
registry")
 public class BeanDevConsole extends AbstractDevConsole {
@@ -30,15 +36,66 @@ public class BeanDevConsole extends AbstractDevConsole {
         super("camel", "bean", "Bean", "Displays Java beans from the 
registry");
     }
 
+    /**
+     * Filters the beans matching by name
+     */
+    public static final String FILTER = "filter";
+
+    /**
+     * Whether to include bean properties
+     */
+    public static final String PROPERTIES = "properties";
+
+    /**
+     * Whether to include null values
+     */
+    public static final String NULLS = "nulls";
+
+    /**
+     * Whether to include internal Camel beans
+     */
+    public static final String INTERNAL = "internal";
+
     @Override
     protected String doCallText(Map<String, Object> options) {
+        String filter = (String) options.get(FILTER);
+        boolean properties = "true".equals(options.getOrDefault(PROPERTIES, 
"true").toString());
+        boolean nulls = "true".equals(options.getOrDefault(NULLS, 
"true").toString());
+        boolean internal = "true".equals(options.getOrDefault(INTERNAL, 
"true").toString());
+
         StringBuilder sb = new StringBuilder();
 
+        BeanIntrospection bi = 
PluginHelper.getBeanIntrospection(getCamelContext());
         Map<String, Object> beans = 
getCamelContext().getRegistry().findByTypeWithName(Object.class);
-        Stream<String> keys = 
beans.keySet().stream().sorted(String::compareToIgnoreCase);
+        Stream<String> keys = beans.keySet().stream().filter(r -> accept(r, 
filter)).sorted(String::compareToIgnoreCase);
         keys.forEach(k -> {
-            String v = beans.getOrDefault(k, "<null>").getClass().getName();
-            sb.append(String.format("    %s (class: %s)%n", k, v));
+            Object bean = beans.get(k);
+            if (bean != null) {
+                boolean include = internal || 
!bean.getClass().getName().startsWith("org.apache.camel.");
+                if (include) {
+                    sb.append(String.format("    %s (class: %s)%n", k, 
bean.getClass().getName()));
+
+                    Map<String, Object> values = new TreeMap<>();
+                    if (properties) {
+                        try {
+                            bi.getProperties(bean, values, null);
+                        } catch (Throwable e) {
+                            // ignore
+                        }
+                        values.forEach((pk, pv) -> {
+                            if (pv == null) {
+                                if (nulls) {
+                                    sb.append(String.format("        %s = 
null%n", pk));
+                                }
+                            } else {
+                                String t = pv.getClass().getName();
+                                sb.append(String.format("        %s (%s) = 
%s%n", pk, t, pv));
+                            }
+                        });
+                    }
+                }
+            }
+            sb.append("\n");
         });
 
         return sb.toString();
@@ -46,17 +103,78 @@ public class BeanDevConsole extends AbstractDevConsole {
 
     @Override
     protected JsonObject doCallJson(Map<String, Object> options) {
-        JsonObject root = new JsonObject();
+        String filter = (String) options.get(FILTER);
+        boolean properties = "true".equals(options.getOrDefault(PROPERTIES, 
"true").toString());
+        boolean nulls = "true".equals(options.getOrDefault(NULLS, 
"true").toString());
+        boolean internal = "true".equals(options.getOrDefault(INTERNAL, 
"true").toString());
 
+        JsonObject root = new JsonObject();
         JsonObject jo = new JsonObject();
         root.put("beans", jo);
+
+        BeanIntrospection bi = 
PluginHelper.getBeanIntrospection(getCamelContext());
         Map<String, Object> beans = 
getCamelContext().getRegistry().findByTypeWithName(Object.class);
-        Stream<String> keys = 
beans.keySet().stream().sorted(String::compareToIgnoreCase);
+        Stream<String> keys = beans.keySet().stream().filter(r -> accept(r, 
filter)).sorted(String::compareToIgnoreCase);
+
         keys.forEach(k -> {
-            String v = beans.getOrDefault(k, "null").getClass().getName();
-            beans.put(k, v);
+            Object bean = beans.get(k);
+            if (bean != null) {
+                boolean include = internal || 
!bean.getClass().getName().startsWith("org.apache.camel.");
+                if (include) {
+                    Map<String, Object> values = new TreeMap<>();
+                    if (properties) {
+                        try {
+                            bi.getProperties(bean, values, null);
+                        } catch (Throwable e) {
+                            // ignore
+                        }
+                    }
+                    JsonObject jb = new JsonObject();
+                    jb.put("name", k);
+                    jb.put("type", bean.getClass().getName());
+                    jo.put(k, jb);
+
+                    if (!values.isEmpty()) {
+                        JsonArray arr = new JsonArray();
+                        values.forEach((pk, pv) -> {
+                            Object value = pv;
+                            String type = pv != null ? pv.getClass().getName() 
: null;
+                            if (type != null) {
+                                value = Jsoner.trySerialize(pv);
+                                if (value == null) {
+                                    // cannot serialize so escape
+                                    value = Jsoner.escape(pv.toString());
+                                } else {
+                                    // okay so use the value as-s
+                                    value = pv;
+                                }
+                            }
+                            JsonObject jp = new JsonObject();
+                            jp.put("name", pk);
+                            if (type != null) {
+                                jp.put("type", type);
+                            }
+                            jp.put("value", value);
+                            boolean accept = value != null || nulls;
+                            if (accept) {
+                                arr.add(jp);
+                            }
+                        });
+                        jb.put("properties", arr);
+                    }
+                }
+            }
         });
 
         return root;
     }
+
+    private static boolean accept(String name, String filter) {
+        if (filter == null || filter.isBlank()) {
+            return true;
+        }
+
+        return PatternHelper.matchPattern(name, filter);
+    }
+
 }
diff --git 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/VariablesDevConsole.java
 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/VariablesDevConsole.java
index 95260395237..bb36068f931 100644
--- 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/VariablesDevConsole.java
+++ 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/VariablesDevConsole.java
@@ -76,10 +76,10 @@ public class VariablesDevConsole extends AbstractDevConsole 
{
             String t = v != null ? v.getClass().getName() : null;
             JsonObject e = new JsonObject();
             e.put("key", k);
-            e.put("value", v);
             if (t != null) {
-                e.put("className", t);
+                e.put("type", t);
             }
+            e.put("value", v);
             arr.add(e);
         }
         return arr;
diff --git 
a/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/dev-console/bean-model.json
 
b/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/dev-console/bean-model.json
new file mode 100644
index 00000000000..76e6cae37bf
--- /dev/null
+++ 
b/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/dev-console/bean-model.json
@@ -0,0 +1,15 @@
+{
+  "console": {
+    "kind": "console",
+    "group": "camel",
+    "name": "bean-model",
+    "title": "Bean Model",
+    "description": "Displays beans from the DSL model",
+    "deprecated": false,
+    "javaType": "org.apache.camel.model.console.BeanModelDevConsole",
+    "groupId": "org.apache.camel",
+    "artifactId": "camel-core-model",
+    "version": "4.7.0-SNAPSHOT"
+  }
+}
+
diff --git 
a/core/camel-core-model/src/generated/resources/META-INF/services/org/apache/camel/dev-console/bean-model
 
b/core/camel-core-model/src/generated/resources/META-INF/services/org/apache/camel/dev-console/bean-model
new file mode 100644
index 00000000000..1e9e05a4ed1
--- /dev/null
+++ 
b/core/camel-core-model/src/generated/resources/META-INF/services/org/apache/camel/dev-console/bean-model
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.model.console.BeanModelDevConsole
diff --git 
a/core/camel-core-model/src/generated/resources/META-INF/services/org/apache/camel/dev-consoles.properties
 
b/core/camel-core-model/src/generated/resources/META-INF/services/org/apache/camel/dev-consoles.properties
new file mode 100644
index 00000000000..a3834de6926
--- /dev/null
+++ 
b/core/camel-core-model/src/generated/resources/META-INF/services/org/apache/camel/dev-consoles.properties
@@ -0,0 +1,7 @@
+# Generated by camel build tools - do NOT edit this file!
+dev-consoles=bean-model
+groupId=org.apache.camel
+artifactId=camel-core-model
+version=4.7.0-SNAPSHOT
+projectName=Camel :: Core Model
+projectDescription=Camel model
diff --git 
a/core/camel-core-model/src/main/java/org/apache/camel/model/console/BeanModelDevConsole.java
 
b/core/camel-core-model/src/main/java/org/apache/camel/model/console/BeanModelDevConsole.java
new file mode 100644
index 00000000000..fc537b718fb
--- /dev/null
+++ 
b/core/camel-core-model/src/main/java/org/apache/camel/model/console/BeanModelDevConsole.java
@@ -0,0 +1,217 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.model.console;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.camel.model.BeanFactoryDefinition;
+import org.apache.camel.model.Model;
+import org.apache.camel.spi.BeanIntrospection;
+import org.apache.camel.spi.annotations.DevConsole;
+import org.apache.camel.support.PatternHelper;
+import org.apache.camel.support.PluginHelper;
+import org.apache.camel.support.console.AbstractDevConsole;
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
+
+@DevConsole(name = "bean-model", description = "Displays beans from the DSL 
model")
+public class BeanModelDevConsole extends AbstractDevConsole {
+
+    /**
+     * Filters the beans matching by name
+     */
+    public static final String FILTER = "filter";
+
+    /**
+     * Whether to include bean properties
+     */
+    public static final String PROPERTIES = "properties";
+
+    /**
+     * Whether to include null values
+     */
+    public static final String NULLS = "nulls";
+
+    public BeanModelDevConsole() {
+        super("camel", "bean-model", "Bean Model", "Displays beans from the 
DSL model");
+    }
+
+    @Override
+    protected String doCallText(Map<String, Object> options) {
+        String filter = (String) options.get(FILTER);
+        boolean properties = "true".equals(options.getOrDefault(PROPERTIES, 
"true"));
+        boolean nulls = "true".equals(options.getOrDefault(NULLS, "true"));
+
+        StringBuilder sb = new StringBuilder();
+
+        BeanIntrospection bi = 
PluginHelper.getBeanIntrospection(getCamelContext());
+        Model model = 
getCamelContext().getCamelContextExtension().getContextPlugin(Model.class);
+        if (model != null) {
+            for (BeanFactoryDefinition<?> b : model.getCustomBeans()) {
+                String name = b.getName();
+                if (!accept(name, filter)) {
+                    continue;
+                }
+
+                Map<String, Object> values = new TreeMap<>();
+                Object target = 
getCamelContext().getRegistry().lookupByName(name);
+                if (target != null && properties) {
+                    try {
+                        bi.getProperties(target, values, null);
+                    } catch (Throwable e) {
+                        // ignore
+                    }
+                }
+                sb.append(String.format("    %s (%s)%n", b.getName(), 
b.getType()));
+                if (properties && b.getProperties() != null) {
+                    b.getProperties().forEach((k, v) -> {
+                        Object rv = values.get(k);
+                        String type;
+                        if (rv == null) {
+                            if (nulls) {
+                                sb.append(String.format("        %s = null%n", 
k));
+
+                            }
+                        } else {
+                            type = rv.getClass().getName();
+                            sb.append(String.format("        %s = %s 
(type:%s)%n", k, rv, type));
+                        }
+                    });
+                }
+                sb.append("\n");
+            }
+        }
+
+        return sb.toString();
+    }
+
+    @Override
+    protected JsonObject doCallJson(Map<String, Object> options) {
+        String filter = (String) options.get(FILTER);
+        boolean properties = "true".equals(options.getOrDefault(PROPERTIES, 
"true"));
+        boolean nulls = "true".equals(options.getOrDefault(NULLS, "true"));
+
+        JsonObject root = new JsonObject();
+
+        JsonObject jo = new JsonObject();
+        root.put("beans", jo);
+
+        BeanIntrospection bi = 
PluginHelper.getBeanIntrospection(getCamelContext());
+        Model model = 
getCamelContext().getCamelContextExtension().getContextPlugin(Model.class);
+        if (model != null) {
+            for (BeanFactoryDefinition<?> b : model.getCustomBeans()) {
+                String name = b.getName();
+                if (!accept(name, filter)) {
+                    continue;
+                }
+
+                Map<String, Object> values = new TreeMap<>();
+                Object target = 
getCamelContext().getRegistry().lookupByName(name);
+                if (target != null && properties) {
+                    try {
+                        bi.getProperties(target, values, null);
+                    } catch (Throwable e) {
+                        // ignore
+                    }
+                }
+                JsonObject jb = new JsonObject();
+                jo.put(b.getName(), jb);
+                jb.put("name", b.getName());
+                jb.put("type", b.getType());
+                if (b.getInitMethod() != null) {
+                    jb.put("initMethod", b.getInitMethod());
+                }
+                if (b.getDestroyMethod() != null) {
+                    jb.put("destroyMethod", b.getDestroyMethod());
+                }
+                if (b.getBuilderClass() != null) {
+                    jb.put("builderClass", b.getBuilderClass());
+                }
+                if (b.getBuilderMethod() != null) {
+                    jb.put("builderMethod", b.getBuilderMethod());
+                }
+                if (b.getFactoryBean() != null) {
+                    jb.put("factoryBean", b.getFactoryBean());
+                }
+                if (b.getFactoryMethod() != null) {
+                    jb.put("factoryMethod", b.getFactoryMethod());
+                }
+                if (b.getProperties() != null) {
+                    JsonArray arr = new JsonArray();
+                    b.getProperties().forEach((k, v) -> {
+                        Object rv = values.get(k);
+                        String type = rv != null ? rv.getClass().getName() : 
null;
+                        JsonObject jp = new JsonObject();
+                        jp.put("name", k);
+                        if (type != null) {
+                            jp.put("type", type);
+                        }
+                        jp.put("value", v);
+                        boolean accept = v != null || nulls;
+                        if (accept) {
+                            arr.add(jp);
+                        }
+                    });
+                    if (!arr.isEmpty()) {
+                        jb.put("modelProperties", arr);
+                    }
+                    JsonArray arr2 = new JsonArray();
+                    b.getProperties().forEach((k, v) -> {
+                        Object rv = values.get(k);
+                        Object value = rv;
+                        String type = rv != null ? rv.getClass().getName() : 
null;
+                        if (type != null) {
+                            value = Jsoner.trySerialize(rv);
+                            if (value == null) {
+                                // cannot serialize so escape
+                                value = Jsoner.escape(rv.toString());
+                            } else {
+                                // okay so use the value as-s
+                                value = rv;
+                            }
+                        }
+                        JsonObject jp = new JsonObject();
+                        jp.put("name", k);
+                        if (type != null) {
+                            jp.put("type", type);
+                        }
+                        jp.put("value", value);
+                        boolean accept = value != null || nulls;
+                        if (accept) {
+                            arr2.add(jp);
+                        }
+                    });
+                    if (!arr2.isEmpty()) {
+                        jb.put("properties", arr2);
+                    }
+                }
+            }
+        }
+        return root;
+    }
+
+    private static boolean accept(String name, String filter) {
+        if (filter == null || filter.isBlank()) {
+            return true;
+        }
+
+        return PatternHelper.matchPattern(name, filter);
+    }
+
+}
diff --git 
a/core/camel-util/src/main/java/org/apache/camel/util/ObjectHelper.java 
b/core/camel-util/src/main/java/org/apache/camel/util/ObjectHelper.java
index bc4f3da3b6e..3227ce736e0 100644
--- a/core/camel-util/src/main/java/org/apache/camel/util/ObjectHelper.java
+++ b/core/camel-util/src/main/java/org/apache/camel/util/ObjectHelper.java
@@ -495,7 +495,7 @@ public final class ObjectHelper {
             return Object[].class;
         } else if ("java.lang.String[]".equals(name) || 
"String[]".equals(name)) {
             return String[].class;
-            // and these is common as well
+            // and these are common as well
         } else if ("java.lang.String".equals(name) || "String".equals(name)) {
             return String.class;
         } else if ("java.lang.Boolean".equals(name) || "Boolean".equals(name)) 
{
diff --git 
a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
 
b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
index f288cc1f174..d230f7fd06e 100644
--- 
a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
+++ 
b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
@@ -263,15 +263,16 @@ public class LocalCliConnector extends ServiceSupport 
implements CliConnector, C
                 doActionSendTask(root);
             } else if ("transform".equals(action)) {
                 doActionTransformTask(root);
+            } else if ("bean".equals(action)) {
+                doActionBeanTask(root);
             }
-
-            // action done so delete file
-            FileUtil.deleteFile(actionFile);
-
         } catch (Exception e) {
             // ignore
             LOG.debug("Error executing action file: {} due to: {}. This 
exception is ignored.", actionFile, e.getMessage(),
                     e);
+        } finally {
+            // action done so delete file
+            FileUtil.deleteFile(actionFile);
         }
     }
 
@@ -694,6 +695,31 @@ public class LocalCliConnector extends ServiceSupport 
implements CliConnector, C
         }
     }
 
+    private void doActionBeanTask(JsonObject root) throws IOException {
+        String filter = root.getStringOrDefault("filter", "");
+        String properties = root.getStringOrDefault("properties", "true");
+        String nulls = root.getStringOrDefault("nulls", "true");
+        String internal = root.getStringOrDefault("internal", "false");
+
+        Map<String, Object> options = Map.of("filter", filter, "properties", 
properties, "nulls", nulls, "internal", internal);
+
+        JsonObject answer = new JsonObject();
+        DevConsole dc1 = 
camelContext.getCamelContextExtension().getContextPlugin(DevConsoleRegistry.class)
+                .resolveById("bean");
+        DevConsole dc2 = 
camelContext.getCamelContextExtension().getContextPlugin(DevConsoleRegistry.class)
+                .resolveById("bean-model");
+        if (dc1 != null) {
+            JsonObject json = (JsonObject) dc1.call(DevConsole.MediaType.JSON, 
options);
+            answer.put("beans", json.getMap("beans"));
+        }
+        if (dc2 != null) {
+            JsonObject json = (JsonObject) dc2.call(DevConsole.MediaType.JSON, 
options);
+            answer.put("bean-models", json.getMap("beans"));
+        }
+        LOG.trace("Updating output file: {}", outputFile);
+        IOHelper.writeText(answer.toJson(), outputFile);
+    }
+
     private void doActionResetStatsTask() throws Exception {
         ManagedCamelContext mcc = 
camelContext.getCamelContextExtension().getContextPlugin(ManagedCamelContext.class);
         if (mcc != null) {
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
index 7e295c9c2c8..31a64f611fc 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
@@ -93,6 +93,7 @@ public class CamelJBangMain implements Callable<Integer> {
                         .addSubcommand("event", new CommandLine(new 
ListEvent(main)))
                         .addSubcommand("inflight", new CommandLine(new 
ListInflight(main)))
                         .addSubcommand("blocked", new CommandLine(new 
ListBlocked(main)))
+                        .addSubcommand("bean", new CommandLine(new 
CamelBeanDump(main)))
                         .addSubcommand("route-controller", new CommandLine(new 
RouteControllerAction(main)))
                         .addSubcommand("transformer", new CommandLine(new 
ListTransformer(main)))
                         .addSubcommand("circuit-breaker", new CommandLine(new 
ListCircuitBreaker(main)))
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelBeanDump.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelBeanDump.java
new file mode 100644
index 00000000000..7c46cc313e2
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelBeanDump.java
@@ -0,0 +1,241 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands.action;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import com.github.freva.asciitable.AsciiTable;
+import com.github.freva.asciitable.Column;
+import com.github.freva.asciitable.HorizontalAlign;
+import com.github.freva.asciitable.OverflowBehaviour;
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.util.FileUtil;
+import org.apache.camel.util.IOHelper;
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+
+@Command(name = "bean", description = "List beans in a running Camel 
integration", sortOptions = false)
+public class CamelBeanDump extends ActionBaseCommand {
+
+    public static class NameTypeCompletionCandidates implements 
Iterable<String> {
+
+        public NameTypeCompletionCandidates() {
+        }
+
+        @Override
+        public Iterator<String> iterator() {
+            return List.of("name", "type").iterator();
+        }
+
+    }
+
+    @CommandLine.Parameters(description = "Name or pid of running Camel 
integration", arity = "0..1")
+    String name = "*";
+
+    @CommandLine.Option(names = { "--sort" }, completionCandidates = 
NameTypeCompletionCandidates.class,
+                        description = "Sort by name or type", defaultValue = 
"name")
+    String sort;
+
+    @CommandLine.Option(names = { "--filter" },
+                        description = "Filter beans names (use all to include 
all beans)", defaultValue = "all")
+    String filter;
+
+    @CommandLine.Option(names = { "--properties" },
+                        description = "Show bean properties", defaultValue = 
"true")
+    boolean properties;
+
+    @CommandLine.Option(names = { "--nulls" },
+                        description = "Include null values", defaultValue = 
"true")
+    boolean nulls;
+
+    @CommandLine.Option(names = { "--internal" },
+                        description = "Include internal Camel beans", 
defaultValue = "false")
+    boolean internal;
+
+    @CommandLine.Option(names = { "--dsl" },
+                        description = "Include only beans from YAML or XML 
DSL", defaultValue = "false")
+    boolean dsl;
+
+    private volatile long pid;
+
+    public CamelBeanDump(CamelJBangMain main) {
+        super(main);
+    }
+
+    @Override
+    public Integer doCall() throws Exception {
+        List<Row> rows = new ArrayList<>();
+
+        List<Long> pids = findPids(name);
+        if (pids.isEmpty()) {
+            return 0;
+        } else if (pids.size() > 1) {
+            printer().println("Name or pid " + name + " matches " + pids.size()
+                              + " running Camel integrations. Specify a name 
or PID that matches exactly one.");
+            return 0;
+        }
+
+        this.pid = pids.get(0);
+
+        // ensure output file is deleted before executing action
+        File outputFile = getOutputFile(Long.toString(pid));
+        FileUtil.deleteFile(outputFile);
+
+        JsonObject root = new JsonObject();
+        root.put("action", "bean");
+        if (!"all".equals(filter)) {
+            root.put("filter", filter);
+        }
+        root.put("properties", properties);
+        root.put("nulls", nulls);
+        root.put("internal", internal);
+        File f = getActionFile(Long.toString(pid));
+        try {
+            IOHelper.writeText(root.toJson(), f);
+        } catch (Exception e) {
+            // ignore
+        }
+
+        JsonObject jo = waitForOutputFile(outputFile);
+
+        if (jo != null) {
+            JsonObject beans;
+            if (dsl) {
+                beans = (JsonObject) jo.get("bean-models");
+            } else {
+                beans = (JsonObject) jo.get("beans");
+            }
+            for (String name : beans.keySet()) {
+                JsonObject jt = (JsonObject) beans.get(name);
+                Row row = new Row();
+                row.name = jt.getString("name");
+                row.type = jt.getString("type");
+                JsonArray arr = jt.getCollection("properties");
+                JsonArray arr2 = jt.getCollection("modelProperties");
+                if (arr != null) {
+                    row.properties = new ArrayList<>();
+                    for (int i = 0; i < arr.size(); i++) {
+                        PropertyRow pr = new PropertyRow();
+                        row.properties.add(pr);
+                        JsonObject p = (JsonObject) arr.get(i);
+                        pr.name = p.getString("name");
+                        pr.type = p.getString("type");
+                        pr.value = p.get("value");
+                        if (arr2 != null) {
+                            JsonObject p2 = (JsonObject) arr2.get(i);
+                            if (p2 != null) {
+                                pr.configValue = p2.getString("value");
+                            }
+                        }
+                    }
+                }
+                rows.add(row);
+            }
+        } else {
+            printer().println("Response from running Camel with PID " + pid + 
" not received within 5 seconds");
+            return 1;
+        }
+
+        // sort rows
+        rows.sort(this::sortRow);
+        if (properties) {
+            for (Row row : rows) {
+                String line = "BEAN: " + row.name + " (" + row.type + "):";
+                printer().println(line);
+                printer().println("-".repeat(line.length()));
+                if (row.properties != null) {
+                    propertiesTable(row.properties);
+                }
+                printer().println();
+            }
+        } else {
+            singleTable(rows);
+        }
+
+        // delete output file after use
+        FileUtil.deleteFile(outputFile);
+
+        return 0;
+    }
+
+    protected void singleTable(List<Row> rows) {
+        printer().println(AsciiTable.getTable(AsciiTable.NO_BORDERS, rows, 
Arrays.asList(
+                new 
Column().header("NAME").dataAlign(HorizontalAlign.LEFT).maxWidth(60, 
OverflowBehaviour.ELLIPSIS_RIGHT)
+                        .with(r -> r.name),
+                new 
Column().header("TYPE").dataAlign(HorizontalAlign.LEFT).maxWidth(100, 
OverflowBehaviour.CLIP_LEFT)
+                        .with(r -> r.type))));
+    }
+
+    protected void propertiesTable(List<PropertyRow> rows) {
+        printer().println(AsciiTable.getTable(AsciiTable.NO_BORDERS, rows, 
Arrays.asList(
+                new 
Column().header("PROPERTY").dataAlign(HorizontalAlign.LEFT).maxWidth(40, 
OverflowBehaviour.ELLIPSIS_RIGHT)
+                        .with(r -> r.name),
+                new 
Column().header("TYPE").dataAlign(HorizontalAlign.LEFT).maxWidth(40, 
OverflowBehaviour.ELLIPSIS_RIGHT)
+                        .with(r -> r.type),
+                new 
Column().header("CONFIGURATION").visible(dsl).dataAlign(HorizontalAlign.LEFT)
+                        .maxWidth(80, OverflowBehaviour.NEWLINE)
+                        .with(r -> r.configValue),
+                new 
Column().header("VALUE").dataAlign(HorizontalAlign.LEFT).maxWidth(80, 
OverflowBehaviour.NEWLINE)
+                        .with(this::getValue))));
+    }
+
+    private String getValue(PropertyRow r) {
+        if (r.value != null) {
+            return r.value.toString();
+        }
+        return "null";
+    }
+
+    protected int sortRow(Row o1, Row o2) {
+        String s = sort;
+        int negate = 1;
+        if (s.startsWith("-")) {
+            s = s.substring(1);
+            negate = -1;
+        }
+        switch (s) {
+            case "name":
+                return o1.name.compareToIgnoreCase(o2.name) * negate;
+            default:
+                return 0;
+        }
+    }
+
+    protected JsonObject waitForOutputFile(File outputFile) {
+        return getJsonObject(outputFile);
+    }
+
+    private static class Row {
+        String name;
+        String type;
+        List<PropertyRow> properties;
+    }
+
+    private static class PropertyRow {
+        String name;
+        String type;
+        Object value;
+        String configValue;
+    }
+
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListVariable.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListVariable.java
index c5910e354aa..f19fa750447 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListVariable.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListVariable.java
@@ -89,7 +89,7 @@ public class ListVariable extends ProcessWatchCommand {
                                     JsonObject jo = (JsonObject) arr.get(i);
                                     row.id = id;
                                     row.key = jo.getString("key");
-                                    row.className = jo.getString("className");
+                                    row.type = jo.getString("type");
                                     row.value = jo.get("value");
                                     rows.add(row);
                                 }
@@ -108,7 +108,7 @@ public class ListVariable extends ProcessWatchCommand {
                             .with(r -> r.name),
                     new 
Column().header("REPOSITORY").headerAlign(HorizontalAlign.CENTER).with(r -> 
r.id),
                     new 
Column().header("TYPE").headerAlign(HorizontalAlign.CENTER)
-                            .maxWidth(40, 
OverflowBehaviour.ELLIPSIS_LEFT).with(r -> r.className),
+                            .maxWidth(40, 
OverflowBehaviour.ELLIPSIS_LEFT).with(r -> r.type),
                     new 
Column().header("KEY").dataAlign(HorizontalAlign.LEFT).maxWidth(50, 
OverflowBehaviour.ELLIPSIS_RIGHT)
                             .with(r -> r.key),
                     new 
Column().header("VALUE").headerAlign(HorizontalAlign.RIGHT).maxWidth(80, 
OverflowBehaviour.NEWLINE)
@@ -154,7 +154,7 @@ public class ListVariable extends ProcessWatchCommand {
         String name;
         String id;
         String key;
-        String className;
+        String type;
         Object value;
 
         Row copy() {


Reply via email to