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 <[email protected]>
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;
+ }
+ }
+ }
+
+}