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 2025/11/03 11:06:40 UTC
(camel) branch pc created (now 4af1b9930867)
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a change to branch pc
in repository https://gitbox.apache.org/repos/asf/camel.git
at 4af1b9930867 CAMEL-22629: camel-console - Add producer dev console, and jbang command.
This branch includes the following new commits:
new 4af1b9930867 CAMEL-22629: camel-console - Add producer dev console, and jbang command.
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(camel) 01/01: CAMEL-22629: camel-console - Add producer dev console, and jbang command.
Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch pc
in repository https://gitbox.apache.org/repos/asf/camel.git
commit 4af1b9930867317f075539953d31223b16a76435
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Mon Nov 3 12:06:21 2025 +0100
CAMEL-22629: camel-console - Add producer dev console, and jbang command.
---
.../apache/camel/catalog/dev-consoles.properties | 1 +
.../camel/catalog/dev-consoles/producer.json | 15 ++
.../org/apache/camel/dev-console/producer.json | 15 ++
.../services/org/apache/camel/dev-console/producer | 2 +
.../org/apache/camel/dev-consoles.properties | 2 +-
.../camel/impl/console/ProducerDevConsole.java | 119 ++++++++++++
.../camel/cli/connector/LocalCliConnector.java | 7 +
.../dsl/jbang/core/commands/CamelJBangMain.java | 1 +
.../jbang/core/commands/process/ListProducer.java | 212 +++++++++++++++++++++
9 files changed, 373 insertions(+), 1 deletion(-)
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles.properties b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles.properties
index 7f97cf277718..9b787ecb27df 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles.properties
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles.properties
@@ -31,6 +31,7 @@ memory
micrometer
platform-http
processor
+producer
properties
quartz
receive
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/producer.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/producer.json
new file mode 100644
index 000000000000..957fd297a24e
--- /dev/null
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/producer.json
@@ -0,0 +1,15 @@
+{
+ "console": {
+ "kind": "console",
+ "group": "camel",
+ "name": "producer",
+ "title": "Producers",
+ "description": "Display information about Camel producers",
+ "deprecated": false,
+ "javaType": "org.apache.camel.impl.console.ProducerDevConsole",
+ "groupId": "org.apache.camel",
+ "artifactId": "camel-console",
+ "version": "4.16.0-SNAPSHOT"
+ }
+}
+
diff --git a/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/producer.json b/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/producer.json
new file mode 100644
index 000000000000..957fd297a24e
--- /dev/null
+++ b/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/producer.json
@@ -0,0 +1,15 @@
+{
+ "console": {
+ "kind": "console",
+ "group": "camel",
+ "name": "producer",
+ "title": "Producers",
+ "description": "Display information about Camel producers",
+ "deprecated": false,
+ "javaType": "org.apache.camel.impl.console.ProducerDevConsole",
+ "groupId": "org.apache.camel",
+ "artifactId": "camel-console",
+ "version": "4.16.0-SNAPSHOT"
+ }
+}
+
diff --git a/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/producer b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/producer
new file mode 100644
index 000000000000..3e7926c16fc5
--- /dev/null
+++ b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/producer
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.impl.console.ProducerDevConsole
diff --git a/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-consoles.properties b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-consoles.properties
index af617cb7354a..0591dd83a003 100644
--- a/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-consoles.properties
+++ b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-consoles.properties
@@ -1,5 +1,5 @@
# Generated by camel build tools - do NOT edit this file!
-dev-consoles=bean blocked browse circuit-breaker consumer context debug endpoint event gc health inflight internal-tasks java-security jvm log memory processor properties receive reload rest route route-controller route-dump route-group route-structure send service source startup-recorder system-properties thread top trace transformers type-converters variables
+dev-consoles=bean blocked browse circuit-breaker consumer context debug endpoint event gc health inflight internal-tasks java-security jvm log memory processor producer properties receive reload rest route route-controller route-dump route-group route-structure send service source startup-recorder system-properties thread top trace transformers type-converters variables
groupId=org.apache.camel
artifactId=camel-console
version=4.16.0-SNAPSHOT
diff --git a/core/camel-console/src/main/java/org/apache/camel/impl/console/ProducerDevConsole.java b/core/camel-console/src/main/java/org/apache/camel/impl/console/ProducerDevConsole.java
new file mode 100644
index 000000000000..b8c9d96da367
--- /dev/null
+++ b/core/camel-console/src/main/java/org/apache/camel/impl/console/ProducerDevConsole.java
@@ -0,0 +1,119 @@
+/*
+ * 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.impl.console;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.api.management.mbean.ManagedProducerMBean;
+import org.apache.camel.spi.annotations.DevConsole;
+import org.apache.camel.support.console.AbstractDevConsole;
+import org.apache.camel.util.json.JsonObject;
+
+@DevConsole(name = "producer", displayName = "Producers", description = "Display information about Camel producers")
+public class ProducerDevConsole extends AbstractDevConsole {
+
+ public ProducerDevConsole() {
+ super("camel", "producer", "Producers", "Display information about Camel producers");
+ }
+
+ @Override
+ protected String doCallText(Map<String, Object> options) {
+ StringBuilder sb = new StringBuilder();
+
+ MBeanServer mbeanServer = getCamelContext().getManagementStrategy().getManagementAgent().getMBeanServer();
+ if (mbeanServer != null) {
+ try {
+ String jmxDomain
+ = getCamelContext().getManagementStrategy().getManagementAgent().getMBeanObjectDomainName();
+ String prefix
+ = getCamelContext().getManagementStrategy().getManagementAgent().getIncludeHostName() ? "*/" : "";
+ ObjectName query = ObjectName.getInstance(
+ jmxDomain + ":context=" + prefix + getCamelContext().getManagementName() + ",type=producers,*");
+ Set<ObjectName> set = mbeanServer.queryNames(query, null);
+ if (set != null && !set.isEmpty()) {
+ for (ObjectName on : set) {
+ ManagedProducerMBean mp = getManagedProducer(getCamelContext(), on);
+ if (!sb.isEmpty()) {
+ sb.append("\n");
+ }
+ sb.append(String.format("\n Uri: %s", mp.getEndpointUri()));
+ sb.append(String.format("\n State: %s", mp.getState()));
+ sb.append(String.format("\n Class: %s", mp.getServiceType()));
+ sb.append(String.format("\n Remote: %b", mp.isRemoteEndpoint()));
+ sb.append(String.format("\n Singleton: %b", mp.isSingleton()));
+ if (mp.getRouteId() != null) {
+ sb.append(String.format("\n Route Id: %s", mp.getRouteId()));
+ }
+ }
+ }
+ } catch (Exception e) {
+ // ignore
+ }
+ }
+ return sb.toString();
+ }
+
+ @Override
+ protected JsonObject doCallJson(Map<String, Object> options) {
+ final JsonObject root = new JsonObject();
+ final List<JsonObject> list = new ArrayList<>();
+ root.put("producers", list);
+
+ MBeanServer mbeanServer = getCamelContext().getManagementStrategy().getManagementAgent().getMBeanServer();
+ if (mbeanServer != null) {
+ try {
+ String jmxDomain
+ = getCamelContext().getManagementStrategy().getManagementAgent().getMBeanObjectDomainName();
+ String prefix
+ = getCamelContext().getManagementStrategy().getManagementAgent().getIncludeHostName() ? "*/" : "";
+ ObjectName query = ObjectName.getInstance(
+ jmxDomain + ":context=" + prefix + getCamelContext().getManagementName() + ",type=producers,*");
+ Set<ObjectName> set = mbeanServer.queryNames(query, null);
+ if (set != null && !set.isEmpty()) {
+ for (ObjectName on : set) {
+ ManagedProducerMBean mp = getManagedProducer(getCamelContext(), on);
+ JsonObject jo = new JsonObject();
+ jo.put("uri", mp.getEndpointUri());
+ jo.put("state", mp.getState());
+ jo.put("class", mp.getServiceType());
+ jo.put("remote", mp.isRemoteEndpoint());
+ jo.put("singleton", mp.isSingleton());
+ if (mp.getRouteId() != null) {
+ jo.put("routeId", mp.getRouteId());
+ }
+ list.add(jo);
+ }
+ }
+ } catch (Exception e) {
+ // ignore
+ }
+ }
+ return root;
+ }
+
+ private static ManagedProducerMBean getManagedProducer(CamelContext camelContext, ObjectName on) {
+ return camelContext.getManagementStrategy().getManagementAgent().newProxyClient(on, ManagedProducerMBean.class);
+ }
+
+}
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 907dd7ae94f2..dd1e43add1f2 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
@@ -1098,6 +1098,13 @@ public class LocalCliConnector extends ServiceSupport implements CliConnector, C
root.put("consumers", json);
}
}
+ DevConsole dc14b = dcr.resolveById("producer");
+ if (dc14b != null) {
+ JsonObject json = (JsonObject) dc14b.call(DevConsole.MediaType.JSON);
+ if (json != null && !json.isEmpty()) {
+ root.put("producers", json);
+ }
+ }
DevConsole dc15 = dcr.resolveById("variables");
if (dc15 != null) {
JsonObject json = (JsonObject) dc15.call(DevConsole.MediaType.JSON);
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 994e1368c79c..2c9e43633764 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
@@ -105,6 +105,7 @@ public class CamelJBangMain implements Callable<Integer> {
.addSubcommand("properties", new CommandLine(new ListProperties(main)))
.addSubcommand("variable", new CommandLine(new ListVariable(main)))
.addSubcommand("consumer", new CommandLine(new ListConsumer(main)))
+ .addSubcommand("producer", new CommandLine(new ListProducer(main)))
.addSubcommand("endpoint", new CommandLine(new ListEndpoint(main)))
.addSubcommand("event", new CommandLine(new ListEvent(main)))
.addSubcommand("inflight", new CommandLine(new ListInflight(main)))
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListProducer.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListProducer.java
new file mode 100644
index 000000000000..c19723fe8929
--- /dev/null
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListProducer.java
@@ -0,0 +1,212 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import com.github.freva.asciitable.AsciiTable;
+import com.github.freva.asciitable.Column;
+import com.github.freva.asciitable.HorizontalAlign;
+import com.github.freva.asciitable.OverflowBehaviour;
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.dsl.jbang.core.common.PidNameAgeCompletionCandidates;
+import org.apache.camel.dsl.jbang.core.common.ProcessHelper;
+import org.apache.camel.support.PatternHelper;
+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;
+
+@Command(name = "producer", description = "Get status of Camel producers", sortOptions = false, showDefaultValues = true)
+public class ListProducer extends ProcessWatchCommand {
+
+ @CommandLine.Parameters(description = "Name or pid of running Camel integration", arity = "0..1")
+ String name = "*";
+
+ @CommandLine.Option(names = { "--sort" }, completionCandidates = PidNameAgeCompletionCandidates.class,
+ description = "Sort by pid, name or age", defaultValue = "pid")
+ String sort;
+
+ @CommandLine.Option(names = { "--limit" },
+ description = "Filter producers by limiting to the given number of rows")
+ int limit;
+
+ @CommandLine.Option(names = { "--filter" },
+ description = "Filter producers by URI")
+ String filter;
+
+ @CommandLine.Option(names = { "--short-uri" },
+ description = "List endpoint URI without query parameters (short)")
+ boolean shortUri;
+
+ @CommandLine.Option(names = { "--wide-uri" },
+ description = "List endpoint URI in full details")
+ boolean wideUri;
+
+ public ListProducer(CamelJBangMain main) {
+ super(main);
+ }
+
+ @Override
+ public Integer doProcessWatchCall() throws Exception {
+ List<Row> rows = new ArrayList<>();
+
+ // make it easier to filter
+ if (filter != null && !filter.endsWith("*")) {
+ filter += "*";
+ }
+
+ List<Long> pids = findPids(name);
+ ProcessHandle.allProcesses()
+ .filter(ph -> pids.contains(ph.pid()))
+ .forEach(ph -> {
+ JsonObject root = loadStatus(ph.pid());
+ if (root != null) {
+ JsonObject context = (JsonObject) root.get("context");
+ JsonObject jo = (JsonObject) root.get("producers");
+ if (context != null && jo != null) {
+ JsonArray array = (JsonArray) jo.get("producers");
+ for (int i = 0; i < array.size(); i++) {
+ JsonObject o = (JsonObject) array.get(i);
+ Row row = new Row();
+ row.name = context.getString("name");
+ if ("CamelJBang".equals(row.name)) {
+ row.name = ProcessHelper.extractName(root, ph);
+ }
+ row.pid = Long.toString(ph.pid());
+ row.uri = o.getString("uri");
+ row.id = o.getString("routeId");
+ row.state = o.getString("state");
+ row.className = o.getString("class");
+ row.singleton = o.getBoolean("singleton");
+ row.remote = o.getBoolean("remote");
+ row.uptime = extractSince(ph);
+ row.age = TimeUtils.printSince(row.uptime);
+ boolean add = true;
+ if (filter != null) {
+ String f = filter;
+ boolean negate = filter.startsWith("-");
+ if (negate) {
+ f = f.substring(1);
+ }
+ boolean match = PatternHelper.matchPattern(row.uri, f);
+ if (negate) {
+ match = !match;
+ }
+ if (!match) {
+ add = false;
+ }
+ }
+ if (limit > 0 && rows.size() >= limit) {
+ add = false;
+ }
+ if (add) {
+ rows.add(row);
+ }
+ }
+ }
+ }
+ });
+
+ // sort rows
+ rows.sort(this::sortRow);
+
+ if (!rows.isEmpty()) {
+ printTable(rows);
+ }
+
+ return 0;
+ }
+
+ protected void printTable(List<Row> rows) {
+ printer().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("AGE").headerAlign(HorizontalAlign.CENTER).with(r -> r.age),
+ new Column().header("ID").dataAlign(HorizontalAlign.LEFT).maxWidth(20, OverflowBehaviour.ELLIPSIS_RIGHT)
+ .with(r -> r.id),
+ new Column().header("STATUS").headerAlign(HorizontalAlign.CENTER).with(this::getState),
+ new Column().header("TYPE").dataAlign(HorizontalAlign.LEFT).maxWidth(20, OverflowBehaviour.ELLIPSIS_RIGHT)
+ .with(this::getType),
+ new Column().header("URI").visible(!wideUri).dataAlign(HorizontalAlign.LEFT)
+ .maxWidth(90, OverflowBehaviour.ELLIPSIS_RIGHT)
+ .with(this::getUri),
+ new Column().header("URI").visible(wideUri).dataAlign(HorizontalAlign.LEFT)
+ .maxWidth(140, OverflowBehaviour.NEWLINE)
+ .with(this::getUri))));
+ }
+
+ private String getUri(Row r) {
+ String u = r.uri;
+ if (shortUri) {
+ int pos = u.indexOf('?');
+ if (pos > 0) {
+ u = u.substring(0, pos);
+ }
+ }
+ return u;
+ }
+
+ private String getState(Row r) {
+ return r.state;
+ }
+
+ private String getType(Row r) {
+ String s = r.className;
+ if (s.endsWith("Producer")) {
+ s = s.substring(0, s.length() - 8);
+ }
+ return s;
+ }
+
+ 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;
+ }
+ }
+
+ static class Row {
+ String pid;
+ String name;
+ long uptime;
+ String age;
+ String id;
+ String uri;
+ String state;
+ String className;
+ boolean singleton;
+ boolean remote;
+ }
+
+}