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

gnodet pushed a commit to branch copy-shroud
in repository https://gitbox.apache.org/repos/asf/camel.git

commit d1044c98603769a40a4306b542af249af0c442d0
Author: Guillaume Nodet <[email protected]>
AuthorDate: Tue Mar 24 07:57:25 2026 +0100

    CAMEL-23236: Add camel doctor diagnostic command
    
    New `camel doctor` command that checks the environment and reports:
    - Java version (21+ required)
    - JBang version
    - Camel version
    - Maven Central reachability
    - Docker daemon status
    - Common port conflicts (8080, 8443, 9090)
    - Disk space in temp directory
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
 .../dsl/jbang/core/commands/CamelJBangMain.java    |   1 +
 .../camel/dsl/jbang/core/commands/Doctor.java      | 136 +++++++++++++++++++++
 2 files changed, 137 insertions(+)

diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
index bd03af9ee39c..3a3f89a0bd3b 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
@@ -131,6 +131,7 @@ public class CamelJBangMain implements Callable<Integer> {
                         .addSubcommand("runtime", new CommandLine(new 
DependencyRuntime(this)))
                         .addSubcommand("update", new CommandLine(new 
DependencyUpdate(this))))
                 .addSubcommand("dirty", new CommandLine(new Dirty(this)))
+                .addSubcommand("doctor", new CommandLine(new Doctor(this)))
                 .addSubcommand("eval", new CommandLine(new EvalCommand(this))
                         .addSubcommand("expression", new CommandLine(new 
EvalExpressionCommand(this))))
                 .addSubcommand("export", new CommandLine(new Export(this)))
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Doctor.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Doctor.java
new file mode 100644
index 000000000000..7527f1fc5d53
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Doctor.java
@@ -0,0 +1,136 @@
+/*
+ * 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.dsl.jbang.core.commands;
+
+import java.io.File;
+import java.net.HttpURLConnection;
+import java.net.URI;
+
+import org.apache.camel.catalog.CamelCatalog;
+import org.apache.camel.catalog.DefaultCamelCatalog;
+import org.apache.camel.dsl.jbang.core.common.VersionHelper;
+import picocli.CommandLine.Command;
+
+@Command(name = "doctor", description = "Checks the environment and reports 
potential issues",
+         sortOptions = false, showDefaultValues = true)
+public class Doctor extends CamelCommand {
+
+    public Doctor(CamelJBangMain main) {
+        super(main);
+    }
+
+    @Override
+    public Integer doCall() throws Exception {
+        printer().println("Camel JBang Doctor");
+        printer().println("==================");
+        printer().println();
+
+        checkJava();
+        checkJBang();
+        checkCamelVersion();
+        checkMavenCentral();
+        checkDocker();
+        checkCommonPorts();
+        checkDiskSpace();
+
+        return 0;
+    }
+
+    private void checkJava() {
+        String version = System.getProperty("java.version");
+        String vendor = System.getProperty("java.vendor", "");
+        int major = Runtime.version().feature();
+        String status = major >= 21 ? "OK" : "WARN (21+ required)";
+        printer().printf("  Java:        %s (%s) [%s]%n", version, vendor, 
status);
+    }
+
+    private void checkJBang() {
+        String version = VersionHelper.getJBangVersion();
+        if (version != null) {
+            printer().printf("  JBang:       %s (OK)%n", version);
+        } else {
+            printer().printf("  JBang:       not detected%n");
+        }
+    }
+
+    private void checkCamelVersion() {
+        CamelCatalog catalog = new DefaultCamelCatalog();
+        String version = catalog.getCatalogVersion();
+        printer().printf("  Camel:       %s%n", version);
+    }
+
+    private void checkMavenCentral() {
+        try {
+            HttpURLConnection conn = (HttpURLConnection) 
URI.create("https://repo1.maven.org/maven2/";)
+                    .toURL().openConnection();
+            conn.setConnectTimeout(5000);
+            conn.setReadTimeout(5000);
+            conn.setRequestMethod("HEAD");
+            int code = conn.getResponseCode();
+            conn.disconnect();
+            printer().printf("  Maven:       %s%n", code == 200 ? "reachable 
(OK)" : "returned " + code);
+        } catch (Exception e) {
+            printer().printf("  Maven:       unreachable (%s)%n", 
e.getMessage());
+        }
+    }
+
+    private void checkDocker() {
+        try {
+            Process p = new ProcessBuilder("docker", "info")
+                    .redirectErrorStream(true)
+                    .start();
+            int exit = p.waitFor();
+            printer().printf("  Docker:      %s%n", exit == 0 ? "running (OK)" 
: "not running");
+        } catch (Exception e) {
+            printer().printf("  Docker:      not found%n");
+        }
+    }
+
+    private void checkCommonPorts() {
+        StringBuilder conflicts = new StringBuilder();
+        for (int port : new int[] { 8080, 8443, 9090 }) {
+            if (isPortInUse(port)) {
+                if (!conflicts.isEmpty()) {
+                    conflicts.append(", ");
+                }
+                conflicts.append(port);
+            }
+        }
+        if (!conflicts.isEmpty()) {
+            printer().printf("  Ports:       in use: %s%n", conflicts);
+        } else {
+            printer().printf("  Ports:       8080, 8443, 9090 free (OK)%n");
+        }
+    }
+
+    private static boolean isPortInUse(int port) {
+        try (java.net.ServerSocket ss = new java.net.ServerSocket(port)) {
+            ss.setReuseAddress(true);
+            return false;
+        } catch (Exception e) {
+            return true;
+        }
+    }
+
+    private void checkDiskSpace() {
+        File tmpDir = new File(System.getProperty("java.io.tmpdir"));
+        long free = tmpDir.getFreeSpace();
+        long mb = free / (1024 * 1024);
+        String status = mb > 500 ? "OK" : "LOW";
+        printer().printf("  Disk space:  %d MB free in temp (%s)%n", mb, 
status);
+    }
+}

Reply via email to