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

davsclaus pushed a commit to branch fix/CAMEL-23688-actions-2
in repository https://gitbox.apache.org/repos/asf/camel.git

commit a8f2e01d36effd7eae01333679be7f4db8bd393b
Author: Claus Ibsen <[email protected]>
AuthorDate: Fri Oct 16 23:01:00 2026 +0200

    CAMEL-23688: camel-jbang - Tests for get bean, get route-controller and top 
source
    
    Tests for camel get bean (58% covered), camel get route-controller (52%)
    and camel top source (52%), with the answers of the bean, bean-model,
    route-controller and top dev consoles. They found these bugs, fixed
    here:
    
    * camel get bean --dsl paired the configuration of the properties with
      the properties by position: it showed the configuration of another
      property, or failed with an IndexOutOfBoundsException with
      --nulls=false, where the two lists differ. Now by name. An unknown
      --scope showed every bean; now a usage error.
    * camel get route-controller read the back-off times and attempts, which
      are longs, as ints, and showed "Starting Routes: false" for the default
      route controller, which does not report it.
    * camel top source --json had its counters as text, unlike the JSON of
      the other listings.
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc    |  10 +
 .../jbang/core/commands/action/CamelBeanDump.java  |  25 ++-
 .../jbang/core/commands/action/CamelSourceTop.java |  15 +-
 .../commands/action/RouteControllerAction.java     |  18 +-
 .../core/commands/action/CamelBeanDumpTest.java    | 238 +++++++++++++++++----
 .../core/commands/action/CamelSourceTopTest.java   | 185 +++++++++++++---
 .../commands/action/RouteControllerActionTest.java | 233 +++++++++++++++++---
 7 files changed, 597 insertions(+), 127 deletions(-)

diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index b332441dfd88..4bcbd57be301 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -521,6 +521,16 @@ or follows, like Enter does.
   nothing), and a route that cannot be read, such as invalid YAML, exits with 
`1` and the error of the parser (it failed
   with an internal error, `70`, and a stack trace).
 
+=== camel-jbang - camel get bean, get route-controller and top source
+
+* `camel get bean --dsl` shows the configuration of each property next to that 
property: it paired them by position,
+  which showed the configuration of another property, or failed with an 
internal error (`70`) with `--nulls=false`.
+  An unknown `--scope` exits with `2` (it showed every bean).
+* `camel get route-controller` shows back-off times and attempts that do not 
fit in an int (it showed other numbers),
+  and no longer shows `Starting Routes: false` for the default route 
controller, which does not tell.
+* `camel top source --json` has the counters `total`, `mean`, `min`, `max` and 
`last` as numbers instead of text, as
+  the other listings.
+
 === camel-jbang - a boolean option given without a value is on
 
 A boolean option that is on by default is now on when it is given without a 
value, like any other boolean option:
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
index 79b817aadc4c..644f6220ad93 100644
--- 
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
@@ -18,8 +18,10 @@ package org.apache.camel.dsl.jbang.core.commands.action;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 
 import com.github.freva.asciitable.AsciiTable;
 import com.github.freva.asciitable.Column;
