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 2026/09/02 12:04:59 UTC

(camel) branch fix/CAMEL-24590 created (now d9bda238d3d6)

This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a change to branch fix/CAMEL-24590
in repository https://gitbox.apache.org/repos/asf/camel.git


      at d9bda238d3d6 CAMEL-24590: Route group MBean now aggregates performance statistics across all member routes

This branch includes the following new commits:

     new d9bda238d3d6 CAMEL-24590: Route group MBean now aggregates performance statistics across all member routes

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-24590: Route group MBean now aggregates performance statistics across all member routes

Posted by da...@apache.org.
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 <cl...@gmail.com>
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 <no...@anthropic.com>
    Signed-off-by: Claus Ibsen <cl...@gmail.com>
---
 .../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