Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ public void execute(String... args) {
.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)))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* 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.ServerSocket;
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();
// drain output to prevent blocking
p.getInputStream().transferTo(java.io.OutputStream.nullOutputStream());
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 (ServerSocket ss = new 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.Stack;
import java.util.StringJoiner;

Expand All @@ -48,7 +51,8 @@
import static org.apache.camel.dsl.jbang.core.common.GitHubHelper.fetchGithubUrls;

@Command(name = "init", description = "Creates a new Camel integration",
sortOptions = false, showDefaultValues = true)
sortOptions = false, showDefaultValues = true,
footer = "%nTip: For AI-assisted project scaffolding, try: camel plugin add kit")
public class Init extends CamelCommand {

@Parameters(description = "Name of integration file (or a github link)", arity = "0..1",
Expand Down Expand Up @@ -95,7 +99,12 @@ public Integer doCall() throws Exception {
return listTemplates();
}
if (file == null) {
// try interactive picker if running in a TTY and not in CI
if (System.console() != null && System.getenv("CI") == null) {
return interactivePicker();
}
printer().printErr("Missing required parameter: <file>");
printer().printErr("Run 'camel init --list' to see available templates, or run interactively in a terminal.");
return 1;
}
int code = execute();
Expand Down Expand Up @@ -271,6 +280,104 @@ private int listTemplates() {
return 0;
}

private int interactivePicker() throws Exception {
// Build template categories
Map<String, List<String[]>> categories = new LinkedHashMap<>();
categories.put("Routes", List.of(
new String[] { "yaml", "YAML DSL route", ".yaml" },
new String[] { "java", "Java DSL route", ".java" },
new String[] { "xml", "XML DSL route", ".xml" }));
categories.put("Kamelets", List.of(
new String[] { "kamelet-source.yaml", "Kamelet source connector", ".kamelet.yaml" },
new String[] { "kamelet-sink.yaml", "Kamelet sink connector", ".kamelet.yaml" },
new String[] { "kamelet-action.yaml", "Kamelet action processor", ".kamelet.yaml" }));
List<String[]> pipeTemplates = new ArrayList<>();
pipeTemplates.add(new String[] { "init-pipe.yaml", "Pipe CR (source to sink)", ".yaml" });
categories.put("Pipes and CRs", pipeTemplates);

Scanner scanner = new Scanner(System.in);

// Step 1: Pick a category
printer().println("Select a template category:");
List<String> categoryNames = new ArrayList<>(categories.keySet());
for (int i = 0; i < categoryNames.size(); i++) {
printer().printf(" %d) %s%n", i + 1, categoryNames.get(i));
}
printer().print("Choice [1]: ");
String categoryInput = scanner.nextLine().trim();
int categoryIdx;
try {
categoryIdx = categoryInput.isEmpty() ? 0 : Integer.parseInt(categoryInput) - 1;
} catch (NumberFormatException e) {
printer().printErr("Invalid choice: " + categoryInput);
return 1;
}
if (categoryIdx < 0 || categoryIdx >= categoryNames.size()) {
printer().printErr("Invalid choice: must be between 1 and " + categoryNames.size());
return 1;
}

// Step 2: Pick a template
String selectedCategory = categoryNames.get(categoryIdx);
List<String[]> templates = categories.get(selectedCategory);
printer().println();
printer().println("Select a template:");
for (int i = 0; i < templates.size(); i++) {
printer().printf(" %d) %s%n", i + 1, templates.get(i)[1]);
}
printer().print("Choice [1]: ");
String templateInput = scanner.nextLine().trim();
int templateIdx;
try {
templateIdx = templateInput.isEmpty() ? 0 : Integer.parseInt(templateInput) - 1;
} catch (NumberFormatException e) {
printer().printErr("Invalid choice: " + templateInput);
return 1;
}
if (templateIdx < 0 || templateIdx >= templates.size()) {
printer().printErr("Invalid choice: must be between 1 and " + templates.size());
return 1;
}

String[] selected = templates.get(templateIdx);
String ext = selected[2];
String defaultName = "MyRoute" + ext;
if (ext.endsWith(".kamelet.yaml")) {
if (selected[0].contains("source")) {
defaultName = "my-source.kamelet.yaml";
} else if (selected[0].contains("sink")) {
defaultName = "my-sink.kamelet.yaml";
} else {
defaultName = "my-action.kamelet.yaml";
}
} else if (selected[0].contains("pipe")) {
defaultName = "my-pipe.yaml";
pipe = true;
}

// Step 3: Prompt for filename
printer().println();
printer().printf("Filename [%s]: ", defaultName);
String filename = scanner.nextLine().trim();
if (filename.isEmpty()) {
filename = defaultName;
}

this.file = filename;
int code = execute();
if (code == 0) {
createWorkingDirectoryIfAbsent();
printer().println();
printer().println("Created: " + filename);
printer().println();
printer().println("Next steps:");
printer().println(" Run: camel run " + filename);
printer().println(" Run (live): camel run " + filename + " --dev");
printer().println(" Documentation: camel doc <component>");
}
return code;
}

private void createWorkingDirectoryIfAbsent() {
Path work = CommandLineHelper.getWorkDir();
if (!Files.exists(work)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,15 @@ public class Run extends CamelCommand {
description = "Skip resolving plugin dependencies")
boolean skipPlugins;

@Option(names = { "--example" },
description = "Run a built-in example by name (e.g., timer-log, rest-api). Use --example --list to show available examples.",
arity = "0..1", fallbackValue = "")
String example;

@Option(names = { "--example-list" },
description = "List available built-in examples")
boolean exampleList;

public Run(CamelJBangMain main) {
super(main);
}
Expand All @@ -344,13 +353,75 @@ public boolean disarrangeLogging() {

@Override
public Integer doCall() throws Exception {
// handle --example
if (exampleList || (example != null && example.isEmpty())) {
return listExamples();
}
if (example != null) {
return runExample();
}

if (!exportRun) {
printConfigurationValues("Running integration with the following configuration:");
}
// run
return run();
}

private int listExamples() {
printer().println("Available built-in examples:");
printer().println();
printer().printf(" %-20s %s%n", "timer-log", "Simple timer that logs messages every second");
printer().printf(" %-20s %s%n", "rest-api", "REST API with hello endpoints");
printer().printf(" %-20s %s%n", "cron-log", "Scheduled task that logs every 5 seconds");
printer().println();
printer().println("Usage: camel run --example <name>");
printer().println(" camel run --example <name> --dev");
return 0;
}

private static final List<String> EXAMPLE_NAMES = List.of("timer-log", "rest-api", "cron-log");

private int runExample() throws Exception {
String resourcePath = "examples/" + example + ".yaml";
InputStream is = Run.class.getClassLoader().getResourceAsStream(resourcePath);
if (is == null) {
List<String> suggestions
= org.apache.camel.main.util.SuggestSimilarHelper.didYouMean(EXAMPLE_NAMES, example);
if (!suggestions.isEmpty()) {
printer().printErr("Unknown example: " + example + ". Did you mean? " + String.join(", ", suggestions));
} else {
printer().printErr("Unknown example: " + example);
}
printer().printErr("Run 'camel run --example-list' to see available examples.");
return 1;
}

// extract example to a temp file and run it
Path tempDir = Files.createTempDirectory("camel-example-");
Path exampleFile = tempDir.resolve(example + ".yaml");
try {
String content = IOHelper.loadText(is);
IOHelper.close(is);
Files.writeString(exampleFile, content);

printer().println("Running example: " + example);
files.add(exampleFile.toString());
if ("CamelJBang".equals(name)) {
name = example;
}

if (!exportRun) {
printConfigurationValues("Running integration with the following configuration:");
}
return run();
} finally {
// clean up temp files on JVM exit
exampleFile.toFile().deleteOnExit();
tempDir.toFile().deleteOnExit();
}
}

public Integer runExport() throws Exception {
return runExport(false);
}
Expand Down
Loading
Loading