@@ -46,6 +48,8 @@ import picocli.CommandLine.Command;
                  "  camel get bean --json" })
 public class CamelBeanDump extends ActionBaseCommand {
 
+    private static final List<String> SCOPES = List.of("all", "user", "camel", 
"spring", "quarkus");
+
     private static final RowSort<Row> ORDER = new RowSort<Row>()
             .text("name", r -> r.name)
             .text("type", r -> r.type);
@@ -69,7 +73,7 @@ public class CamelBeanDump extends ActionBaseCommand {
 
         @Override
         public Iterator<String> iterator() {
-            return List.of("all", "user", "camel", "spring", 
"quarkus").iterator();
+            return SCOPES.iterator();
         }
 
     }
@@ -122,6 +126,10 @@ public class CamelBeanDump extends ActionBaseCommand {
     public Integer doCall() throws Exception {
         List<Row> rows = new ArrayList<>();
 
+        if (!SCOPES.contains(scope)) {
+            throw CliException.usage("--scope must be one of " + 
String.join(", ", SCOPES) + ", was: " + scope);
+        }
+
         CamelProcess process = findOne(name);
         this.pid = process.pid();
 
@@ -149,7 +157,15 @@ public class CamelBeanDump extends ActionBaseCommand {
             row.name = jt.getString("name");
             row.type = jt.getString("type");
             JsonArray arr = jt.getCollection("properties");
+            // the configuration of the properties in the DSL, by name: the 
two lists can differ without --nulls
+            Map<String, String> configValues = new HashMap<>();
             JsonArray arr2 = jt.getCollection("modelProperties");
+            if (arr2 != null) {
+                for (Object o : arr2) {
+                    JsonObject p2 = (JsonObject) o;
+                    configValues.put(p2.getString("name"), 
p2.getString("value"));
+                }
+            }
             if (arr != null) {
                 row.properties = new ArrayList<>();
                 for (int i = 0; i < arr.size(); i++) {
@@ -159,12 +175,7 @@ public class CamelBeanDump extends ActionBaseCommand {
                     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");
-                        }
-                    }
+                    pr.configValue = configValues.get(pr.name);
                 }
             }
             rows.add(row);
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSourceTop.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSourceTop.java
index d2816a69d494..bfec4d73ff79 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSourceTop.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSourceTop.java
@@ -133,11 +133,12 @@ public class CamelSourceTop extends ActionWatchCommand {
                 row.put("id", r.id);
                 row.put("routeId", r.routeId);
                 row.put("location", r.location);
-                row.put("total", r.total);
-                row.put("mean", r.mean);
-                row.put("min", r.min);
-                row.put("max", r.max);
-                row.put("last", r.last);
+                // the counters are numbers, as in the JSON of the other 
listings
+                row.put("total", number(r.total));
+                row.put("mean", number(r.mean));
+                row.put("min", number(r.min));
+                row.put("max", number(r.max));
+                row.put("last", number(r.last));
                 row.put("code", r.code.stream().map(c -> {
                     JsonObject line = new JsonObject();
                     line.put("line", c.line);
@@ -176,6 +177,10 @@ public class CamelSourceTop extends ActionWatchCommand {
         }
     }
 
+    private static Long number(String value) {
+        return value != null ? Long.valueOf(value) : null;
+    }
+
     protected int sortRow(Row o1, Row o2) {
         // sort for highest mean value as we want the slowest in the top
         long m1 = o1.mean != null ? Long.parseLong(o1.mean) : 0;
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/RouteControllerAction.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/RouteControllerAction.java
index 535591b3eeb3..22375e42107b 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/RouteControllerAction.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/RouteControllerAction.java
@@ -164,14 +164,15 @@ public class RouteControllerAction extends 
ActionWatchCommand {
                     printer().printf("\tInitial Starting Routes: %b%n", 
jo.getBoolean("startingRoutes"));
                     printer().printf("\tUnhealthy Routes: %b%n", 
jo.getBoolean("unhealthyRoutes"));
                     printer().printf("\tRoutes Total: %s%n", 
jo.getInteger("totalRoutes"));
-                    printer().printf("\tRoutes Started: %d%n", 
jo.getInteger("startedRoutes"));
+                    printer().printf("\tRoutes Started: %d%n", 
jo.getLong("startedRoutes"));
                     printer().printf("\tRoutes Restarting: %d%n", 
jo.getInteger("restartingRoutes"));
                     printer().printf("\tRoutes Exhausted: %d%n", 
jo.getInteger("exhaustedRoutes"));
-                    printer().printf("\tInitial Delay: %d%n", 
jo.getInteger("initialDelay"));
-                    printer().printf("\tBackoff Delay: %d%n", 
jo.getInteger("backoffDelay"));
-                    printer().printf("\tBackoff Max Delay: %d%n", 
jo.getInteger("backoffMaxDelay"));
-                    printer().printf("\tBackoff Max Elapsed Time: %d%n", 
jo.getInteger("backoffMaxElapsedTime"));
-                    printer().printf("\tBackoff Max Attempts: %d%n", 
jo.getInteger("backoffMaxAttempts"));
+                    // the delays and times in millis, and the attempts, are 
longs
+                    printer().printf("\tInitial Delay: %d%n", 
jo.getLong("initialDelay"));
+                    printer().printf("\tBackoff Delay: %d%n", 
jo.getLong("backoffDelay"));
+                    printer().printf("\tBackoff Max Delay: %d%n", 
jo.getLong("backoffMaxDelay"));
+                    printer().printf("\tBackoff Max Elapsed Time: %d%n", 
jo.getLong("backoffMaxElapsedTime"));
+                    printer().printf("\tBackoff Max Attempts: %d%n", 
jo.getLong("backoffMaxAttempts"));
                     printer().printf("\tThread Pool Size: %d%n", 
jo.getInteger("threadPoolSize"));
                     printer().printf("\tUnhealthy On Restarting: %b%n", 
jo.getBoolean("unhealthyOnRestarting"));
                     printer().printf("\tUnhealthy On Exhaust: %b%n", 
jo.getBoolean("unhealthyOnExhausted"));
@@ -181,7 +182,10 @@ public class RouteControllerAction extends 
ActionWatchCommand {
             } else {
                 if (header) {
                     printer().println("Default Route Controller");
-                    printer().printf("\tStarting Routes: %b%n", 
jo.getBoolean("startingRoutes"));
+                    // only the supervising route controller tells
+                    if (jo.containsKey("startingRoutes")) {
+                        printer().printf("\tStarting Routes: %b%n", 
jo.getBoolean("startingRoutes"));
+                    }
                     printer().printf("\tRoutes Total: %s%n", 
jo.getInteger("totalRoutes"));
                     printer().println("\n");
                 }
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelBeanDumpTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelBeanDumpTest.java
index ba23696ab8b6..8f40c1036f76 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelBeanDumpTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelBeanDumpTest.java
@@ -16,20 +16,29 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.action;
 
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Stream;
+
 import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
 import org.apache.camel.dsl.jbang.core.common.CliException;
 import org.apache.camel.dsl.jbang.core.common.CliExitCode;
 import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.junit.jupiter.MockitoExtension;
+import picocli.CommandLine;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
+/**
+ * camel get bean: the beans of an integration with their properties, as the 
bean dev console has them, and the beans of
+ * the YAML or XML DSL with their configuration, as the bean-model dev console 
has them.
+ */
 @ExtendWith(MockitoExtension.class)
 class CamelBeanDumpTest extends ActionCommandTestSupport {
 
@@ -37,62 +46,207 @@ class CamelBeanDumpTest extends ActionCommandTestSupport {
     void testRequestsBeanDumpAndRendersBeans() throws Exception {
         writeStatusFile(TEST_PID, "myApp");
 
-        CamelBeanDump command = new CamelBeanDump(new 
CamelJBangMain().withPrinter(printer));
-        command.name = "myApp";
-        // options are normally defaulted by picocli; set them as we construct 
the command directly
-        command.filter = "all";
-        command.sort = "name";
-        command.scope = "all";
-        command.properties = true;
-        command.nulls = true;
+        assertThat(callWithResponse(command(), beans(bean("myBean", 
"com.foo.MyBean",
+                property("greeting", "java.lang.String", 
"helloWorld"))))).isZero();
+
+        JsonObject action = readActionFile(TEST_PID);
+        assertThat(action.getString("action")).isEqualTo("bean");
+        assertThat(action.containsKey("filter")).isFalse();
+        assertThat(action.getBoolean("properties")).isTrue();
+        assertThat(action.getBoolean("nulls")).isTrue();
+        assertThat(action.getBoolean("internal")).isFalse();
+        assertThat(printer.getOutput()).contains("BEAN: myBean 
(com.foo.MyBean):", "PROPERTY", "TYPE", "VALUE", "greeting",
+                "java.lang.String", "helloWorld");
+    }
+
+    @Test
+    void aNullValue() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command(), beans(bean("myBean", 
"com.foo.MyBean",
+                property("greeting", "java.lang.String", null))))).isZero();
+
+        assertThat(lineOf("greeting")).contains("null");
+    }
+
+    @Test
+    void withoutPropertiesATableOfTheBeans() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command("--properties=false"), 
someBeans())).isZero();
+
+        String[] lines = printer.getOutput().split(System.lineSeparator());
+        assertThat(lines[0]).contains("NAME", "TYPE");
+        assertThat(printer.getOutput()).doesNotContain("BEAN:", "PROPERTY");
+        // by name
+        assertThat(lines[1]).contains("camel-tracer");
+        assertThat(lines[2]).contains("executor", 
"java.util.concurrent.ThreadPoolExecutor");
+    }
+
+    @Test
+    void sortByType() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command("--properties=false", 
"--sort=type"), someBeans())).isZero();
+
+        String[] lines = printer.getOutput().split(System.lineSeparator());
+        assertThat(lines[1]).contains("com.foo.OrderService");
+        assertThat(lines[5]).contains("org.springframework");
+    }
+
+    @Test
+    void userScopeLeavesOutTheBeansOfTheFrameworksAndTheJdk() throws Exception 
{
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command("--properties=false", 
"--scope=user"), someBeans())).isZero();
+
+        // the internal beans are asked for, as a scope picks from all of them
+        assertThat(readActionFile(TEST_PID).getBoolean("internal")).isTrue();
+        
assertThat(printer.getOutput()).contains("orders").doesNotContain("camel-tracer",
 "executor", "quarkus",
+                "spring");
+    }
+
+    @Test
+    void camelScope() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command("--properties=false", 
"--scope=camel"), someBeans())).isZero();
+
+        
assertThat(printer.getOutput()).contains("camel-tracer").doesNotContain("orders",
 "executor");
