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 b4073ca92553 CAMEL-25057: camel-console - Dev consoles: fix bugs found
in a deep review (#26940)
b4073ca92553 is described below
commit b4073ca92553cfe68af02e22c21c1dff9f7a3a40
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Sep 28 22:41:01 2026 +0200
CAMEL-25057: camel-console - Dev consoles: fix bugs found in a deep review
(#26940)
- the consumer console shows the details of scheduled poll consumers
- the processor console JSON limit no longer returns one too many
- the processor console does not fail without camel-management
- the trace console only dumps with dump=true
- the browse console with freshSize=true honours limit
- camel-management as test dependency so management driven consoles can be
tested
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Signed-off-by: Claus Ibsen <[email protected]>
---
core/camel-console/pom.xml | 6 ++
.../camel/impl/console/BrowseDevConsole.java | 8 +++
.../camel/impl/console/ConsumerDevConsole.java | 20 ++++--
.../camel/impl/console/ProcessorDevConsole.java | 14 +++-
.../apache/camel/impl/console/TraceDevConsole.java | 4 +-
.../camel/impl/console/ConsoleEdgeCasesTest.java | 78 ++++++++++++++++++++++
.../impl/console/ManagedConsoleEdgeCasesTest.java | 75 +++++++++++++++++++++
7 files changed, 198 insertions(+), 7 deletions(-)
diff --git a/core/camel-console/pom.xml b/core/camel-console/pom.xml
index 8c79ab89f431..aded5064b66f 100644
--- a/core/camel-console/pom.xml
+++ b/core/camel-console/pom.xml
@@ -68,6 +68,12 @@
<type>test-jar</type>
<scope>test</scope>
</dependency>
+ <!-- many consoles are driven by the managed MBeans (JMX) -->
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-management</artifactId>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
diff --git
a/core/camel-console/src/main/java/org/apache/camel/impl/console/BrowseDevConsole.java
b/core/camel-console/src/main/java/org/apache/camel/impl/console/BrowseDevConsole.java
index b678fb301745..f6ff67937a37 100644
---
a/core/camel-console/src/main/java/org/apache/camel/impl/console/BrowseDevConsole.java
+++
b/core/camel-console/src/main/java/org/apache/camel/impl/console/BrowseDevConsole.java
@@ -130,6 +130,10 @@ public class BrowseDevConsole extends AbstractDevConsole {
begin = Math.max(0, list.size() - pos);
list = list.subList(begin, list.size());
}
+ if (list != null && list.size() > max) {
+ // fresh size gets all the messages (to know the size)
but only limit messages are dumped
+ list = list.subList(0, max);
+ }
if (list != null) {
sb.append("\n");
sb.append(String.format("Browse: %s (size: %d limit:
%d position: %d)%n", endpoint.getEndpointUri(),
@@ -178,6 +182,10 @@ public class BrowseDevConsole extends AbstractDevConsole {
begin = Math.max(0, list.size() - pos);
list = list.subList(begin, list.size());
}
+ if (list != null && list.size() > max) {
+ // fresh size gets all the messages (to know the size)
but only limit messages are dumped
+ list = list.subList(0, max);
+ }
if (list != null) {
Long firstTimestamp = null;
Long lastTimestamp = null;
diff --git
a/core/camel-console/src/main/java/org/apache/camel/impl/console/ConsumerDevConsole.java
b/core/camel-console/src/main/java/org/apache/camel/impl/console/ConsumerDevConsole.java
index d63c937e18ce..c010760518ea 100644
---
a/core/camel-console/src/main/java/org/apache/camel/impl/console/ConsumerDevConsole.java
+++
b/core/camel-console/src/main/java/org/apache/camel/impl/console/ConsumerDevConsole.java
@@ -32,6 +32,7 @@ import
org.apache.camel.api.management.mbean.ManagedRouteMBean;
import org.apache.camel.api.management.mbean.ManagedSchedulePollConsumerMBean;
import org.apache.camel.spi.Metadata;
import org.apache.camel.spi.annotations.DevConsole;
+import org.apache.camel.support.ScheduledPollConsumer;
import org.apache.camel.support.console.AbstractDevConsole;
import org.apache.camel.util.json.JsonRecordSupport;
@@ -122,7 +123,8 @@ public class ConsumerDevConsole extends AbstractDevConsole {
sb.append(String.format("%n Remote: %b",
mc.isRemoteEndpoint()));
sb.append(String.format("%n Hosted: %b",
mc.isHostedService()));
sb.append(String.format("%n Inflight: %d", inflight));
- if (mcc instanceof ManagedSchedulePollConsumerMBean mpc) {
+ ManagedSchedulePollConsumerMBean mpc =
scheduledPollConsumer(mcc, route);
+ if (mpc != null) {
sb.append(String.format("%n Polling: %s",
mpc.isPolling()));
sb.append(String.format("%n First Poll Done: %s",
mpc.isFirstPollDone()));
sb.append(String.format("%n Scheduler Started: %s",
mpc.isSchedulerStarted()));
@@ -185,6 +187,17 @@ public class ConsumerDevConsole extends AbstractDevConsole
{
return sb.toString();
}
+ /**
+ * The managed scheduled poll consumer of the route, or null if it is not
a scheduled poll consumer. The managed
+ * consumer is a proxy of the interface it is asked for, so it is not an
instance of the scheduled poll interface.
+ */
+ private static ManagedSchedulePollConsumerMBean
scheduledPollConsumer(ManagedCamelContext mcc, Route route) {
+ if (route.getConsumer() instanceof ScheduledPollConsumer) {
+ return mcc.getManagedConsumer(route.getId(),
ManagedSchedulePollConsumerMBean.class);
+ }
+ return null;
+ }
+
@Override
protected Map<String, Object> doCallJson(Map<String, Object> options) {
final List<ConsumerEntry> list = new ArrayList<>();
@@ -222,9 +235,8 @@ public class ConsumerDevConsole extends AbstractDevConsole {
Boolean fixedRate = null;
Long period = null;
- // NOTE: this checks mcc (the ManagedCamelContext), not mc
(the consumer) - so this branch is
- // effectively dead code, but that pre-existing behavior
is preserved as-is here
- if (mcc instanceof ManagedSchedulePollConsumerMBean mpc) {
+ ManagedSchedulePollConsumerMBean mpc =
scheduledPollConsumer(mcc, route);
+ if (mpc != null) {
scheduled = true;
polling = mpc.isPolling();
firstPollDone = mpc.isFirstPollDone();
diff --git
a/core/camel-console/src/main/java/org/apache/camel/impl/console/ProcessorDevConsole.java
b/core/camel-console/src/main/java/org/apache/camel/impl/console/ProcessorDevConsole.java
index 932fd6ee3333..9d815205df4a 100644
---
a/core/camel-console/src/main/java/org/apache/camel/impl/console/ProcessorDevConsole.java
+++
b/core/camel-console/src/main/java/org/apache/camel/impl/console/ProcessorDevConsole.java
@@ -123,6 +123,10 @@ public class ProcessorDevConsole extends
AbstractDevConsole {
ManagedCamelContext mcc =
getCamelContext().getCamelContextExtension().getContextPlugin(ManagedCamelContext.class);
final StringBuilder sb = new StringBuilder();
+ if (mcc == null) {
+ // camel-management is not in use
+ return sb.toString();
+ }
final AtomicInteger counter = new AtomicInteger();
for (Route r : getCamelContext().getRoutes()) {
ManagedRouteMBean mrb = mcc.getManagedRoute(r.getRouteId());
@@ -245,6 +249,10 @@ public class ProcessorDevConsole extends
AbstractDevConsole {
List<ProcessorEntry> list = new ArrayList<>();
ManagedCamelContext mcc =
getCamelContext().getCamelContextExtension().getContextPlugin(ManagedCamelContext.class);
+ if (mcc == null) {
+ // camel-management is not in use
+ return JsonRecordSupport.toJsonObject(new Response(list));
+ }
for (Route r : getCamelContext().getRoutes()) {
ManagedRouteMBean mrb = mcc.getManagedRoute(r.getRouteId());
includeProcessorsJson(mrb, list, filter, max);
@@ -284,7 +292,7 @@ public class ProcessorDevConsole extends AbstractDevConsole
{
CamelContext camelContext, List<ProcessorEntry> list, int max,
List<ManagedProcessorMBean> mps) {
for (int i = 0; i < mps.size(); i++) {
ManagedProcessorMBean mp = mps.get(i);
- if (list.size() > max) {
+ if (list.size() >= max) {
return;
}
@@ -409,6 +417,10 @@ public class ProcessorDevConsole extends
AbstractDevConsole {
List<ManagedProcessorMBean> mps = new ArrayList<>();
ManagedCamelContext mcc =
getCamelContext().getCamelContextExtension().getContextPlugin(ManagedCamelContext.class);
+ if (mcc == null) {
+ // camel-management is not in use
+ return;
+ }
for (Route r : getCamelContext().getRoutes()) {
ManagedRouteMBean mrb = mcc.getManagedRoute(r.getRouteId());
try {
diff --git
a/core/camel-console/src/main/java/org/apache/camel/impl/console/TraceDevConsole.java
b/core/camel-console/src/main/java/org/apache/camel/impl/console/TraceDevConsole.java
index 81881de640d5..a8fdf375fe34 100644
---
a/core/camel-console/src/main/java/org/apache/camel/impl/console/TraceDevConsole.java
+++
b/core/camel-console/src/main/java/org/apache/camel/impl/console/TraceDevConsole.java
@@ -96,7 +96,7 @@ public class TraceDevConsole extends AbstractDevConsole {
BacklogTracer tracer =
getCamelContext().getCamelContextExtension().getContextPlugin(BacklogTracer.class);
if (tracer != null) {
- if (dump != null) {
+ if ("true".equalsIgnoreCase(dump)) {
for (BacklogTracerEventMessage t :
tracer.dumpAllTracedMessages()) {
addMessage(t);
}
@@ -156,7 +156,7 @@ public class TraceDevConsole extends AbstractDevConsole {
BacklogTracer tracer =
getCamelContext().getCamelContextExtension().getContextPlugin(BacklogTracer.class);
if (tracer != null) {
- if (dump != null) {
+ if ("true".equalsIgnoreCase(dump)) {
for (BacklogTracerEventMessage t :
tracer.dumpAllTracedMessages()) {
addMessage(t);
}
diff --git
a/core/camel-console/src/test/java/org/apache/camel/impl/console/ConsoleEdgeCasesTest.java
b/core/camel-console/src/test/java/org/apache/camel/impl/console/ConsoleEdgeCasesTest.java
new file mode 100644
index 000000000000..0feadd4e1963
--- /dev/null
+++
b/core/camel-console/src/test/java/org/apache/camel/impl/console/ConsoleEdgeCasesTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.List;
+import java.util.Map;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.console.DevConsole;
+import org.apache.camel.support.PluginHelper;
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Consoles used without JMX (not enabled by ContextTestSupport).
+ */
+public class ConsoleEdgeCasesTest extends ContextTestSupport {
+
+ private DevConsole console(String id) {
+ return
PluginHelper.getDevConsoleResolver(context).resolveDevConsole(id);
+ }
+
+ @Test
+ public void testTraceConsoleDumpFalse() {
+ context.setBacklogTracing(true);
+ JsonObject out = (JsonObject)
console("trace").call(DevConsole.MediaType.JSON, Map.of("dump", "false"));
+ // not dumping, so the status of the tracer
+ assertThat(out.get("traces")).isNull();
+ }
+
+ @Test
+ public void testBrowseConsoleFreshSizeWithLimit() {
+ for (int i = 0; i < 20; i++) {
+ template.sendBody("direct:start", "Message " + i);
+ }
+ JsonObject out = (JsonObject)
console("browse").call(DevConsole.MediaType.JSON,
+ Map.of("freshSize", "true", "limit", "5", "filter", "seda*"));
+ JsonArray browse = out.getCollection("browse");
+ JsonObject entry = (JsonObject) browse.get(0);
+ assertThat(entry.getInteger("queueSize")).isEqualTo(20);
+ List<?> messages = entry.getCollection("messages");
+ assertThat(messages).hasSize(5);
+
+ String text = (String)
console("browse").call(DevConsole.MediaType.TEXT,
+ Map.of("freshSize", "true", "limit", "5", "filter", "seda*"));
+ assertThat(text).contains("Message 4").doesNotContain("Message 5");
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ context.setBacklogTracingStandby(true);
+
+ from("direct:start").to("seda:queue1");
+ }
+ };
+ }
+}
diff --git
a/core/camel-console/src/test/java/org/apache/camel/impl/console/ManagedConsoleEdgeCasesTest.java
b/core/camel-console/src/test/java/org/apache/camel/impl/console/ManagedConsoleEdgeCasesTest.java
new file mode 100644
index 000000000000..f661e104d384
--- /dev/null
+++
b/core/camel-console/src/test/java/org/apache/camel/impl/console/ManagedConsoleEdgeCasesTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.Map;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.console.DevConsole;
+import org.apache.camel.support.PluginHelper;
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Consoles driven by camel-management (JMX enabled).
+ */
+public class ManagedConsoleEdgeCasesTest extends ContextTestSupport {
+
+ @Override
+ protected boolean useJmx() {
+ return true;
+ }
+
+ private DevConsole console(String id) {
+ return
PluginHelper.getDevConsoleResolver(context).resolveDevConsole(id);
+ }
+
+ @Test
+ public void testConsumerConsoleScheduledPollConsumer() {
+ JsonObject out = (JsonObject)
console("consumer").call(DevConsole.MediaType.JSON);
+ JsonArray consumers = out.getCollection("consumers");
+ JsonObject file = (JsonObject) consumers.stream()
+ .filter(c -> ((JsonObject)
c).getString("uri").startsWith("file:")).findFirst().orElseThrow();
+ assertThat(file.getBoolean("scheduled")).isTrue();
+ assertThat(file.getLong("delay")).isEqualTo(5000L);
+
+ String text = (String)
console("consumer").call(DevConsole.MediaType.TEXT);
+ assertThat(text).contains("Polling:");
+ }
+
+ @Test
+ public void testProcessorConsoleLimit() {
+ JsonObject out = (JsonObject)
console("processor").call(DevConsole.MediaType.JSON, Map.of("limit", "1"));
+ JsonArray processors = out.getCollection("processors");
+ assertThat(processors).hasSize(1);
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+
from("file:target/data/console-edge?delay=5000&initialDelay=600000").routeId("files")
+ .to("log:a").to("log:b").to("log:c");
+ }
+ };
+ }
+}