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 7c55523d99e CAMEL-21481: camel-jbang - Capture reload error when doing 
route reloads (#16399)
7c55523d99e is described below

commit 7c55523d99ebbbf0fee379228c4a030e8029b13c
Author: Claus Ibsen <claus.ib...@gmail.com>
AuthorDate: Thu Nov 28 18:09:28 2024 +0100

    CAMEL-21481: camel-jbang - Capture reload error when doing route reloads 
(#16399)
    
    * CAMEL-21481: camel-jbang - Capture reload error when doing route reloads
---
 .../java/org/apache/camel/spi/ReloadStrategy.java  |  5 +++
 .../camel/impl/console/ContextDevConsole.java      | 39 ++++++++++++++++----
 .../camel/impl/console/ReloadDevConsole.java       | 20 ++++++++++
 .../support/DefaultContextReloadStrategy.java      |  8 ++++
 .../support/FileWatcherResourceReloadStrategy.java |  2 +
 .../support/ResourceReloadStrategySupport.java     | 18 +++++++++
 .../camel/support/RouteOnDemandReloadStrategy.java |  2 +
 .../core/commands/process/CamelContextStatus.java  | 43 +++++++++++++++++++---
 .../jbang/core/commands/process/ListProcess.java   | 31 +++++++++++++++-
 9 files changed, 153 insertions(+), 15 deletions(-)

diff --git 
a/core/camel-api/src/main/java/org/apache/camel/spi/ReloadStrategy.java 
b/core/camel-api/src/main/java/org/apache/camel/spi/ReloadStrategy.java
index c390f732172..ca32988afb2 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/ReloadStrategy.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/ReloadStrategy.java
@@ -48,4 +48,9 @@ public interface ReloadStrategy extends StaticService, 
CamelContextAware {
      * Reset the counters.
      */
     void resetCounters();
+
+    /**
+     * Gets the last error if reloading failed
+     */
+    Exception getLastError();
 }
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 5981c5d7a8d..9f6b5ae2d67 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
@@ -16,6 +16,7 @@
  */
 package org.apache.camel.impl.console;
 
+import java.util.Collections;
 import java.util.Date;
 import java.util.Locale;
 import java.util.Map;
@@ -26,8 +27,10 @@ import 
org.apache.camel.api.management.mbean.ManagedCamelContextMBean;
 import org.apache.camel.spi.ReloadStrategy;
 import org.apache.camel.spi.annotations.DevConsole;
 import org.apache.camel.support.CamelContextHelper;
+import org.apache.camel.support.ExceptionHelper;
 import org.apache.camel.support.console.AbstractDevConsole;
 import org.apache.camel.util.TimeUtils;
+import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
 
 @DevConsole(name = "context", displayName = "CamelContext", description = 
"Overall information about the CamelContext")
@@ -57,9 +60,11 @@ public class ContextDevConsole extends AbstractDevConsole {
             ManagedCamelContextMBean mb = mcc.getManagedCamelContext();
             if (mb != null) {
                 int reloaded = 0;
+                int reloadedFailed = 0;
                 Set<ReloadStrategy> rs = 
getCamelContext().hasServices(ReloadStrategy.class);
                 for (ReloadStrategy r : rs) {
                     reloaded += r.getReloadCounter();
+                    reloadedFailed += r.getFailedCounter();
                 }
                 String load1 = getLoad1(mb);
                 String load5 = getLoad5(mb);
@@ -80,7 +85,7 @@ public class ContextDevConsole extends AbstractDevConsole {
                 } else {
                     sb.append(String.format("\n    Idle Since: %s", ""));
                 }
-                sb.append(String.format("\n    Reloaded: %s", reloaded));
+                sb.append(String.format("\n    Reloaded: %s/%s", reloaded, 
reloadedFailed));
                 sb.append(String.format("\n    Mean Time: %s", 
TimeUtils.printDuration(mb.getMeanProcessingTime(), true)));
                 sb.append(String.format("\n    Max Time: %s", 
TimeUtils.printDuration(mb.getMaxProcessingTime(), true)));
                 sb.append(String.format("\n    Min Time: %s", 
TimeUtils.printDuration(mb.getMinProcessingTime(), true)));
@@ -131,11 +136,6 @@ public class ContextDevConsole extends AbstractDevConsole {
             if (mb != null) {
                 JsonObject stats = new JsonObject();
 
-                int reloaded = 0;
-                Set<ReloadStrategy> rs = 
getCamelContext().hasServices(ReloadStrategy.class);
-                for (ReloadStrategy r : rs) {
-                    reloaded += r.getReloadCounter();
-                }
                 String load1 = getLoad1(mb);
                 String load5 = getLoad5(mb);
                 String load15 = getLoad15(mb);
@@ -155,7 +155,6 @@ public class ContextDevConsole extends AbstractDevConsole {
                 stats.put("remoteExchangesTotal", 
mb.getRemoteExchangesTotal());
                 stats.put("remoteExchangesFailed", 
mb.getRemoteExchangesFailed());
                 stats.put("remoteExchangesInflight", 
mb.getRemoteExchangesInflight());
-                stats.put("reloaded", reloaded);
                 stats.put("meanProcessingTime", mb.getMeanProcessingTime());
                 stats.put("maxProcessingTime", mb.getMaxProcessingTime());
                 stats.put("minProcessingTime", mb.getMinProcessingTime());
@@ -175,6 +174,32 @@ public class ContextDevConsole extends AbstractDevConsole {
                 if (last != null) {
                     stats.put("lastFailedExchangeTimestamp", last.getTime());
                 }
+                // reload stats
+                int reloaded = 0;
+                int reloadedFailed = 0;
+                Exception reloadCause = null;
+                Set<ReloadStrategy> rs = 
getCamelContext().hasServices(ReloadStrategy.class);
+                for (ReloadStrategy r : rs) {
+                    reloaded += r.getReloadCounter();
+                    reloadedFailed += r.getFailedCounter();
+                    if (reloadCause == null) {
+                        reloadCause = r.getLastError();
+                    }
+                }
+                JsonObject ro = new JsonObject();
+                ro.put("reloaded", reloaded);
+                ro.put("failed", reloadedFailed);
+                if (reloadCause != null) {
+                    JsonObject eo = new JsonObject();
+                    eo.put("message", reloadCause.getMessage());
+                    JsonArray arr2 = new JsonArray();
+                    final String trace = 
ExceptionHelper.stackTraceToString(reloadCause);
+                    eo.put("stackTrace", arr2);
+                    Collections.addAll(arr2, trace.split("\n"));
+                    ro.put("lastError", eo);
+                }
+                stats.put("reload", ro);
+
                 root.put("statistics", stats);
             }
         }
diff --git 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/ReloadDevConsole.java
 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/ReloadDevConsole.java
index 4c899caccb4..a9f6e21a597 100644
--- 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/ReloadDevConsole.java
+++ 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/ReloadDevConsole.java
@@ -16,6 +16,7 @@
  */
 package org.apache.camel.impl.console;
 
+import java.util.Collections;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
@@ -24,6 +25,7 @@ import java.util.concurrent.TimeUnit;
 
 import org.apache.camel.spi.ReloadStrategy;
 import org.apache.camel.spi.annotations.DevConsole;
+import org.apache.camel.support.ExceptionHelper;
 import org.apache.camel.support.console.AbstractDevConsole;
 import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
@@ -71,6 +73,14 @@ public class ReloadDevConsole extends AbstractDevConsole {
                 sb.append(String.format("\nReloadStrategy: %s", 
r.getClass().getName()));
                 sb.append(String.format("\n    Reloaded: %s", 
r.getReloadCounter()));
                 sb.append(String.format("\n    Failed: %s", 
r.getFailedCounter()));
+                Exception cause = r.getLastError();
+                if (cause != null) {
+                    sb.append(String.format("\n    Error Message: %s", 
cause.getMessage()));
+                    final String stackTrace = 
ExceptionHelper.stackTraceToString(cause);
+                    sb.append("\n\n");
+                    sb.append(stackTrace);
+                    sb.append("\n\n");
+                }
             }
         }
         if (trigger) {
@@ -117,6 +127,16 @@ public class ReloadDevConsole extends AbstractDevConsole {
                 jo.put("className", r.getClass().getName());
                 jo.put("reloaded", r.getReloadCounter());
                 jo.put("failed", r.getFailedCounter());
+                Throwable cause = r.getLastError();
+                if (cause != null) {
+                    JsonObject eo = new JsonObject();
+                    eo.put("message", cause.getMessage());
+                    JsonArray arr2 = new JsonArray();
+                    final String trace = 
ExceptionHelper.stackTraceToString(cause);
+                    eo.put("stackTrace", arr2);
+                    Collections.addAll(arr2, trace.split("\n"));
+                    jo.put("lastError", eo);
+                }
             }
         }
 
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/DefaultContextReloadStrategy.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/DefaultContextReloadStrategy.java
index 068060ad069..54ec13fa789 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/DefaultContextReloadStrategy.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/DefaultContextReloadStrategy.java
@@ -37,6 +37,7 @@ public class DefaultContextReloadStrategy extends 
ServiceSupport implements Cont
     private CamelContext camelContext;
     private int succeeded;
     private int failed;
+    private Exception lastError;
 
     @Override
     public CamelContext getCamelContext() {
@@ -57,12 +58,14 @@ public class DefaultContextReloadStrategy extends 
ServiceSupport implements Cont
     public void onReload(Object source) {
         LOG.info("Reloading CamelContext ({}) triggered by: {}", 
camelContext.getName(), source);
         try {
+            lastError = null;
             EventHelper.notifyContextReloading(getCamelContext(), source);
             reloadProperties(source);
             reloadRoutes(source);
             incSucceededCounter();
             EventHelper.notifyContextReloaded(getCamelContext(), source);
         } catch (Exception e) {
+            lastError = e;
             incFailedCounter();
             LOG.warn("Error reloading CamelContext ({}) due to: {}", 
camelContext.getName(), e.getMessage(), e);
             EventHelper.notifyContextReloadFailure(getCamelContext(), source, 
e);
@@ -106,6 +109,11 @@ public class DefaultContextReloadStrategy extends 
ServiceSupport implements Cont
         failed = 0;
     }
 
+    @Override
+    public Exception getLastError() {
+        return lastError;
+    }
+
     protected void incSucceededCounter() {
         succeeded++;
     }
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/FileWatcherResourceReloadStrategy.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/FileWatcherResourceReloadStrategy.java
index 554c02b92d2..9415307fe7f 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/FileWatcherResourceReloadStrategy.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/FileWatcherResourceReloadStrategy.java
@@ -305,12 +305,14 @@ public class FileWatcherResourceReloadStrategy extends 
ResourceReloadStrategySup
                         if (accept) {
                             LOG.debug("Accepted Modified/Created file: {}", 
name);
                             try {
+                                setLastError(null);
                                 // must use file resource loader as we cannot 
load from classpath
                                 Resource resource
                                         = 
PluginHelper.getResourceLoader(getCamelContext()).resolveResource("file:" + 
name);
                                 getResourceReload().onReload(name, resource);
                                 incSucceededCounter();
                             } catch (Exception e) {
+                                setLastError(e);
                                 incFailedCounter();
                                 LOG.warn("Error reloading routes from file: {} 
due to: {}. This exception is ignored.", name,
                                         e.getMessage(), e);
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/ResourceReloadStrategySupport.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/ResourceReloadStrategySupport.java
index 73c55f78f48..75a2ea44a5d 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/ResourceReloadStrategySupport.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/ResourceReloadStrategySupport.java
@@ -32,6 +32,7 @@ public abstract class ResourceReloadStrategySupport extends 
ServiceSupport imple
     private CamelContext camelContext;
     private int succeeded;
     private int failed;
+    private Exception lastError;
 
     @Override
     public CamelContext getCamelContext() {
@@ -77,6 +78,23 @@ public abstract class ResourceReloadStrategySupport extends 
ServiceSupport imple
         failed = 0;
     }
 
+    public void setLastError(Exception throwable) {
+        this.lastError = throwable;
+    }
+
+    @Override
+    public Exception getLastError() {
+        return lastError;
+    }
+
+    @ManagedOperation(description = "Last error message")
+    public String lastErrorMessage() {
+        if (lastError != null) {
+            return lastError.getMessage();
+        }
+        return null;
+    }
+
     protected void incSucceededCounter() {
         succeeded++;
     }
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/RouteOnDemandReloadStrategy.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/RouteOnDemandReloadStrategy.java
index 552b9a64760..a178c48da8e 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/RouteOnDemandReloadStrategy.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/RouteOnDemandReloadStrategy.java
@@ -66,6 +66,7 @@ public class RouteOnDemandReloadStrategy extends 
RouteWatcherReloadStrategy {
     public void onReload(Object source) {
         ClassLoader cl = Thread.currentThread().getContextClassLoader();
         try {
+            setLastError(null);
             // use bootstrap classloader from camel so its consistent
             ClassLoader acl = 
getCamelContext().getApplicationContextClassLoader();
             if (acl != null) {
@@ -74,6 +75,7 @@ public class RouteOnDemandReloadStrategy extends 
RouteWatcherReloadStrategy {
             doOnReload(source);
             incSucceededCounter();
         } catch (Exception e) {
+            setLastError(e);
             incFailedCounter();
             LOG.warn("Error reloading routes due to {}. This exception is 
ignored.", e.getMessage(), e);
         } finally {
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/CamelContextStatus.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/CamelContextStatus.java
index 706b2f6c541..0833ea3214f 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/CamelContextStatus.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/CamelContextStatus.java
@@ -104,7 +104,6 @@ public class CamelContextStatus extends ProcessWatchCommand 
{
                             if (num != null) {
                                 row.inflightRemote = num.toString();
                             }
-                            row.reloaded = stats.get("reloaded").toString();
                             Object last = stats.get("lastProcessingTime");
                             if (last != null) {
                                 row.last = last.toString();
@@ -128,6 +127,16 @@ public class CamelContextStatus extends 
ProcessWatchCommand {
                                 long time = Long.parseLong(last.toString());
                                 row.sinceLastFailed = 
TimeUtils.printSince(time);
                             }
+                            row.reloaded = (String) stats.get("reloaded"); // 
backwards compatible
+                            stats = (Map<String, ?>) stats.get("reload");
+                            if (stats != null) {
+                                row.reloaded = 
stats.get("reloaded").toString();
+                                row.reloadedFailed = 
stats.get("failed").toString();
+                                stats = (Map<String, ?>) 
stats.get("lastError");
+                                if (stats != null) {
+                                    row.reloadedError = 
stats.get("message").toString();
+                                }
+                            }
                         }
                         JsonArray array = (JsonArray) root.get("routes");
                         for (int i = 0; i < array.size(); i++) {
@@ -163,9 +172,8 @@ public class CamelContextStatus extends ProcessWatchCommand 
{
                     new 
Column().header("PROFILE").dataAlign(HorizontalAlign.LEFT).with(this::getProfile),
                     new 
Column().header("READY").dataAlign(HorizontalAlign.CENTER).with(r -> r.ready),
                     new 
Column().header("STATUS").headerAlign(HorizontalAlign.CENTER)
-                            .with(r -> extractState(r.state)),
-                    new 
Column().header("RELOAD").headerAlign(HorizontalAlign.CENTER)
-                            .with(r -> r.reloaded),
+                            .with(this::getStatus),
+                    new Column().header("RELOAD").with(this::getReloaded),
                     new 
Column().header("AGE").headerAlign(HorizontalAlign.CENTER).with(r -> r.age),
                     new Column().header("ROUTE").with(this::getRoutes),
                     new Column().header("MSG/S").with(this::getThroughput),
@@ -174,8 +182,11 @@ public class CamelContextStatus extends 
ProcessWatchCommand {
                     new Column().header("FAIL").with(this::getFailed),
                     new Column().header("INFLIGHT").with(this::getInflight),
                     new Column().header("LAST").with(r -> r.last),
-                    new Column().header("DELTA").with(this::getDelta),
-                    new 
Column().header("SINCE-LAST").with(this::getSinceLast))));
+                    new Column().header("SINCE-LAST").with(this::getSinceLast),
+                    new Column().header("") // empty header as we only show 
info when there is an error
+                            
.headerAlign(HorizontalAlign.LEFT).dataAlign(HorizontalAlign.LEFT)
+                            .maxWidth(70, OverflowBehaviour.NEWLINE)
+                            .with(this::getDescription))));
         }
 
         return 0;
@@ -221,6 +232,20 @@ public class CamelContextStatus extends 
ProcessWatchCommand {
         }
     }
 
+    private String getStatus(Row r) {
+        if (r.reloadedError != null) {
+            return "Error";
+        }
+        return extractState(r.state);
+    }
+
+    private String getDescription(Row r) {
+        if (r.reloadedError != null) {
+            return "Reload failed due to: " + r.reloadedError;
+        }
+        return null;
+    }
+
     private String getTotal(Row r) {
         return r.total;
     }
@@ -289,6 +314,10 @@ public class CamelContextStatus extends 
ProcessWatchCommand {
         return s;
     }
 
+    protected String getReloaded(Row row) {
+        return row.reloaded + "/" + row.reloadedFailed;
+    }
+
     protected String getRoutes(Row r) {
         return r.routeStarted + "/" + r.routeTotal;
     }
@@ -305,6 +334,8 @@ public class CamelContextStatus extends ProcessWatchCommand 
{
         int routeTotal;
         int state;
         String reloaded;
+        String reloadedFailed;
+        String reloadedError;
         String age;
         long uptime;
         String throughput;
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListProcess.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListProcess.java
index 9a5d0f73ef8..db486d1ea33 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListProcess.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListProcess.java
@@ -96,7 +96,15 @@ public class ListProcess extends ProcessWatchCommand {
                             if (num != null) {
                                 row.inflightRemote = num.toString();
                             }
+                            stats = (Map<String, ?>) stats.get("reload");
+                            if (stats != null) {
+                                stats = (Map<String, ?>) 
stats.get("lastError");
+                            }
+                            if (stats != null) {
+                                row.reloadError = (String) 
stats.get("message");
+                            }
                         }
+
                         rows.add(row);
                     }
                 });
@@ -115,18 +123,36 @@ public class ListProcess extends ProcessWatchCommand {
                                 .with(r -> r.name),
                         new 
Column().header("READY").dataAlign(HorizontalAlign.CENTER).with(r -> r.ready),
                         new 
Column().header("STATUS").headerAlign(HorizontalAlign.CENTER)
-                                .with(r -> extractState(r.state)),
+                                .with(this::getStatus),
                         new 
Column().header("AGE").headerAlign(HorizontalAlign.CENTER).with(r -> r.ago),
                         new Column().header("TOTAL").with(this::getTotal),
                         new 
Column().header("REMOTE").with(this::getTotalRemote),
                         new Column().header("FAIL").with(this::getFailed),
-                        new 
Column().header("INFLIGHT").with(this::getInflight))));
+                        new 
Column().header("INFLIGHT").with(this::getInflight),
+                        new Column().header("") // empty header as we only 
show info when there is an error
+                                
.headerAlign(HorizontalAlign.LEFT).dataAlign(HorizontalAlign.LEFT)
+                                .maxWidth(70, OverflowBehaviour.NEWLINE)
+                                .with(this::getDescription))));
             }
         }
 
         return 0;
     }
 
+    private String getStatus(Row r) {
+        if (r.reloadError != null) {
+            return "Error";
+        }
+        return extractState(r.state);
+    }
+
+    private String getDescription(Row r) {
+        if (r.reloadError != null) {
+            return "Reload failed due to: " + r.reloadError;
+        }
+        return null;
+    }
+
     private String getTotal(Row r) {
         return r.total;
     }
@@ -184,6 +210,7 @@ public class ListProcess extends ProcessWatchCommand {
         String failedRemote;
         String inflight;
         String inflightRemote;
+        String reloadError;
     }
 
 }

Reply via email to