+    }
+
+    @Test
+    void unknownScopeIsAUsageError() {
+        CamelBeanDump command = command("--scope=mine");
+
+        assertThatThrownBy(command::doCall)
+                .isInstanceOfSatisfying(CliException.class,
+                        e -> 
assertThat(e.getExitCode()).isEqualTo(CliExitCode.USAGE))
+                .hasMessage("--scope must be one of all, user, camel, spring, 
quarkus, was: mine");
+    }
 
-        int exit = callWithResponse(command, singleBeanResponse());
+    @Test
+    void theFilterAndOptionsAreSentToTheIntegration() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
 
-        assertEquals(0, exit);
+        assertThat(callWithResponse(command("--filter=order*", 
"--nulls=false", "--internal"), someBeans())).isZero();
 
         JsonObject action = readActionFile(TEST_PID);
-        assertNotNull(action, "action file should be written for the matched 
process");
-        assertEquals("bean", action.getString("action"));
+        assertThat(action.getString("filter")).isEqualTo("order*");
+        assertThat(action.getBoolean("nulls")).isFalse();
+        assertThat(action.getBoolean("internal")).isTrue();
+    }
+
+    @Test
+    void dslShowsTheConfigurationOfEachProperty() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+        // without --nulls the configuration has only the properties that are 
configured
+        JsonObject bean = bean("orders", "com.foo.OrderService",
+                property("retries", "java.lang.Integer", 3), property("url", 
"java.lang.String", "http://x";));
+        bean.put("modelProperties", new JsonArray(List.of(property("url", 
null, "{{orders.url}}"))));
+        JsonObject answer = new JsonObject();
+        answer.put("bean-models", new JsonObject(Map.of("orders", bean)));
+
+        assertThat(callWithResponse(command("--dsl", "--nulls=false"), 
answer)).isZero();
+
+        assertThat(printer.getOutput()).contains("BEAN: orders 
(com.foo.OrderService):", "CONFIGURATION");
+        
assertThat(lineOf("retries")).contains("3").doesNotContain("{{orders.url}}");
+        assertThat(lineOf("url")).contains("{{orders.url}}", "http://x";);
+    }
+
+    @Test
+    void jsonOfTheBeans() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+        JsonObject bean = bean("orders", "com.foo.OrderService", 
property("url", "java.lang.String", "http://x";));
+        bean.put("modelProperties", new JsonArray(List.of(property("url", 
null, "{{orders.url}}"))));
+        JsonObject answer = new JsonObject();
+        answer.put("bean-models", new JsonObject(Map.of("orders", bean)));
+
+        assertThat(callWithResponse(command("--dsl", "--json"), 
answer)).isZero();
+
+        JsonArray beans = Jsoner.deserialize(printer.getOutput(), (JsonArray) 
null);
+        JsonObject orders = beans.getMap(0);
+        assertThat(orders.getString("name")).isEqualTo("orders");
+        assertThat(orders.getString("type")).isEqualTo("com.foo.OrderService");
+        List<JsonObject> properties = orders.getCollection("properties");
+        assertThat(properties.get(0).getString("name")).isEqualTo("url");
+        assertThat(properties.get(0).getString("value")).isEqualTo("http://x";);
+        
assertThat(properties.get(0).getString("configValue")).isEqualTo("{{orders.url}}");
+    }
+
+    @Test
+    void anAnswerWithoutBeansIsAFailure() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+        CamelBeanDump command = command();
 
