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 6792a554bef9 CAMEL-25065: camel-management - Processor statistics 
freeze after a route restart, and ContextOnly context statistics stay 0 (#26959)
6792a554bef9 is described below

commit 6792a554bef9efcaad31b35e34df29d005f3b996
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Sep 28 09:25:29 2026 +0200

    CAMEL-25065: camel-management - Processor statistics freeze after a route 
restart, and ContextOnly context statistics stay 0 (#26959)
    
    - the managed object of a processor is reused when its route is started 
again, so the exchanges are
      counted on the registered MBean (regression since 4.14, CAMEL-22312)
    - routes whose MBean is not registered (mbeansLevel=ContextOnly, 
registerNewRoutes, ...) are counted
      on the CamelContext MBean
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../management/JmxManagementLifecycleStrategy.java | 34 +++++++++++--
 .../ManagedContextOnlyStatisticsTest.java          | 55 ++++++++++++++++++++
 ...edProcessorStatisticsAfterRouteRestartTest.java | 59 ++++++++++++++++++++++
 3 files changed, 144 insertions(+), 4 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 26b7fab1fa4c..8f39265ef786 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
@@ -143,6 +143,8 @@ public class JmxManagementLifecycleStrategy extends 
ServiceSupport implements Li
     // the wrapped processors is for performance counters, which are in use 
for the created routes
     // when a route is removed, we should remove the associated processors 
from this map
     private final Map<Processor, KeyValueHolder<NamedNode, 
InstrumentationProcessor<?>>> wrappedProcessors = new HashMap<>();
+    // the managed object of a processor (by its definition), which is reused 
when its route is started again
+    private final Map<NamedNode, Object> managedProcessors = new HashMap<>();
     private final 
List<java.util.function.Consumer<JmxManagementLifecycleStrategy>> preServices = 
new ArrayList<>();
     private final TimerListenerManager loadTimer = new ManagedLoadTimer();
     private final TimerListenerManagerStartupListener loadTimerStartupListener 
= new TimerListenerManagerStartupListener();
@@ -516,6 +518,12 @@ public class JmxManagementLifecycleStrategy extends 
ServiceSupport implements Li
                 LOG.warn("Could not unregister service: {} as Service MBean.", 
service, e);
             }
         }
+        if (service instanceof Processor processor) {
+            KeyValueHolder<NamedNode, InstrumentationProcessor<?>> holder = 
wrappedProcessors.get(processor);
+            if (holder != null) {
+                managedProcessors.remove(holder.getKey());
+            }
+        }
     }
 
     private Object getManagedObjectForService(CamelContext context, Service 
service, Route route) {
@@ -635,9 +643,17 @@ public class JmxManagementLifecycleStrategy extends 
ServiceSupport implements Li
             return null;
         }
 
-        // get the managed object as it can be a specialized type such as a 
Delayer/Throttler etc.
-        Object managedObject
-                = 
getManagementObjectStrategy().getManagedObjectForProcessor(context, processor, 
holder.getKey(), route);
+        // reuse the managed object of the processor, as it is already 
registered when its route is started again
+        // (otherwise the statistics would be counted on a new object that is 
not the registered MBean)
+        Object managedObject = managedProcessors.get(holder.getKey());
+        if (managedObject == null) {
+            // get the managed object as it can be a specialized type such as 
a Delayer/Throttler etc.
+            managedObject
+                    = 
getManagementObjectStrategy().getManagedObjectForProcessor(context, processor, 
holder.getKey(), route);
+            if (managedObject != null) {
+                managedProcessors.put(holder.getKey(), managedObject);
+            }
+        }
         // only manage if we have a name for it as otherwise we do not want to 
