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 6981adc0a4b CAMEL-18556: camel-jbang - DevConsole should be started 
when used for Camel CLI
6981adc0a4b is described below

commit 6981adc0a4b6ae8004fc03a51b739ecd3597d47b
Author: Claus Ibsen <claus.ib...@gmail.com>
AuthorDate: Mon Sep 26 17:16:00 2022 +0200

    CAMEL-18556: camel-jbang - DevConsole should be started when used for Camel 
CLI
---
 .../impl/engine/DefaultDevConsoleResolver.java     |   8 +-
 .../impl/console/DefaultDevConsoleRegistry.java    |   2 +
 .../apache/camel/impl/console/EventConsole.java    |  12 +-
 .../camel/cli/connector/LocalCliConnector.java     |  81 +++++-----
 .../dsl/jbang/core/commands/CamelJBangMain.java    |   2 +
 .../dsl/jbang/core/commands/process/ListEvent.java | 168 +++++++++++++++++++++
 6 files changed, 229 insertions(+), 44 deletions(-)

diff --git 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultDevConsoleResolver.java
 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultDevConsoleResolver.java
index 695de1ed992..c2dd15b2a1d 100644
--- 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultDevConsoleResolver.java
+++ 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultDevConsoleResolver.java
@@ -72,6 +72,8 @@ public class DefaultDevConsoleResolver implements 
DevConsoleResolver, CamelConte
             if (DevConsole.class.isAssignableFrom(type)) {
                 answer = (DevConsole) 
camelContext.getInjector().newInstance(type, false);
                 CamelContextAware.trySetCamelContext(answer, camelContext);
+                // ensure console is started
+
             } else {
                 throw new IllegalArgumentException(
                         "Resolving dev-console: " + id + " detected type 
conflict: Not a DevConsole implementation. Found: "
@@ -92,6 +94,10 @@ public class DefaultDevConsoleResolver implements 
DevConsoleResolver, CamelConte
     @Override
     public Optional<DevConsole> lookupDevConsole(String id) {
         DevConsoleRegistry dcr = 
camelContext.getExtension(DevConsoleRegistry.class);
-        return dcr.getConsole(id);
+        if (dcr != null) {
+            return dcr.getConsole(id);
+        } else {
+            return Optional.empty();
+        }
     }
 }
diff --git 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/DefaultDevConsoleRegistry.java
 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/DefaultDevConsoleRegistry.java
index 7b97e6a7f96..c84337d47cc 100644
--- 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/DefaultDevConsoleRegistry.java
+++ 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/DefaultDevConsoleRegistry.java
@@ -138,6 +138,8 @@ public class DefaultDevConsoleRegistry extends 
ServiceSupport implements DevCons
         result = consoles.add(console);
         if (result) {
             CamelContextAware.trySetCamelContext(console, camelContext);
+            // ensure console is started as it may be registered later
+            ServiceHelper.startService(console);
             LOG.debug("DevConsole with id {} successfully registered", 
console.getId());
         }
         return result;
diff --git 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/EventConsole.java
 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/EventConsole.java
index 67e1195e623..57b2b9fee66 100644
--- 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/EventConsole.java
+++ 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/EventConsole.java
@@ -74,7 +74,7 @@ public class EventConsole extends AbstractDevConsole {
     protected String doCallText(Map<String, Object> options) {
         StringBuilder sb = new StringBuilder();
 
-        if (!events.isEmpty()) {
+        if (events != null && !events.isEmpty()) {
             sb.append(String.format("Last %s Camel Events:", events.size()));
             for (CamelEvent event : events) {
                 if (event.getTimestamp() > 0) {
@@ -85,7 +85,7 @@ public class EventConsole extends AbstractDevConsole {
             }
             sb.append("\n");
         }
-        if (!routeEvents.isEmpty()) {
+        if (routeEvents != null && !routeEvents.isEmpty()) {
             sb.append("\n");
             sb.append(String.format("Last %s Route Events:", 
routeEvents.size()));
             for (CamelEvent.RouteEvent event : routeEvents) {
@@ -97,7 +97,7 @@ public class EventConsole extends AbstractDevConsole {
             }
             sb.append("\n");
         }
-        if (!exchangeEvents.isEmpty()) {
+        if (exchangeEvents != null && !exchangeEvents.isEmpty()) {
             sb.append("\n");
             sb.append(String.format("Last %s Exchange Events:", 
exchangeEvents.size()));
             for (CamelEvent.ExchangeEvent event : exchangeEvents) {
@@ -116,7 +116,7 @@ public class EventConsole extends AbstractDevConsole {
     protected JsonObject doCallJson(Map<String, Object> options) {
         JsonObject root = new JsonObject();
 
-        if (!events.isEmpty()) {
+        if (events != null && !events.isEmpty()) {
             List<JsonObject> arr = new ArrayList<>();
             for (CamelEvent event : events) {
                 JsonObject jo = new JsonObject();
@@ -129,7 +129,7 @@ public class EventConsole extends AbstractDevConsole {
             }
             root.put("events", arr);
         }
-        if (!routeEvents.isEmpty()) {
+        if (routeEvents != null && !routeEvents.isEmpty()) {
             List<JsonObject> arr = new ArrayList<>();
             for (CamelEvent event : routeEvents) {
                 JsonObject jo = new JsonObject();
@@ -142,7 +142,7 @@ public class EventConsole extends AbstractDevConsole {
             }
             root.put("routeEvents", arr);
         }
-        if (!exchangeEvents.isEmpty()) {
+        if (exchangeEvents != null && !exchangeEvents.isEmpty()) {
             List<JsonObject> arr = new ArrayList<>();
             for (CamelEvent.ExchangeEvent event : exchangeEvents) {
                 JsonObject jo = new JsonObject();
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 64cb2e9f0eb..62b5243fba9 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
@@ -42,6 +42,7 @@ import org.apache.camel.ExtendedCamelContext;
 import org.apache.camel.Route;
 import org.apache.camel.api.management.ManagedCamelContext;
 import org.apache.camel.console.DevConsole;
+import org.apache.camel.console.DevConsoleRegistry;
 import org.apache.camel.spi.CliConnector;
 import org.apache.camel.spi.CliConnectorFactory;
 import org.apache.camel.spi.ContextReloadStrategy;
@@ -256,24 +257,21 @@ public class LocalCliConnector extends ServiceSupport 
implements CliConnector, C
                     mcc.getManagedCamelContext().reset(true);
                 }
             } else if ("thread-dump".equals(action)) {
-                DevConsole dc = camelContext.adapt(ExtendedCamelContext.class)
-                        .getDevConsoleResolver().resolveDevConsole("thread");
+                DevConsole dc = 
camelContext.getExtension(DevConsoleRegistry.class).resolveById("thread");
                 if (dc != null) {
                     JsonObject json = (JsonObject) 
dc.call(DevConsole.MediaType.JSON, Map.of("stackTrace", "true"));
                     LOG.trace("Updating output file: {}", outputFile);
                     IOHelper.writeText(json.toJson(), outputFile);
                 }
             } else if ("top-processors".equals(action)) {
-                DevConsole dc = camelContext.adapt(ExtendedCamelContext.class)
-                        .getDevConsoleResolver().resolveDevConsole("top");
+                DevConsole dc = 
camelContext.getExtension(DevConsoleRegistry.class).resolveById("top");
                 if (dc != null) {
                     JsonObject json = (JsonObject) 
dc.call(DevConsole.MediaType.JSON, Map.of(Exchange.HTTP_PATH, "/*"));
                     LOG.trace("Updating output file: {}", outputFile);
                     IOHelper.writeText(json.toJson(), outputFile);
                 }
             } else if ("source".equals(action)) {
-                DevConsole dc = camelContext.adapt(ExtendedCamelContext.class)
-                        .getDevConsoleResolver().resolveDevConsole("source");
+                DevConsole dc = 
camelContext.getExtension(DevConsoleRegistry.class).resolveById("source");
                 if (dc != null) {
                     String filter = root.getString("filter");
                     JsonObject json = (JsonObject) 
dc.call(DevConsole.MediaType.JSON, Map.of("filter", filter));
@@ -336,38 +334,47 @@ public class LocalCliConnector extends ServiceSupport 
implements CliConnector, C
             }
             root.put("runtime", rc);
 
-            // collect details via console
-            DevConsole dc = camelContext.adapt(ExtendedCamelContext.class)
-                    .getDevConsoleResolver().resolveDevConsole("context");
-            DevConsole dc2 = camelContext.adapt(ExtendedCamelContext.class)
-                    .getDevConsoleResolver().resolveDevConsole("route");
-            if (dc != null && dc2 != null) {
-                JsonObject json = (JsonObject) 
dc.call(DevConsole.MediaType.JSON);
-                JsonObject json2 = (JsonObject) 
dc2.call(DevConsole.MediaType.JSON, Map.of("processors", "true"));
-                if (json != null && json2 != null) {
-                    root.put("context", json);
-                    root.put("routes", json2.get("routes"));
+            DevConsoleRegistry dcr = 
camelContext.getExtension(DevConsoleRegistry.class);
+            if (dcr != null) {
+                // collect details via console
+                DevConsole dc = dcr.resolveById("context");
+                DevConsole dc2 = dcr.resolveById("route");
+                if (dc != null && dc2 != null) {
+                    JsonObject json = (JsonObject) 
dc.call(DevConsole.MediaType.JSON);
+                    JsonObject json2 = (JsonObject) 
dc2.call(DevConsole.MediaType.JSON, Map.of("processors", "true"));
+                    if (json != null && json2 != null) {
+                        root.put("context", json);
+                        root.put("routes", json2.get("routes"));
+                    }
                 }
-            }
-            DevConsole dc3 = camelContext.adapt(ExtendedCamelContext.class)
-                    .getDevConsoleResolver().resolveDevConsole("endpoint");
-            if (dc3 != null) {
-                JsonObject json = (JsonObject) 
dc3.call(DevConsole.MediaType.JSON);
-                root.put("endpoints", json);
-            }
-            DevConsole dc4 = camelContext.adapt(ExtendedCamelContext.class)
-                    .getDevConsoleResolver().resolveDevConsole("health");
-            if (dc4 != null) {
-                // include full details in health checks
-                JsonObject json = (JsonObject) 
dc4.call(DevConsole.MediaType.JSON, Map.of("exposureLevel", "full"));
-                root.put("healthChecks", json);
-            }
-            DevConsole dc5 = camelContext.adapt(ExtendedCamelContext.class)
-                    .getDevConsoleResolver().resolveDevConsole("log");
-            if (dc5 != null) {
-                JsonObject json = (JsonObject) 
dc5.call(DevConsole.MediaType.JSON);
-                if (json != null && !json.isEmpty()) {
-                    root.put("logger", json);
+                DevConsole dc3 = dcr.resolveById("endpoint");
+                if (dc3 != null) {
+                    JsonObject json = (JsonObject) 
dc3.call(DevConsole.MediaType.JSON);
+                    if (json != null && !json.isEmpty()) {
+                        root.put("endpoints", json);
+                    }
+                }
+                DevConsole dc4 = dcr.resolveById("health");
+                if (dc4 != null) {
+                    // include full details in health checks
+                    JsonObject json = (JsonObject) 
dc4.call(DevConsole.MediaType.JSON, Map.of("exposureLevel", "full"));
+                    if (json != null && !json.isEmpty()) {
+                        root.put("healthChecks", json);
+                    }
+                }
+                DevConsole dc5 = dcr.resolveById("event");
+                if (dc5 != null) {
+                    JsonObject json = (JsonObject) 
dc5.call(DevConsole.MediaType.JSON);
+                    if (json != null && !json.isEmpty()) {
+                        root.put("events", json);
+                    }
+                }
+                DevConsole dc6 = dcr.resolveById("log");
+                if (dc6 != null) {
+                    JsonObject json = (JsonObject) 
dc6.call(DevConsole.MediaType.JSON);
+                    if (json != null && !json.isEmpty()) {
+                        root.put("logger", json);
+                    }
                 }
             }
             // various details
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 3bd66b67e3c..681343cf276 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
@@ -48,6 +48,7 @@ import 
org.apache.camel.dsl.jbang.core.commands.process.CamelStatus;
 import org.apache.camel.dsl.jbang.core.commands.process.CamelTop;
 import org.apache.camel.dsl.jbang.core.commands.process.Hawtio;
 import org.apache.camel.dsl.jbang.core.commands.process.Jolokia;
+import org.apache.camel.dsl.jbang.core.commands.process.ListEvent;
 import org.apache.camel.dsl.jbang.core.commands.process.ListProcess;
 import org.apache.camel.dsl.jbang.core.commands.process.ListService;
 import org.apache.camel.dsl.jbang.core.commands.process.ListVault;
@@ -71,6 +72,7 @@ public class CamelJBangMain implements Callable<Integer> {
                         .addSubcommand("route", new CommandLine(new 
CamelRouteStatus(main)))
                         .addSubcommand("processor", new CommandLine(new 
CamelProcessorStatus(main)))
                         .addSubcommand("endpoint", new CommandLine(new 
CamelEndpointStatus(main)))
+                        .addSubcommand("event", new CommandLine(new 
ListEvent(main)))
                         .addSubcommand("service", new CommandLine(new 
ListService(main)))
                         .addSubcommand("source", new CommandLine(new 
CamelSourceAction(main)))
                         .addSubcommand("vault", new CommandLine(new 
ListVault(main))))
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListEvent.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListEvent.java
new file mode 100644
index 00000000000..c114e3a9c18
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListEvent.java
@@ -0,0 +1,168 @@
+/*
+ * 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.process;
+
+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.TimeUtils;
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@Command(name = "event", aliases = { "event", "events" },
+         description = "Get latest 25 events of Camel integrations")
+public class ListEvent extends ProcessBaseCommand {
+
+    @CommandLine.Parameters(description = "Name or pid of running Camel 
integration", arity = "0..1")
+    String name = "*";
+
+    @CommandLine.Option(names = { "--sort" },
+                        description = "Sort by pid, name or age", defaultValue 
= "pid")
+    String sort;
+
+    @CommandLine.Option(names = { "--filter" },
+                        description = "Filter event by event type: context, 
route, or exchange")
+    String filter;
+
+    public ListEvent(CamelJBangMain main) {
+        super(main);
+    }
+
+    @Override
+    public Integer call() throws Exception {
+        List<Row> rows = new ArrayList<>();
+
+        List<Long> pids = findPids(name);
+        ProcessHandle.allProcesses()
+                .filter(ph -> pids.contains(ph.pid()))
+                .forEach(ph -> {
+                    JsonObject root = loadStatus(ph.pid());
+                    // there must be a status file for the running Camel 
integration
+                    if (root != null) {
+                        Row row = new Row();
+                        JsonObject context = (JsonObject) root.get("context");
+                        row.name = context.getString("name");
+                        if ("CamelJBang".equals(row.name)) {
+                            row.name = extractName(root, ph);
+                        }
+                        row.pid = "" + ph.pid();
+                        row.uptime = extractSince(ph);
+                        row.age = TimeUtils.printSince(row.uptime);
+
+                        if (filter == null || filter.contains("context")) {
+                            fetchEvents(root, row, "events", rows);
+                        }
+                        if (filter == null || filter.contains("route")) {
+                            fetchEvents(root, row, "routeEvents", rows);
+                        }
+                        if (filter == null || filter.contains("exchange")) {
+                            fetchEvents(root, row, "exchangeEvents", rows);
+                        }
+                    }
+                });
+
+        // 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(30, 
OverflowBehaviour.ELLIPSIS_RIGHT)
+                            .with(r -> r.name),
+                    new 
Column().header("TYPE").dataAlign(HorizontalAlign.LEFT).with(r -> r.type),
+                    new 
Column().header("AGE").dataAlign(HorizontalAlign.RIGHT).with(this::getTimestamp),
+                    new 
Column().header("MESSAGE").dataAlign(HorizontalAlign.LEFT).with(r -> 
r.message))));
+        }
+
+        return 0;
+    }
+
+    private static void fetchEvents(JsonObject root, Row row, String type, 
List<Row> rows) {
+        JsonObject jo = (JsonObject) root.get("events");
+        if (jo != null) {
+            JsonArray arr = (JsonArray) jo.get(type);
+            if (arr != null) {
+                for (Object o : arr) {
+                    row = row.copy();
+                    jo = (JsonObject) o;
+                    row.type = jo.getString("type");
+                    Long ts = jo.getLong("timestamp");
+                    if (ts != null) {
+                        row.timestamp = ts;
+                    }
+                    row.exchangeId = jo.getString("exchangeId");
+                    row.message = jo.getString("message");
+                    rows.add(row);
+                }
+            }
+        }
+    }
+
+    private String getTimestamp(Row r) {
+        if (r.timestamp > 0) {
+            return TimeUtils.printSince(r.timestamp);
+        }
+        return "";
+    }
+
+    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 "pid":
+                return Long.compare(Long.parseLong(o1.pid), 
Long.parseLong(o2.pid)) * negate;
+            case "name":
+                return o1.name.compareToIgnoreCase(o2.name) * negate;
+            case "age":
+                return Long.compare(o1.uptime, o2.uptime) * negate;
+            default:
+                return 0;
+        }
+    }
+
+    private static class Row implements Cloneable {
+        String pid;
+        String name;
+        String age;
+        long uptime;
+        String type;
+        long timestamp;
+        String exchangeId;
+        String message;
+
+        Row copy() {
+            try {
+                return (Row) clone();
+            } catch (CloneNotSupportedException e) {
+                return null;
+            }
+        }
+    }
+
+}

Reply via email to