-        String out = printer.getOutput();
-        assertTrue(out.contains("BEAN: myBean"), "should print the bean name, 
was: " + out);
-        assertTrue(out.contains("com.foo.MyBean"), "should print the bean 
type, was: " + out);
-        assertTrue(out.contains("greeting"), "should print the bean property 
name, was: " + out);
-        assertTrue(out.contains("helloWorld"), "should print the bean property 
value, was: " + out);
+        assertThatThrownBy(() -> callWithResponse(command, new JsonObject()))
+                .isInstanceOfSatisfying(CliException.class,
+                        e -> 
assertThat(e.getExitCode()).isEqualTo(CliExitCode.FAILURE))
+                .hasMessage("Bean data not available from the running 
integration");
     }
 
     @Test
     void testReturnsErrorWhenNameDoesNotMatch() throws Exception {
         writeStatusFile(TEST_PID, "myApp");
+        CamelBeanDump command = new CamelBeanDump(new 
CamelJBangMain().withPrinter(printer));
+        CommandLine.populateCommand(command, "doesNotExist");
+
+        assertThatThrownBy(() -> callWithSingleProcess(command))
+                .isInstanceOfSatisfying(CliException.class,
+                        e -> 
assertThat(e.getExitCode()).isEqualTo(CliExitCode.NOT_FOUND))
+                .hasMessage("No running Camel integration matches: 
doesNotExist");
+        assertThat(printer.getOutput()).isEmpty();
+    }
 
+    private CamelBeanDump command(String... args) {
         CamelBeanDump command = new CamelBeanDump(new 
CamelJBangMain().withPrinter(printer));
-        command.name = "doesNotExist";
+        CommandLine.populateCommand(command, Stream.concat(Stream.of("myApp"), 
Arrays.stream(args)).toArray(String[]::new));
+        return command;
+    }
 
-        CliException e = assertThrows(CliException.class, () -> 
callWithSingleProcess(command));
+    private String lineOf(String text) {
+        return Arrays.stream(printer.getOutput().split(System.lineSeparator()))
+                .filter(l -> l.strip().startsWith(text + " 
")).findFirst().orElseThrow();
+    }
 
-        assertEquals(CliExitCode.NOT_FOUND, e.getExitCode());
-        assertEquals("No running Camel integration matches: doesNotExist", 
e.getMessage());
-        assertTrue(printer.getOutput().isEmpty(), "nothing should be rendered 
when no process matches");
+    // the beans of a user, of Camel, of the JDK, of Spring and of Quarkus
+    private static JsonObject someBeans() {
+        return beans(
+                bean("orders", "com.foo.OrderService"),
+                bean("camel-tracer", 
"org.apache.camel.impl.engine.DefaultTracer"),
+                bean("executor", "java.util.concurrent.ThreadPoolExecutor"),
+                bean("spring", "org.springframework.core.env.Environment"),
+                bean("quarkus", "io.quarkus.runtime.Application"));
     }
 
-    private static JsonObject singleBeanResponse() {
-        JsonObject property = new JsonObject();
-        property.put("name", "greeting");
-        property.put("type", "java.lang.String");
-        property.put("value", "helloWorld");
-        JsonArray properties = new JsonArray();
-        properties.add(property);
+    // as the bean dev console answers
+    private static JsonObject beans(JsonObject... beans) {
+        JsonObject map = new JsonObject();
+        for (JsonObject bean : beans) {
+            map.put(bean.getString("name"), bean);
+        }
+        JsonObject answer = new JsonObject();
+        answer.put("beans", map);
+        return answer;
+    }
 
+    private static JsonObject bean(String name, String type, JsonObject... 
properties) {
         JsonObject bean = new JsonObject();
-        bean.put("name", "myBean");
-        bean.put("type", "com.foo.MyBean");
-        bean.put("properties", properties);
-
-        JsonObject beans = new JsonObject();
-        beans.put("myBean", bean);
+        bean.put("name", name);
+        bean.put("type", type);
+        if (properties.length > 0) {
+            bean.put("properties", new JsonArray(Arrays.asList(properties)));
+        }
+        return bean;
+    }
 
-        JsonObject response = new JsonObject();
-        response.put("beans", beans);
-        return response;
+    private static JsonObject property(String name, String type, Object value) 
{
+        JsonObject property = new JsonObject();
+        property.put("name", name);
+        if (type != null) {
+            property.put("type", type);
+        }
+        property.put("value", value);
+        return property;
     }
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSourceTopTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSourceTopTest.java
index 39a30f6bb341..7f380c8abb6e 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSourceTopTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSourceTopTest.java
@@ -16,20 +16,28 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.action;
 
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Stream;
+
 import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
 import org.apache.camel.dsl.jbang.core.common.CliException;
 import org.apache.camel.dsl.jbang.core.common.CliExitCode;
 import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.junit.jupiter.MockitoExtension;
+import picocli.CommandLine;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
+/**
+ * camel top source: the slowest processors of an integration with their 
source code, as the top dev console has them.
+ */
 @ExtendWith(MockitoExtension.class)
 class CamelSourceTopTest extends ActionCommandTestSupport {
 
@@ -37,55 +45,164 @@ class CamelSourceTopTest extends ActionCommandTestSupport {
     void testRequestsTopProcessorsAndRendersRows() throws Exception {
         writeStatusFile(TEST_PID, "myApp");
 
-        CamelSourceTop command = new CamelSourceTop(new 
CamelJBangMain().withPrinter(printer));
-        command.name = "myApp";
+        assertThat(callWithResponse(command(), processors(log(), 
to()))).isZero();
 
-        int exit = callWithResponse(command, singleProcessorResponse());
+        
assertThat(readActionFile(TEST_PID).getString("action")).isEqualTo("top-processors");
+        assertThat(printer.getOutput())
+                .contains("Route: orders\tSource: file:orders.yaml:12 Total: 5 
Mean: 2 Max: 3 Min: 1 Last: 2");
+    }
 
-        assertEquals(0, exit);
+    @Test
+    void theSlowestFirstWithTheirCode() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
 
-        JsonObject action = readActionFile(TEST_PID);
-        assertNotNull(action, "action file should be written for the matched 
process");
-        assertEquals("top-processors", action.getString("action"));
+        assertThat(callWithResponse(command(), processors(log(), 
to()))).isZero();
 
         String out = printer.getOutput();
-        assertTrue(out.contains("Route: myRoute"), "should print the route id, 
was: " + out);
-        assertTrue(out.contains("Total: 5"), "should print the processor 
statistics, was: " + out);
+        assertThat(out.indexOf("Mean: 30")).isLessThan(out.indexOf("Mean: 2 
"));
+        // the line of the processor is marked, and the code is unescaped
+        assertThat(out).contains("  13:" + " ".repeat(9) + "- to:", "  14: 
-->" + " ".repeat(9) + "uri: \"kafka:out\"",
+                "  15:" + " ".repeat(13) + "id: to1");
+    }
+
+    @Test
+    void filterMeanShowsTheSlowerProcessors() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command("--filter-mean=10"), 
processors(log(), to()))).isZero();
+
+        assertThat(printer.getOutput()).contains("Mean: 
30").doesNotContain("Mean: 2 ");
+    }
+
+    @Test
+    void limitShowsTheSlowest() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        // the integration lists the slowest first
+        assertThat(callWithResponse(command("--limit=1"), processors(to(), 
log()))).isZero();
+
+        assertThat(printer.getOutput()).contains("Mean: 
30").doesNotContain("Mean: 2 ");
+    }
+
+    @Test
+    void aProcessorWithoutExchangesYet() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+        JsonObject idle = processor("idle1", "file:orders.yaml:20", -1, -1, 
List.of());
+
+        assertThat(callWithResponse(command(), processors(idle))).isZero();
+
+        assertThat(printer.getOutput()).contains("Total: 0 Mean:  Max: 0 Min: 
0 Last:");
+    }
+
+    @Test
+    void jsonHasTheNumbersAndTheCode() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command("--json"), processors(log(), 
to()))).isZero();
+
+        JsonArray rows = Jsoner.deserialize(printer.getOutput(), (JsonArray) 
null);
+        assertThat(rows).hasSize(2);
+        JsonObject to = rows.getMap(0);
+        assertThat(to.getString("id")).isEqualTo("to1");
+        assertThat(to.getString("routeId")).isEqualTo("orders");
+        assertThat(to.getString("location")).isEqualTo("file:orders.yaml:14");
+        // numbers, not text
+        assertThat(to.get("total")).isInstanceOf(Number.class);
+        assertThat(to.get("mean")).isInstanceOf(Number.class);
+        assertThat(to.getLong("total")).isEqualTo(5L);
+        assertThat(to.getLong("mean")).isEqualTo(30L);
+        assertThat(to.getLong("max")).isEqualTo(59L);
+        List<JsonObject> code = to.getCollection("code");
+        assertThat(code).hasSize(3);
+        assertThat(code.get(1).getBoolean("match")).isTrue();
+        assertThat(code.get(0).containsKey("match")).isFalse();
+    }
+
+    @Test
+    void jsonOfAProcessorWithoutExchangesYet() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+        JsonObject idle = processor("idle1", "file:orders.yaml:20", -1, -1, 
List.of());
+
+        assertThat(callWithResponse(command("--json"), 
processors(idle))).isZero();
+
+        JsonObject row = Jsoner.deserialize(printer.getOutput(), (JsonArray) 
null).getMap(0);
+        assertThat(row.get("mean")).isNull();
+        assertThat(row.get("last")).isNull();
+        assertThat(row.getLong("total")).isZero();
+    }
+
+    @Test
+    void anAnswerWithoutProcessorsIsAFailure() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+        CamelSourceTop command = command();
+
+        assertThatThrownBy(() -> callWithResponse(command, new JsonObject()))
+                .isInstanceOfSatisfying(CliException.class,
+                        e -> 
assertThat(e.getExitCode()).isEqualTo(CliExitCode.FAILURE))
+                .hasMessage("Source top data not available from the running 
integration");
     }
 
     @Test
     void testReturnsErrorWhenNameDoesNotMatch() throws Exception {
         writeStatusFile(TEST_PID, "myApp");
+        CamelSourceTop command = new CamelSourceTop(new 
CamelJBangMain().withPrinter(printer));
+        CommandLine.populateCommand(command, "doesNotExist");
 
+        assertThatThrownBy(() -> callWithSingleProcess(command))
+                .isInstanceOfSatisfying(CliException.class,
+                        e -> 
assertThat(e.getExitCode()).isEqualTo(CliExitCode.NOT_FOUND))
+                .hasMessage("No running Camel integration matches: 
doesNotExist");
+        assertThat(printer.getOutput()).isEmpty();
+    }
+
+    private CamelSourceTop command(String... args) {
         CamelSourceTop command = new CamelSourceTop(new 
CamelJBangMain().withPrinter(printer));
-        command.name = "doesNotExist";
+        CommandLine.populateCommand(command, Stream.concat(Stream.of("myApp"), 
Arrays.stream(args)).toArray(String[]::new));
+        return command;
+    }
 
-        CliException e = assertThrows(CliException.class, () -> 
callWithSingleProcess(command));
+    // as the top dev console answers
+    private static JsonObject processors(JsonObject... processors) {
+        JsonObject answer = new JsonObject();
+        answer.put("processors", new JsonArray(Arrays.asList(processors)));
+        return answer;
+    }
 
-        assertEquals(CliExitCode.NOT_FOUND, e.getExitCode());
-        assertEquals("No running Camel integration matches: doesNotExist", 
e.getMessage());
-        assertTrue(printer.getOutput().isEmpty(), "nothing should be rendered 
when no process matches");
+    private static JsonObject log() {
+        return processor("log1", "file:orders.yaml:12", 2, 2, List.of());
     }
 
-    private static JsonObject singleProcessorResponse() {
-        JsonObject stats = new JsonObject();
-        stats.put("exchangesTotal", 5);
-        stats.put("meanProcessingTime", 2);
-        stats.put("maxProcessingTime", 3);
-        stats.put("minProcessingTime", 1);
-        stats.put("lastProcessingTime", 2);
+    private static JsonObject to() {
+        return processor("to1", "file:orders.yaml:14", 30, 25, List.of(
+                line(13, "    - to:", false),
+                line(14, "        uri: \\\"kafka:out\\\"", true),
+                line(15, "        id: to1", false)));
+    }
 
+    private static JsonObject processor(String id, String location, long mean, 
long last, List<JsonObject> code) {
+        JsonObject stats = new JsonObject();
+        stats.put("exchangesTotal", mean < 0 ? 0 : 5);
+        stats.put("meanProcessingTime", mean);
+        stats.put("maxProcessingTime", Math.max(0, mean * 2 - 1));
+        stats.put("minProcessingTime", mean < 0 ? 0 : 1);
+        stats.put("lastProcessingTime", last);
         JsonObject processor = new JsonObject();
-        processor.put("processorId", "myProcessor");
-        processor.put("routeId", "myRoute");
-        processor.put("location", "myroute.yaml");
+        processor.put("routeId", "orders");
+        processor.put("processorId", id);
+        processor.put("location", location);
         processor.put("statistics", stats);
+        if (!code.isEmpty()) {
+            processor.put("code", new JsonArray(code));
+        }
+        return processor;
+    }
 
-        JsonArray processors = new JsonArray();
-        processors.add(processor);
-
-        JsonObject response = new JsonObject();
-        response.put("processors", processors);
-        return response;
+    // the code is escaped as JSON text
+    private static JsonObject line(int line, String code, boolean match) {
+        JsonObject jo = new JsonObject(Map.of("line", line, "code", code));
+        if (match) {
+            jo.put("match", true);
+        }
+        return jo;
     }
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/RouteControllerActionTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/RouteControllerActionTest.java
index be05d8fcd2c5..ec84d0aefc10 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/RouteControllerActionTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/RouteControllerActionTest.java
@@ -16,20 +16,28 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.action;
 
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Stream;
+
 import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
 import org.apache.camel.dsl.jbang.core.common.CliException;
 import org.apache.camel.dsl.jbang.core.common.CliExitCode;
 import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.junit.jupiter.MockitoExtension;
+import picocli.CommandLine;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
+/**
+ * camel get route-controller: the route controller of an integration and its 
routes, as the route-controller dev
+ * console has them: the default controller, or the supervising controller 
with the routes it restarts.
+ */
 @ExtendWith(MockitoExtension.class)
 class RouteControllerActionTest extends ActionCommandTestSupport {
 
@@ -37,52 +45,213 @@ class RouteControllerActionTest extends 
ActionCommandTestSupport {
     void testRequestsRouteControllerAndRendersRoutes() throws Exception {
         writeStatusFile(TEST_PID, "myApp");
 
-        RouteControllerAction command = new RouteControllerAction(new 
CamelJBangMain().withPrinter(printer));
-        command.name = "myApp";
-        // options are normally defaulted by picocli; set them as we construct 
the command directly
-        command.sort = "id";
-        command.header = true;
+        assertThat(callWithResponse(command(), defaultController())).isZero();
 
-        int exit = callWithResponse(command, defaultControllerResponse());
+        JsonObject action = readActionFile(TEST_PID);
+        assertThat(action.getString("action")).isEqualTo("route-controller");
+        // stack traces by default
+        assertThat(action.getString("stacktrace")).isEqualTo("true");
+        String out = printer.getOutput();
+        assertThat(out).contains("Default Route Controller", "Routes Total: 
1", "myRoute", "timer://foo", "Started")
+                // the default controller does not tell whether its routes are 
starting
+                .doesNotContain("Starting Routes", "ATTEMPT");
+    }
 
-        assertEquals(0, exit);
+    @Test
+    void supervisingControllerWithTheRoutesItRestarts() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
 
-        JsonObject action = readActionFile(TEST_PID);
-        assertNotNull(action, "action file should be written for the matched 
process");
-        assertEquals("route-controller", action.getString("action"));
+        assertThat(callWithResponse(command(), 
supervisingController())).isZero();
 
         String out = printer.getOutput();
-        assertTrue(out.contains("Default Route Controller"), "should print the 
controller header, was: " + out);
-        assertTrue(out.contains("myRoute"), "should print the route id, was: " 
+ out);
+        assertThat(out).contains("Supervising Route Controller", "Initial 
Starting Routes: false",
+                "Unhealthy Routes: true", "Routes Total: 4", "Routes Started: 
1", "Routes Restarting: 2",
+                "Routes Exhausted: 1", "Backoff Delay: 2000", "Thread Pool 
Size: 1");
+        String[] lines = table(out);
+        assertThat(lines[0]).contains("ID", "URI", "STATE", "ATTEMPT", 
"ELAPSED", "LAST-AGO", "ERROR-MESSAGE");
+        // the routes by id; an active route is starting at its first attempt, 
and restarting after
+        assertThat(lines[1]).contains("ftp", "Starting", " 1 ", "Cannot 
login");
+        assertThat(lines[2]).contains("kafka", "Restarting", " 3 ", "Broker 
down");
+        assertThat(lines[3]).contains("mail", "Exhausted", " 10 ", "No host");
+        assertThat(lines[4]).contains("timer", "Started");
     }
 
     @Test
-    void testReturnsErrorWhenNameDoesNotMatch() throws Exception {
+    void stackTracesOfTheRoutesThatFail() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command(), 
supervisingController())).isZero();
+
+        String out = printer.getOutput();
+        assertThat(out).contains("STACK-TRACE", "ID: kafka", "URI: 
kafka://orders", "STATE: Restarting",
+                "java.io.IOException: Broker down", "at 
org.example.Kafka.connect(Kafka.java:10)",
+                "at org.example.Kafka.start(Kafka.java:20)");
+    }
+
+    @Test
+    void depthLimitsTheStackTraces() throws Exception {
         writeStatusFile(TEST_PID, "myApp");
 
+        assertThat(callWithResponse(command("--depth=2"), 
supervisingController())).isZero();
+
+        assertThat(printer.getOutput()).contains("at 
org.example.Kafka.connect(Kafka.java:10)")
+                .doesNotContain("at org.example.Kafka.start(Kafka.java:20)");
+    }
+
+    @Test
+    void withoutTraceTheStackTracesAreNotAskedFor() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command("--trace=false"), 
supervisingController())).isZero();
+
+        
assertThat(readActionFile(TEST_PID).getString("stacktrace")).isEqualTo("false");
+        assertThat(printer.getOutput()).contains("Broker 
down").doesNotContain("STACK-TRACE");
+    }
+
+    @Test
+    void withoutHeader() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command("--header=false"), 
supervisingController())).isZero();
+
+        assertThat(printer.getOutput()).doesNotContain("Supervising Route 
Controller", "Backoff Delay")
+                .contains("kafka", "Restarting");
+    }
+
+    @Test
+    void backoffTimesLongerThanAnInt() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+        JsonObject answer = supervisingController();
+        // 30 days
+        answer.put("backoffMaxElapsedTime", 2_592_000_000L);
+        answer.put("backoffMaxAttempts", Long.MAX_VALUE);
+
+        assertThat(callWithResponse(command(), answer)).isZero();
+
+        assertThat(printer.getOutput()).contains("Backoff Max Elapsed Time: 
2592000000",
+                "Backoff Max Attempts: " + Long.MAX_VALUE);
+    }
+
+    @Test
+    void sortByState() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command("--sort=state", "--trace=false"), 
supervisingController())).isZero();
+
+        // by the status of the routes
+        String[] lines = table(printer.getOutput());
+        assertThat(lines[1]).contains("timer");
+    }
+
+    @Test
+    void jsonIsTheAnswerOfTheIntegration() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+
+        assertThat(callWithResponse(command("--json"), 
supervisingController())).isZero();
+
+        JsonObject json = Jsoner.deserialize(printer.getOutput(), (JsonObject) 
null);
+        
assertThat(json.getString("controller")).isEqualTo("SupervisingRouteController");
+        JsonArray routes = (JsonArray) json.get("routes");
+        assertThat(routes).hasSize(4);
+    }
+
+    @Test
+    void anAnswerWithoutRoutesIsAFailure() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+        RouteControllerAction command = command();
+
+        assertThatThrownBy(() -> callWithResponse(command, new JsonObject()))
+                .isInstanceOfSatisfying(CliException.class,
+                        e -> 
assertThat(e.getExitCode()).isEqualTo(CliExitCode.FAILURE))
+                .hasMessage("Route controller data not available from the 
running integration");
+    }
+
+    @Test
+    void testReturnsErrorWhenNameDoesNotMatch() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
         RouteControllerAction command = new RouteControllerAction(new 
CamelJBangMain().withPrinter(printer));
-        command.name = "doesNotExist";
+        CommandLine.populateCommand(command, "doesNotExist");
+
+        assertThatThrownBy(() -> callWithSingleProcess(command))
+                .isInstanceOfSatisfying(CliException.class,
+                        e -> 
assertThat(e.getExitCode()).isEqualTo(CliExitCode.NOT_FOUND))
+                .hasMessage("No running Camel integration matches: 
doesNotExist");
+        assertThat(printer.getOutput()).isEmpty();
+    }
 
-        CliException e = assertThrows(CliException.class, () -> 
callWithSingleProcess(command));
+    private RouteControllerAction command(String... args) {
+        RouteControllerAction command = new RouteControllerAction(new 
CamelJBangMain().withPrinter(printer));
+        CommandLine.populateCommand(command, Stream.concat(Stream.of("myApp"), 
Arrays.stream(args)).toArray(String[]::new));
+        return command;
+    }
 
-        assertEquals(CliExitCode.NOT_FOUND, e.getExitCode());
-        assertEquals("No running Camel integration matches: doesNotExist", 
e.getMessage());
-        assertTrue(printer.getOutput().isEmpty(), "nothing should be rendered 
when no process matches");
+    // the lines of the table of the routes, from its header
+    private static String[] table(String out) {
+        String table = out.substring(out.indexOf("ID "));
+        int end = table.indexOf("---");
+        return (end > 0 ? table.substring(0, end) : 
table).strip().split(System.lineSeparator());
     }
 
-    private static JsonObject defaultControllerResponse() {
+    // as the route-controller dev console answers
+    private static JsonObject defaultController() {
         JsonObject route = new JsonObject();
         route.put("routeId", "myRoute");
         route.put("uri", "timer://foo");
         route.put("status", "Started");
-        JsonArray routes = new JsonArray();
-        routes.add(route);
-
-        JsonObject response = new JsonObject();
-        response.put("controller", "DefaultRouteController");
-        response.put("startingRoutes", false);
-        response.put("totalRoutes", 1);
-        response.put("routes", routes);
-        return response;
+        JsonObject answer = new JsonObject();
+        answer.put("controller", "DefaultRouteController");
+        answer.put("totalRoutes", 1);
+        answer.put("routes", new JsonArray(List.of(route)));
+        return answer;
+    }
+
+    private static JsonObject supervisingController() {
+        long now = System.currentTimeMillis();
+        JsonObject answer = new JsonObject();
+        answer.put("controller", "SupervisingRouteController");
+        answer.put("startingRoutes", false);
+        answer.put("unhealthyRoutes", true);
+        answer.put("totalRoutes", 4);
+        answer.put("startedRoutes", 1L);
+        answer.put("restartingRoutes", 2);
+        answer.put("exhaustedRoutes", 1);
+        answer.put("initialDelay", 0L);
+        answer.put("backoffDelay", 2000L);
+        answer.put("backoffMaxDelay", 0L);
+        answer.put("backoffMaxElapsedTime", 0L);
+        answer.put("backoffMaxAttempts", 10L);
+        answer.put("threadPoolSize", 1);
+        answer.put("unhealthyOnRestarting", true);
+        answer.put("unhealthyOnExhausted", true);
+        answer.put("routes", new JsonArray(
+                List.of(
+                        route("timer", "timer://tick", "Started", null, 0, 0, 
null),
+                        route("kafka", "kafka://orders", "Stopped", "Active", 
3, now - 5000, "Broker down"),
+                        route("ftp", "ftp://host";, "Stopped", "Active", 1, now 
- 1000, "Cannot login"),
+                        route("mail", "smtp://host", "Stopped", "Exhausted", 
10, 0, "No host"))));
+        return answer;
+    }
+
+    private static JsonObject route(
+            String routeId, String uri, String status, String supervising, 
long attempts, long lastAttempt,
+            String error) {
+        JsonObject route = new JsonObject();
+        route.put("routeId", routeId);
+        route.put("uri", uri);
+        route.put("status", status);
+        route.put("attempts", attempts);
+        route.put("lastAttempt", lastAttempt);
+        route.put("nextAttempt", 0L);
+        route.put("elapsed", lastAttempt > 0 ? 7000L : 0L);
+        if (supervising != null) {
+            route.put("supervising", supervising);
+        }
+        if (error != null) {
+            route.put("error", error);
+            route.put("stackTrace", new JsonArray(
+                    List.of("java.io.IOException: " + error,
+                            "\tat org.example.Kafka.connect(Kafka.java:10)", 
"\tat org.example.Kafka.start(Kafka.java:20)")));
+        }
+        return route;
     }
 }

Reply via email to