manage it anyway
         if (managedObject != null) {
             // is it a performance counter then we need to set our counter
@@ -667,7 +683,15 @@ public class JmxManagementLifecycleStrategy extends 
ServiceSupport implements Li
             }
 
             if (!shouldRegister(route, route)) {
-                // avoid registering if not needed, skip to next route
+                // avoid registering if not needed, but the exchanges of the 
route are still counted on the
+                // CamelContext MBean (such as with mbeansLevel=ContextOnly)
+                if (camelContextMBean != null
+                        && route.getProcessor() instanceof InternalProcessor 
internal) {
+                    DefaultInstrumentationProcessor task = 
internal.getAdvice(DefaultInstrumentationProcessor.class);
+                    if (task != null) {
+                        task.setCounter(camelContextMBean);
+                    }
+                }
                 continue;
             }
 
@@ -925,6 +949,7 @@ public class JmxManagementLifecycleStrategy extends 
ServiceSupport implements Li
                 KeyValueHolder<NamedNode, InstrumentationProcessor<?>> holder 
= it.next();
                 RouteDefinition def = 
ProcessorDefinitionHelper.getRoute(holder.getKey());
                 if (def != null && id.equals(def.getId())) {
+                    managedProcessors.remove(holder.getKey());
                     it.remove();
                 }
             }
@@ -1137,6 +1162,7 @@ public class JmxManagementLifecycleStrategy extends 
ServiceSupport implements Li
         knowRouteIds.clear();
         preServices.clear();
         wrappedProcessors.clear();
+        managedProcessors.clear();
         managedBacklogTracers.clear();
         managedBacklogDebuggers.clear();
         managedThreadPools.clear();
diff --git 
a/core/camel-management/src/test/java/org/apache/camel/management/ManagedContextOnlyStatisticsTest.java
 
b/core/camel-management/src/test/java/org/apache/camel/management/ManagedContextOnlyStatisticsTest.java
new file mode 100644
index 000000000000..a43240e9e3cf
--- /dev/null
+++ 
b/core/camel-management/src/test/java/org/apache/camel/management/ManagedContextOnlyStatisticsTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.CamelContext;
+import org.apache.camel.ManagementMBeansLevel;
+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;
+
+@DisabledOnOs(OS.AIX)
+public class ManagedContextOnlyStatisticsTest extends ManagementTestSupport {
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        CamelContext context = super.createCamelContext();
+        
context.getManagementStrategy().getManagementAgent().setMBeansLevel(ManagementMBeansLevel.ContextOnly);
+        return context;
+    }
+
+    @Test
+    public void testContextStatistics() throws Exception {
+        template.sendBody("direct:start", "Hello");
+
+        Long total = (Long) 
getMBeanServer().getAttribute(getContextObjectName(), "ExchangesTotal");
+        assertEquals(1L, total);
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:start").to("mock:result");
+            }
+        };
+    }
+}
diff --git 
a/core/camel-management/src/test/java/org/apache/camel/management/ManagedProcessorStatisticsAfterRouteRestartTest.java
 
b/core/camel-management/src/test/java/org/apache/camel/management/ManagedProcessorStatisticsAfterRouteRestartTest.java
new file mode 100644
index 000000000000..029b8b77a4c7
--- /dev/null
+++ 
b/core/camel-management/src/test/java/org/apache/camel/management/ManagedProcessorStatisticsAfterRouteRestartTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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 javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+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.apache.camel.management.DefaultManagementObjectNameStrategy.TYPE_PROCESSOR;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@DisabledOnOs(OS.AIX)
+public class ManagedProcessorStatisticsAfterRouteRestartTest extends 
ManagementTestSupport {
+
+    @Test
+    public void testProcessorStatisticsAfterRouteRestart() throws Exception {
+        MBeanServer mbeanServer = getMBeanServer();
+        ObjectName on = getCamelObjectName(TYPE_PROCESSOR, "myLog");
+
+        template.sendBody("direct:start", "A");
+        assertEquals(1L, mbeanServer.getAttribute(on, "ExchangesCompleted"));
+
+        context.getRouteController().stopRoute("foo");
+        context.getRouteController().startRoute("foo");
+
+        template.sendBody("direct:start", "B");
+        assertEquals(2L, mbeanServer.getAttribute(on, "ExchangesCompleted"));
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:start").routeId("foo")
+                        .to("log:foo").id("myLog")
+                        .to("mock:result");
+            }
+        };
+    }
+}

Reply via email to