This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch fix/CAMEL-25237 in repository https://gitbox.apache.org/repos/asf/camel.git
commit 754612ca131081e288aa1caf4ed255dfb2cddb1a Author: Claus Ibsen <[email protected]> AuthorDate: Thu Oct 1 18:44:12 2026 +0200 CAMEL-25237: camel-core - Switch EIP should count the messages per case The send of a Switch case is a managed processor with its own statistics, but it was not found by its id: Model.getProcessorDefinition walks the processor children of the routes, and a case is a SwitchCaseDefinition whose send is not among them. The management and dev consoles (camel get processor, the route structure) look processors up by id, so the cases were left out while the otherwise (whose send is a child) was shown. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- .../java/org/apache/camel/impl/DefaultModel.java | 28 +++++++++---- .../org/apache/camel/processor/SwitchTest.java | 20 +++++++++ .../apache/camel/management/ManagedSwitchTest.java | 47 ++++++++++++++++++++++ 3 files changed, 87 insertions(+), 8 deletions(-) diff --git a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java index 48107f9cb2f4..0bf095fdb152 100644 --- a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java +++ b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java @@ -51,6 +51,8 @@ import org.apache.camel.model.RouteFilters; import org.apache.camel.model.RouteTemplateDefinition; import org.apache.camel.model.RouteTemplateParameterDefinition; import org.apache.camel.model.RoutesDefinition; +import org.apache.camel.model.SwitchCaseDefinition; +import org.apache.camel.model.SwitchDefinition; import org.apache.camel.model.TemplatedRouteDefinition; import org.apache.camel.model.TemplatedRouteParameterDefinition; import org.apache.camel.model.ToDefinition; @@ -849,16 +851,16 @@ public class DefaultModel implements Model { Collection<ProcessorDefinition> col = ProcessorDefinitionHelper.filterTypeInOutputs(route.getOutputs(), ProcessorDefinition.class); for (ProcessorDefinition proc : col) { - String pid = proc.getId(); - // match direct by ids - if (id.equals(pid)) { + if (matchesId(proc, id)) { return proc; } - // try to match via node prefix id - if (proc.getNodePrefixId() != null) { - pid = proc.getNodePrefixId() + pid; - if (id.equals(pid)) { - return proc; + if (proc instanceof SwitchDefinition sw) { + // a case of a Switch is not a processor in the tree: its send is, under the id of the case + for (SwitchCaseDefinition c : sw.getCases()) { + ToDefinition send = c.getToDefinition(); + if (send != null && matchesId(send, id)) { + return send; + } } } } @@ -866,6 +868,16 @@ public class DefaultModel implements Model { return null; } + private static boolean matchesId(ProcessorDefinition<?> proc, String id) { + String pid = proc.getId(); + // match direct by ids + if (id.equals(pid)) { + return true; + } + // try to match via node prefix id + return proc.getNodePrefixId() != null && id.equals(proc.getNodePrefixId() + pid); + } + @Override public <T extends ProcessorDefinition<T>> T getProcessorDefinition(String id, Class<T> type) { ProcessorDefinition<?> answer = getProcessorDefinition(id); diff --git a/core/camel-core/src/test/java/org/apache/camel/processor/SwitchTest.java b/core/camel-core/src/test/java/org/apache/camel/processor/SwitchTest.java index 1abc11b39954..6c93cbf1a654 100644 --- a/core/camel-core/src/test/java/org/apache/camel/processor/SwitchTest.java +++ b/core/camel-core/src/test/java/org/apache/camel/processor/SwitchTest.java @@ -27,12 +27,18 @@ import java.util.concurrent.atomic.AtomicInteger; import org.apache.camel.ContextTestSupport; import org.apache.camel.Exchange; import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.model.Model; +import org.apache.camel.model.ProcessorDefinition; +import org.apache.camel.model.SwitchCaseDefinition; +import org.apache.camel.model.SwitchDefinition; +import org.apache.camel.model.ToDefinition; import org.apache.camel.support.ExpressionAdapter; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; class SwitchTest extends ContextTestSupport { @@ -53,6 +59,20 @@ class SwitchTest extends ContextTestSupport { assertMockEndpointsSatisfied(); } + @Test + void theSendOfACaseIsFoundByTheIdOfTheCase() { + // the management and the dev consoles look a processor up by its id: a case is found as its send + // (CAMEL-25237), as the otherwise is + Model model = context.getCamelContextExtension().getContextPlugin(Model.class); + SwitchDefinition sw = (SwitchDefinition) model.getRouteDefinitions().get(0).getOutputs().get(0); + for (SwitchCaseDefinition c : sw.getCases()) { + ProcessorDefinition<?> found = model.getProcessorDefinition(c.getId()); + assertSame(c.getToDefinition(), found, c.getId()); + } + assertSame(sw.getOtherwiseDefinition(), model.getProcessorDefinition(sw.getId() + "-otherwise")); + assertEquals("mock:billing", ((ToDefinition) model.getProcessorDefinition(sw.getCases().get(0).getId())).getUri()); + } + @Test void structuredSelectorResultsFailWithoutUsingFallback() throws Exception { getMockEndpoint("mock:review").expectedMessageCount(0); diff --git a/core/camel-management/src/test/java/org/apache/camel/management/ManagedSwitchTest.java b/core/camel-management/src/test/java/org/apache/camel/management/ManagedSwitchTest.java index d8f6520adfe0..e67abfa88a3d 100644 --- a/core/camel-management/src/test/java/org/apache/camel/management/ManagedSwitchTest.java +++ b/core/camel-management/src/test/java/org/apache/camel/management/ManagedSwitchTest.java @@ -16,10 +16,15 @@ */ package org.apache.camel.management; +import java.util.Collection; + import javax.management.ObjectName; import javax.management.openmbean.TabularData; +import org.apache.camel.api.management.ManagedCamelContext; import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.model.Model; +import org.apache.camel.model.SwitchDefinition; import org.apache.camel.processor.SendProcessor; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledOnOs; @@ -60,6 +65,43 @@ class ManagedSwitchTest extends ManagementTestSupport { assertEquals(0L, getMBeanServer().getAttribute(name, "UnmatchedCount")); } + @Test + void countsEveryCaseWithAGeneratedId() throws Exception { + // a Switch as YAML routes write it: no ids on the cases, so they are generated (CAMEL-25237); the route is + // one of those the context starts with (createRouteBuilder) + template.sendBodyAndHeader("direct:generated", "a", "specialist", "weather"); + template.sendBodyAndHeader("direct:generated", "b", "specialist", "weather"); + template.sendBodyAndHeader("direct:generated", "c", "specialist", "reservation"); + template.sendBodyAndHeader("direct:generated", "d", "specialist", "cost"); + + // the ids the cases got: generated, as the route structure and the diagrams show them + SwitchDefinition sw = (SwitchDefinition) context.getCamelContextExtension() + .getContextPlugin(Model.class).getRouteDefinition("generatedRoute").getOutputs().get(0); + String reservation = sw.getCases().get(0).getId(); + String weather = sw.getCases().get(1).getId(); + assertNotNull(reservation); + assertNotNull(weather); + String all = getMBeanServer().queryNames(new ObjectName("org.apache.camel:type=processors,*"), null).toString(); + assertEquals(1L, getMBeanServer().getAttribute(getCamelObjectName(TYPE_PROCESSOR, reservation), "ExchangesTotal"), + all); + assertEquals(2L, getMBeanServer().getAttribute(getCamelObjectName(TYPE_PROCESSOR, weather), "ExchangesTotal")); + assertEquals(1L, getMBeanServer().getAttribute( + getCamelObjectName(TYPE_PROCESSOR, sw.getId() + "-otherwise"), "ExchangesTotal")); + // the route lists them among its processors, as camel get processor and the dev consoles show them + assertEquals("generatedRoute", + getMBeanServer().getAttribute(getCamelObjectName(TYPE_PROCESSOR, weather), "RouteId")); + @SuppressWarnings("unchecked") + Collection<String> ids = (Collection<String>) getMBeanServer().invoke( + getCamelObjectName(DefaultManagementObjectNameStrategy.TYPE_ROUTE, "generatedRoute"), "processorIds", null, + null); + assertTrue(ids.contains(reservation) && ids.contains(weather), ids.toString()); + // and are found by their id, as the dev consoles (camel get processor, route structure) look them up + ManagedCamelContext mcc = context.getCamelContextExtension().getContextPlugin(ManagedCamelContext.class); + assertNotNull(mcc.getManagedProcessor(reservation), "the reservation case is a managed processor"); + assertEquals(2L, mcc.getManagedProcessor(weather).getExchangesTotal()); + assertNotNull(mcc.getManagedProcessor(sw.getId() + "-otherwise")); + } + @ParameterizedTest @NullSource @ValueSource(booleans = { true, false }) @@ -95,6 +137,11 @@ class ManagedSwitchTest extends ManagementTestSupport { .doSwitch(header("decision")).id("dispatch") .doCase("billing").id("urgentCase").to("mock:urgent") .otherwise("mock:review"); + from("direct:generated").routeId("generatedRoute") + .doSwitch(header("specialist")) + .doCase("reservation", "mock:reservation") + .doCase("weather", "mock:weather") + .otherwise("mock:other"); } }; }
