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

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

commit 9b76cad3e8227356e7b93fd3dccc250c990354d3
Author: Claus Ibsen <[email protected]>
AuthorDate: Tue May 26 12:59:38 2026 +0200

    CAMEL-23615: Add getLastExchangeFailureHandledTimestamp() to management API 
and dev consoles
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
 .../camel/impl/console/ConsumerDevConsole.java     |  4 ++
 .../camel/impl/console/ContextDevConsole.java      |  9 +++
 .../camel/impl/console/ProcessorDevConsole.java    |  9 +++
 .../apache/camel/impl/console/RouteDevConsole.java |  9 +++
 .../camel/impl/console/RouteGroupDevConsole.java   |  9 +++
 .../mbean/ManagedPerformanceCounterMBean.java      |  3 +
 .../mbean/ManagedPerformanceCounter.java           | 13 ++++
 .../ManagedFailureHandledTimestampTest.java        | 81 ++++++++++++++++++++++
 8 files changed, 137 insertions(+)

diff --git 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/ConsumerDevConsole.java
 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/ConsumerDevConsole.java
index bdba7ca057b1..7c8680a23df2 100644
--- 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/ConsumerDevConsole.java
+++ 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/ConsumerDevConsole.java
@@ -245,6 +245,10 @@ public class ConsumerDevConsole extends AbstractDevConsole 
{
         if (last != null) {
             stats.put("lastCompletedExchangeTimestamp", last.getTime());
         }
+        last = mr.getLastExchangeFailureHandledTimestamp();
+        if (last != null) {
+            stats.put("lastFailureHandledExchangeTimestamp", last.getTime());
+        }
         last = mr.getLastExchangeFailureTimestamp();
         if (last != null) {
             stats.put("lastFailedExchangeTimestamp", last.getTime());
diff --git 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/ContextDevConsole.java
 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/ContextDevConsole.java
index 9e590550b410..af9df21329c8 100644
--- 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/ContextDevConsole.java
+++ 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/ContextDevConsole.java
@@ -111,6 +111,11 @@ public class ContextDevConsole extends AbstractDevConsole {
                     String ago = TimeUtils.printSince(last.getTime());
                     sb.append(String.format("%n    Since Last Completed: %s", 
ago));
                 }
+                last = mb.getLastExchangeFailureHandledTimestamp();
+                if (last != null) {
+                    String ago = TimeUtils.printSince(last.getTime());
+                    sb.append(String.format("%n    Since Last Failure Handled: 
%s", ago));
+                }
                 last = mb.getLastExchangeFailureTimestamp();
                 if (last != null) {
                     String ago = TimeUtils.printSince(last.getTime());
@@ -183,6 +188,10 @@ public class ContextDevConsole extends AbstractDevConsole {
                 if (last != null) {
                     stats.put("lastCompletedExchangeTimestamp", 
last.getTime());
                 }
+                last = mb.getLastExchangeFailureHandledTimestamp();
+                if (last != null) {
+                    stats.put("lastFailureHandledExchangeTimestamp", 
last.getTime());
+                }
                 last = mb.getLastExchangeFailureTimestamp();
                 if (last != null) {
                     stats.put("lastFailedExchangeTimestamp", last.getTime());
diff --git 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/ProcessorDevConsole.java
 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/ProcessorDevConsole.java
index a3306453fad7..b19974090332 100644
--- 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/ProcessorDevConsole.java
+++ 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/ProcessorDevConsole.java
@@ -175,6 +175,11 @@ public class ProcessorDevConsole extends 
AbstractDevConsole {
                 String ago = TimeUtils.printSince(last.getTime());
                 sb.append(String.format("%n        Since Last Completed: %s", 
ago));
             }
+            last = mp.getLastExchangeFailureHandledTimestamp();
+            if (last != null) {
+                String ago = TimeUtils.printSince(last.getTime());
+                sb.append(String.format("%n        Since Last Failure Handled: 
%s", ago));
+            }
             last = mp.getLastExchangeFailureTimestamp();
             if (last != null) {
                 String ago = TimeUtils.printSince(last.getTime());
@@ -346,6 +351,10 @@ public class ProcessorDevConsole extends 
AbstractDevConsole {
         if (last != null) {
             stats.put("lastCompletedExchangeTimestamp", last.getTime());
         }
+        last = mp.getLastExchangeFailureHandledTimestamp();
+        if (last != null) {
+            stats.put("lastFailureHandledExchangeTimestamp", last.getTime());
+        }
         last = mp.getLastExchangeFailureTimestamp();
         if (last != null) {
             stats.put("lastFailedExchangeTimestamp", last.getTime());
diff --git 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteDevConsole.java
 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteDevConsole.java
index f5df4eb4e91f..88df80b03187 100644
--- 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteDevConsole.java
+++ 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteDevConsole.java
@@ -170,6 +170,11 @@ public class RouteDevConsole extends AbstractDevConsole {
                 String ago = TimeUtils.printSince(last.getTime());
                 sb.append(String.format("%n    Since Last Completed: %s", 
ago));
             }
+            last = mrb.getLastExchangeFailureHandledTimestamp();
+            if (last != null) {
+                String ago = TimeUtils.printSince(last.getTime());
+                sb.append(String.format("%n    Since Last Failure Handled: 
%s", ago));
+            }
             last = mrb.getLastExchangeFailureTimestamp();
             if (last != null) {
                 String ago = TimeUtils.printSince(last.getTime());
@@ -495,6 +500,10 @@ public class RouteDevConsole extends AbstractDevConsole {
         if (last != null) {
             stats.put("lastCompletedExchangeTimestamp", last.getTime());
         }
+        last = mrb.getLastExchangeFailureHandledTimestamp();
+        if (last != null) {
+            stats.put("lastFailureHandledExchangeTimestamp", last.getTime());
+        }
         last = mrb.getLastExchangeFailureTimestamp();
         if (last != null) {
             stats.put("lastFailedExchangeTimestamp", last.getTime());
diff --git 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteGroupDevConsole.java
 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteGroupDevConsole.java
index f0df217590bd..04101fc83c4d 100644
--- 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteGroupDevConsole.java
+++ 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteGroupDevConsole.java
@@ -124,6 +124,11 @@ public class RouteGroupDevConsole extends 
AbstractDevConsole {
                 String ago = TimeUtils.printSince(last.getTime());
                 sb.append(String.format("%n    Since Last Completed: %s", 
ago));
             }
+            last = mrg.getLastExchangeFailureHandledTimestamp();
+            if (last != null) {
+                String ago = TimeUtils.printSince(last.getTime());
+                sb.append(String.format("%n    Since Last Failure Handled: 
%s", ago));
+            }
             last = mrg.getLastExchangeFailureTimestamp();
             if (last != null) {
                 String ago = TimeUtils.printSince(last.getTime());
@@ -191,6 +196,10 @@ public class RouteGroupDevConsole extends 
AbstractDevConsole {
             if (last != null) {
                 stats.put("lastCompletedExchangeTimestamp", last.getTime());
             }
+            last = mrg.getLastExchangeFailureHandledTimestamp();
+            if (last != null) {
+                stats.put("lastFailureHandledExchangeTimestamp", 
last.getTime());
+            }
             last = mrg.getLastExchangeFailureTimestamp();
             if (last != null) {
                 stats.put("lastFailedExchangeTimestamp", last.getTime());
diff --git 
a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedPerformanceCounterMBean.java
 
b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedPerformanceCounterMBean.java
index b7b1f83d72ad..53a38810e07c 100644
--- 
a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedPerformanceCounterMBean.java
+++ 
b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedPerformanceCounterMBean.java
@@ -78,6 +78,9 @@ public interface ManagedPerformanceCounterMBean extends 
ManagedCounterMBean {
     @ManagedAttribute(description = "First Exchange Completed ExchangeId")
     String getFirstExchangeCompletedExchangeId();
 
+    @ManagedAttribute(description = "Last Exchange Failure Handled Timestamp")
+    Date getLastExchangeFailureHandledTimestamp();
+
     @ManagedAttribute(description = "Last Exchange Failed Timestamp")
     Date getLastExchangeFailureTimestamp();
 
diff --git 
a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedPerformanceCounter.java
 
b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedPerformanceCounter.java
index f60378d8a5f6..150434e4ec30 100644
--- 
a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedPerformanceCounter.java
+++ 
b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedPerformanceCounter.java
@@ -53,6 +53,7 @@ public abstract class ManagedPerformanceCounter extends 
ManagedCounter
     private Statistic lastExchangeCreatedTimestamp;
     private Statistic lastExchangeCompletedTimestamp;
     private String lastExchangeCompletedExchangeId;
+    private Statistic lastExchangeFailureHandledTimestamp;
     private Statistic lastExchangeFailureTimestamp;
     private String lastExchangeFailureExchangeId;
     private boolean statisticsEnabled = true;
@@ -79,6 +80,7 @@ public abstract class ManagedPerformanceCounter extends 
ManagedCounter
         this.firstExchangeFailureTimestamp = new StatisticValue();
         this.lastExchangeCreatedTimestamp = new StatisticValue();
         this.lastExchangeCompletedTimestamp = new StatisticValue();
+        this.lastExchangeFailureHandledTimestamp = new StatisticValue();
         this.lastExchangeFailureTimestamp = new StatisticValue();
     }
 
@@ -104,6 +106,7 @@ public abstract class ManagedPerformanceCounter extends 
ManagedCounter
         lastExchangeCreatedTimestamp.reset();
         lastExchangeCompletedTimestamp.reset();
         lastExchangeCompletedExchangeId = null;
+        lastExchangeFailureHandledTimestamp.reset();
         lastExchangeFailureTimestamp.reset();
         lastExchangeFailureExchangeId = null;
     }
@@ -212,6 +215,12 @@ public abstract class ManagedPerformanceCounter extends 
ManagedCounter
         return firstExchangeCompletedExchangeId;
     }
 
+    @Override
+    public Date getLastExchangeFailureHandledTimestamp() {
+        long value = lastExchangeFailureHandledTimestamp.getValue();
+        return value > 0 ? new Date(value) : null;
+    }
+
     @Override
     public Date getLastExchangeFailureTimestamp() {
         long value = lastExchangeFailureTimestamp.getValue();
@@ -261,6 +270,7 @@ public abstract class ManagedPerformanceCounter extends 
ManagedCounter
 
         if (ExchangeHelper.isFailureHandled(exchange)) {
             failuresHandled.increment();
+            
lastExchangeFailureHandledTimestamp.updateValue(System.currentTimeMillis());
         }
         if (exchange.isExternalRedelivered()) {
             externalRedeliveries.increment();
@@ -349,6 +359,8 @@ public abstract class ManagedPerformanceCounter extends 
ManagedCounter
             sb.append(String.format(" lastExchangeCompletedTimestamp=\"%s\"",
                     dateAsString(lastExchangeCompletedTimestamp.getValue())));
             sb.append(String.format(" lastExchangeCompletedExchangeId=\"%s\"", 
nullSafe(lastExchangeCompletedExchangeId)));
+            sb.append(String.format(" 
lastExchangeFailureHandledTimestamp=\"%s\"",
+                    
dateAsString(lastExchangeFailureHandledTimestamp.getValue())));
             sb.append(String.format(" lastExchangeFailureTimestamp=\"%s\"",
                     dateAsString(lastExchangeFailureTimestamp.getValue())));
             sb.append(String.format(" lastExchangeFailureExchangeId=\"%s\"", 
nullSafe(lastExchangeFailureExchangeId)));
@@ -389,6 +401,7 @@ public abstract class ManagedPerformanceCounter extends 
ManagedCounter
             jo.put("lastExchangeCreatedTimestamp", 
lastExchangeCreatedTimestamp.getValue());
             jo.put("lastExchangeCompletedTimestamp", 
lastExchangeCompletedTimestamp.getValue());
             jo.put("lastExchangeCompletedExchangeId", 
lastExchangeCompletedExchangeId);
+            jo.put("lastExchangeFailureHandledTimestamp", 
lastExchangeFailureHandledTimestamp.getValue());
             jo.put("lastExchangeFailureTimestamp", 
lastExchangeFailureTimestamp.getValue());
             jo.put("lastExchangeFailureExchangeId", 
lastExchangeFailureExchangeId);
         }
diff --git 
a/core/camel-management/src/test/java/org/apache/camel/management/ManagedFailureHandledTimestampTest.java
 
b/core/camel-management/src/test/java/org/apache/camel/management/ManagedFailureHandledTimestampTest.java
new file mode 100644
index 000000000000..35c57d66411d
--- /dev/null
+++ 
b/core/camel-management/src/test/java/org/apache/camel/management/ManagedFailureHandledTimestampTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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 java.util.Date;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+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_ROUTE;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+@DisabledOnOs(OS.AIX)
+public class ManagedFailureHandledTimestampTest extends ManagementTestSupport {
+
+    @Test
+    public void testLastExchangeFailureHandledTimestamp() throws Exception {
+        MBeanServer mbeanServer = getMBeanServer();
+
+        MockEndpoint dead = getMockEndpoint("mock:dead");
+        dead.expectedMessageCount(1);
+
+        template.sendBody("direct:start", "Hello World");
+
+        assertMockEndpointsSatisfied();
+
+        ObjectName on = getCamelObjectName(TYPE_ROUTE, "route1");
+
+        Long completed = (Long) mbeanServer.getAttribute(on, 
"ExchangesCompleted");
+        assertEquals(1, completed.longValue());
+
+        Long failed = (Long) mbeanServer.getAttribute(on, "ExchangesFailed");
+        assertEquals(0, failed.longValue());
+
+        Long failuresHandled = (Long) mbeanServer.getAttribute(on, 
"FailuresHandled");
+        assertEquals(1, failuresHandled.longValue());
+
+        Date handledTs = (Date) mbeanServer.getAttribute(on, 
"LastExchangeFailureHandledTimestamp");
+        assertNotNull(handledTs);
+
+        Date failureTs = (Date) mbeanServer.getAttribute(on, 
"LastExchangeFailureTimestamp");
+        assertNull(failureTs);
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                errorHandler(deadLetterChannel("mock:dead"));
+
+                from("direct:start")
+                        .process(exchange -> {
+                            throw new IllegalArgumentException("Forced");
+                        });
+            }
+        };
+    }
+}

Reply via email to