This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch fix/CAMEL-24590 in repository https://gitbox.apache.org/repos/asf/camel.git
commit d9bda238d3d67bc43ac9c14c1caa67187043b277 Author: Claus Ibsen <[email protected]> AuthorDate: Wed Sep 2 14:04:41 2026 +0200 CAMEL-24590: Route group MBean now aggregates performance statistics across all member routes The ManagedRouteGroup MBean extends ManagedPerformanceCounter and is fed via a CompositePerformanceCounter wired onto each member route. However a fresh ManagedRouteGroup instance was created per route, and only the first one was registered as the JMX MBean. Every route's counter was therefore wired to a different (mostly unregistered) instance, so the group MBean reflected only the first-registered member route instead of aggregating. This caused group-level counters such as getFailuresHandled() and getLastExchangeFailureHandledTimestamp() (and getExchangesCompleted/Failed, processing times, etc.) to be silently wrong. Fix by caching a single ManagedRouteGroup per group name so all member routes share the same counter, producing a true aggregate as documented. Co-Authored-By: Claude Opus 4.8 <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- .../management/JmxManagementLifecycleStrategy.java | 28 +++++--- .../ManagedRouteGroupFailuresHandledTest.java | 83 ++++++++++++++++++++++ .../camel/management/ManagedRouteGroupTest.java | 5 +- 3 files changed, 107 insertions(+), 9 deletions(-) diff --git a/core/camel-management/src/main/java/org/apache/camel/management/JmxManagementLifecycleStrategy.java b/core/camel-management/src/main/java/org/apache/camel/management/JmxManagementLifecycleStrategy.java index be5a14327610..26b7fab1fa4c 100644 --- a/core/camel-management/src/main/java/org/apache/camel/management/JmxManagementLifecycleStrategy.java +++ b/core/camel-management/src/main/java/org/apache/camel/management/JmxManagementLifecycleStrategy.java @@ -153,6 +153,9 @@ public class JmxManagementLifecycleStrategy extends ServiceSupport implements Li private final Map<BacklogTracer, ManagedBacklogTracer> managedBacklogTracers = new HashMap<>(); private final Map<DefaultBacklogDebugger, ManagedBacklogDebugger> managedBacklogDebuggers = new HashMap<>(); private final Map<Object, Object> managedThreadPools = new HashMap<>(); + // route group MBean is shared by all routes in the same group, so its performance counters + // aggregate the statistics across all the member routes + private final Map<String, ManagedRouteGroup> managedRouteGroups = new HashMap<>(); public JmxManagementLifecycleStrategy() { } @@ -675,8 +678,15 @@ public class JmxManagementLifecycleStrategy extends ServiceSupport implements Li LOG.trace("The route is already managed: {}", route); continue; } - ManagedRouteGroup mrg = (ManagedRouteGroup) getManagementObjectStrategy() - .getManagedObjectForRouteGroup(camelContext, route.getGroup()); + // the route group MBean is shared by all the routes in the same group, so its + // performance counters aggregate the statistics across all the member routes. Only + // the first route in a group creates and registers it; the rest reuse the same instance + ManagedRouteGroup mrg = null; + String group = route.getGroup(); + if (group != null) { + mrg = managedRouteGroups.computeIfAbsent(group, g -> (ManagedRouteGroup) getManagementObjectStrategy() + .getManagedObjectForRouteGroup(camelContext, g)); + } // get the wrapped instrumentation processor from this route // and set me as the counter @@ -746,12 +756,13 @@ public class JmxManagementLifecycleStrategy extends ServiceSupport implements Li int size = camelContext.getRoutesByGroup(route.getGroup()).size(); // if size is 1 then it is because its ourselves that we are currently removing if (size <= 1) { - ManagedRouteGroup mrg = (ManagedRouteGroup) getManagementObjectStrategy() - .getManagedObjectForRouteGroup(camelContext, route.getGroup()); - try { - unmanageObject(mrg); - } catch (Exception e) { - LOG.warn("Could not unregister Route Group MBean", e); + ManagedRouteGroup mrg = managedRouteGroups.remove(route.getGroup()); + if (mrg != null) { + try { + unmanageObject(mrg); + } catch (Exception e) { + LOG.warn("Could not unregister Route Group MBean", e); + } } } } @@ -1129,6 +1140,7 @@ public class JmxManagementLifecycleStrategy extends ServiceSupport implements Li managedBacklogTracers.clear(); managedBacklogDebuggers.clear(); managedThreadPools.clear(); + managedRouteGroups.clear(); } } diff --git a/core/camel-management/src/test/java/org/apache/camel/management/ManagedRouteGroupFailuresHandledTest.java b/core/camel-management/src/test/java/org/apache/camel/management/ManagedRouteGroupFailuresHandledTest.java new file mode 100644 index 000000000000..38b7a53e1064 --- /dev/null +++ b/core/camel-management/src/test/java/org/apache/camel/management/ManagedRouteGroupFailuresHandledTest.java @@ -0,0 +1,83 @@ +/* + * 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.management; + +import org.apache.camel.api.management.ManagedCamelContext; +import org.apache.camel.api.management.mbean.ManagedRouteGroupMBean; +import org.apache.camel.builder.RouteBuilder; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledOnOs; +import org.junit.jupiter.api.condition.OS; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * Tests that a route group's performance counters aggregate handled failures recorded on any of its member routes + * (CAMEL-24590). + */ +@DisabledOnOs(OS.AIX) +public class ManagedRouteGroupFailuresHandledTest extends ManagementTestSupport { + + @Test + public void testGroupAggregatesFailuresHandled() throws Exception { + // the trigger route throws an exception that is handled and then hops to sibling routes in the same group + template.sendBody("direct:trigger", "Hello World"); + + ManagedCamelContext mcc = context.getCamelContextExtension().getContextPlugin(ManagedCamelContext.class); + ManagedRouteGroupMBean group = mcc.getManagedRouteGroup("flow"); + assertNotNull(group); + + // group stats must aggregate across all member routes: trigger, step1 and step2 each completed once + assertEquals(3, group.getExchangesCompleted()); + + // the handled failure recorded on the trigger route must be reflected at the group level + assertEquals(1, group.getFailuresHandled()); + assertNotNull(group.getLastExchangeFailureHandledTimestamp(), + "Group should report the last handled-failure timestamp of its member route"); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + onException(Exception.class) + .maximumRedeliveries(0) + .handled(true) + .to("direct:step1"); + + from("direct:step1") + .routeGroup("flow") + .routeId("step1") + .setBody(constant("123")) + .to("direct:step2"); + + from("direct:step2") + .routeGroup("flow") + .routeId("step2") + .setBody(constant("456")); + + from("direct:trigger") + .routeGroup("flow") + .routeId("trigger") + .throwException(new RuntimeException("Test failure")); + } + }; + } + +} diff --git a/core/camel-management/src/test/java/org/apache/camel/management/ManagedRouteGroupTest.java b/core/camel-management/src/test/java/org/apache/camel/management/ManagedRouteGroupTest.java index 199b14ea41e2..aee8516341c8 100644 --- a/core/camel-management/src/test/java/org/apache/camel/management/ManagedRouteGroupTest.java +++ b/core/camel-management/src/test/java/org/apache/camel/management/ManagedRouteGroupTest.java @@ -53,12 +53,15 @@ public class ManagedRouteGroupTest extends ManagementTestSupport { String group = (String) mbeanServer.getAttribute(on, "RouteGroup"); assertTrue(group.equals("first") || group.equals("second")); Long val = (Long) mbeanServer.getAttribute(on, "ExchangesTotal"); - assertEquals(1, val); Integer size = (Integer) mbeanServer.getAttribute(on, "GroupSize"); if ("first".equals(group)) { assertEquals(3, size); + // group stats aggregate across all member routes (start, a, e each processed the exchange) + assertEquals(3, val); } else { assertEquals(2, size); + // group stats aggregate across all member routes (c, d each processed the exchange) + assertEquals(2, val); } // stop all the route
