This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch fix/CAMEL-25057 in repository https://gitbox.apache.org/repos/asf/camel.git
commit 455dd8da08fa16f483fd833c81fdc05a78a3a3f2 Author: Claus Ibsen <[email protected]> AuthorDate: Sun Sep 27 17:28:19 2026 +0200 CAMEL-25057: camel-console - Dev consoles: fix bugs found in a deep review - 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"); + } + }; + } +}
