You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2026/05/20 13:11:56 UTC
(camel) branch worktree-tui5 created (now 8d89bc3a1cb2)
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a change to branch worktree-tui5
in repository https://gitbox.apache.org/repos/asf/camel.git
at 8d89bc3a1cb2 CAMEL-23566: camel-jbang - Source examples from camel-jbang-examples repository
This branch includes the following new commits:
new 8d89bc3a1cb2 CAMEL-23566: camel-jbang - Source examples from camel-jbang-examples repository
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(camel) 01/01: CAMEL-23566: camel-jbang - Source examples from camel-jbang-examples repository
Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch worktree-tui5
in repository https://gitbox.apache.org/repos/asf/camel.git
commit 8d89bc3a1cb2bb37d2516e400eb0c0eef26d7fc8
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Wed May 20 15:11:36 2026 +0200
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 <no...@anthropic.com>
---
.../pages/jbang-commands/camel-jbang-debug.adoc | 4 +-
.../ROOT/pages/jbang-commands/camel-jbang-run.adoc | 4 +-
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 | 153 ++++++--
.../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 | 41 +-
.../dsl/jbang/core/common/ExampleHelperTest.java | 155 ++++++++
31 files changed, 1622 insertions(+), 91 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..5769d9aa2675 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,8 @@ 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. Use --example-list to show available examples. | | String
+| `--example-list` | List available examples. Optionally filter by keyword (e.g., --example-list ai). | | 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..888de5459839 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,8 @@ 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. Use --example-list to show available examples. | | String
+| `--example-list` | List available examples. Optionally filter by keyword (e.g., --example-list ai). | | 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..c774787e2a4d 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..8916144d20bf 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;
@@ -338,13 +339,14 @@ 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. 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;
+ description = "List available examples. Optionally filter by keyword (e.g., --example-list ai).",
+ arity = "0..1", fallbackValue = "")
+ String exampleFilter;
public Run(CamelJBangMain main) {
super(main);
@@ -366,8 +368,8 @@ public class Run extends CamelCommand {
@Override
public Integer doCall() throws Exception {
// handle --example
- if (exampleList || (example != null && example.isEmpty())) {
- return listExamples();
+ if (exampleFilter != null || (example != null && example.isEmpty())) {
+ return listExamples(exampleFilter);
}
if (example != null) {
return runExample();
@@ -380,26 +382,56 @@ 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:");
+ }
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().printf(" %-30s %-14s %-50s %s%n", "NAME", "LEVEL", "DESCRIPTION", "SOURCE");
+ printer().printf(" %-30s %-14s %-50s %s%n", "----", "-----", "-----------", "------");
+ for (JsonObject entry : filtered) {
+ String eName = entry.getString("name");
+ String level = entry.getString("level");
+ if (level == null) {
+ level = "";
+ }
+ String desc = entry.getString("description");
+ if (desc.length() > 50) {
+ desc = desc.substring(0, 47) + "...";
+ }
+ String source = ExampleHelper.isBundled(entry) ? "bundled" : "online";
+ if (ExampleHelper.requiresDocker(entry)) {
+ source += ", docker";
+ }
+ printer().printf(" %-30s %-14s %-50s %s%n", eName, level, desc, source);
+ }
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
- = 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 {
@@ -409,29 +441,80 @@ public class Run extends CamelCommand {
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: user@example.com
+ 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..638eeaebfd21 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,15 +58,27 @@ class RunTest extends CamelCommandBaseTestSupport {
@Test
public void shouldListExamples() throws Exception {
Run command = new Run(new CamelJBangMain().withPrinter(printer));
- command.exampleList = true;
+ command.exampleFilter = "";
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 shouldListExamplesWithFilter() throws Exception {
+ Run command = new Run(new CamelJBangMain().withPrinter(printer));
+ command.exampleFilter = "security";
+ int exit = command.doCall();
+
+ Assertions.assertEquals(0, exit);
+ String output = printer.getOutput();
+ Assertions.assertTrue(output.contains("keycloak"));
+ Assertions.assertFalse(output.contains("circuit-breaker"));
}
@Test
@@ -77,7 +89,7 @@ class RunTest extends CamelCommandBaseTestSupport {
Assertions.assertEquals(0, exit);
String output = printer.getOutput();
- Assertions.assertTrue(output.contains("Available built-in examples:"));
+ Assertions.assertTrue(output.contains("Available examples:"));
}
@Test
@@ -89,12 +101,23 @@ class RunTest extends CamelCommandBaseTestSupport {
Assertions.assertEquals(1, exit);
}
+ @Test
+ public void shouldSuggestSimilarExample() throws Exception {
+ Run command = new Run(new CamelJBangMain().withPrinter(printer));
+ 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
@@ -102,6 +125,6 @@ class RunTest extends CamelCommandBaseTestSupport {
Run command = new Run(new CamelJBangMain());
CommandLine.populateCommand(command, "--example-list");
- Assertions.assertTrue(command.exampleList);
+ Assertions.assertNotNull(command.exampleFilter);
}
}
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"));
+ }
+}