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 7c231133c27e CAMEL-24590: Route group MBean aggregates performance
statistics across all member routes
7c231133c27e is described below
commit 7c231133c27e7f4539782ee5f0af5a4dcd8af8d0
Author: Claus Ibsen <[email protected]>
AuthorDate: Wed Sep 2 18:45:30 2026 +0200
CAMEL-24590: Route group MBean aggregates performance statistics across all
member routes
Route group MBean performance statistics (failuresHandled,
exchangesCompleted,
lastExchangeFailureHandledTimestamp, etc.) did not aggregate across the
group's
member routes; they reported the counters of a single arbitrary member
route.
getManagedObjectForRouteGroup created a new ManagedRouteGroup per call, so
each
member route's composite counter wired to a different instance while only
the
first-registered one became the JMX MBean. Cache a single ManagedRouteGroup
per
group name so all member routes share one counter instance, producing a true
group-wide total matching the documented semantics. The instance is evicted
when
the group's last route is removed and cleared on stop.
Closes #26038
Co-authored-by: Claude Opus 4.8 <[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