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-jbang-examples.git


The following commit(s) were added to refs/heads/main by this push:
     new 024d537  CAMEL-24808: the root README introduces the ladder and lists 
the examples by group (#89)
024d537 is described below

commit 024d537bf96ee9cbe34a95056ce6c68a75f5db27
Author: Claus Ibsen <[email protected]>
AuthorDate: Fri Sep 18 21:40:16 2026 +0200

    CAMEL-24808: the root README introduces the ladder and lists the examples 
by group (#89)
    
    The README says what the repository is, how to install the CLI, how the 
examples are organised (the ladder and the web shop thread), one table per 
group with every example and what you will see when it runs and what it needs, 
how to run and test one, and how to add one. The tables sit between markers and 
are rendered by generate-catalog.sh from the metadata, which now also carries 
ciSkip into the catalog.
    
    Co-Authored-By: Claude Fable 5.1 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01Bp3538HRBPMQkb5ta9xRaj
---
 README.md                        | 201 +++++++++++++++++++++++++++++++++------
 camel-jbang-example-catalog.json |   3 +-
 generate-catalog.sh              |  52 ++++++++++
 3 files changed, 224 insertions(+), 32 deletions(-)

diff --git a/README.md b/README.md
index 0d79e77..2eb5724 100644
--- a/README.md
+++ b/README.md
@@ -1,65 +1,204 @@
 # Apache Camel CLI Examples
 
-[Apache Camel](http://camel.apache.org/) is a powerful open source integration 
framework based on known
-Enterprise Integration Patterns with powerful bean integration.
+[Apache Camel](https://camel.apache.org/) is the open source integration 
framework: routes that connect systems
+with the Enterprise Integration Patterns and 350+ components. These examples 
show it through the
+[Camel CLI](https://camel.apache.org/manual/camel-jbang.html): one tool that 
runs a route, starts the services a
+route needs, sends it a message, and runs its tests. No Maven, no Gradle, no 
Java project; a route is a YAML
+file and a command.
 
-## Introduction
+## Install the Camel CLI
 
-This project provides examples for low-code integrations with Apache Camel CLI.
+Install [JBang](https://www.jbang.dev/download/), then the CLI:
 
-### Examples
+```shell
+jbang app install camel@apache/camel
+camel --version
+```
 
-This git repository hosts a set of ready-to-use examples you can try to learn 
more about Apache Camel,
-and how Camel can be used to integrate systems. These examples are accessible 
for non developers, as
-they can run without having to use traditional Java compilation or build 
systems such as Maven or Gradle.
+The CLI needs a JDK 17 or later. The examples that start a service need Docker 
or Podman running.
 
-All examples can run local on your computer from a CLI terminal by executing a 
few commands.
+## How the examples are organised
 
-The Camel CLI is powered by [JBang](https://www.jbang.dev/) which makes using 
Java much easier.
+The examples form a ladder. Each group is a rung, and each rung builds on the 
ones before it, so read them in
+order if Camel is new to you, or jump to the rung that has the thing you need. 
From the `run` rung onwards the
+examples share one fictional web shop, with its orders, customers, warehouse 
and courier, so what one example
+sets up the next one uses.
 
-## Install Camel CLI
+Every example has the same shape: a README that says what you will see when it 
runs, how it works, how to build
+it yourself step by step and what to try changing; a `metadata.json` that 
lists what it teaches; and a test in
+the [Citrus](https://citrusframework.org/) YAML DSL that the Camel CLI runs.
 
-First install JBang according to https://www.jbang.dev
+<!-- examples:start -->
+### Quick start
 
-When JBang is installed then you should be able to run from a shell:
+The first ten minutes: generic examples with no story and no service, each 
running in seconds.
 
-```shell
-jbang --version
-```
+| Example | What you will see | Needs |
+|---|---|---|
+| [REST API](quick-start/rest-api/) | A REST API on port 8080: GET /api/hello 
answers the greeting from application.properties and GET /api/hello/{name} 
answers a greeting with the name. | nothing |
+| [Routes](quick-start/routes/) | A timer route in YAML calls a Java bean, 
Greeter, that builds the message, and logs what the bean returned; the first 
step from YAML into your own code. | nothing |
+| [Splitter](quick-start/splitter/) | A timer creates a comma-separated batch 
of items, the splitter turns it into one message per item, and each item is 
logged on its own line. | nothing |
+| [Timer Log](quick-start/timer-log/) | A timer fires every second and a log 
line prints the greeting from application.properties; the hello of Camel in one 
file. | nothing |
+
+### Run
+
+Running Camel: timers and cron schedules, a bean in a route, properties and 
profiles.
+
+| Example | What you will see | Needs |
+|---|---|---|
+| [Nightly report](run/nightly-report/) | A cron schedule runs the shop's 
inventory report, every ten seconds in the demo and nightly with a one-line 
change, and each run logs the stock counts with a timestamp. | nothing |
+| [Order generator](run/order-generator/) | A timer creates a shop order every 
five seconds: a Java bean hands out the order number, the body is the order as 
JSON, and the log shows each new order. The order feed every later example 
starts from. | nothing |
+| [Properties and profiles](run/properties-and-profiles/) | A timer logs a 
welcome with the shop name and currency from application.properties; run with 
--profile=prod and application-prod.properties overrides both, so the same 
route greets with the production values. | nothing |
+
+### Transform and map
+
+JSON, XML and CSV in and out, field-by-field mapping, Groovy and XSLT.
+
+| Example | What you will see | Needs |
+|---|---|---|
+| [CSV to JSON](transform/csv-to-json/) | A CSV of invoices dropped in the 
inbox directory is read with the CSV data format, its header line naming the 
fields, split into one message per invoice, and each invoice is logged as JSON 
and written to the outbox directory as its own file. | nothing |
+| [Data mapping](transform/data-mapping/) | The shop's order in order.json is 
mapped field by field to the courier's shipment format, with renamed fields, a 
nested recipient, one parcel per line, a computed total and a service chosen 
from the country; the order is parsed to a map, the script 
shipment-mapping.groovy builds the shipment, and it is logged as JSON. | 
nothing |
+| [Groovy](transform/groovy/) | Two orders come in, one with a valid customer 
email and one with a bad one; a Groovy expression checks the address with 
Apache Commons Validator, a third-party library declared in 
application.properties, and the log shows one order accepted and one rejected. 
| nothing |
+| [JSON transform](transform/json-transform/) | The shop's order in order.json 
is reshaped for the warehouse: jsonpath reads the order id and the number of 
lines into headers, jq builds the pick list with only sku and quantity per 
line, and both the order and the pick list are logged. | nothing |
+| [XML to JSON](transform/xml-to-json/) | A supplier's XML order dropped in 
the inbox directory is read with the Jackson XML data format and written out as 
the shop's JSON with the Jackson JSON data format, both logged; no mapping 
code, the XML elements and attributes become JSON fields. | nothing |
+| [XSLT](transform/xslt/) | A supplier's XML order dropped in the inbox 
directory is transformed by the stylesheet packing-slip.xsl into the packing 
slip the warehouse prints, one item per line and the total pieces to pick, and 
the slip is logged. | nothing |
+
+### Route
+
+The routing patterns: content-based router, splitter, aggregator, filter and 
multicast.
+
+| Example | What you will see | Needs |
+|---|---|---|
+| [Aggregator](route/aggregator/) | The warehouse reports each picked line on 
its own and the aggregator collects the lines of one order back into a 
shipment, correlated by the order id and complete when as many lines are in as 
the order had; each shipment is logged as JSON. | nothing |
+| [Content-based router](route/content-based-router/) | Three orders from 
three countries are read from the orders directory and a choice routes each by 
its country: the Danish order to local delivery, the German order to EU 
shipping without customs, the US order to export with a customs declaration; 
each branch logs what it did. | nothing |
+| [Filter and multicast](route/filter-and-multicast/) | Three orders are read 
from the orders directory; a filter lets only the paid ones through and a 
multicast sends each paid order to both the warehouse route and the invoicing 
route, which log their part; the pending order is logged as received and goes 
no further. | nothing |
+| [Order lines](route/order-lines/) | Each order read from the orders 
directory is split into one message per line, the order id travels along in a 
header, and the log shows the order, one pick line per line, and the parent's 
confirmation that all lines went to picking. | nothing |
+
+### Fail well
+
+Retries, a dead letter channel, and a circuit breaker in front of a flaky 
service.
+
+| Example | What you will see | Needs |
+|---|---|---|
+| [Circuit breaker](fail-well/circuit-breaker/) | A stock check calls the 
supplier every second; the supplier goes down for nine calls, the breaker opens 
after two failures in its window of four, answers from the fallback while open, 
tries the supplier again after five seconds and closes once a call succeeds; 
each line logs the breaker state. | nothing |
+| [Error handling](fail-well/error-handling/) | The three orders go to a 
payment provider: the first is charged at once, the second gets no answer twice 
and is charged on the third attempt after two retries logged as warnings, and 
the third is declined, logged as such and parked as a file for manual review. | 
nothing |
+
+### Connect without a service
+
+Files, an HTTP client and a REST server; everything runs inside the example.
+
+| Example | What you will see | Needs |
+|---|---|---|
+| [File processing](connect/file-processing/) | A courier route copies five 
files into an inbox; the invoices are checked, archived under a month directory 
and moved to done, the invoice with a negative amount is rejected with a 
warning and moved to failed, and the driver's note is left alone because only 
.json files are picked up. | nothing |
+| [HTTP client](connect/http-client/) | Every line of the three orders is 
checked against the stock service over HTTP, served by the same example; the 
log shows each line as ok, or back-order when the stock is short, with the 
stock level from the response. | nothing |
+| [Stock API](connect/stock-api/) | The shop's stock service on port 8080: GET 
/stock returns the stock file, GET /stock/{sku} returns one SKU as JSON and a 
404 with an error message for an unknown SKU. | nothing |
+
+### Connect to one service
 
-This will output the version of JBang.
+SQL, JMS, MQTT, Kafka and FTP against a service the Camel CLI starts for you 
with `camel infra run`.
 
-To run this example you can install Camel on JBang via:
+| Example | What you will see | Needs |
+|---|---|---|
+| [Artemis queue](connect-service/artemis/) | The three orders are put on the 
orders queue of an ActiveMQ Artemis broker started with camel infra, and a 
consumer route takes them off and logs the customer and line count; the 
connection factory is declared in application.properties. | `camel infra run 
artemis` |
+| [Camel 1.0 tribute](connect-service/camel-1-tribute/) | The very first Camel 
example of 2007, JMS to file: a timer sends ten messages to a queue on an 
Artemis broker started with camel infra, and the consumer writes each message 
to a file in outbox; the log shows both sides. | `camel infra run artemis` |
+| [FTP courier](connect-service/ftp/) | Each order on the shipments queue of 
an Artemis broker becomes a file on the courier's FTP server, both started with 
camel infra; the log shows each shipment uploaded and camel infra get ftp shows 
where the files landed. | `camel infra run artemis ftp` |
+| [Kafka orders](connect-service/kafka-orders/) | The three orders go through 
a shared validation route to a Kafka topic on a broker started with camel 
infra; the pending one is stopped, the paid ones are dispatched to the 
fulfilment and notifications topics, whose consumers log the picking and the 
email; camel cmd route-topology draws the flow. | `camel infra run kafka` |
+| [MQTT sensors](connect-service/mqtt/) | Temperature sensors in the 
warehouse's cold rooms publish JSON readings on an MQTT topic of a Mosquitto 
broker started with camel infra; a consumer logs each reading and raises a 
warning when a room is above 8 degrees. | `camel infra run mosquitto` |
+| [SQL database](connect-service/sql/) | Against a Postgres started with camel 
infra, a customers table is created, the three orders register their customers 
with an insert that counts orders on conflict, and every ten seconds a select 
lists the customers; run it twice and the order counts go up. | `camel infra 
run postgres` |
+
+### Contracts and security
+
+An OpenAPI contract served and called, and an API protected by Keycloak.
+
+| Example | What you will see | Needs |
+|---|---|---|
+| [Keycloak security](contracts/keycloak-security-rest/) | Two HTTP endpoints 
on port 8081: the public one answers everyone, the protected one requires a 
bearer token from a Keycloak started with camel infra whose user has the admin 
role, otherwise 403; the realm, client and users are created in the Keycloak 
console as the README describes. | `camel infra run keycloak` |
+| [OpenAPI client](contracts/openapi-client/) | The picking desk reserves 
stock for every order line by calling the stock API by contract: rest-openapi 
turns the operationId reserveStock into the HTTP call from stock-api.json; the 
log shows each reservation and one 409 for the cap that is out of stock. Needs 
the openapi-server example running. | nothing |
+| [OpenAPI server](contracts/openapi-server/) | The stock API contract first: 
stock-api.json is the OpenAPI contract, the REST DSL serves its three 
operations on port 8080 with request validation, GET /stock/{sku} answers from 
a file or 404, POST /stock/{sku}/reserve answers 200, 409 when the stock is 
short or 400 for a bad reservation, and /openapi serves the contract. | nothing 
|
+
+### AI
+
+A local model writing text, routes exposed as MCP tools, RAG over documents, 
PII redaction.
+
+| Example | What you will see | Needs |
+|---|---|---|
+| [Document Analysis with Docling and LangChain4j 
RAG](ai/docling-langchain4j-rag/) | Documents dropped in a directory are 
converted by a running Docling service, chunked and summarised by a local 
Ollama model through langchain4j-chat, and written to an output directory; an 
HTTP endpoint answers questions against the converted documents. | `camel infra 
run docling ollama` |
+| [LangChain4j chat](ai/langchain4j-chat/) | A local Ollama model started with 
camel infra writes the shipping notification for each of the three orders; the 
chat model is a bean built from properties, the prompt comes from the order, 
and the log shows the reply with its token counts. | `camel infra run ollama`, 
a local model |
+| [MCP server](ai/mcp-server/) | Two routes are exposed as MCP tools, 
stock_level by SKU and order_status by order id, on http://localhost:8080/mcp 
with nothing but properties to switch the server on; any MCP client, a coding 
agent included, can list and call them, and the log shows each call. | nothing |
+| [OpenAI PII Redaction](ai/openai-pii-redaction/) | Text typed on standard 
input is sent to an OpenAI-compatible model with a JSON schema that asks for 
the personal identifiers redacted, and the redacted text is printed on standard 
output. | nothing |
+
+### Cloud
+
+A cloud service, run locally through LocalStack and switched to the real thing 
by properties.
+
+| Example | What you will see | Needs |
+|---|---|---|
+| [AWS SQS](cloud/aws-sqs/) | Each of the three orders is put on an Amazon SQS 
queue called shipments and a courier route takes it off and logs the order, 
country and line count; locally the queue lives on the LocalStack that camel 
infra run aws sqs starts, and the same properties point at real AWS. | `camel 
infra run aws` |
+
+### Showcase
+
+Tooling demos outside the ladder: the TUI, a memory leak, message sizes, log 
analysis.
+
+| Example | What you will see | Needs |
+|---|---|---|
+| [Memory Leak](showcase/memory-leak/) | Three timer routes run side by side: 
two leak memory into a cache and a buffer, one is healthy, so a JFR Old Object 
Sample recording and the CLI's heap tools show which route leaks. | nothing |
+| [Message Size](showcase/message-size/) | Timer routes send messages of 
small, medium and large sizes to seda queues with a Content-Length header, so 
camel cmd and the CLI's message-size views show sizes per endpoint. | nothing |
+| [Smart Log Analyzer](showcase/smart-log-analyzer/) | A multi-part showcase: 
a load generator produces logs and traces, a correlator maps OpenTelemetry logs 
to traces, an LLM analyses the correlated records, and a small web console with 
a REST API shows the results. | nothing |
+| [TUI Hello World](showcase/tui-hello-world/) | A one-shot timer prints an 
invitation; the route direct:greet then logs and greets any message sent to it 
from the CLI (camel cmd send --endpoint=direct:greet --body=...) or from a tool 
that can send messages. | nothing |
+<!-- examples:end -->
+
+The directories `security`, `transformation` and the rest of `cloud` hold 
larger examples outside the ladder,
+kept as reference: post-quantum cryptography, OCSF, an LDAP to Keycloak 
migration, EDI over AS2, IBM Cloud
+Object Storage and Kubernetes. Each has its own README.
+
+## Run an example
+
+Every README says the same three things. Go into the directory and run it:
 
 ```shell
-jbang app install camel@apache/camel
+cd route/content-based-router
+camel run *
 ```
 
-Which allows to run Camel with `camel` as shown below.
+When an example needs a service, start it first, in another terminal; the CLI 
starts it in a container and
+prints the connection details, which match the example's 
`application.properties`:
 
 ```shell
-camel --version
+camel infra run postgres
 ```
 
-## Integration testing
+Send a message to a running route from a third terminal, when you want to poke 
at it:
 
-The examples provide automated integration tests in the 
[Citrus](https://citrusframework.org/) YAML DSL, which the Camel CLI runs (the 
test plugin installs on first use).
+```shell
+camel cmd send --endpoint=direct:greet --body="Hello"
+```
+
+Stop a route with `ctrl` + `c` and a service with `camel infra stop postgres`.
 
-Usually the Citrus tests are written in YAML files and named accordingly to 
the Camel CLI route source file.
+## Test an example
 
-For instance the Camel route `mqtt.camel.yaml` route provides a test named 
`mqtt.citrus.it.yaml`.
-You can run the test with the Camel CLI like this:
+The test of an example lives in its `test/` directory, in the Citrus YAML DSL, 
named after the route file:
+`route/aggregator/test/aggregator.citrus.it.yaml`. The Camel CLI runs it, and 
the test starts the route, and the
+service if it needs one, itself:
 
 ```shell
-camel test run test/mqtt.citrus.it.yaml
+camel test run test/aggregator.citrus.it.yaml
 ```
 
-Usually the test prepares the complete infrastructure (e.g. via Docker 
compose) and starts the Camel route automatically.
-Of course the test also performs some validation steps to make sure that the 
Camel route works as expected.
+The test plugin installs on first use. The `build.yml` workflow runs every 
test on every pull request; the few
+examples marked `ciSkip` in their metadata, the ones that need a language 
model, are run by hand.
+
+## Add an example
+
+`AGENTS.md` has the conventions and a checklist, written for people and for 
coding agents alike. In short: one
+directory per example, the route in canonical YAML DSL, a README in the shape 
above, a `metadata.json` with the
+group as `level` and a `teaches` block, a Citrus YAML test, an entry in the CI 
matrix, and
+`./generate-catalog.sh` to refresh the catalog and the tables above.
 
-## Other Examples
+## Other examples
 
-You can also find a set of various Camel CLI examples at: 
https://github.com/apache/camel-kamelets-examples/tree/main/jbang
+More Camel CLI examples, built on Kamelets, are at
+https://github.com/apache/camel-kamelets-examples/tree/main/jbang
 
 ## Help and contributions
 
diff --git a/camel-jbang-example-catalog.json b/camel-jbang-example-catalog.json
index fd9e530..87b8eb0 100644
--- a/camel-jbang-example-catalog.json
+++ b/camel-jbang-example-catalog.json
@@ -86,7 +86,8 @@
         ],
         "infraServices": [
             "ollama"
-        ]
+        ],
+        "ciSkip": true
     },
     {
         "name": "ai/mcp-server",
diff --git a/generate-catalog.sh b/generate-catalog.sh
index 915ddb2..5a3b9c9 100755
--- a/generate-catalog.sh
+++ b/generate-catalog.sh
@@ -116,6 +116,8 @@ for dirpath, dirnames, filenames in 
sorted(os.walk(repo_root)):
     }
     if "infraServices" in meta:
         entry["infraServices"] = meta["infraServices"]
+    if meta.get("ciSkip", False):
+        entry["ciSkip"] = True
     catalog.append(entry)
 
 with open(catalog_file, "w") as f:
@@ -123,4 +125,54 @@ with open(catalog_file, "w") as f:
     f.write("\n")
 
 print(f"Generated {catalog_file} with {len(catalog)} examples")
+
+# render the example tables of README.md, between the markers, one table per 
group in ladder order
+GROUPS = [
+    ("quick-start", "Quick start", "The first ten minutes: generic examples 
with no story and no service, each running in seconds."),
+    ("run", "Run", "Running Camel: timers and cron schedules, a bean in a 
route, properties and profiles."),
+    ("transform", "Transform and map", "JSON, XML and CSV in and out, 
field-by-field mapping, Groovy and XSLT."),
+    ("route", "Route", "The routing patterns: content-based router, splitter, 
aggregator, filter and multicast."),
+    ("fail-well", "Fail well", "Retries, a dead letter channel, and a circuit 
breaker in front of a flaky service."),
+    ("connect", "Connect without a service", "Files, an HTTP client and a REST 
server; everything runs inside the example."),
+    ("connect-service", "Connect to one service", "SQL, JMS, MQTT, Kafka and 
FTP against a service the Camel CLI starts for you with `camel infra run`."),
+    ("contracts", "Contracts and security", "An OpenAPI contract served and 
called, and an API protected by Keycloak."),
+    ("ai", "AI", "A local model writing text, routes exposed as MCP tools, RAG 
over documents, PII redaction."),
+    ("cloud", "Cloud", "A cloud service, run locally through LocalStack and 
switched to the real thing by properties."),
+    ("showcase", "Showcase", "Tooling demos outside the ladder: the TUI, a 
memory leak, message sizes, log analysis."),
+]
+by_level = {}
+for e in catalog:
+    by_level.setdefault(e["level"], []).append(e)
+
+def needs(e):
+    parts = []
+    if e.get("infraServices"):
+        parts.append("`camel infra run " + " ".join(e["infraServices"]) + "`")
+    if e.get("ciSkip"):
+        parts.append("a local model")
+    return ", ".join(parts) if parts else "nothing"
+
+lines = []
+for level, title, intro in GROUPS:
+    entries = sorted(by_level.get(level, []), key=lambda x: x["name"])
+    if not entries:
+        continue
+    lines.append(f"### {title}")
+    lines.append("")
+    lines.append(intro)
+    lines.append("")
+    lines.append("| Example | What you will see | Needs |")
+    lines.append("|---|---|---|")
+    for e in entries:
+        lines.append(f"| [{e['title']}]({e['name']}/) | {e['description']} | 
{needs(e)} |")
+    lines.append("")
+readme_path = os.path.join(repo_root, "README.md")
+readme = open(readme_path).read()
+start, end = "<!-- examples:start -->", "<!-- examples:end -->"
+if start in readme and end in readme:
+    a = readme.index(start) + len(start)
+    b = readme.index(end)
+    readme = readme[:a] + "\n" + "\n".join(lines) + readme[b:]
+    open(readme_path, "w").write(readme)
+    print(f"Updated the example tables in {readme_path}")
 PYEOF

Reply via email to