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 b3d42cb59133 CAMEL-26794: camel-cli-connector - an Error in its 
scheduled task no longer stops the status and the CLI actions for good (#29522)
b3d42cb59133 is described below

commit b3d42cb5913331fab0d11608d31504cf2a35da94
Author: Claus Ibsen <[email protected]>
AuthorDate: Tue Nov 10 07:46:27 2026 +0100

    CAMEL-26794: camel-cli-connector - an Error in its scheduled task no longer 
stops the status and the CLI actions for good (#29522)
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 .../apache/camel/cli/connector/ConnectorTasks.java | 60 ++++++++++++++++++++++
 .../cli/connector/FileCliConnectorTransport.java   |  6 ++-
 .../connector/WebSocketCliConnectorTransport.java  |  6 +--
 .../connector/FileCliConnectorTransportTest.java   | 23 ++++++++-
 4 files changed, 87 insertions(+), 8 deletions(-)

diff --git 
a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/ConnectorTasks.java
 
b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/ConnectorTasks.java
new file mode 100644
index 000000000000..40aa283c3809
--- /dev/null
+++ 
b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/ConnectorTasks.java
@@ -0,0 +1,60 @@
+/*
+ * 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.cli.connector;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The periodic tasks of the connector, which must never end: a task that 
throws is not run again by its scheduler, and
+ * these tasks are the only way the Camel CLI reaches the integration (its 
status, the actions, stop). An Error too: an
+ * OutOfMemoryError of a moment while the status is collected would otherwise 
leave the status as it was then for good,
+ * and the integration out of reach of camel stop.
+ */
+final class ConnectorTasks {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(ConnectorTasks.class);
+    // an Error is logged as a warning once: the task runs every second, and 
may fail every time
+    private static final AtomicBoolean WARNED = new AtomicBoolean();
+
+    private ConnectorTasks() {
+    }
+
+    /** Runs the task, and ignores what it throws, so the scheduler runs it 
again. */
+    static void safely(Runnable task) {
+        try {
+            task.run();
+        } catch (Exception e) {
+            // e and not e.getMessage(): the class name says what it was when 
the message is null (a NullPointerException)
+            LOG.debug("Error in Camel CLI connector task due to: {}. This 
exception is ignored.", e, e);
+        } catch (Throwable e) {
+            try {
+                if (WARNED.compareAndSet(false, true)) {
+                    LOG.warn("Error in Camel CLI connector task due to: {}. 
This error is ignored, and the task runs"
+                             + " again.",
+                            e, e);
+                } else {
+                    LOG.debug("Error in Camel CLI connector task due to: {}. 
This error is ignored.", e, e);
+                }
+            } catch (Throwable ignored) {
+                // logging fails too when the memory is short; the task runs 
again all the same
+            }
+        }
+    }
+}
diff --git 
a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/FileCliConnectorTransport.java
 
b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/FileCliConnectorTransport.java
index 23499d104a05..3bad67b2eb86 100644
--- 
a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/FileCliConnectorTransport.java
+++ 
b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/FileCliConnectorTransport.java
@@ -133,7 +133,8 @@ public class FileCliConnectorTransport extends 
ServiceSupport implements CliConn
             debugFile = createLockFile(lockFile.getName() + "-debug.json");
             receiveFile = createLockFile(lockFile.getName() + "-receive.json");
             timelineFile = createLockFile(lockFile.getName() + 
"-timeline.json");
-            scheduledFuture = executor.scheduleWithFixedDelay(this::task, 0, 
delay, TimeUnit.MILLISECONDS);
+            scheduledFuture = executor.scheduleWithFixedDelay(() -> 
ConnectorTasks.safely(this::task), 0, delay,
+                    TimeUnit.MILLISECONDS);
             LOG.info("Camel CLI connector enabled");
         } else {
             LOG.warn("Cannot create PID file: {}. This integration cannot be 
managed by Camel CLI connector.", getPid());
@@ -155,7 +156,8 @@ public class FileCliConnectorTransport extends 
ServiceSupport implements CliConn
         boolean done = scheduledFuture == null || scheduledFuture.isDone();
         if (done) {
             this.delay = delay;
-            scheduledFuture = executor.scheduleWithFixedDelay(this::task, 0, 
delay, TimeUnit.MILLISECONDS);
+            scheduledFuture = executor.scheduleWithFixedDelay(() -> 
ConnectorTasks.safely(this::task), 0, delay,
+                    TimeUnit.MILLISECONDS);
         }
     }
 
diff --git 
a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/WebSocketCliConnectorTransport.java
 
b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/WebSocketCliConnectorTransport.java
index 6e1e764f6fdf..1a148b3f2138 100644
--- 
a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/WebSocketCliConnectorTransport.java
+++ 
b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/WebSocketCliConnectorTransport.java
@@ -653,11 +653,7 @@ public class WebSocketCliConnectorTransport extends 
ServiceSupport implements Cl
     // ---- helpers ----
 
     private static void safely(Runnable task) {
-        try {
-            task.run();
-        } catch (Exception e) {
-            LOG.debug("Error in Camel CLI connector task due to: {}. This 
exception is ignored.", e.getMessage(), e);
-        }
+        ConnectorTasks.safely(task);
     }
 
     private void execute(Runnable task) {
diff --git 
a/dsl/camel-cli-connector/src/test/java/org/apache/camel/cli/connector/FileCliConnectorTransportTest.java
 
b/dsl/camel-cli-connector/src/test/java/org/apache/camel/cli/connector/FileCliConnectorTransportTest.java
index e0cbef902e21..52a953210919 100644
--- 
a/dsl/camel-cli-connector/src/test/java/org/apache/camel/cli/connector/FileCliConnectorTransportTest.java
+++ 
b/dsl/camel-cli-connector/src/test/java/org/apache/camel/cli/connector/FileCliConnectorTransportTest.java
@@ -54,6 +54,7 @@ class FileCliConnectorTransportTest {
     private File dir;
     private DefaultCamelContext context;
     private FileCliConnectorTransport transport;
+    private final EmptySnapshots snapshots = new EmptySnapshots();
 
     @BeforeEach
     void start() {
@@ -63,7 +64,7 @@ class FileCliConnectorTransportTest {
 
         context = new DefaultCamelContext();
         transport = new FileCliConnectorTransport();
-        transport.configure(context, FileCliConnectorTransportTest::dispatch, 
new EmptySnapshots(), () -> {
+        transport.configure(context, FileCliConnectorTransportTest::dispatch, 
snapshots, () -> {
         });
         transport.start();
         // poll faster than the default second
@@ -89,6 +90,18 @@ class FileCliConnectorTransportTest {
         assertThat(file(PID + "-status.json.tmp")).doesNotExist();
     }
 
+    @Test
+    void anErrorWhileCollectingTheStatusDoesNotEndTheTask() {
+        awaitJson(file(PID + "-status.json"));
+        // CAMEL-26794: an OutOfMemoryError of a moment; the scheduler would 
never run a task that threw it again, and
+        // the integration would no longer write its status nor answer an 
action
+        snapshots.error = new OutOfMemoryError("Java heap space");
+        await().atMost(10, TimeUnit.SECONDS).until(() -> snapshots.error == 
null);
+
+        writeAction(action("result"), "r1");
+        assertThat(awaitJson(file(PID + 
"-output-r1.json"))).containsEntry("status", "success");
+    }
+
     @Test
     void resultIsTheReply() {
         writeAction(action("result"), "r1");
@@ -328,8 +341,16 @@ class FileCliConnectorTransportTest {
     }
 
     private static final class EmptySnapshots implements CliSnapshotProducer {
+        // thrown once by the next status
+        volatile Error error;
+
         @Override
         public JsonObject status() {
+            Error e = error;
+            if (e != null) {
+                error = null;
+                throw e;
+            }
             return new JsonObject();
         }
 

Reply via email to