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 2022/08/26 10:37:05 UTC

[camel] 03/04: CAMEL-18425: camel-cli - Make regular Camel applications work with Camel CLI

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

commit 462c4e8ff30a1fca46c1ba6d3e5b75d7c2d70c61
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Fri Aug 26 12:30:07 2022 +0200

    CAMEL-18425: camel-cli - Make regular Camel applications work with Camel CLI
---
 .../org/apache/camel/spi/CliConnectorFactory.java  |  10 ++
 .../cli/connector/DefaultCliConnectorFactory.java  |  11 ++
 .../camel/cli/connector/LocalCliConnector.java     |  12 +-
 .../core/commands/process/CamelContextStatus.java  | 101 +++++++---------
 .../core/commands/process/CamelRouteStatus.java    | 134 +++++++++------------
 .../jbang/core/commands/process/ListProcess.java   |  62 ++++++----
 .../core/commands/process/ProcessBaseCommand.java  |  84 +++++++++++--
 7 files changed, 240 insertions(+), 174 deletions(-)

diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/CliConnectorFactory.java b/core/camel-api/src/main/java/org/apache/camel/spi/CliConnectorFactory.java
index 6d34675eace..ef323b6025d 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/CliConnectorFactory.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/CliConnectorFactory.java
@@ -60,6 +60,16 @@ public interface CliConnectorFactory {
      */
     String getRuntimeVersion();
 
+    /**
+     * The main class used by the runtime to start.
+     */
+    void setRuntimeStartClass(String className);
+
+    /**
+     * The main class used by the runtime to start.
+     */
+    String getRuntimeStartClass();
+
     /**
      * Creates the connector which will be added as a {@link Service} to {@link org.apache.camel.CamelContext} as the
      * lifecycle to start and stop the connector.
diff --git a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/DefaultCliConnectorFactory.java b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/DefaultCliConnectorFactory.java
index 07f59815f6e..2f70726cbf0 100644
--- a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/DefaultCliConnectorFactory.java
+++ b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/DefaultCliConnectorFactory.java
@@ -26,6 +26,7 @@ public class DefaultCliConnectorFactory implements CliConnectorFactory {
     private boolean enabled = true;
     private String runtime;
     private String runtimeVersion;
+    private String runtimeStartClass;
 
     @Override
     public boolean isEnabled() {
@@ -55,6 +56,16 @@ public class DefaultCliConnectorFactory implements CliConnectorFactory {
         this.runtimeVersion = runtimeVersion;
     }
 
+    @Override
+    public String getRuntimeStartClass() {
+        return runtimeStartClass;
+    }
+
+    @Override
+    public void setRuntimeStartClass(String runtimeStartClass) {
+        this.runtimeStartClass = runtimeStartClass;
+    }
+
     @Override
     public Service createConnector() {
         if (enabled) {
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 60500113d95..34fbb5a62ff 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
@@ -49,6 +49,7 @@ public class LocalCliConnector extends ServiceSupport implements CamelContextAwa
     private int delay = 2000;
     private String platform;
     private String platformVersion;
+    private String mainClass;
     private ScheduledExecutorService executor;
     private File lockFile;
     private File statusFile;
@@ -76,10 +77,11 @@ public class LocalCliConnector extends ServiceSupport implements CamelContextAwa
         if (lockFile != null) {
             statusFile = createLockFile(lockFile.getName() + "-status.json");
         }
-        executor.scheduleWithFixedDelay(this::statusTask, 1000, delay, TimeUnit.MILLISECONDS);
+        executor.scheduleWithFixedDelay(this::statusTask, 0, delay, TimeUnit.MILLISECONDS);
 
         // what platform are we running
         CliConnectorFactory ccf = camelContext.adapt(ExtendedCamelContext.class).getCliConnectorFactory();
+        mainClass = ccf.getRuntimeStartClass();
         platform = ccf.getRuntime();
         if (platform == null) {
             // use camel context name to guess platform if not specified
@@ -125,10 +127,18 @@ public class LocalCliConnector extends ServiceSupport implements CamelContextAwa
 
             // what runtime are in use
             JsonObject rc = new JsonObject();
+            String dir = new File(".").getAbsolutePath();
+            dir = FileUtil.onlyPath(dir);
+            rc.put("pid", ProcessHandle.current().pid());
+            rc.put("directory", dir);
+            ProcessHandle.current().info().user().ifPresent(u -> rc.put("user", u));
             rc.put("platform", platform);
             if (platformVersion != null) {
                 rc.put("version", platformVersion);
             }
+            if (mainClass != null) {
+                rc.put("mainClass", mainClass);
+            }
             root.put("runtime", rc);
 
             // collect details via console
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/CamelContextStatus.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/CamelContextStatus.java
index 64edfda5faf..9e43658d841 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/CamelContextStatus.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/CamelContextStatus.java
@@ -16,8 +16,6 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.process;
 
-import java.io.File;
-import java.io.FileInputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -29,12 +27,8 @@ 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.IOHelper;
-import org.apache.camel.util.ObjectHelper;
-import org.apache.camel.util.StringHelper;
 import org.apache.camel.util.TimeUtils;
 import org.apache.camel.util.json.JsonObject;
-import org.apache.camel.util.json.Jsoner;
 import picocli.CommandLine;
 import picocli.CommandLine.Command;
 
@@ -60,50 +54,40 @@ public class CamelContextStatus extends ProcessBaseCommand {
         List<Long> pids = findPids(name);
         ProcessHandle.allProcesses()
                 .filter(ph -> pids.contains(ph.pid()))
-                .sorted((o1, o2) -> {
-                    switch (sort) {
-                        case "pid":
-                            return Long.compare(o1.pid(), o2.pid());
-                        case "name":
-                            return extractName(o1).compareTo(extractName(o2));
-                        case "age":
-                            // we want newest in top
-                            return Long.compare(extractSince(o1), extractSince(o2)) * -1;
-                        default:
-                            return 0;
-                    }
-                })
                 .forEach(ph -> {
-                    Row row = new Row();
-                    row.name = extractName(ph);
-                    if (ObjectHelper.isNotEmpty(row.name)) {
+                    JsonObject root = loadStatus(ph.pid());
+                    // there must be a status file for the running Camel integration
+                    if (root != null) {
+                        Row row = new Row();
+                        row.name = extractName(root, ph);
                         row.pid = "" + ph.pid();
-                        row.ago = TimeUtils.printSince(extractSince(ph));
-                        JsonObject root = loadStatus(ph.pid());
-                        if (root != null) {
-                            JsonObject runtime = (JsonObject) root.get("runtime");
-                            row.platform = runtime != null ? runtime.getString("platform") : null;
-                            row.platformVersion = runtime != null ? runtime.getString("version") : null;
-                            JsonObject context = (JsonObject) root.get("context");
-                            row.state = context.getString("state").toLowerCase(Locale.ROOT);
-                            row.camelVersion = context.getString("version");
-                            Map<String, ?> stats = context.getMap("statistics");
-                            if (stats != null) {
-                                row.total = stats.get("exchangesTotal").toString();
-                                row.inflight = stats.get("exchangesInflight").toString();
-                                row.failed = stats.get("exchangesFailed").toString();
-                                Object last = stats.get("sinceLastExchange");
-                                if (last != null) {
-                                    row.sinceLast = last.toString();
-                                }
+                        row.uptime = extractSince(ph);
+                        row.ago = TimeUtils.printSince(row.uptime);
+                        JsonObject runtime = (JsonObject) root.get("runtime");
+                        row.platform = runtime != null ? runtime.getString("platform") : null;
+                        row.platformVersion = runtime != null ? runtime.getString("version") : null;
+                        JsonObject context = (JsonObject) root.get("context");
+                        row.state = context.getString("state").toLowerCase(Locale.ROOT);
+                        row.camelVersion = context.getString("version");
+                        Map<String, ?> stats = context.getMap("statistics");
+                        if (stats != null) {
+                            row.total = stats.get("exchangesTotal").toString();
+                            row.inflight = stats.get("exchangesInflight").toString();
+                            row.failed = stats.get("exchangesFailed").toString();
+                            Object last = stats.get("sinceLastExchange");
+                            if (last != null) {
+                                row.sinceLast = last.toString();
                             }
-                            JsonObject hc = (JsonObject) root.get("healthChecks");
-                            row.ready = hc != null ? hc.getString("ready") + "/" + hc.getString("total") : null;
                         }
+                        JsonObject hc = (JsonObject) root.get("healthChecks");
+                        row.ready = hc != null ? hc.getString("ready") + "/" + hc.getString("total") : null;
                         rows.add(row);
                     }
                 });
 
+        // sort rows
+        rows.sort(this::sortRow);
+
         if (!rows.isEmpty()) {
             System.out.println(AsciiTable.getTable(AsciiTable.NO_BORDERS, rows, Arrays.asList(
                     new Column().header("PID").headerAlign(HorizontalAlign.CENTER).with(r -> r.pid),
@@ -112,8 +96,8 @@ public class CamelContextStatus extends ProcessBaseCommand {
                     new Column().header("CAMEL").dataAlign(HorizontalAlign.LEFT).with(r -> r.camelVersion),
                     new Column().header("PLATFORM").dataAlign(HorizontalAlign.LEFT).with(this::getPlatform),
                     new Column().header("READY").dataAlign(HorizontalAlign.CENTER).with(r -> r.ready),
-                    new Column().header("STATE").headerAlign(HorizontalAlign.CENTER)
-                            .with(r -> StringHelper.capitalize(r.state)),
+                    new Column().header("STATUS").headerAlign(HorizontalAlign.CENTER)
+                            .with(r -> extractState(r.state)),
                     new Column().header("AGE").headerAlign(HorizontalAlign.CENTER).with(r -> r.ago),
                     new Column().header("TOTAL").with(r -> r.total),
                     new Column().header("FAILED").with(r -> r.failed),
@@ -124,6 +108,19 @@ public class CamelContextStatus extends ProcessBaseCommand {
         return 0;
     }
 
+    protected int sortRow(Row o1, Row o2) {
+        switch (sort) {
+            case "pid":
+                return Long.compare(Long.parseLong(o1.pid), Long.parseLong(o2.pid));
+            case "name":
+                return o1.name.compareToIgnoreCase(o2.name);
+            case "age":
+                return Long.compare(o1.uptime, o2.uptime);
+            default:
+                return 0;
+        }
+    }
+
     private String getPlatform(Row r) {
         if (r.platformVersion != null) {
             return r.platform + " v" + r.platformVersion;
@@ -132,21 +129,6 @@ public class CamelContextStatus extends ProcessBaseCommand {
         }
     }
 
-    private JsonObject loadStatus(long pid) {
-        try {
-            File f = getStatusFile("" + pid);
-            if (f != null) {
-                FileInputStream fis = new FileInputStream(f);
-                String text = IOHelper.loadText(fis);
-                IOHelper.close(fis);
-                return (JsonObject) Jsoner.deserialize(text);
-            }
-        } catch (Throwable e) {
-            // ignore
-        }
-        return null;
-    }
-
     private static class Row {
         String pid;
         String platform;
@@ -156,6 +138,7 @@ public class CamelContextStatus extends ProcessBaseCommand {
         String ready;
         String state;
         String ago;
+        long uptime;
         String total;
         String failed;
         String inflight;
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/CamelRouteStatus.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/CamelRouteStatus.java
index 62267d0efca..9bcdf49c06d 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/CamelRouteStatus.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/CamelRouteStatus.java
@@ -16,8 +16,6 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.process;
 
-import java.io.File;
-import java.io.FileInputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -30,12 +28,10 @@ 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.ObjectHelper;
 import org.apache.camel.util.StringHelper;
+import org.apache.camel.util.TimeUtils;
 import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
-import org.apache.camel.util.json.Jsoner;
 import picocli.CommandLine;
 import picocli.CommandLine.Command;
 
@@ -68,63 +64,49 @@ public class CamelRouteStatus extends ProcessBaseCommand {
         List<Long> pids = findPids(name);
         ProcessHandle.allProcesses()
                 .filter(ph -> pids.contains(ph.pid()))
-                .sorted((o1, o2) -> {
-                    switch (sort) {
-                        case "pid":
-                            return Long.compare(o1.pid(), o2.pid());
-                        case "name":
-                            return extractName(o1).compareTo(extractName(o2));
-                        case "age":
-                            // we want newest in top
-                            return Long.compare(extractSince(o1), extractSince(o2)) * -1;
-                        default:
-                            return 0;
-                    }
-                })
                 .forEach(ph -> {
-                    String name = extractName(ph);
-                    if (ObjectHelper.isNotEmpty(name)) {
-                        JsonObject status = loadStatus(ph.pid());
-                        if (status != null) {
-                            JsonArray array = (JsonArray) status.get("routes");
-                            for (int i = 0; i < array.size(); i++) {
-                                JsonObject o = (JsonObject) array.get(i);
-                                Row row = new Row();
-                                row.pid = "" + ph.pid();
-                                row.name = name;
-                                row.routeId = o.getString("routeId");
-                                row.from = o.getString("from");
-                                row.source = o.getString("source");
-                                row.state = o.getString("state").toLowerCase(Locale.ROOT);
-                                row.uptime = o.getString("uptime");
-                                Map<String, ?> stats = o.getMap("statistics");
-                                if (stats != null) {
-                                    row.total = stats.get("exchangesTotal").toString();
-                                    row.inflight = stats.get("exchangesInflight").toString();
-                                    row.failed = stats.get("exchangesFailed").toString();
-                                    row.mean = stats.get("meanProcessingTime").toString();
-                                    if ("-1".equals(row.mean)) {
-                                        row.mean = null;
-                                    }
-                                    row.max = stats.get("maxProcessingTime").toString();
-                                    row.min = stats.get("minProcessingTime").toString();
-                                    Object last = stats.get("sinceLastExchange");
-                                    if (last != null) {
-                                        row.sinceLast = last.toString();
-                                    }
-                                }
-
-                                boolean add = true;
-                                if (mean > 0 && row.mean != null && Long.parseLong(row.mean) < mean) {
-                                    add = false;
-                                }
-                                if (limit > 0 && rows.size() >= limit) {
-                                    add = false;
+                    JsonObject root = loadStatus(ph.pid());
+                    if (root != null) {
+                        String name = extractName(root, ph);
+                        JsonArray array = (JsonArray) root.get("routes");
+                        for (int i = 0; i < array.size(); i++) {
+                            JsonObject o = (JsonObject) array.get(i);
+                            Row row = new Row();
+                            row.name = name;
+                            row.pid = "" + ph.pid();
+                            row.routeId = o.getString("routeId");
+                            row.from = o.getString("from");
+                            row.source = o.getString("source");
+                            row.state = o.getString("state").toLowerCase(Locale.ROOT);
+                            row.age = o.getString("uptime");
+                            row.uptime = row.age != null ? TimeUtils.toMilliSeconds(row.age) : 0;
+                            Map<String, ?> stats = o.getMap("statistics");
+                            if (stats != null) {
+                                row.total = stats.get("exchangesTotal").toString();
+                                row.inflight = stats.get("exchangesInflight").toString();
+                                row.failed = stats.get("exchangesFailed").toString();
+                                row.mean = stats.get("meanProcessingTime").toString();
+                                if ("-1".equals(row.mean)) {
+                                    row.mean = null;
                                 }
-                                if (add) {
-                                    rows.add(row);
+                                row.max = stats.get("maxProcessingTime").toString();
+                                row.min = stats.get("minProcessingTime").toString();
+                                Object last = stats.get("sinceLastExchange");
+                                if (last != null) {
+                                    row.sinceLast = last.toString();
                                 }
                             }
+
+                            boolean add = true;
+                            if (mean > 0 && row.mean != null && Long.parseLong(row.mean) < mean) {
+                                add = false;
+                            }
+                            if (limit > 0 && rows.size() >= limit) {
+                                add = false;
+                            }
+                            if (add) {
+                                rows.add(row);
+                            }
                         }
                     }
                 });
@@ -143,9 +125,9 @@ public class CamelRouteStatus extends ProcessBaseCommand {
                             .with(r -> r.routeId),
                     new Column().header("FROM").dataAlign(HorizontalAlign.LEFT).maxWidth(40, OverflowBehaviour.ELLIPSIS)
                             .with(r -> r.from),
-                    new Column().header("STATE").headerAlign(HorizontalAlign.CENTER)
-                            .with(r -> StringHelper.capitalize(r.state)),
-                    new Column().header("AGE").headerAlign(HorizontalAlign.CENTER).with(r -> r.uptime),
+                    new Column().header("STATUS").headerAlign(HorizontalAlign.CENTER)
+                            .with(r -> extractState(r.state)),
+                    new Column().header("AGE").headerAlign(HorizontalAlign.CENTER).with(r -> r.age),
                     new Column().header("TOTAL").with(r -> r.total),
                     new Column().header("FAILED").with(r -> r.failed),
                     new Column().header("INFLIGHT").with(r -> r.inflight),
@@ -158,21 +140,6 @@ public class CamelRouteStatus extends ProcessBaseCommand {
         return 0;
     }
 
-    private JsonObject loadStatus(long pid) {
-        try {
-            File f = getStatusFile("" + pid);
-            if (f != null) {
-                FileInputStream fis = new FileInputStream(f);
-                String text = IOHelper.loadText(fis);
-                IOHelper.close(fis);
-                return (JsonObject) Jsoner.deserialize(text);
-            }
-        } catch (Throwable e) {
-            // ignore
-        }
-        return null;
-    }
-
     protected String sourceLocLine(String location) {
         while (StringHelper.countChar(location, ':') > 1) {
             location = location.substring(location.indexOf(':') + 1);
@@ -190,18 +157,27 @@ public class CamelRouteStatus extends ProcessBaseCommand {
     }
 
     protected int sortRow(Row o1, Row o2) {
-        // no sort by default
-        return 0;
+        switch (sort) {
+            case "pid":
+                return Long.compare(Long.parseLong(o1.pid), Long.parseLong(o2.pid));
+            case "name":
+                return o1.name.compareToIgnoreCase(o2.name);
+            case "age":
+                return Long.compare(o1.uptime, o2.uptime);
+            default:
+                return 0;
+        }
     }
 
     static class Row {
         String pid;
         String name;
+        long uptime;
         String routeId;
         String from;
         String source;
-        String uptime;
         String state;
+        String age;
         String total;
         String failed;
         String inflight;
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListProcess.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListProcess.java
index cb1046c55c8..0c00c56ddaf 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListProcess.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListProcess.java
@@ -19,14 +19,15 @@ package org.apache.camel.dsl.jbang.core.commands.process;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Locale;
 
 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.ObjectHelper;
 import org.apache.camel.util.TimeUtils;
+import org.apache.camel.util.json.JsonObject;
 import picocli.CommandLine;
 import picocli.CommandLine.Command;
 
@@ -45,47 +46,62 @@ public class ListProcess extends ProcessBaseCommand {
     public Integer call() throws Exception {
         List<Row> rows = new ArrayList<>();
 
-        final long cur = ProcessHandle.current().pid();
+        List<Long> pids = findPids("*");
         ProcessHandle.allProcesses()
-                .filter(ph -> ph.pid() != cur)
-                .sorted((o1, o2) -> {
-                    switch (sort) {
-                        case "pid":
-                            return Long.compare(o1.pid(), o2.pid());
-                        case "name":
-                            return extractName(o1).compareTo(extractName(o2));
-                        case "age":
-                            // we want newest in top
-                            return Long.compare(extractSince(o1), extractSince(o2)) * -1;
-                        default:
-                            return 0;
-                    }
-                })
+                .filter(ph -> pids.contains(ph.pid()))
                 .forEach(ph -> {
-                    Row row = new Row();
-                    row.name = extractName(ph);
-                    if (ObjectHelper.isNotEmpty(row.name)) {
+                    JsonObject root = loadStatus(ph.pid());
+                    if (root != null) {
+                        Row row = new Row();
+                        row.name = extractName(root, ph);
                         row.pid = "" + ph.pid();
-                        row.age = TimeUtils.printSince(extractSince(ph));
+                        row.uptime = extractSince(ph);
+                        row.ago = TimeUtils.printSince(row.uptime);
+                        JsonObject context = (JsonObject) root.get("context");
+                        row.state = context.getString("state").toLowerCase(Locale.ROOT);
+                        JsonObject hc = (JsonObject) root.get("healthChecks");
+                        row.ready = hc != null ? hc.getString("ready") + "/" + hc.getString("total") : null;
                         rows.add(row);
                     }
                 });
 
+        // sort rows
+        rows.sort(this::sortRow);
+
         if (!rows.isEmpty()) {
             System.out.println(AsciiTable.getTable(AsciiTable.NO_BORDERS, rows, Arrays.asList(
                     new Column().header("PID").headerAlign(HorizontalAlign.CENTER).with(r -> r.pid),
-                    new Column().header("NAME").dataAlign(HorizontalAlign.LEFT).maxWidth(40, OverflowBehaviour.ELLIPSIS)
+                    new Column().header("NAME").dataAlign(HorizontalAlign.LEFT).maxWidth(30, OverflowBehaviour.ELLIPSIS)
                             .with(r -> r.name),
-                    new Column().header("AGE").headerAlign(HorizontalAlign.CENTER).with(r -> r.age))));
+                    new Column().header("READY").dataAlign(HorizontalAlign.CENTER).with(r -> r.ready),
+                    new Column().header("STATUS").headerAlign(HorizontalAlign.CENTER)
+                            .with(r -> extractState(r.state)),
+                    new Column().header("AGE").headerAlign(HorizontalAlign.CENTER).with(r -> r.ago))));
         }
 
         return 0;
     }
 
+    protected int sortRow(Row o1, Row o2) {
+        switch (sort) {
+            case "pid":
+                return Long.compare(Long.parseLong(o1.pid), Long.parseLong(o2.pid));
+            case "name":
+                return o1.name.compareToIgnoreCase(o2.name);
+            case "age":
+                return Long.compare(o1.uptime, o2.uptime);
+            default:
+                return 0;
+        }
+    }
+
     private static class Row {
         String pid;
         String name;
-        String age;
+        String ready;
+        String state;
+        String ago;
+        long uptime;
     }
 
 }
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ProcessBaseCommand.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ProcessBaseCommand.java
index 14075f5f92e..222774f586b 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ProcessBaseCommand.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ProcessBaseCommand.java
@@ -16,6 +16,8 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.process;
 
+import java.io.File;
+import java.io.FileInputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -27,7 +29,10 @@ import org.apache.camel.dsl.jbang.core.commands.CamelCommand;
 import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
 import org.apache.camel.support.PatternHelper;
 import org.apache.camel.util.FileUtil;
+import org.apache.camel.util.IOHelper;
 import org.apache.camel.util.StringHelper;
+import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
 
 abstract class ProcessBaseCommand extends CamelCommand {
 
@@ -38,7 +43,7 @@ abstract class ProcessBaseCommand extends CamelCommand {
         super(main);
     }
 
-    static List<Long> findPids(String name) {
+    List<Long> findPids(String name) {
         List<Long> pids = new ArrayList<>();
 
         // we need to know the pids of the running camel integrations
@@ -56,21 +61,50 @@ abstract class ProcessBaseCommand extends CamelCommand {
         ProcessHandle.allProcesses()
                 .filter(ph -> ph.pid() != cur)
                 .forEach(ph -> {
-                    String pName = extractName(ph);
-                    // ignore file extension, so it is easier to match by name
-                    pName = FileUtil.onlyName(pName);
-                    if (!pName.isEmpty() && PatternHelper.matchPattern(pName, pattern)) {
-                        pids.add(ph.pid());
+                    JsonObject root = loadStatus(ph.pid());
+                    // there must be a status file for the running Camel integration
+                    if (root != null) {
+                        String pName = extractName(root, ph);
+                        // ignore file extension, so it is easier to match by name
+                        pName = FileUtil.onlyName(pName);
+                        if (pName != null && !pName.isEmpty() && PatternHelper.matchPattern(pName, pattern)) {
+                            pids.add(ph.pid());
+                        }
                     }
                 });
 
         return pids;
     }
 
-    static String extractName(ProcessHandle ph) {
+    static String extractMainClass(JsonObject root) {
+        JsonObject runtime = (JsonObject) root.get("runtime");
+        return runtime != null ? runtime.getString("mainClass") : null;
+    }
+
+    static String extractName(JsonObject root, ProcessHandle ph) {
+        String name = doExtractName(root, ph);
+        return FileUtil.stripPath(name);
+    }
+
+    static String doExtractName(JsonObject root, ProcessHandle ph) {
+        // favour main class if known
+        if (root != null) {
+            String mc = extractMainClass(root);
+            if (mc != null) {
+                return mc;
+            }
+        }
         String cl = ph.info().commandLine().orElse("");
 
-        // TODO: parent/child when mvn spring-boot:run etc
+        // this may be a maven plugin run that spawns a child process where Camel actually runs (link to child)
+        String mvn = extractMavenPluginName(cl);
+        if (mvn != null) {
+            // is camel running in any of the children?
+            boolean camel = ph.children().anyMatch(ch -> !extractName(root, ch).isEmpty());
+            if (camel) {
+                return ""; // skip parent as we want only the child process with camel
+            }
+        }
 
         // try first camel-jbang
         String name = extractCamelJBangName(cl);
@@ -78,8 +112,8 @@ abstract class ProcessBaseCommand extends CamelCommand {
             return name;
         }
 
-        // this may be a maven plugin run, so check that first
-        String mvn = extractMavenPluginName(cl);
+        // this may be a maven plugin run that spawns a child process where Camel actually runs (link to parent)
+        mvn = extractMavenPluginName(cl);
         if (mvn == null && ph.parent().isPresent()) {
             // try parent as it may spawn a sub process
             String clp = ph.parent().get().info().commandLine().orElse("");
@@ -100,8 +134,12 @@ abstract class ProcessBaseCommand extends CamelCommand {
 
     private static String extractCamelName(String cl, String mvn) {
         if (cl != null) {
-            if (cl.contains("camel-spring-boot") && mvn != null) {
-                return mvn;
+            if (cl.contains("camel-spring-boot")) {
+                if (mvn != null) {
+                    return mvn;
+                } else {
+                    return "camel-spring-boot";
+                }
             } else if (cl.contains("camel-quarkus") && mvn != null) {
                 return mvn;
             } else if ((cl.contains("camel-main") || cl.contains("camel-core")) && mvn != null) {
@@ -149,4 +187,26 @@ abstract class ProcessBaseCommand extends CamelCommand {
         return since;
     }
 
+    static String extractState(String status) {
+        if ("started".equalsIgnoreCase(status)) {
+            status = "Running"; // favour using running instead of started
+        }
+        return StringHelper.capitalize(status);
+    }
+
+    JsonObject loadStatus(long pid) {
+        try {
+            File f = getStatusFile("" + pid);
+            if (f != null) {
+                FileInputStream fis = new FileInputStream(f);
+                String text = IOHelper.loadText(fis);
+                IOHelper.close(fis);
+                return (JsonObject) Jsoner.deserialize(text);
+            }
+        } catch (Throwable e) {
+            // ignore
+        }
+        return null;
+    }
+
 }