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 3ad447c4bee5 CAMEL-26427: camel-jbang - Every CLI command waits 300 ms 
before the JVM exits (#28762)
3ad447c4bee5 is described below

commit 3ad447c4bee5268b3eff0bd700c40a2c365d0d1a
Author: Claus Ibsen <[email protected]>
AuthorDate: Sat Oct 31 08:31:15 2026 +0100

    CAMEL-26427: camel-jbang - Every CLI command waits 300 ms before the JVM 
exits (#28762)
    
    Signed-off-by: Claus Ibsen <[email protected]>
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 .../camel/dsl/jbang/core/commands/Harden.java       | 21 +++++++++++++++------
 .../dsl/jbang/core/commands/CamelJBangMainTest.java | 18 ++++++++++++++++++
 2 files changed, 33 insertions(+), 6 deletions(-)

diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Harden.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Harden.java
index 2ad81ad4358b..63461f1e5382 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Harden.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Harden.java
@@ -197,9 +197,18 @@ public class Harden extends CamelCommand {
     // whether the answer for the current file was printed as it came (reset 
per file)
     private boolean streamed;
 
-    private final HttpClient httpClient = HttpClient.newBuilder()
-            .connectTimeout(Duration.ofSeconds(CONNECT_TIMEOUT_SECONDS))
-            .build();
+    // created on first use: the CLI creates all its commands at startup, and 
the selector thread of a client makes the
+    // JVM wait up to 300 ms when it exits
+    private HttpClient httpClient;
+
+    private HttpClient httpClient() {
+        if (httpClient == null) {
+            httpClient = HttpClient.newBuilder()
+                    
.connectTimeout(Duration.ofSeconds(CONNECT_TIMEOUT_SECONDS))
+                    .build();
+        }
+        return httpClient;
+    }
 
     public Harden(CamelJBangMain main) {
         super(main);
@@ -370,7 +379,7 @@ public class Harden extends CamelCommand {
                     .timeout(Duration.ofSeconds(HEALTH_CHECK_TIMEOUT_SECONDS))
                     .GET()
                     .build();
-            HttpResponse<String> response = httpClient.send(request, 
HttpResponse.BodyHandlers.ofString());
+            HttpResponse<String> response = httpClient().send(request, 
HttpResponse.BodyHandlers.ofString());
             return response.statusCode() == 200;
         } catch (Exception e) {
             return false;
@@ -628,7 +637,7 @@ public class Harden extends CamelCommand {
                     .POST(HttpRequest.BodyPublishers.ofString(body.toJson()))
                     .build();
 
-            HttpResponse<Stream<String>> response = httpClient.send(
+            HttpResponse<Stream<String>> response = httpClient().send(
                     request, HttpResponse.BodyHandlers.ofLines());
 
             if (response.statusCode() != 200) {
@@ -678,7 +687,7 @@ public class Harden extends CamelCommand {
                 builder.header("Authorization", "Bearer " + authKey);
             }
 
-            HttpResponse<String> response = httpClient.send(builder.build(), 
HttpResponse.BodyHandlers.ofString());
+            HttpResponse<String> response = httpClient().send(builder.build(), 
HttpResponse.BodyHandlers.ofString());
 
             if (response.statusCode() == 200) {
                 return (JsonObject) Jsoner.deserialize(response.body());
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMainTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMainTest.java
index f017fadbbd10..ab1e9c1b3060 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMainTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMainTest.java
@@ -18,12 +18,14 @@ package org.apache.camel.dsl.jbang.core.commands;
 
 import java.lang.reflect.Field;
 import java.util.List;
+import java.util.Set;
 
 import org.apache.camel.dsl.jbang.core.common.PluginHelper;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import picocli.CommandLine;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
@@ -66,4 +68,20 @@ class CamelJBangMainTest extends CamelCommandBaseTestSupport 
{
         List<String> output = printer.getLines();
         assertFalse(output.stream().anyMatch(l -> l.contains("Plugins (not 
installed):")));
     }
+
+    @Test
+    public void creatingTheCommandsStartsNoThread() {
+        // the CLI creates all its commands at startup: a thread that a 
command starts when it is created is in every
+        // run, and a thread in native code when the JVM exits (such as the 
selector of an HTTP client) makes every
+        // command wait up to 300 ms before the JVM exits
+        Set<Thread> before = Thread.getAllStackTraces().keySet();
+
+        main.newCommandLine();
+
+        List<String> started = Thread.getAllStackTraces().keySet().stream()
+                .filter(t -> !before.contains(t) && t.isAlive())
+                .map(Thread::getName)
+                .toList();
+        assertEquals(List.of(), started, "Threads started by creating the 
commands of the CLI");
+    }
 }

Reply via email to