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 134c4f0b55fb CAMEL-23566: camel-jbang - Source examples from
camel-jbang-examples repository (#23375)
134c4f0b55fb is described below
commit 134c4f0b55fbac761a42db7d551b7ac9cc02e8bf
Author: Claus Ibsen <[email protected]>
AuthorDate: Wed May 20 16:02:50 2026 +0200
CAMEL-23566: camel-jbang - Source examples from camel-jbang-examples
repository (#23375)
* CAMEL-23566: camel-jbang - Source examples from camel-jbang-examples
repository
Replace the 3 hardcoded built-in examples with a catalog-driven system
sourcing examples from the apache/camel-jbang-examples repository.
- Add ExampleHelper with catalog loading, filtering by name/tag/level,
bundled extraction (with subdirectory support), and GitHub fetch
- Refactor Run.java --example/--example-list to use the catalog JSON
- --example-list now shows NAME, LEVEL, DESCRIPTION, SOURCE columns
and supports filtering (e.g. --example-list beginner, --example-list ai)
- Bundled examples (7) work offline, others fetch from GitHub on demand
- Add -Psync-example-catalog Maven profile to download latest catalog
- Bundle 7 curated examples with READMEs: timer-log, rest-api, cron-log,
circuit-breaker, groovy, routes, xslt
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* CAMEL-23566: camel-jbang - Polish --example listing and remove
--example-list
Remove the separate --example-list flag. Using --example without a value
now lists available examples. Group examples by level (beginner first),
sort alphabetically within groups, and use emoji icons for source/docker
indicators to give descriptions more room.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
---------
Co-authored-by: Claude Opus 4.6 <[email protected]>
---
.../pages/jbang-commands/camel-jbang-debug.adoc | 3 +-
.../ROOT/pages/jbang-commands/camel-jbang-run.adoc | 3 +-
dsl/camel-jbang/camel-jbang-core/pom.xml | 30 ++
.../META-INF/camel-jbang-commands-metadata.json | 4 +-
.../apache/camel/dsl/jbang/core/commands/Run.java | 185 +++++++--
.../camel/dsl/jbang/core/common/ExampleHelper.java | 164 ++++++++
.../examples/camel-jbang-example-catalog.json | 436 +++++++++++++++++++++
.../resources/examples/circuit-breaker/README.adoc | 61 +++
.../examples/circuit-breaker/route.camel.yaml | 43 ++
.../src/main/resources/examples/cron-log.yaml | 10 -
.../main/resources/examples/cron-log/README.adoc | 7 +
.../examples/cron-log/cron-log.camel.yaml | 27 ++
.../src/main/resources/examples/groovy/README.adoc | 76 ++++
.../examples/groovy/application.properties | 17 +
.../resources/examples/groovy/groovy.camel.yaml | 49 +++
.../src/main/resources/examples/rest-api.yaml | 21 -
.../main/resources/examples/rest-api/README.adoc | 12 +
.../examples/rest-api/rest-api.camel.yaml | 38 ++
.../main/resources/examples/routes/Greeter.java | 36 ++
.../src/main/resources/examples/routes/README.adoc | 78 ++++
.../src/main/resources/examples/routes/beans.yaml | 22 ++
.../resources/examples/routes/routes.camel.yaml | 29 ++
.../src/main/resources/examples/timer-log.yaml | 10 -
.../main/resources/examples/timer-log/README.adoc | 7 +
.../examples/timer-log/timer-log.camel.yaml | 27 ++
.../src/main/resources/examples/xslt/README.adoc | 66 ++++
.../resources/examples/xslt/consumer.camel.yaml | 30 ++
.../main/resources/examples/xslt/input/account.xml | 27 ++
.../main/resources/examples/xslt/stylesheet.xsl | 29 ++
.../camel/dsl/jbang/core/commands/RunTest.java | 32 +-
.../dsl/jbang/core/common/ExampleHelperTest.java | 155 ++++++++
31 files changed, 1630 insertions(+), 104 deletions(-)
diff --git
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-debug.adoc
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-debug.adoc
index 2ba7c40779cd..85f1d2a3722d 100644
--- a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-debug.adoc
+++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-debug.adoc
@@ -31,8 +31,7 @@ camel debug [options]
| `--dep,--dependency` | Add additional dependencies | | List
| `--download` | Whether to allow automatic downloading JAR dependencies (over
the internet) | true | boolean
| `--empty` | Run an empty Camel without loading source files | false | boolean
-| `--example` | Run a built-in example by name (e.g., timer-log, rest-api).
Use --example --list to show available examples. | | String
-| `--example-list` | List available built-in examples | | boolean
+| `--example` | Run an example by name, or list available examples when no
name is given. | | String
| `--exclude` | Exclude files by name or pattern | | List
| `--fresh` | Make sure we use fresh (i.e. non-cached) resources | false |
boolean
| `--gav` | The Maven group:artifact:version (used during exporting) | |
String
diff --git
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-run.adoc
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-run.adoc
index 936fd1385e3a..d729c8e3c418 100644
--- a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-run.adoc
+++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-run.adoc
@@ -29,8 +29,7 @@ camel run [options]
| `--dep,--dependency` | Add additional dependencies | | List
| `--download` | Whether to allow automatic downloading JAR dependencies (over
the internet) | true | boolean
| `--empty` | Run an empty Camel without loading source files | false | boolean
-| `--example` | Run a built-in example by name (e.g., timer-log, rest-api).
Use --example --list to show available examples. | | String
-| `--example-list` | List available built-in examples | | boolean
+| `--example` | Run an example by name, or list available examples when no
name is given. | | String
| `--exclude` | Exclude files by name or pattern | | List
| `--fresh` | Make sure we use fresh (i.e. non-cached) resources | false |
boolean
| `--gav` | The Maven group:artifact:version (used during exporting) | |
String
diff --git a/dsl/camel-jbang/camel-jbang-core/pom.xml
b/dsl/camel-jbang/camel-jbang-core/pom.xml
index 194fce245ab7..a59b10bcccbc 100644
--- a/dsl/camel-jbang/camel-jbang-core/pom.xml
+++ b/dsl/camel-jbang/camel-jbang-core/pom.xml
@@ -250,4 +250,34 @@
</plugins>
</build>
+ <profiles>
+ <profile>
+ <id>sync-example-catalog</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>download-example-catalog</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ <configuration>
+ <target>
+ <get
src="https://raw.githubusercontent.com/apache/camel-jbang-examples/main/camel-jbang-example-catalog.json"
+
dest="${project.basedir}/src/main/resources/examples/camel-jbang-example-catalog.json"
+ usetimestamp="true"/>
+ </target>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+
</project>
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json
b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json
index 055c7288eedb..e9e4c918ffd1 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json
+++
b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json
@@ -5,7 +5,7 @@
{ "name": "cmd", "fullName": "cmd", "description": "Performs commands in
the running Camel integrations, such as start\/stop route, or change logging
levels.", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.action.CamelAction", "options": [ {
"names": "-h,--help", "description": "Display the help and sub-commands",
"javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name":
"browse", "fullName": "cmd browse", "description": "Browse pending messages on
endpoints [...]
{ "name": "completion", "fullName": "completion", "description": "Generate
completion script for bash\/zsh", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.Complete", "options": [ { "names":
"-h,--help", "description": "Display the help and sub-commands", "javaType":
"boolean", "type": "boolean" } ] },
{ "name": "config", "fullName": "config", "description": "Get and set user
configuration values", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.config.ConfigCommand", "options": [ {
"names": "-h,--help", "description": "Display the help and sub-commands",
"javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": "get",
"fullName": "config get", "description": "Display user configuration value",
"sourceClass": "org.apache.camel.dsl.jbang.core.commands.config. [...]
- { "name": "debug", "fullName": "debug", "description": "Debug local Camel
integration", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Debug",
"options": [ { "names": "--ago", "description": "Use ago instead of yyyy-MM-dd
HH:mm:ss in timestamp.", "javaType": "boolean", "type": "boolean" }, { "names":
"--background", "description": "Run in the background", "defaultValue":
"false", "javaType": "boolean", "type": "boolean" }, { "names":
"--background-wait", "description": "To [...]
+ { "name": "debug", "fullName": "debug", "description": "Debug local Camel
integration", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Debug",
"options": [ { "names": "--ago", "description": "Use ago instead of yyyy-MM-dd
HH:mm:ss in timestamp.", "javaType": "boolean", "type": "boolean" }, { "names":
"--background", "description": "Run in the background", "defaultValue":
"false", "javaType": "boolean", "type": "boolean" }, { "names":
"--background-wait", "description": "To [...]
{ "name": "dependency", "fullName": "dependency", "description": "Displays
all Camel dependencies required to run", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.DependencyCommand", "options": [ {
"names": "-h,--help", "description": "Display the help and sub-commands",
"javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name":
"copy", "fullName": "dependency copy", "description": "Copies all Camel
dependencies required to run to a specific directory", "sourc [...]
{ "name": "dirty", "fullName": "dirty", "description": "Check if there are
dirty files from previous Camel runs that did not terminate gracefully",
"sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.Dirty",
"options": [ { "names": "--clean", "description": "Clean dirty files which are
no longer in use", "defaultValue": "false", "javaType": "boolean", "type":
"boolean" }, { "names": "-h,--help", "description": "Display the help and
sub-commands", "javaType": "boolean", " [...]
{ "name": "doc", "fullName": "doc", "description": "Shows documentation
for kamelet, component, and other Camel resources", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.catalog.CatalogDoc", "options": [ {
"names": "--camel-version", "description": "To use a different Camel version
than the default version", "javaType": "java.lang.String", "type": "string" },
{ "names": "--download", "description": "Whether to allow automatic downloading
JAR dependencies (over the internet [...]
@@ -23,7 +23,7 @@
{ "name": "nano", "fullName": "nano", "description": "Nano editor to edit
file", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Nano",
"options": [ { "names": "-h,--help", "description": "Display the help and
sub-commands", "javaType": "boolean", "type": "boolean" } ] },
{ "name": "plugin", "fullName": "plugin", "description": "Manage plugins
that add sub-commands to this CLI", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.plugin.PluginCommand", "options": [ {
"names": "-h,--help", "description": "Display the help and sub-commands",
"javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": "add",
"fullName": "plugin add", "description": "Add new plugin", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.plugin.PluginA [...]
{ "name": "ps", "fullName": "ps", "description": "List running Camel
integrations", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.process.ListProcess", "options": [ {
"names": "--json", "description": "Output in JSON Format", "javaType":
"boolean", "type": "boolean" }, { "names": "--pid", "description": "List only
pid in the output", "javaType": "boolean", "type": "boolean" }, { "names":
"--remote", "description": "Break down counters into remote\/total pairs",
"javaType": [...]
- { "name": "run", "fullName": "run", "description": "Run as local Camel
integration", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Run",
"options": [ { "names": "--background", "description": "Run in the background",
"defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names":
"--background-wait", "description": "To wait for run in background to startup
successfully, before returning", "defaultValue": "true", "javaType": "boolean",
"type": "boolean" }, { [...]
+ { "name": "run", "fullName": "run", "description": "Run as local Camel
integration", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Run",
"options": [ { "names": "--background", "description": "Run in the background",
"defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names":
"--background-wait", "description": "To wait for run in background to startup
successfully, before returning", "defaultValue": "true", "javaType": "boolean",
"type": "boolean" }, { [...]
{ "name": "sbom", "fullName": "sbom", "description": "Generate a CycloneDX
or SPDX SBOM for a specific project", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.SBOMGenerator", "options": [ {
"names": "--build-property", "description": "Maven build properties, ex.
--build-property=prop1=foo", "javaType": "java.util.List", "type": "array" }, {
"names": "--camel-spring-boot-version", "description": "Camel version to use
with Spring Boot", "javaType": "java.lang.String", "type" [...]
{ "name": "script", "fullName": "script", "description": "Run Camel
integration as shell script for terminal scripting", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.Script", "options": [ { "names":
"--logging", "description": "Can be used to turn on logging (logs to file in
<user home>\/.camel directory)", "defaultValue": "false", "javaType":
"boolean", "type": "boolean" }, { "names": "--logging-level", "description":
"Logging level (ERROR, WARN, INFO, DEBUG, TRACE)", "d [...]
{ "name": "shell", "fullName": "shell", "description": "Interactive Camel
JBang shell.", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Shell",
"options": [ { "names": "-h,--help", "description": "Display the help and
sub-commands", "javaType": "boolean", "type": "boolean" } ] },
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
index 9876387f3ac8..2bba0b7f69a8 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
@@ -44,6 +44,7 @@ import org.apache.camel.catalog.CamelCatalog;
import org.apache.camel.catalog.DefaultCamelCatalog;
import org.apache.camel.dsl.jbang.core.common.CommandLineHelper;
import org.apache.camel.dsl.jbang.core.common.EnvironmentHelper;
+import org.apache.camel.dsl.jbang.core.common.ExampleHelper;
import org.apache.camel.dsl.jbang.core.common.JavaVersionCompletionCandidates;
import org.apache.camel.dsl.jbang.core.common.LauncherHelper;
import org.apache.camel.dsl.jbang.core.common.LoggingLevelCompletionCandidates;
@@ -99,7 +100,9 @@ import static
org.apache.camel.dsl.jbang.core.common.GitHubHelper.fetchGithubUrl
" camel run *",
" camel run hello.java --dev",
" camel run hello.java --port=8080",
- " camel run https://gist.github.com/user/123456" })
+ " camel run https://gist.github.com/user/123456",
+ " camel run --example",
+ " camel run --example=timer-log" })
public class Run extends CamelCommand {
// special template for running camel-jbang in docker containers
@@ -338,14 +341,10 @@ public class Run extends CamelCommand {
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.",
+ description = "Run an example by name, or list available examples
when no name is given.",
arity = "0..1", fallbackValue = "")
String example;
- @Option(names = { "--example-list" },
- description = "List available built-in examples")
- boolean exampleList;
-
public Run(CamelJBangMain main) {
super(main);
}
@@ -366,8 +365,8 @@ public class Run extends CamelCommand {
@Override
public Integer doCall() throws Exception {
// handle --example
- if (exampleList || (example != null && example.isEmpty())) {
- return listExamples();
+ if (example != null && example.isEmpty()) {
+ return listExamples(null);
}
if (example != null) {
return runExample();
@@ -380,58 +379,162 @@ public class Run extends CamelCommand {
return run();
}
- private int listExamples() {
- printer().println("Available built-in examples:");
+ private int listExamples(String filter) {
+ List<JsonObject> catalog = ExampleHelper.loadCatalog();
+ if (catalog.isEmpty()) {
+ printer().printErr("No example catalog found.");
+ return 1;
+ }
+
+ List<JsonObject> filtered = ExampleHelper.filterExamples(catalog,
filter);
+ if (filtered.isEmpty()) {
+ printer().printErr("No examples matching: " + filter);
+ return 1;
+ }
+
+ if (filter != null && !filter.isEmpty()) {
+ printer().println("Examples matching '" + filter + "':");
+ } else {
+ printer().println("Available examples:");
+ }
+
+ Map<String, List<JsonObject>> groups = new LinkedHashMap<>();
+ for (String level : new String[] { "beginner", "intermediate",
"advanced" }) {
+ groups.put(level, new ArrayList<>());
+ }
+ for (JsonObject entry : filtered) {
+ String level = entry.getString("level");
+ if (level == null) {
+ level = "intermediate";
+ }
+ groups.computeIfAbsent(level, k -> new ArrayList<>()).add(entry);
+ }
+
+ for (Map.Entry<String, List<JsonObject>> group : groups.entrySet()) {
+ List<JsonObject> entries = group.getValue();
+ if (entries.isEmpty()) {
+ continue;
+ }
+ entries.sort(Comparator.comparing(e -> e.getString("name")));
+ printer().println();
+ printer().println(group.getKey().substring(0, 1).toUpperCase() +
group.getKey().substring(1) + ":");
+ printer().printf(" %-30s %s%n", "NAME", "DESCRIPTION");
+ printer().printf(" %-30s %s%n", "----", "-----------");
+ for (JsonObject entry : entries) {
+ String eName = entry.getString("name");
+ String desc = entry.getString("description");
+ StringBuilder icons = new StringBuilder();
+ if (ExampleHelper.isBundled(entry)) {
+ icons.append("📦");
+ } else {
+ icons.append("🌐");
+ }
+ if (ExampleHelper.requiresDocker(entry)) {
+ icons.append("🐳");
+ } else {
+ icons.append(" ");
+ }
+ printer().printf(" %s %-30s %s%n", icons, eName, desc);
+ }
+ }
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(" 📦 = bundled (works offline) 🌐 = online (fetched
from GitHub) 🐳 = requires Docker");
printer().println();
- printer().println("Usage: camel run --example <name>");
- printer().println(" camel run --example <name> --dev");
+ 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
- = SuggestSimilarHelper.didYouMean(EXAMPLE_NAMES, example);
+ List<JsonObject> catalog = ExampleHelper.loadCatalog();
+ JsonObject entry = ExampleHelper.findExample(catalog, example);
+
+ if (entry == null) {
+ List<String> names = ExampleHelper.getExampleNames(catalog);
+ List<String> suggestions = SuggestSimilarHelper.didYouMean(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.");
+ printer().printErr("Run 'camel run --example' 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");
+ if (ExampleHelper.isBundled(entry)) {
+ return runBundledExample(entry);
+ } else {
+ return runGithubExample(entry);
+ }
+ }
+
+ private int runBundledExample(JsonObject entry) throws Exception {
+ String eName = entry.getString("name");
+ Path tempDir = ExampleHelper.extractBundledExample(entry);
+ List<String> exampleFiles = ExampleHelper.getFiles(entry);
+
+ printer().println("Running example: " + eName);
+ for (String f : exampleFiles) {
+ files.add(tempDir.resolve(f).toString());
+ }
+ if ("CamelJBang".equals(name)) {
+ name = eName;
+ }
+
+ if (!exportRun) {
+ printConfigurationValues("Running integration with the following
configuration:");
+ }
+ return run();
+ }
+
+ private int runGithubExample(JsonObject entry) throws Exception {
+ String eName = entry.getString("name");
+ String url = ExampleHelper.getGithubUrl(entry);
+
+ printer().println("Fetching example from GitHub: " + eName);
+ if (ExampleHelper.requiresDocker(entry)) {
+ printer().println("Note: this example requires Docker/Podman");
+ }
+
+ StringJoiner routes = new StringJoiner(",");
+ StringJoiner kamelets = new StringJoiner(",");
+ StringJoiner properties = new StringJoiner(",");
try {
- String content = IOHelper.loadText(is);
- IOHelper.close(is);
- Files.writeString(exampleFile, content);
+ fetchGithubUrls(url, routes, kamelets, properties);
+ } catch (Exception e) {
+ printer().printErr("Failed to fetch example from GitHub: " +
e.getMessage());
+ printer().printErr("This example requires an internet
connection.");
+ return 1;
+ }
- printer().println("Running example: " + example);
- files.add(exampleFile.toString());
- if ("CamelJBang".equals(name)) {
- name = example;
- }
+ if (routes.length() == 0 && kamelets.length() == 0 &&
properties.length() == 0) {
+ printer().printErr("No files found for example: " + eName);
+ return 1;
+ }
- if (!exportRun) {
- printConfigurationValues("Running integration with the
following configuration:");
+ if (routes.length() > 0) {
+ for (String r : routes.toString().split(",")) {
+ files.add(r);
}
- return run();
- } finally {
- // clean up temp files on JVM exit
- exampleFile.toFile().deleteOnExit();
- tempDir.toFile().deleteOnExit();
}
+ if (kamelets.length() > 0) {
+ for (String k : kamelets.toString().split(",")) {
+ files.add(k);
+ }
+ }
+ if (properties.length() > 0) {
+ for (String p : properties.toString().split(",")) {
+ files.add(p);
+ }
+ }
+ if ("CamelJBang".equals(name)) {
+ name = eName;
+ }
+
+ if (!exportRun) {
+ printConfigurationValues("Running integration with the following
configuration:");
+ }
+ return run();
}
public Integer runExport() throws Exception {
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/ExampleHelper.java
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/ExampleHelper.java
new file mode 100644
index 000000000000..3ce60b684c1e
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/ExampleHelper.java
@@ -0,0 +1,164 @@
+/*
+ * 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.common;
+
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.camel.util.IOHelper;
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
+
+public final class ExampleHelper {
+
+ private static final String CATALOG_RESOURCE =
"examples/camel-jbang-example-catalog.json";
+ private static final String GITHUB_EXAMPLES_URL
+ = "https://github.com/apache/camel-jbang-examples/tree/main/";
+
+ private ExampleHelper() {
+ }
+
+ public static List<JsonObject> loadCatalog() {
+ List<JsonObject> catalog = new ArrayList<>();
+ try (InputStream is =
ExampleHelper.class.getClassLoader().getResourceAsStream(CATALOG_RESOURCE)) {
+ if (is == null) {
+ return catalog;
+ }
+ String json = IOHelper.loadText(is);
+ JsonArray array = (JsonArray) Jsoner.deserialize(json);
+ for (Object item : array) {
+ catalog.add((JsonObject) item);
+ }
+ } catch (Exception e) {
+ // ignore
+ }
+ return catalog;
+ }
+
+ public static JsonObject findExample(List<JsonObject> catalog, String
name) {
+ for (JsonObject entry : catalog) {
+ if (name.equals(entry.getString("name"))) {
+ return entry;
+ }
+ }
+ return null;
+ }
+
+ public static List<String> getExampleNames(List<JsonObject> catalog) {
+ List<String> names = new ArrayList<>();
+ for (JsonObject entry : catalog) {
+ names.add(entry.getString("name"));
+ }
+ return names;
+ }
+
+ public static List<JsonObject> filterExamples(List<JsonObject> catalog,
String filter) {
+ if (filter == null || filter.isEmpty()) {
+ return catalog;
+ }
+ String lowerFilter = filter.toLowerCase();
+ List<JsonObject> result = new ArrayList<>();
+ for (JsonObject entry : catalog) {
+ if (matches(entry, lowerFilter)) {
+ result.add(entry);
+ }
+ }
+ return result;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static boolean matches(JsonObject entry, String filter) {
+ String name = entry.getString("name");
+ if (name != null && name.toLowerCase().contains(filter)) {
+ return true;
+ }
+ String title = entry.getString("title");
+ if (title != null && title.toLowerCase().contains(filter)) {
+ return true;
+ }
+ String desc = entry.getString("description");
+ if (desc != null && desc.toLowerCase().contains(filter)) {
+ return true;
+ }
+ String level = entry.getString("level");
+ if (level != null && level.toLowerCase().contains(filter)) {
+ return true;
+ }
+ Collection<String> tags = (Collection<String>) entry.get("tags");
+ if (tags != null) {
+ for (String tag : tags) {
+ if (tag.toLowerCase().contains(filter)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public static boolean isBundled(JsonObject entry) {
+ Boolean bundled = entry.getBoolean("bundled");
+ return bundled != null && bundled;
+ }
+
+ public static boolean requiresDocker(JsonObject entry) {
+ Boolean docker = entry.getBoolean("requiresDocker");
+ return docker != null && docker;
+ }
+
+ @SuppressWarnings("unchecked")
+ public static List<String> getFiles(JsonObject entry) {
+ Collection<String> files = (Collection<String>) entry.get("files");
+ if (files == null) {
+ return List.of();
+ }
+ return new ArrayList<>(files);
+ }
+
+ public static Path extractBundledExample(JsonObject entry) throws
Exception {
+ String name = entry.getString("name");
+ List<String> fileNames = getFiles(entry);
+ Path tempDir = Files.createTempDirectory("camel-example-");
+
+ for (String fileName : fileNames) {
+ String resourcePath = "examples/" + name + "/" + fileName;
+ try (InputStream is =
ExampleHelper.class.getClassLoader().getResourceAsStream(resourcePath)) {
+ if (is != null) {
+ String content = IOHelper.loadText(is);
+ Path targetFile = tempDir.resolve(fileName);
+ // create parent dirs for nested files like
input/account.xml
+ Files.createDirectories(targetFile.getParent());
+ Files.writeString(targetFile, content);
+ targetFile.toFile().deleteOnExit();
+ targetFile.getParent().toFile().deleteOnExit();
+ }
+ }
+ }
+
+ tempDir.toFile().deleteOnExit();
+ return tempDir;
+ }
+
+ public static String getGithubUrl(JsonObject entry) {
+ return GITHUB_EXAMPLES_URL + entry.getString("name");
+ }
+
+}
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/camel-jbang-example-catalog.json
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/camel-jbang-example-catalog.json
new file mode 100644
index 000000000000..cdd5c70d7a03
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/camel-jbang-example-catalog.json
@@ -0,0 +1,436 @@
+[
+ {
+ "name": "artemis",
+ "title": "Apache ActiveMQ Artemis",
+ "description": "Setup connection factory to a remote Apache ActiveMQ
Artemis messaging broker",
+ "level": "intermediate",
+ "tags": [
+ "messaging",
+ "jms",
+ "artemis"
+ ],
+ "bundled": false,
+ "requiresDocker": false,
+ "files": [
+ "README.adoc",
+ "application.properties",
+ "consumer.camel.yaml",
+ "producer.camel.yaml"
+ ]
+ },
+ {
+ "name": "aws/aws-s3-event-based",
+ "title": "AWS S3 CDC",
+ "description": "Consume S3 events using EventBridge and SQS for change
data capture",
+ "level": "advanced",
+ "tags": [
+ "aws",
+ "s3",
+ "sqs",
+ "eventbridge",
+ "cloud"
+ ],
+ "bundled": false,
+ "requiresDocker": false,
+ "files": [
+ "README.adoc",
+ "application.properties",
+ "aws-s3-cdc-log.camel.yaml",
+ "example-file.txt",
+ "policy-queue.json",
+ "terraform/main.tf"
+ ]
+ },
+ {
+ "name": "aws/aws-sqs",
+ "title": "AWS SQS Sink",
+ "description": "Push messages to AWS SQS queue via HTTP service",
+ "level": "advanced",
+ "tags": [
+ "aws",
+ "sqs",
+ "http",
+ "cloud"
+ ],
+ "bundled": false,
+ "requiresDocker": false,
+ "files": [
+ "README.adoc",
+ "application.properties",
+ "http-to-aws-sqs.camel.yaml"
+ ]
+ },
+ {
+ "name": "circuit-breaker",
+ "title": "Circuit Breaker",
+ "description": "Use the circuit breaker EIP for fault tolerance",
+ "level": "beginner",
+ "tags": [
+ "eip",
+ "resilience"
+ ],
+ "bundled": true,
+ "requiresDocker": false,
+ "files": [
+ "README.adoc",
+ "route.camel.yaml"
+ ]
+ },
+ {
+ "name": "cron-log",
+ "title": "Cron Log",
+ "description": "Scheduled task that logs every 5 seconds",
+ "level": "beginner",
+ "tags": [
+ "beginner",
+ "timer",
+ "cron",
+ "log"
+ ],
+ "bundled": true,
+ "requiresDocker": false,
+ "files": [
+ "README.adoc",
+ "cron-log.camel.yaml"
+ ]
+ },
+ {
+ "name": "docling-langchain4j-rag",
+ "title": "Document Analysis with Docling and LangChain4j RAG",
+ "description": "RAG workflow combining Docling document conversion
with LangChain4j and Ollama",
+ "level": "advanced",
+ "tags": [
+ "ai",
+ "rag",
+ "langchain4j",
+ "docling"
+ ],
+ "bundled": false,
+ "requiresDocker": true,
+ "files": [
+ "README.adoc",
+ "application.properties",
+ "compose.yaml",
+ "docling-langchain4j-rag.yaml",
+ "sample.md"
+ ]
+ },
+ {
+ "name": "financial-doc-analyzer",
+ "title": "Financial Document Analyzer",
+ "description": "Automated financial document analysis with Docling,
LangChain4j, and market data",
+ "level": "advanced",
+ "tags": [
+ "ai",
+ "langchain4j",
+ "docling",
+ "finance"
+ ],
+ "bundled": false,
+ "requiresDocker": false,
+ "files": [
+ "README.adoc",
+ "application.properties",
+ "examples/banking-sector-brief.pdf",
+ "examples/magnificent-seven-update.pdf",
+ "examples/semiconductor-sector-analysis.pdf",
+ "examples/tesla-q3-2024.pdf",
+ "financial-doc-analyzer.yaml"
+ ]
+ },
+ {
+ "name": "ftp",
+ "title": "ActiveMQ to FTP",
+ "description": "Integrate ActiveMQ messaging with an FTP server",
+ "level": "intermediate",
+ "tags": [
+ "messaging",
+ "ftp",
+ "activemq"
+ ],
+ "bundled": false,
+ "requiresDocker": true,
+ "files": [
+ "README.adoc",
+ "application.properties",
+ "compose.yaml",
+ "ftp.camel.yaml",
+ "jbang.properties"
+ ]
+ },
+ {
+ "name": "groovy",
+ "title": "Groovy",
+ "description": "Use Groovy with extra dependencies and content-based
routing",
+ "level": "beginner",
+ "tags": [
+ "language",
+ "groovy",
+ "eip"
+ ],
+ "bundled": true,
+ "requiresDocker": false,
+ "files": [
+ "README.adoc",
+ "application.properties",
+ "groovy.camel.yaml"
+ ]
+ },
+ {
+ "name": "keycloak-introspection-rest",
+ "title": "Keycloak Token Introspection REST API",
+ "description": "Secure REST APIs with Keycloak OAuth 2.0 token
introspection",
+ "level": "advanced",
+ "tags": [
+ "security",
+ "keycloak",
+ "rest",
+ "oauth"
+ ],
+ "bundled": false,
+ "requiresDocker": false,
+ "files": [
+ "README.adoc",
+ "application.properties",
+ "rest-api.camel.yaml"
+ ]
+ },
+ {
+ "name": "keycloak-security-rest",
+ "title": "Keycloak Security REST API",
+ "description": "Secure REST APIs with Keycloak authentication and
authorization",
+ "level": "advanced",
+ "tags": [
+ "security",
+ "keycloak",
+ "rest",
+ "oauth"
+ ],
+ "bundled": false,
+ "requiresDocker": false,
+ "files": [
+ "README.adoc",
+ "application.properties",
+ "rest-api.camel.yaml"
+ ]
+ },
+ {
+ "name": "mqtt",
+ "title": "MQTT",
+ "description": "Receive MQTT events from an external MQTT broker",
+ "level": "intermediate",
+ "tags": [
+ "messaging",
+ "mqtt",
+ "iot"
+ ],
+ "bundled": false,
+ "requiresDocker": true,
+ "files": [
+ "README.adoc",
+ "application.properties",
+ "compose.yaml",
+ "infra/mosquitto.conf",
+ "mqtt.camel.yaml",
+ "start.sh"
+ ]
+ },
+ {
+ "name": "openai/pii-redaction",
+ "title": "OpenAI PII Redaction",
+ "description": "Redact personal identifiable information from text
using OpenAI-compatible LLMs",
+ "level": "advanced",
+ "tags": [
+ "ai",
+ "openai",
+ "privacy",
+ "security"
+ ],
+ "bundled": false,
+ "requiresDocker": false,
+ "files": [
+ "README.adoc",
+ "application.properties",
+ "pii-redaction.camel.yaml",
+ "pii.schema.json"
+ ]
+ },
+ {
+ "name": "openapi/client",
+ "title": "OpenAPI Client",
+ "description": "REST client generated from an OpenAPI specification",
+ "level": "intermediate",
+ "tags": [
+ "rest",
+ "openapi",
+ "client"
+ ],
+ "bundled": false,
+ "requiresDocker": false,
+ "files": [
+ "README.adoc",
+ "application.properties",
+ "examples/1001.json",
+ "petstore-api.json",
+ "petstore-client.camel.yaml"
+ ]
+ },
+ {
+ "name": "openapi/server",
+ "title": "OpenAPI Server",
+ "description": "REST service implemented from an OpenAPI
specification",
+ "level": "intermediate",
+ "tags": [
+ "rest",
+ "openapi",
+ "server"
+ ],
+ "bundled": false,
+ "requiresDocker": false,
+ "files": [
+ "README.adoc",
+ "application.properties",
+ "examples/pet/1000.json",
+ "petstore-api.json",
+ "petstore.camel.yaml"
+ ]
+ },
+ {
+ "name": "rest-api",
+ "title": "REST API",
+ "description": "REST API with hello endpoints",
+ "level": "beginner",
+ "tags": [
+ "beginner",
+ "rest",
+ "http"
+ ],
+ "bundled": true,
+ "requiresDocker": false,
+ "files": [
+ "README.adoc",
+ "rest-api.camel.yaml"
+ ]
+ },
+ {
+ "name": "routes",
+ "title": "Routes",
+ "description": "Define routes in YAML with Java beans",
+ "level": "beginner",
+ "tags": [
+ "beginner",
+ "yaml",
+ "bean"
+ ],
+ "bundled": true,
+ "requiresDocker": false,
+ "files": [
+ "Greeter.java",
+ "README.adoc",
+ "beans.yaml",
+ "routes.camel.yaml"
+ ]
+ },
+ {
+ "name": "smart-log-analyzer",
+ "title": "Smart Log Analyzer",
+ "description": "Intelligent observability correlating OpenTelemetry
logs and traces with LLM analysis",
+ "level": "advanced",
+ "tags": [
+ "ai",
+ "observability",
+ "opentelemetry",
+ "logging"
+ ],
+ "bundled": false,
+ "requiresDocker": false,
+ "files": [
+ "README.adoc",
+ "analyzer/application-dev.properties",
+ "analyzer/error-analyzer.camel.yaml",
+ "containers/caches/infinispan-events-config.json",
+ "containers/caches/infinispan-events-to-process-config.json",
+ "containers/docker-compose.yaml",
+ "containers/otel-collector-config.yaml",
+ "correlator/application-dev.properties",
+ "correlator/correlated-log-schema.json",
+ "correlator/correlated-trace-schema.json",
+ "correlator/infinispan.camel.yaml",
+ "correlator/kafka-ca-cert.pem",
+ "correlator/kaoto-datamapper-4a94acc3.xsl",
+ "correlator/kaoto-datamapper-8f5bb2dd.xsl",
+ "correlator/logs-mapper.camel.yaml",
+ "correlator/otel-log-record-schema.json",
+ "correlator/otel-logs-schema.json",
+ "correlator/otel-span-schema.json",
+ "correlator/otel-traces-schema.json",
+ "correlator/traces-mapper.camel.yaml",
+ "first-iteration/analyzer.camel.yaml",
+ "first-iteration/application.properties",
+ "first-iteration/load-generator.camel.yaml",
+ "log-generator/agent.properties",
+ "log-generator/application-dev.properties",
+ "log-generator/log-generator.camel.yaml",
+ "log-generator/opentelemetry-javaagent.jar",
+ "ui-console/application-dev.properties",
+ "ui-console/index.html",
+ "ui-console/jms-file-storage.camel.yaml",
+ "ui-console/rest-api.camel.yaml"
+ ]
+ },
+ {
+ "name": "sql",
+ "title": "SQL Database",
+ "description": "Use a SQL database with Camel and Postgres",
+ "level": "intermediate",
+ "tags": [
+ "database",
+ "sql",
+ "postgres"
+ ],
+ "bundled": false,
+ "requiresDocker": true,
+ "files": [
+ "README.adoc",
+ "application.properties",
+ "compose.yaml",
+ "sql.camel.yaml"
+ ]
+ },
+ {
+ "name": "timer-log",
+ "title": "Timer Log",
+ "description": "Simple timer that logs a hello message every second",
+ "level": "beginner",
+ "tags": [
+ "beginner",
+ "timer",
+ "log"
+ ],
+ "bundled": true,
+ "requiresDocker": false,
+ "files": [
+ "README.adoc",
+ "timer-log.camel.yaml"
+ ]
+ },
+ {
+ "name": "xslt",
+ "title": "XSLT Transformation",
+ "description": "Basic XML transformation using XSLT style sheets",
+ "level": "beginner",
+ "tags": [
+ "transformation",
+ "xml",
+ "xslt"
+ ],
+ "bundled": true,
+ "requiresDocker": false,
+ "files": [
+ "README.adoc",
+ "consumer.camel.yaml",
+ "input/account.xml",
+ "stylesheet.xsl"
+ ]
+ }
+]
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/circuit-breaker/README.adoc
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/circuit-breaker/README.adoc
new file mode 100644
index 000000000000..cca5a275abf1
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/circuit-breaker/README.adoc
@@ -0,0 +1,61 @@
+== Circuit Breaker
+
+This example shows how Camel JBang can use circuit breaker EIP.
+
+=== Install JBang
+
+First install JBang according to https://www.jbang.dev
+
+When JBang is installed then you should be able to run from a shell:
+
+[source,sh]
+----
+$ jbang --version
+----
+
+This will output the version of JBang.
+
+To run this example you can either install Camel on JBang via:
+
+[source,sh]
+----
+$ jbang app install camel@apache/camel
+----
+
+Which allows to run Camel JBang with `camel` as shown below.
+
+=== How to run
+
+You can run this example using:
+
+[source,sh]
+----
+$ camel run *
+----
+
+While the Camel integration is running, then from another terminal type:
+
+[source,sh]
+----
+$ camel get circuit-breaker
+----
+
+Which then output the state of the circuit breaker. You can run this command
with `--watch` and see
+how the state of the circuit breaker changes from closed to open due to many
failures.
+
+[source,sh]
+----
+$ camel get circuit-breaker --watch
+----
+
+
+
+=== Help and contributions
+
+If you hit any problem using Camel or have some feedback, then please
+https://camel.apache.org/community/support/[let us know].
+
+We also love contributors, so
+https://camel.apache.org/community/contributing/[get involved] :-)
+
+The Camel riders!
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/circuit-breaker/route.camel.yaml
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/circuit-breaker/route.camel.yaml
new file mode 100644
index 000000000000..7566bd348447
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/circuit-breaker/route.camel.yaml
@@ -0,0 +1,43 @@
+#
+# 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.
+#
+
+- route:
+ from:
+ uri: timer:start
+ steps:
+ - setBody:
+ expression:
+ constant:
+ expression: Hello Camel
+ - circuitBreaker:
+ resilience4jConfiguration:
+ minimumNumberOfCalls: 10
+ failureRateThreshold: 50
+ waitDurationInOpenState: 20
+ steps:
+ - filter:
+ expression:
+ simple:
+ expression: ${random(10)} > 2
+ steps:
+ - throwException:
+ message: Forced error
+ exceptionType: java.lang.IllegalArgumentException
+ - log:
+ message: "${body} (CircuitBreaker is open:
${exchangeProperty.CamelCircuitBreakerResponseShortCircuited})"
+ parameters:
+ period: 1000
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/cron-log.yaml
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/cron-log.yaml
deleted file mode 100644
index c31bdb1cec3d..000000000000
--- a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/cron-log.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-- route:
- id: cron-log
- from:
- uri: timer:cron
- parameters:
- period: "5000"
- steps:
- - setBody:
- simple: "Scheduled task running at ${date:now:HH:mm:ss}"
- - log: "${body}"
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/cron-log/README.adoc
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/cron-log/README.adoc
new file mode 100644
index 000000000000..7a5721140db9
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/cron-log/README.adoc
@@ -0,0 +1,7 @@
+== Cron Log
+
+This example shows a scheduled task that logs the current time every 5 seconds.
+
+=== How to run
+
+ camel run cron-log.camel.yaml
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/cron-log/cron-log.camel.yaml
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/cron-log/cron-log.camel.yaml
new file mode 100644
index 000000000000..fd729c9f5ea0
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/cron-log/cron-log.camel.yaml
@@ -0,0 +1,27 @@
+#
+# 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.
+#
+
+- route:
+ id: cron-log
+ from:
+ uri: timer:cron
+ parameters:
+ period: "5000"
+ steps:
+ - setBody:
+ simple: "Scheduled task running at ${date:now:HH:mm:ss}"
+ - log: "${body}"
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/groovy/README.adoc
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/groovy/README.adoc
new file mode 100644
index 000000000000..d6910f56215e
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/groovy/README.adoc
@@ -0,0 +1,76 @@
+== Groovy
+
+This example shows how to use Groovy with extra dependencies in Camel JBang.
+
+The route uses `EmailValidator` from
https://commons.apache.org/proper/commons-validator/[Apache Commons Validator]
+to validate an email address and route the message accordingly using
content-based routing.
+
+The extra dependency is declared in `application.properties` using the
+`camel.jbang.dependencies` property:
+
+[source,properties]
+----
+camel.jbang.dependencies=commons-validator:commons-validator:1.10.1
+----
+
+=== Install JBang
+
+First install JBang according to https://www.jbang.dev
+
+When JBang is installed then you should be able to run from a shell:
+
+[source,sh]
+----
+$ jbang --version
+----
+
+This will output the version of JBang.
+
+To run this example you can either install Camel on JBang via:
+
+[source,sh]
+----
+$ jbang app install camel@apache/camel
+----
+
+Which allows to run Camel JBang with `camel` as shown below.
+
+=== How to run
+
+You can run this example using:
+
+[source,sh]
+----
+$ camel run *
+----
+
+To see the invalid email branch, edit `groovy.camel.yaml` and change the
`contactEmail` header in the `once` URI to an invalid value:
+
+[source,yaml]
+----
+uri: once:validate?header.contactEmail=not-a-valid-email
+----
+
+You can also declare dependencies as a modeline comment at the top of the YAML
route file:
+
+[source,yaml]
+----
+#//DEPS commons-validator:commons-validator:1.10.1
+----
+
+Or pass the dependency on the command line:
+
+[source,sh]
+----
+$ camel run groovy.camel.yaml --dep=commons-validator:commons-validator:1.10.1
+----
+
+=== Help and contributions
+
+If you hit any problem using Camel or have some feedback, then please
+https://camel.apache.org/community/support/[let us know].
+
+We also love contributors, so
+https://camel.apache.org/community/contributing/[get involved] :-)
+
+The Camel riders!
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/groovy/application.properties
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/groovy/application.properties
new file mode 100644
index 000000000000..0bea6e4cd400
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/groovy/application.properties
@@ -0,0 +1,17 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+camel.jbang.dependencies=commons-validator:commons-validator:1.10.1
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/groovy/groovy.camel.yaml
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/groovy/groovy.camel.yaml
new file mode 100644
index 000000000000..2b89a31150bb
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/groovy/groovy.camel.yaml
@@ -0,0 +1,49 @@
+#
+# 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.
+#
+
+- route:
+ id: route-1681
+ from:
+ id: from-1133
+ uri: once
+ parameters:
+ name: validate
+ steps:
+ - setHeader:
+ id: setHeader-4262
+ constant:
+ expression: [email protected]
+ name: contactEmail
+ - choice:
+ id: choice-1178
+ otherwise:
+ id: otherwise-1234
+ steps:
+ - log:
+ id: log-3646
+ message: "Invalid email: ${header.contactEmail}"
+ when:
+ - id: when-1441
+ steps:
+ - log:
+ id: log-2349
+ message: "Valid contact: ${header.contactEmail}"
+ groovy:
+ expression: >-
+ import org.apache.commons.validator.routines.EmailValidator
+
+
EmailValidator.getInstance().isValid(request.headers['contactEmail'])
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/rest-api.yaml
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/rest-api.yaml
deleted file mode 100644
index 2e1035cf1f0f..000000000000
--- a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/rest-api.yaml
+++ /dev/null
@@ -1,21 +0,0 @@
-- rest:
- path: /api
- get:
- - path: /hello
- to: direct:hello
- - path: /hello/{name}
- to: direct:hello-name
-- route:
- id: hello
- from:
- uri: direct:hello
- steps:
- - setBody:
- constant: "Hello from Camel REST API!"
-- route:
- id: hello-name
- from:
- uri: direct:hello-name
- steps:
- - setBody:
- simple: "Hello ${header.name} from Camel REST API!"
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/rest-api/README.adoc
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/rest-api/README.adoc
new file mode 100644
index 000000000000..c68c2872843b
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/rest-api/README.adoc
@@ -0,0 +1,12 @@
+== REST API
+
+This example shows a REST API with hello endpoints.
+
+=== How to run
+
+ camel run rest-api.camel.yaml
+
+=== Try it
+
+ curl http://localhost:8080/api/hello
+ curl http://localhost:8080/api/hello/World
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/rest-api/rest-api.camel.yaml
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/rest-api/rest-api.camel.yaml
new file mode 100644
index 000000000000..dda5c6cba0fc
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/rest-api/rest-api.camel.yaml
@@ -0,0 +1,38 @@
+#
+# 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.
+#
+
+- rest:
+ path: /api
+ get:
+ - path: /hello
+ to: direct:hello
+ - path: /hello/{name}
+ to: direct:hello-name
+- route:
+ id: hello
+ from:
+ uri: direct:hello
+ steps:
+ - setBody:
+ constant: "Hello from Camel REST API!"
+- route:
+ id: hello-name
+ from:
+ uri: direct:hello-name
+ steps:
+ - setBody:
+ simple: "Hello ${header.name} from Camel REST API!"
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/routes/Greeter.java
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/routes/Greeter.java
new file mode 100644
index 000000000000..1fe603c4eeef
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/routes/Greeter.java
@@ -0,0 +1,36 @@
+/*
+ * 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 camel.example;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+
+public class Greeter implements Processor {
+
+ private String message;
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ @Override
+ public void process(Exchange exchange) throws Exception {
+ String body = exchange.getIn().getBody(String.class);
+ exchange.getIn().setBody(message + " " + body);
+ }
+
+}
\ No newline at end of file
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/routes/README.adoc
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/routes/README.adoc
new file mode 100644
index 000000000000..5cf864ca5b61
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/routes/README.adoc
@@ -0,0 +1,78 @@
+== Routes
+
+This example shows how routes are defined in Yaml.
+
+=== Install JBang
+
+First install JBang according to https://www.jbang.dev
+
+When JBang is installed then you should be able to run from a shell:
+
+[source,sh]
+----
+$ jbang --version
+----
+
+This will output the version of JBang.
+
+To run this example you can either install Camel on JBang via:
+
+[source,sh]
+----
+$ jbang app install camel@apache/camel
+----
+
+Which allows to run Camel JBang with `camel` as shown below.
+
+=== How to run
+
+You can run this example using:
+
+[source,sh]
+----
+$ camel run *
+----
+
+Camel will start a route that periodically provides a greeting message.
+
+=== Live reload
+
+You can run the example in dev mode which allows you to edit the example,
+and hot-reload when the file is saved.
+
+[source,sh]
+----
+$ camel run * --dev
+----
+
+=== Run directly from GitHub
+
+The example can also be run directly by referring to the GitHub URL as shown:
+
+[source,sh]
+----
+$ camel run https://github.com/apache/camel-jbang-examples/tree/main/routes
+----
+
+=== Developer Web Console
+
+You can enable the developer console via `--console` flag as show:
+
+[source,sh]
+----
+$ camel run * --console
+----
+
+Then you can browse: http://localhost:8080/q/dev to introspect the running
Camel Application.
+Under "beans" Camel should display bean `greeter`.
+
+
+=== Help and contributions
+
+If you hit any problem using Camel or have some feedback, then please
+https://camel.apache.org/community/support/[let us know].
+
+We also love contributors, so
+https://camel.apache.org/community/contributing/[get involved] :-)
+
+The Camel riders!
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/routes/beans.yaml
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/routes/beans.yaml
new file mode 100644
index 000000000000..70bc6a6d5d35
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/routes/beans.yaml
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+- beans:
+ - name: "greeter"
+ type: "camel.example.Greeter"
+ properties:
+ message: 'Hello!'
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/routes/routes.camel.yaml
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/routes/routes.camel.yaml
new file mode 100644
index 000000000000..a1bfb7dcb291
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/routes/routes.camel.yaml
@@ -0,0 +1,29 @@
+#
+# 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.
+#
+
+- route:
+ id: greeting-route
+ from:
+ uri: timer:start
+ parameters:
+ period: 1000
+ steps:
+ - setBody:
+ simple: I'm ${routeId}
+ - bean:
+ ref: greeter
+ - log: ${body}
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/timer-log.yaml
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/timer-log.yaml
deleted file mode 100644
index cdff71b4d71f..000000000000
---
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/timer-log.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-- route:
- id: timer-log
- from:
- uri: timer:tick
- parameters:
- period: "1000"
- steps:
- - setBody:
- simple: "Hello Camel! (message
#${exchangeProperty.CamelTimerCounter})"
- - log: "${body}"
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/timer-log/README.adoc
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/timer-log/README.adoc
new file mode 100644
index 000000000000..3abb3a0f9b31
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/timer-log/README.adoc
@@ -0,0 +1,7 @@
+== Timer Log
+
+This example shows a simple timer that logs a hello message every second.
+
+=== How to run
+
+ camel run timer-log.camel.yaml
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/timer-log/timer-log.camel.yaml
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/timer-log/timer-log.camel.yaml
new file mode 100644
index 000000000000..48124209c60d
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/timer-log/timer-log.camel.yaml
@@ -0,0 +1,27 @@
+#
+# 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.
+#
+
+- route:
+ id: timer-log
+ from:
+ uri: timer:tick
+ parameters:
+ period: "1000"
+ steps:
+ - setBody:
+ simple: "Hello Camel! (message
#${exchangeProperty.CamelTimerCounter})"
+ - log: "${body}"
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/xslt/README.adoc
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/xslt/README.adoc
new file mode 100644
index 000000000000..0d3cc77abf8e
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/xslt/README.adoc
@@ -0,0 +1,66 @@
+== XSLT Transformation
+
+This example shows a basic XML transformation using XSLT style sheet.
+
+=== Install JBang
+
+First install JBang according to https://www.jbang.dev
+
+When JBang is installed then you should be able to run from a shell:
+
+[source,sh]
+----
+$ jbang --version
+----
+
+This will output the version of JBang.
+
+To run this example you can either install Camel on JBang via:
+
+[source,sh]
+----
+$ jbang app install camel@apache/camel
+----
+
+Which allows to run Camel JBang with `camel` as shown below.
+
+=== How to run
+
+Then you can run this example using:
+
+[source,sh]
+----
+$ camel run *
+----
+
+This reads the XML input file from _./input/account.xml_ and applies XSL
transformation.
+
+=== Live updates of message transformation
+
+You can do live changes to the stylesheet and see the output in real-time with
Camel JBang by running:
+
+[source,bash]
+----
+$ camel transform message --body=file:input/account.xml --component=xslt
--template=file:stylesheet.xsl --pretty --watch
+----
+
+You can then edit the `stylesheet.xsl` file, and save the file, and watch the
terminal for updated result.
+
+=== Run directly from GitHub
+
+The example can also be run directly by referring to the GitHub URL as shown:
+
+[source,sh]
+----
+$ camel run https://github.com/apache/camel-jbang-examples/tree/main/xslt
+----
+
+=== Help and contributions
+
+If you hit any problem using Camel or have some feedback, then please
+https://camel.apache.org/community/support/[let us know].
+
+We also love contributors, so
+https://camel.apache.org/community/contributing/[get involved] :-)
+
+The Camel riders!
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/xslt/consumer.camel.yaml
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/xslt/consumer.camel.yaml
new file mode 100644
index 000000000000..18c6663e29aa
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/xslt/consumer.camel.yaml
@@ -0,0 +1,30 @@
+#
+# 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.
+#
+
+- route:
+ from:
+ uri: file://input
+ parameters:
+ fileName: account.xml
+ noop: true
+ steps:
+ - to:
+ uri: xslt
+ parameters:
+ resourceUri: stylesheet.xsl
+ - log:
+ message: Transformed data:\n ${prettyBody}
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/xslt/input/account.xml
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/xslt/input/account.xml
new file mode 100644
index 000000000000..50b8c1fe887b
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/xslt/input/account.xml
@@ -0,0 +1,27 @@
+<!--
+
+ 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.
+
+-->
+<hash>
+ <id type="integer">1369</id>
+ <uid>8c946e1a-fdc5-40d3-9098-44271bdfad65</uid>
+ <account-number>8673088731</account-number>
+ <iban>GB38EFUA27474531363797</iban>
+ <bank-name>ABN AMRO MEZZANINE (UK) LIMITED</bank-name>
+ <routing-number>053228004</routing-number>
+ <swift-bic>AACCGB21</swift-bic>
+</hash>
\ No newline at end of file
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/xslt/stylesheet.xsl
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/xslt/stylesheet.xsl
new file mode 100644
index 000000000000..fdbb608b22b7
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/xslt/stylesheet.xsl
@@ -0,0 +1,29 @@
+<?xml version = "1.0"?>
+<!--
+
+ 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.
+
+-->
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+ <xsl:template match="/">
+ <bank>
+ <name><xsl:value-of select="/hash/bank-name/text()"/></name>
+ <bic><xsl:value-of select="/hash/swift-bic/text()"/></bic>
+ </bank>
+ </xsl:template>
+
+</xsl:stylesheet>
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/RunTest.java
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/RunTest.java
index 60aff3832db7..707dffb184de 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/RunTest.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/RunTest.java
@@ -58,50 +58,50 @@ class RunTest extends CamelCommandBaseTestSupport {
@Test
public void shouldListExamples() throws Exception {
Run command = new Run(new CamelJBangMain().withPrinter(printer));
- command.exampleList = true;
+ command.example = "";
int exit = command.doCall();
Assertions.assertEquals(0, exit);
String output = printer.getOutput();
- Assertions.assertTrue(output.contains("Available built-in examples:"));
- Assertions.assertTrue(output.contains("timer-log"));
- Assertions.assertTrue(output.contains("rest-api"));
- Assertions.assertTrue(output.contains("cron-log"));
+ Assertions.assertTrue(output.contains("Available examples:"));
+ Assertions.assertTrue(output.contains("circuit-breaker"));
+ Assertions.assertTrue(output.contains("groovy"));
+ Assertions.assertTrue(output.contains("routes"));
}
@Test
- public void shouldListExamplesWithEmptyExampleFlag() throws Exception {
+ public void shouldRejectUnknownExample() throws Exception {
Run command = new Run(new CamelJBangMain().withPrinter(printer));
- command.example = "";
+ command.example = "nonexistent";
int exit = command.doCall();
- Assertions.assertEquals(0, exit);
- String output = printer.getOutput();
- Assertions.assertTrue(output.contains("Available built-in examples:"));
+ Assertions.assertEquals(1, exit);
}
@Test
- public void shouldRejectUnknownExample() throws Exception {
+ public void shouldSuggestSimilarExample() throws Exception {
Run command = new Run(new CamelJBangMain().withPrinter(printer));
- command.example = "nonexistent";
+ command.example = "circuit-brake";
int exit = command.doCall();
Assertions.assertEquals(1, exit);
+ String output = printer.getOutput();
+ Assertions.assertTrue(output.contains("Did you mean"));
}
@Test
public void shouldParseExampleOption() throws Exception {
Run command = new Run(new CamelJBangMain());
- CommandLine.populateCommand(command, "--example=timer-log");
+ CommandLine.populateCommand(command, "--example=circuit-breaker");
- Assertions.assertEquals("timer-log", command.example);
+ Assertions.assertEquals("circuit-breaker", command.example);
}
@Test
public void shouldParseExampleListOption() throws Exception {
Run command = new Run(new CamelJBangMain());
- CommandLine.populateCommand(command, "--example-list");
+ CommandLine.populateCommand(command, "--example");
- Assertions.assertTrue(command.exampleList);
+ Assertions.assertNotNull(command.example);
}
}
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/common/ExampleHelperTest.java
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/common/ExampleHelperTest.java
new file mode 100644
index 000000000000..1e6473bcfd0f
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/common/ExampleHelperTest.java
@@ -0,0 +1,155 @@
+/*
+ * 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.common;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+
+import org.apache.camel.util.json.JsonObject;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class ExampleHelperTest {
+
+ @Test
+ void shouldLoadCatalog() {
+ List<JsonObject> catalog = ExampleHelper.loadCatalog();
+ assertFalse(catalog.isEmpty());
+ }
+
+ @Test
+ void shouldFindExample() {
+ List<JsonObject> catalog = ExampleHelper.loadCatalog();
+ JsonObject entry = ExampleHelper.findExample(catalog,
"circuit-breaker");
+ assertNotNull(entry);
+ assertEquals("Circuit Breaker", entry.getString("title"));
+ }
+
+ @Test
+ void shouldReturnNullForUnknownExample() {
+ List<JsonObject> catalog = ExampleHelper.loadCatalog();
+ JsonObject entry = ExampleHelper.findExample(catalog,
"does-not-exist");
+ assertNull(entry);
+ }
+
+ @Test
+ void shouldFilterByTag() {
+ List<JsonObject> catalog = ExampleHelper.loadCatalog();
+ List<JsonObject> filtered = ExampleHelper.filterExamples(catalog,
"security");
+ assertFalse(filtered.isEmpty());
+ for (JsonObject entry : filtered) {
+ String name = entry.getString("name");
+ assertTrue(name.contains("keycloak") || name.contains("pqc") ||
name.contains("ocsf")
+ || name.contains("pii"),
+ "Expected security-related example but got: " + name);
+ }
+ }
+
+ @Test
+ void shouldFilterByName() {
+ List<JsonObject> catalog = ExampleHelper.loadCatalog();
+ List<JsonObject> filtered = ExampleHelper.filterExamples(catalog,
"mqtt");
+ assertEquals(1, filtered.size());
+ assertEquals("mqtt", filtered.get(0).getString("name"));
+ }
+
+ @Test
+ void shouldReturnAllWhenFilterEmpty() {
+ List<JsonObject> catalog = ExampleHelper.loadCatalog();
+ List<JsonObject> filtered = ExampleHelper.filterExamples(catalog, "");
+ assertEquals(catalog.size(), filtered.size());
+ }
+
+ @Test
+ void shouldDetectBundled() {
+ List<JsonObject> catalog = ExampleHelper.loadCatalog();
+ JsonObject circuitBreaker = ExampleHelper.findExample(catalog,
"circuit-breaker");
+ assertTrue(ExampleHelper.isBundled(circuitBreaker));
+
+ JsonObject mqtt = ExampleHelper.findExample(catalog, "mqtt");
+ assertFalse(ExampleHelper.isBundled(mqtt));
+ }
+
+ @Test
+ void shouldDetectDocker() {
+ List<JsonObject> catalog = ExampleHelper.loadCatalog();
+ JsonObject mqtt = ExampleHelper.findExample(catalog, "mqtt");
+ assertTrue(ExampleHelper.requiresDocker(mqtt));
+
+ JsonObject circuitBreaker = ExampleHelper.findExample(catalog,
"circuit-breaker");
+ assertFalse(ExampleHelper.requiresDocker(circuitBreaker));
+ }
+
+ @Test
+ void shouldGetFiles() {
+ List<JsonObject> catalog = ExampleHelper.loadCatalog();
+ JsonObject routes = ExampleHelper.findExample(catalog, "routes");
+ List<String> files = ExampleHelper.getFiles(routes);
+ assertTrue(files.contains("routes.camel.yaml"));
+ assertTrue(files.contains("Greeter.java"));
+ assertTrue(files.contains("beans.yaml"));
+ }
+
+ @Test
+ void shouldExtractBundledExample() throws Exception {
+ List<JsonObject> catalog = ExampleHelper.loadCatalog();
+ JsonObject entry = ExampleHelper.findExample(catalog,
"circuit-breaker");
+ Path tempDir = ExampleHelper.extractBundledExample(entry);
+
+ assertTrue(Files.exists(tempDir.resolve("route.camel.yaml")));
+ String content = Files.readString(tempDir.resolve("route.camel.yaml"));
+ assertFalse(content.isEmpty());
+ }
+
+ @Test
+ void shouldExtractBundledExampleWithSubdirectory() throws Exception {
+ List<JsonObject> catalog = ExampleHelper.loadCatalog();
+ JsonObject entry = ExampleHelper.findExample(catalog, "xslt");
+ Path tempDir = ExampleHelper.extractBundledExample(entry);
+
+ assertTrue(Files.exists(tempDir.resolve("consumer.camel.yaml")));
+ assertTrue(Files.exists(tempDir.resolve("stylesheet.xsl")));
+ assertTrue(Files.exists(tempDir.resolve("input/account.xml")));
+ }
+
+ @Test
+ void shouldGetGithubUrl() {
+ List<JsonObject> catalog = ExampleHelper.loadCatalog();
+ JsonObject entry = ExampleHelper.findExample(catalog, "mqtt");
+ String url = ExampleHelper.getGithubUrl(entry);
+
assertEquals("https://github.com/apache/camel-jbang-examples/tree/main/mqtt",
url);
+ }
+
+ @Test
+ void shouldGetGithubUrlForNestedExample() {
+ List<JsonObject> catalog = ExampleHelper.loadCatalog();
+ JsonObject entry = ExampleHelper.findExample(catalog, "aws/aws-sqs");
+ String url = ExampleHelper.getGithubUrl(entry);
+
assertEquals("https://github.com/apache/camel-jbang-examples/tree/main/aws/aws-sqs",
url);
+ }
+
+ @Test
+ void shouldGetExampleNames() {
+ List<JsonObject> catalog = ExampleHelper.loadCatalog();
+ List<String> names = ExampleHelper.getExampleNames(catalog);
+ assertTrue(names.contains("circuit-breaker"));
+ assertTrue(names.contains("mqtt"));
+ assertTrue(names.contains("aws/aws-sqs"));
+ }
+}