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 fb6f31c CAMEL-24808: cleanup after the ladder: quick-start on the
ladder, off-shape READMEs reshaped, two bugs in the docling example (#93)
fb6f31c is described below
commit fb6f31cbaa40e9f928be54835efc4ca6620cc91d
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Sep 28 07:20:54 2026 +0200
CAMEL-24808: cleanup after the ladder: quick-start on the ladder, off-shape
READMEs reshaped, two bugs in the docling example (#93)
- The empty "Install Camel CLI" heading in 29 READMEs, a comment pointing
at install.adoc, is now a one-line link to the root README; install.adoc is
gone.
- generate-catalog.sh: the group READMEs only promise a test where every
example has one; the Needs column takes a free-text "needs" from metadata.json
instead of mapping ciSkip to "a local model". PII redaction needs an
OpenAI-compatible API, docling a local model; both are ciSkip.
- AGENTS.md describes the ladder instead of the old flat layout, the README
shape, and the checklist with generate-catalog.sh; CI runs Temurin 21.
- quick-start: the four examples get the ladder-shaped README, a Citrus
test each, and a place in the CI matrix; routes/ calls a plain POJO bean
instead of a Processor.
- camel-1-tribute, keycloak-security-rest, openai-pii-redaction and
docling-langchain4j-rag READMEs reshaped; the PII one had output pasted in from
the Watson example, its systemMessage is a folded block so camel validate is
clean.
- docling-langchain4j-rag: the chatModel bean, dropped by 60a70d8, is back
so the example starts again; the batch timer gets a period instead of firing
every second; documents/ and output/ are ignored.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
---
.github/workflows/build.yml | 8 +
.gitignore | 3 +
AGENTS.md | 48 +-
README.md | 6 +-
ai/README.md | 6 +-
ai/docling-langchain4j-rag/README.md | 642 +++------------------
ai/docling-langchain4j-rag/application.properties | 3 +-
.../docling-langchain4j-rag.yaml | 17 +-
ai/docling-langchain4j-rag/metadata.json | 6 +-
ai/langchain4j-chat/README.md | 3 +-
ai/langchain4j-chat/metadata.json | 3 +-
ai/mcp-server/README.md | 3 +-
ai/openai-pii-redaction/README.md | 110 ++--
ai/openai-pii-redaction/metadata.json | 4 +-
ai/openai-pii-redaction/pii-redaction.camel.yaml | 11 +-
camel-jbang-example-catalog.json | 18 +-
cloud/README.md | 2 +-
cloud/aws-sqs/README.md | 3 +-
connect-service/README.md | 2 +-
connect-service/artemis/README.md | 3 +-
connect-service/camel-1-tribute/README.md | 125 ++--
connect-service/ftp/README.md | 3 +-
connect-service/kafka-orders/README.md | 3 +-
connect-service/mqtt/README.md | 3 +-
connect-service/sql/README.md | 3 +-
connect/README.md | 2 +-
connect/file-processing/README.md | 3 +-
connect/http-client/README.md | 3 +-
connect/stock-api/README.md | 3 +-
contracts/README.md | 2 +-
contracts/keycloak-security-rest/README.md | 392 +++----------
contracts/openapi-client/README.md | 3 +-
contracts/openapi-server/README.md | 3 +-
fail-well/README.md | 2 +-
fail-well/circuit-breaker/README.md | 3 +-
fail-well/error-handling/README.md | 3 +-
generate-catalog.sh | 12 +-
install.adoc | 24 -
quick-start/README.md | 2 +-
quick-start/rest-api/README.md | 75 ++-
quick-start/rest-api/application.properties | 17 +
quick-start/rest-api/test/rest-api.citrus.it.yaml | 40 ++
quick-start/routes/Greeter.java | 40 +-
quick-start/routes/README.md | 85 +--
quick-start/routes/application.properties | 23 +-
quick-start/routes/beans.yaml | 8 +-
quick-start/routes/routes.camel.yaml | 12 +-
quick-start/routes/test/routes.citrus.it.yaml | 19 +
quick-start/splitter/README.md | 74 ++-
quick-start/splitter/test/splitter.citrus.it.yaml | 19 +
quick-start/timer-log/README.md | 70 ++-
quick-start/timer-log/application.properties | 17 +
.../timer-log/test/timer-log.citrus.it.yaml | 16 +
route/README.md | 2 +-
route/aggregator/README.md | 3 +-
route/content-based-router/README.md | 3 +-
route/filter-and-multicast/README.md | 3 +-
route/order-lines/README.md | 3 +-
run/README.md | 2 +-
run/nightly-report/README.md | 3 +-
run/order-generator/README.md | 3 +-
run/properties-and-profiles/README.md | 3 +-
showcase/README.md | 2 +-
transform/README.md | 2 +-
transform/csv-to-json/README.md | 3 +-
transform/data-mapping/README.md | 3 +-
transform/groovy/README.md | 3 +-
transform/json-transform/README.md | 3 +-
transform/xml-to-json/README.md | 3 +-
transform/xslt/README.md | 3 +-
70 files changed, 904 insertions(+), 1153 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 37bea03..05cc0cd 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -28,6 +28,14 @@ jobs:
strategy:
matrix:
include:
+ - name: Timer log
+ path: quick-start/timer-log
+ - name: Routes
+ path: quick-start/routes
+ - name: Splitter
+ path: quick-start/splitter
+ - name: REST API
+ path: quick-start/rest-api
- name: SQL database
path: connect-service/sql
- name: MQTT sensors
diff --git a/.gitignore b/.gitignore
index aaa583b..8bb9c74 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,3 +13,6 @@ parked/
# written by the file-processing example at runtime
inbox/
archive/
+# written by the docling-langchain4j-rag example at runtime
+documents/
+output/
diff --git a/AGENTS.md b/AGENTS.md
index 4856b83..38aae00 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -15,7 +15,7 @@ essentials and adds what is specific to this examples
repository.
## Project Info
- Run with: Camel CLI (`jbang app install camel@apache/camel`) + JBang
-- Java: 17+ (CI uses Temurin 17)
+- Java: 17+ (CI uses Temurin 21)
- Tests: [Citrus](https://citrusframework.org/) YAML tests
- JIRA project: `CAMEL` (https://issues.apache.org/jira/projects/CAMEL)
- Merge strategy (`.asf.yaml`): squash or rebase; protected `main`
@@ -38,11 +38,23 @@ essentials and adds what is specific to this examples
repository.
## Repository structure
-- One example per top-level directory, lowercase and hyphenated
- (e.g. `mqtt/`, `timer-log/`, `ftp/`). Related examples may be grouped under a
- category directory (e.g. `aws/aws-sqs/`, `openapi/server/`).
+The examples form a ladder: one directory per group, one directory per example
+inside it, both lowercase and hyphenated (e.g. `route/aggregator/`,
+`connect-service/mqtt/`). The groups, in reading order, are `quick-start`, then
+the rungs `run`, `transform`, `route`, `fail-well`, `connect`,
`connect-service`,
+`contracts`, `ai`, `cloud`, and `showcase` for tooling demos outside the
ladder.
+The `README.md` at the root explains the ladder; `generate-catalog.sh` holds
the
+group order and intros.
+
+- From the `run` rung onwards the examples share one fictional web shop and the
+ order shape that `run/order-generator` defines; a new example on the ladder
+ reuses that story and that JSON rather than inventing its own domain.
- Each example carries a `metadata.json` that feeds the generated
- `camel-jbang-example-catalog.json`. Do not hand-edit the catalog.
+ `camel-jbang-example-catalog.json` and the example tables in the root and
group
+ READMEs. Do not hand-edit the catalog or those tables; run
+ `./generate-catalog.sh` after adding or changing a `metadata.json`.
+- `security/`, `transformation/` and the non-ladder examples in `cloud/` are
+ larger reference examples marked `"exclude": true`; they are not on the
ladder.
## Anatomy of an example
@@ -51,10 +63,10 @@ essentials and adds what is specific to this examples
repository.
| `README.md` | What it does, how to run, expected output, how to test |
| `<name>.camel.yaml` | The route(s) in Camel YAML DSL |
| `application.properties` | Runtime properties (ASF license header required) |
-| `metadata.json` | Catalog entry: `name`, `title`, `description` (the
behaviour you observe when it runs), `order` (its place in the group, the
reading order the group page and the listings use), `level` (the group:
`quick-start`, then the ladder rungs `run`, `transform`, `route`, `fail-well`,
`connect`, `connect-service`, `contracts`, `ai`, `cloud`, or `showcase` for
tooling demos), `teaches` (the `components`, `eips`, `languages` and
`dataformats` it introduces), `tags`, `infraService [...]
+| `metadata.json` | Catalog entry: `title`, `description` (the behaviour you
observe when it runs), `level` (the group), `order` (its place in the group,
the reading order the group page and the listings use), `teaches` (the
`components`, `eips`, `languages` and `dataformats` it introduces), `tags`,
`infraServices` (what `camel infra run` must start), `needs` (anything else the
*Needs* column should say, e.g. `a local model`), `ciSkip` (the test cannot run
in CI) |
| `compose.yaml` | Optional Docker Compose for required infra |
| `beans.yaml` / `*.java` | Optional beans/processors (package
`camel.example.*`) |
-| `test/<name>.citrus.it.yaml` | Optional Citrus integration test |
+| `test/<name>.citrus.it.yaml` | The Citrus integration test, run by CI |
## Build, run and validate
@@ -91,19 +103,27 @@ example. If your example ships a `test/`, add it to that
workflow.
`log: "..."`, `- simple:` in a `when` item) is deprecated and `camel run`
warns
about it. Check with `camel validate yaml --canonical <file>` (Camel 4.23+);
`camel validate normalize` rewrites a file but drops its comments.
-- **README**: follow the existing examples — title, description, install CLI,
- start infra, how to run, stop/cleanup, integration testing, community footer.
+- **README**: follow the existing examples, `run/order-generator/README.md` is
+ the reference. Title and a two-line description, then these sections in this
+ order: *What you will see* (the literal log output), *Install Camel CLI* (the
+ one-line link to the root README), *Run it* (including how to start the
+ service, if any, and how to stop), *How it works* (one bullet per file),
+ *Build it step by step* (numbered prompts a reader or an assistant can
follow,
+ running after each), *Try changing*, *Integration testing*, and the
+ *Help and contributions* footer.
## Adding a new example (checklist)
-1. Create `<name>/` (or `<category>/<name>/`).
+1. Pick the group (rung) it belongs to and create `<group>/<name>/`.
2. Add `README.md`, `<name>.camel.yaml`, `application.properties` (with
header),
- and a correct `metadata.json`.
-3. Add `compose.yaml` if infra is required; add `test/` Citrus tests where it
- makes sense and wire them into `.github/workflows/build.yml`.
+ and a `metadata.json` with `level`, `order` and `teaches`.
+3. Add `test/<name>.citrus.it.yaml` and wire it into
+ `.github/workflows/build.yml`; the test starts the route, and the service if
+ it needs one, itself. Set `ciSkip` only when the test truly cannot run in
CI.
4. Run it locally with `camel run` and verify the README's expected output;
`camel validate yaml --canonical` must report nothing.
-5. Open the PR from your fork, link the JIRA ticket, and request review from
+5. Run `./generate-catalog.sh` to refresh the catalog and the README tables.
+6. Open the PR from your fork, link the JIRA ticket, and request review from
active committers.
## Links
diff --git a/README.md b/README.md
index df4aadf..28a194f 100644
--- a/README.md
+++ b/README.md
@@ -124,8 +124,8 @@ A local model writing text, routes exposed as MCP tools,
RAG over documents, PII
|---|---|---|
| [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 |
-| [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` |
+| [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. | an OpenAI-compatible API |
+| [Document Analysis with Docling and LangChain4j
RAG](ai/docling-langchain4j-rag/) | Documents dropped in a directory are
converted to Markdown by a Docling service, analysed by a local Ollama model
through langchain4j-chat, and written as a report to an output directory; an
HTTP endpoint answers questions against the latest document. | `camel infra run
docling ollama`, a local model |
### [Cloud](cloud/)
@@ -186,7 +186,7 @@ camel test run test/aggregator.citrus.it.yaml
```
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.
+examples marked `ciSkip` in their metadata, the ones that need a language
model or an API key, are run by hand.
## Add an example
diff --git a/ai/README.md b/ai/README.md
index 59f7969..57614fb 100644
--- a/ai/README.md
+++ b/ai/README.md
@@ -7,10 +7,10 @@ A local model writing text, routes exposed as MCP tools, RAG
over documents, PII
|---|---|---|
| [LangChain4j chat](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](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](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 |
-| [Document Analysis with Docling and LangChain4j
RAG](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` |
+| [OpenAI PII Redaction](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. | an OpenAI-compatible API |
+| [Document Analysis with Docling and LangChain4j
RAG](docling-langchain4j-rag/) | Documents dropped in a directory are converted
to Markdown by a Docling service, analysed by a local Ollama model through
langchain4j-chat, and written as a report to an output directory; an HTTP
endpoint answers questions against the latest document. | `camel infra run
docling ollama`, a local model |
Start with [LangChain4j chat](langchain4j-chat/); the examples read best in
the order above, each one building on what the one before it set up.
-Every example has a README that says what you will see, how it works, how to
build it step by step, what to try changing, and how to run its test with
`camel test run`.
+Every example has a README that says what you will see, how it works, how to
build it step by step and what to try changing; the ones with a `test/`
directory also say how to run their test with `camel test run`.
<!-- group:end -->
diff --git a/ai/docling-langchain4j-rag/README.md
b/ai/docling-langchain4j-rag/README.md
index 28b1915..ef5b184 100644
--- a/ai/docling-langchain4j-rag/README.md
+++ b/ai/docling-langchain4j-rag/README.md
@@ -1,573 +1,113 @@
# Document Analysis with Docling and LangChain4j RAG
-This example demonstrates a complete RAG (Retrieval Augmented Generation)
workflow using Apache Camel, combining:
+Documents dropped in a directory are converted to Markdown by a Docling
service, analysed by a local Ollama
+model through `langchain4j-chat`, and written as a report to an output
directory; an HTTP endpoint answers
+questions against the latest document. Both services are started with `camel
infra`.
-* **Docling** - AI-powered document conversion (PDF, Word, PowerPoint ->
Markdown/JSON)
-* **LangChain4j** - Integration with Large Language Models
-* **Ollama** - Local LLM inference
-
-## Overview
-
-This application provides intelligent document processing capabilities:
-
-* **Automatic Document Conversion** - Convert various document formats to
Markdown using Docling
-* **AI-Powered Analysis** - Analyze documents using LLMs via LangChain4j
-* **Interactive Q&A** - Ask questions about your documents through REST API
-* **Batch Processing** - Summarize multiple documents automatically
-* **Structured Data Extraction** - Extract tables and structured information
from documents
-
-## Architecture
-
-### Components
+## What you will see
```text
-Documents -> Docling (Convert) -> Markdown -> LangChain4j -> Ollama (LLM) ->
Analysis
-```
-
-**Docling-Serve**: Python-based document conversion service running in Docker
-
-**Ollama**: Local LLM server running models like Llama 3.2
-
-**Camel Routes**: Orchestrate the workflow between components
-
-### Features
-
-* **Document Format Support**: PDF, DOCX, PPTX, HTML, Markdown
-* **Multiple Operations**: Analysis, Q&A, Summarization, Data Extraction
-* **Docker-based**: All services run in containers
-* **REST API**: HTTP endpoints for interaction
-* **Automatic Processing**: File watcher for automatic document processing
-
-## Prerequisites
-
-* JBang installed (https://www.jbang.dev)
-* Java 11 or later
-* Docker and Docker Compose
-
-## Project Structure
-
-```text
-docling-langchain4j-rag/
-├── docling-langchain4j-rag.yaml # Main YAML configuration
-├── application.properties # Configuration settings
-├── sample.md # Sample document (copy to documents/ for
testing)
-├── README.md # This file
-├── documents/ # Input directory (files auto-deleted
after processing)
-└── output/ # Analysis reports output
-```
-
-## Setup
-
-### Step 1: Start Required Services
-
-The Camel CLI starts both services in containers (Docker or Podman must be
running):
-
-```sh
-$ camel infra run docling ollama
-```
-
-Docling serves on http://localhost:5001 and Ollama on http://localhost:11434,
where the container pulls the
-`granite4:3b` model on first start; both match `application.properties`. Stop
them later with
-`camel infra stop docling` and `camel infra stop ollama`.
-
-### Step 2: Create Required Directories
-
-The `documents/` and `output/` directories will be created automatically when
needed, but you can create them manually:
-
-```sh
-$ mkdir -p documents output
-```
-
-> **Note:** Files placed in `documents/` will be automatically processed and
then **deleted** after analysis is complete.
-
-### Step 3: Run the Camel Application
-
-```sh
-$ camel run *
- --dep=camel:docling \
- --dep=camel:langchain4j-chat \
- --dep=camel:platform-http \
- --dep=dev.langchain4j:langchain4j:1.6.0 \
- --dep=dev.langchain4j:langchain4j-ollama:1.6.0 \
- --properties=application.properties \
- docling-langchain4j-rag.yaml
-```
-
-The application will start and listen on port 8080.
-
-## Usage
-
-### 1. Automatic Document Analysis
-
-Copy a document to the `documents/` directory for processing:
-
-```sh
-# Using the provided sample
$ cp sample.md documents/
-# Or use your own document
-$ cp /path/to/your/document.pdf documents/
-```
-
-The system will:
-
-1. Detect the new file
-2. Convert it to Markdown using Docling
-3. Analyze it with the LLM
-4. Generate a comprehensive analysis report in `output/`
-5. **Automatically delete the source file** from `documents/` after processing
-
-**Example Output** (`output/sample.md_analysis.md`):
-
-```markdown
-# Document Analysis Report
-
-**File:** document.pdf
-**Date:** 2025-10-14 12:30:45
-
----
-
-## AI Analysis
-
-**Summary:** This document discusses the implementation of RAG systems...
-
-**Key Topics:**
-- Document processing pipelines
-- LLM integration patterns
-- Vector embeddings and similarity search
-
-**Important Findings:**
-- RAG improves LLM accuracy by 40%
-- Hybrid search outperforms pure vector search
-...
-
----
-
-## Full Document Content (Markdown)
-
-[Full converted markdown content here]
-```
-
-### 2. Interactive Q&A
-
-Ask questions about your documents via HTTP API:
-
-```sh
-$ curl -X POST http://localhost:8080/api/ask \
- -H "Content-Type: text/plain" \
- -d "What are the main topics discussed in the document?"
-```
-
-**Response:**
-
-```text
-The document discusses three main topics:
-1. RAG (Retrieval Augmented Generation) architecture
-2. Document processing with Docling
-3. Integration with LangChain4j for LLM orchestration
-```
-
-### 3. Structured Data Extraction
-
-Extract tables and structured data:
-
-```sh
-$ curl -X POST http://localhost:8080/api/extract \
- -H "Content-Type: application/octet-stream" \
- --data-binary "@documents/report.pdf"
+INFO ... docling-langchain4j-rag.yaml:27 : Processing document: sample.md
+INFO ... docling-langchain4j-rag.yaml:38 : Converting document to Markdown
with Docling...
+INFO ... docling-langchain4j-rag.yaml:47 : Document converted to Markdown
successfully
+INFO ... docling-langchain4j-rag.yaml:77 : Analyzing document with AI model...
+INFO ... docling-langchain4j-rag.yaml:89 : AI analysis completed
+INFO ... docling-langchain4j-rag.yaml:120 : Analysis report saved:
sample.md_analysis.md
+INFO ... docling-langchain4j-rag.yaml:137 : Processing complete for: sample.md
```
-**Response:**
+and `output/sample.md_analysis.md` holds the report: the file name and date,
the model's summary, key topics
+and findings, then the full document as Markdown. The wording of the analysis
differs from run to run; the
+first one also takes a while, because the model is loaded.
```text
-**Document Type:** Financial Report
-
-**Key Data Fields:**
-- Revenue: $1.2M (Table 1, Row 3)
-- Expenses: $800K (Table 1, Row 5)
-- Net Profit: $400K (calculated)
-
-**Tables Identified:**
-1. Quarterly Financial Summary (5 rows, 4 columns)
-2. Department Breakdown (8 rows, 3 columns)
-...
-```
-
-### 4. Health Check
-
-Check system status:
-
-```sh
-$ curl http://localhost:8080/api/health
-```
-
-**Response:**
-
-```json
-{
- "status": "healthy",
- "components": {
- "docling": {
- "url": "http://localhost:5001",
- "status": "configured"
- },
- "ollama": {
- "url": "http://localhost:11434",
- "model": "llama3.2",
- "status": "configured"
- }
- },
- "directories": {
- "documents": "documents",
- "output": "output"
- }
-}
-```
-
-## Configuration
-
-### application.properties
-
-```properties
-# Directories
-documents.directory=documents
-output.directory=output
-
-# Docling-Serve URL
-docling.serve.url=http://localhost:5001
-
-# Ollama Configuration
-ollama.base.url=http://localhost:11434
-ollama.model.name=llama3.2
-
-# Server Port
-camel.server.port=8080
-```
-
-### Using Different Ollama Models
-
-Available models:
-
-* **llama3.2** (default) - Latest Llama model, good balance of speed and
quality
-* **llama3.2:1b** - Smaller, faster model
-* **mistral** - Alternative high-quality model
-* **phi3** - Microsoft's efficient model
-* **gemma2** - Google's Gemma model
-
-To use a different model:
-
-1. Pull the model:
-
-```sh
-$ docker exec -it ollama ollama pull mistral
-```
-
-2. Update `application.properties`:
-
-```properties
-ollama.model.name=mistral
-```
-
-3. Restart the Camel application
-
-### Using Remote Ollama Instance
-
-To use Ollama running on a different machine:
-
-```properties
-ollama.base.url=http://remote-server:11434
-```
-
-## Routes Explanation
-
-### Route 1: document-analysis-workflow
-
-**Trigger:** New file in `documents/` directory
-
-**Flow:**
-
-1. Detect new document
-2. Convert to Markdown via Docling
-3. Send to LLM for analysis
-4. Generate comprehensive report
-5. Save to `output/` directory
-
-**Supported Formats:** PDF, DOCX, PPTX, HTML, MD
-
-### Route 2: document-qa-api
-
-**Endpoint:** `POST /api/ask`
-
-**Description:** Answer questions about the most recent document
-
-**Input:** Plain text question
-
-**Output:** AI-generated answer based on document content
-
-### Route 3: batch-summarization
-
-**Trigger:** Timer (configurable)
-
-**Description:** Process all documents in batch and generate summaries
-
-**Configuration:** Set `batch.delay` in application.properties (default:
disabled)
-
-### Route 4: health-check
-
-**Endpoint:** `GET /api/health`
-
-**Description:** System health and configuration status
-
-### Route 5: extract-structured-data
-
-**Endpoint:** `POST /api/extract`
-
-**Description:** Extract tables and structured data from uploaded documents
-
-**Input:** Binary document data
-
-**Output:** AI analysis of extracted structured data
-
-## Advanced Usage
-
-### Batch Processing
-
-Enable automatic batch summarization:
-
-```properties
-# Run every 1 hour (3600000 ms)
-batch.delay=3600000
-```
-
-All documents in the `documents/` directory will be summarized periodically.
-
-### Custom Document Processing
-
-You can extend the routes to add custom processing logic:
-
-```yaml
-- route:
- id: custom-processing
- from:
- uri: file:documents
- parameters:
- include: ".*\\.pdf"
- steps:
- # Your custom processing here
- - to: docling:CONVERT_TO_HTML
- - to: langchain4j-chat:custom
-```
-
-### Integration with Vector Stores
-
-For production RAG, consider adding vector embeddings:
-
-```yaml
-# Add after document conversion
-- to: langchain4j-embeddings:embed
-- to: your-vector-store
-```
-
-## Troubleshooting
-
-### Docling Not Responding
-
-**Check Docling service:**
-
-```sh
-$ docker logs docling-serve
-$ curl http://localhost:5001/
-```
-
-**Restart service:**
-
-```sh
-$ docker restart docling-serve
-```
-
-### Ollama Model Not Found
-
-**Pull the model:**
-
-```sh
-$ docker exec -it ollama ollama pull llama3.2
-```
-
-**Check available models:**
-
-```sh
-$ docker exec -it ollama ollama list
-```
-
-### Slow Document Processing
-
-**Causes:**
-
-* Large documents (>100 pages)
-* Complex layouts with many images
-* Limited CPU/memory
-
-**Solutions:**
-
-* Increase timeout in `application.properties`:
-
-```properties
-ollama.timeout=300
-```
-
-* Use a smaller/faster model (llama3.2:1b)
-* Process smaller documents first
-
-### Out of Memory
-
-**Increase Docker memory:**
-
-```sh
-# In Docker Desktop: Settings -> Resources -> Memory
-# Recommended: 8GB or more for LLMs
-```
-
-## Performance Considerations
-
-### Document Conversion
-
-* **PDF**: 1-5 seconds per page (depends on complexity)
-* **DOCX**: 0.5-2 seconds per page
-* **OCR-required**: 5-10 seconds per page (scanned PDFs)
-
-### LLM Inference
-
-* **llama3.2 (3B)**: 5-15 seconds per response
-* **llama3.2:1b**: 2-5 seconds per response
-* **Speed depends on**: Prompt length, context size, hardware
-
-### Recommended Hardware
-
-* **Minimum**: 8GB RAM, 4 CPU cores
-* **Recommended**: 16GB RAM, 8 CPU cores, GPU (optional)
-
-## Security Considerations
-
-### Current Implementation
-
-* **Development Setup** - Not production-ready
-* **No Authentication** - Open HTTP endpoints
-* **Local Processing** - Data stays on your machine
-
-### Production Recommendations
-
-**1. Authentication & Authorization**
-
-```yaml
-# Add to routes
-- setHeader:
- name: Authorization
- expression:
- constant:
- expression: "Bearer ${env:API_TOKEN}"
-```
-
-**2. Input Validation**
-
-* Validate file sizes
-* Check file types
-* Scan for malware
-
-**3. Rate Limiting**
-
-* Implement request throttling
-* Add queue management
-
-**4. Data Privacy**
-
-* Encrypt sensitive documents
-* Secure API endpoints with TLS
-* Implement access logging
-
-## Production Deployment
-
-### Using Kubernetes
-
-```yaml
-# See k8s-deployment.yaml (example)
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: docling-langchain4j-rag
-spec:
- replicas: 3
- ...
+$ curl -X POST localhost:8080/api/ask -H "Content-Type: text/plain" -d "What
DSLs does Camel support?"
+The document lists four DSLs: Java, XML, YAML and Groovy.
```
-### Scaling Considerations
+## Install Camel CLI
-* **Horizontal**: Multiple Camel instances with load balancer
-* **Vertical**: Increase memory/CPU for Ollama container
-* **Caching**: Cache frequent document conversions
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
-## Cleanup
+## Run it
-Stop all services:
+The example needs a running Docling and a running Ollama, which the Camel CLI
starts for you in containers
+(Docker or Podman must be running). In one terminal:
-```sh
-# Docker Compose
-$ camel infra stop docling
-$ camel infra stop ollama
-
-# Or manual cleanup
-$ docker stop docling-serve ollama
-$ docker rm docling-serve ollama
-```
-
-Remove volumes (optional):
-
-```sh
-$ docker volume rm docling-langchain4j-rag_ollama_data
+```shell
+camel infra run docling ollama
```
-## Alternative Configurations
-
-### Using OpenAI Instead of Ollama
-
-```properties
-# application.properties
-openai.api.key=sk-your-api-key-here
-```
-
-```yaml
-# Update bean configuration
-- name: chatModel
- type: dev.langchain4j.model.chat.ChatLanguageModel
- scriptLanguage: groovy
- script: |
- import dev.langchain4j.model.openai.OpenAiChatModel
-
- return OpenAiChatModel.builder()
- .apiKey(context.resolvePropertyPlaceholders("{{openai.api.key}}"))
- .modelName("gpt-4")
- .temperature(0.3)
- .build()
-```
-
-### Using Cloud Docling Service
-
-If you have a cloud-hosted Docling service:
-
-```properties
-docling.serve.url=https://your-docling-service.com
-docling.auth.token=your-auth-token
-```
-
-## References
-
-* **Docling**: https://github.com/DS4SD/docling
-* **LangChain4j**: https://github.com/langchain4j/langchain4j
-* **Ollama**: https://ollama.ai
-* **Apache Camel**: https://camel.apache.org
-* **Camel Docling Component**:
/home/oscerd/workspace/apache-camel/camel/components/camel-ai/camel-docling/
-* **Camel LangChain4j Components**:
/home/oscerd/workspace/apache-camel/camel/components/camel-ai/
-
-## Help and Contributions
+Docling serves on http://localhost:5001 and Ollama on http://localhost:11434,
where the container pulls the
+`granite4:3b` model on first start, a download of a couple of gigabytes; both
match `application.properties`.
+In another terminal:
+
+```shell
+camel run *
+```
+
+Then drop a document in the `documents` directory, the sample or one of your
own (PDF, Word, PowerPoint,
+HTML or Markdown), and watch the log; the report lands in `output` and the
source file is deleted once it is
+processed. Ask about the latest document with the `curl` above.
+
+Stop the example with `ctrl` + `c` and the services with `camel infra stop
docling ollama`.
+
+## How it works
+
+- `docling-langchain4j-rag.yaml` starts with the bean `chatModel`, an
`OllamaChatModel` built through its
+ LangChain4j builder from the URL and model name in `application.properties`;
the `langchain4j-chat`
+ component picks it up as the one chat model in the registry.
+- `document-analysis-workflow` is the main route: a `file` consumer on
`documents` with `include` for the
+ supported extensions. The body becomes the file's absolute path, the
`docling` endpoint with
+ `CONVERT_TO_MARKDOWN` sends it to the Docling service and returns the
Markdown, which is kept in an
+ exchange property. A `setBody` builds the prompt around it,
`langchain4j-chat` sends it to the model, a
+ Groovy `script` assembles the report from the answer and the Markdown, and a
`file` producer writes it to
+ `output` under the source name plus `_analysis.md`. A last script deletes
the source file.
+- `document-qa-api` is `platform-http` on `POST /api/ask`: a script finds the
newest file in `documents`,
+ Docling converts it, and the question and the Markdown go to the model in
one prompt. That is retrieval
+ augmented generation in its simplest form, the whole document as context;
with no document the route
+ answers an error text.
+- `batch-summarization` is a `timer` route, first after `batch.delay` and then
every `batch.period`, that
+ converts and summarises every file in `documents` in a `split`, and logs
each summary.
+- `health-check` on `GET /api/health` answers the configuration as JSON, and
`extract-structured-data` on
+ `POST /api/extract` takes a document in the request body, asks Docling for
its structured data with
+ `EXTRACT_STRUCTURED_DATA` and asks the model to describe the tables and
fields in it.
+- `application.properties` holds the directories, the two service URLs, the
model name, the batch timing and
+ the HTTP port.
+
+## Build it step by step
+
+Ask your assistant, or type it yourself, one step at a time, and run after
each, with the two services running:
+
+1. A route from `file:documents` that logs the file name.
+2. Set the body to the file's absolute path and send it to `docling` with
`CONVERT_TO_MARKDOWN` against the
+ Docling URL; log the Markdown.
+3. The `chatModel` bean from properties, a prompt around the Markdown, and a
`langchain4j-chat` step; log the
+ answer.
+4. Write the answer and the Markdown to `output` as `<name>_analysis.md` with
a `file` producer.
+5. A `platform-http` route on `/api/ask` that converts the newest document and
asks the model the question in
+ the request body.
+
+## Try changing
+
+- `ollama.model.name=llama3.2` after `docker exec -it ollama ollama pull
llama3.2`, or any other model Ollama
+ serves, and compare the analyses.
+- Change the prompt in `document-analysis-workflow` to ask for the summary in
your language, or as five
+ bullet points.
+- `batch.period=60000` and drop three documents in `documents` to see the
batch route summarise them
+ together every minute; the analysis route deletes them after its own run, so
be quick.
+- For real retrieval, cut the Markdown into chunks with
`langchain4j-tokenizer`, embed them with
+ `langchain4j-embeddings` into a vector store, and put only the chunks
nearest the question in the prompt.
+
+## Integration testing
+
+The example has no Citrus test: it needs the Docling service and a language
model, which take minutes to
+pull and load on a first run. Verify it with the steps under *Run it*.
+
+## Help and contributions
If you hit any problem using Camel or have some feedback, then please
[let us know](https://camel.apache.org/community/support/).
diff --git a/ai/docling-langchain4j-rag/application.properties
b/ai/docling-langchain4j-rag/application.properties
index 601a6c1..9aa31ec 100644
--- a/ai/docling-langchain4j-rag/application.properties
+++ b/ai/docling-langchain4j-rag/application.properties
@@ -32,8 +32,9 @@ ollama.base.url=http://localhost:11434
ollama.model.name=granite4:3b
# Batch Processing Configuration
-# Set to -1 to disable batch processing
+# The batch summary of everything in the documents directory: first run after
ten seconds, then hourly
batch.delay=10000
+batch.period=3600000
# HTTP Server Configuration
camel.server.port=8080
diff --git a/ai/docling-langchain4j-rag/docling-langchain4j-rag.yaml
b/ai/docling-langchain4j-rag/docling-langchain4j-rag.yaml
index ddd36c7..732ac88 100644
--- a/ai/docling-langchain4j-rag/docling-langchain4j-rag.yaml
+++ b/ai/docling-langchain4j-rag/docling-langchain4j-rag.yaml
@@ -1,3 +1,18 @@
+# Documents dropped in a directory are converted by Docling, analysed by a
+# local model and written to an output directory; an HTTP endpoint answers
+# questions against the latest document. The chat model is a bean built from
+# application.properties. Start the services with:
+# camel infra run docling ollama
+- beans:
+ - name: chatModel
+ type: dev.langchain4j.model.ollama.OllamaChatModel
+ builderClass:
dev.langchain4j.model.ollama.OllamaChatModel$OllamaChatModelBuilder
+ builderMethod: build
+ properties:
+ baseUrl: "{{ollama.base.url}}"
+ modelName: "{{ollama.model.name}}"
+ temperature: 0.3
+
- route:
id: document-analysis-workflow
from:
@@ -222,7 +237,7 @@
parameters:
timerName: batchSummarize
delay: "{{batch.delay}}"
- repeatCount: 0
+ period: "{{batch.period}}"
steps:
- log:
message: Starting batch document summarization...
diff --git a/ai/docling-langchain4j-rag/metadata.json
b/ai/docling-langchain4j-rag/metadata.json
index 7e2a571..92b2d1c 100644
--- a/ai/docling-langchain4j-rag/metadata.json
+++ b/ai/docling-langchain4j-rag/metadata.json
@@ -1,6 +1,6 @@
{
"title": "Document Analysis with Docling and LangChain4j RAG",
- "description": "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.",
+ "description": "Documents dropped in a directory are converted to Markdown
by a Docling service, analysed by a local Ollama model through
langchain4j-chat, and written as a report to an output directory; an HTTP
endpoint answers questions against the latest document.",
"level": "ai",
"order": 4,
"teaches": {
@@ -33,5 +33,7 @@
"docling",
"ollama"
],
- "requiresDocker": true
+ "requiresDocker": true,
+ "needs": "a local model",
+ "ciSkip": true
}
diff --git a/ai/langchain4j-chat/README.md b/ai/langchain4j-chat/README.md
index 3a6a23f..8145c5e 100644
--- a/ai/langchain4j-chat/README.md
+++ b/ai/langchain4j-chat/README.md
@@ -15,7 +15,8 @@ The wording is the model's; `granite4:3b` writes plainly, a
larger model writes
## Install Camel CLI
-<!-- see installation instructions in ../../install.adoc -->
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
## Run it
diff --git a/ai/langchain4j-chat/metadata.json
b/ai/langchain4j-chat/metadata.json
index fc8cc23..4238555 100644
--- a/ai/langchain4j-chat/metadata.json
+++ b/ai/langchain4j-chat/metadata.json
@@ -33,5 +33,6 @@
],
"requiresDocker": true,
"bundled": false,
- "ciSkip": true
+ "ciSkip": true,
+ "needs": "a local model"
}
diff --git a/ai/mcp-server/README.md b/ai/mcp-server/README.md
index 9353f2d..2170f2f 100644
--- a/ai/mcp-server/README.md
+++ b/ai/mcp-server/README.md
@@ -21,7 +21,8 @@ INFO ... mcp-server.camel.yaml:81 : Tool
order_status(ORD-1003): Order ORD-1003
## Install Camel CLI
-<!-- see installation instructions in ../../install.adoc -->
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
## Run it
diff --git a/ai/openai-pii-redaction/README.md
b/ai/openai-pii-redaction/README.md
index ecc65e3..b64d1ce 100644
--- a/ai/openai-pii-redaction/README.md
+++ b/ai/openai-pii-redaction/README.md
@@ -1,67 +1,87 @@
-# OpenAI Personal Identifiable Information Redaction
+# OpenAI PII Redaction
-This example demonstrates how to use OpenAI-compatible LLM providers with
Apache Camel to redact personal identifiable information from text.
+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. Works with OpenAI itself
+or with any server that speaks its chat API, such as a local Ollama or
llama.cpp.
-## Prerequisites
+## What you will see
-* Java 17/21
-* A running LLM service with exposed OpenAI-compatible API (for chat
completions)
+```text
+$ echo 'Customer John Doe (email: [email protected]) requested a refund for
order #998877.' | camel run *
+...
+{
+ "detectedPII": [
+ {"span": "John Doe", "type": "PERSON", "action": "REDACTED"},
+ {"span": "[email protected]", "type": "EMAIL", "action": "REDACTED"}
+ ],
+ "sanitizedText": "Customer [REDACTED] ([REDACTED]) requested a refund for
order #998877."
+}
+```
+
+The order number stays: it is not a personal identifier. The exact wording
differs from model to model.
## Install Camel CLI
-<!-- see installation instructions in ../install.adoc -->
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
-## Configure OpenAI properties
+## Run it
-Edit the `application.properties` file or set the following environment
properties:
+The example needs an OpenAI-compatible chat API. Point it at one with three
environment variables, which
+`application.properties` reads:
-```properties
-export OPENAI_API_KEY=<your-openai-api-key>
-export OPENAI_BASE_URL=http://localhost:8181/v1
-export OPENAI_MODEL=unsloth/Ministral-3-8B-Instruct-2512-GGUF
+```shell
+export OPENAI_API_KEY=<your-api-key>
+export OPENAI_BASE_URL=https://api.openai.com/v1
+export OPENAI_MODEL=gpt-4o-mini
```
-**Important**: Replace `<your-openai-api-key>` with your actual OpenAI API key.
-
-## Example: Personal Identifiable Information Redaction
-
-This integration identifies personal identifiable information based on the
provided schema details and redacts it.
-
-### How to run
+For a local server the key is whatever the server expects, often any non-empty
string, and the URL is its
+`/v1` endpoint, for example `http://localhost:11434/v1` for Ollama with
`OPENAI_MODEL` set to a model you have
+pulled. Then pipe the text in:
```shell
echo 'Customer John Doe (email: [email protected]) requested a refund for
order #998877.' | camel run *
```
-### What it does
+The example stops by itself after the one message, because of
`camel.main.durationMaxMessages=1`.
-The integration:
-1. Reads the input from the console's standard input
-2. Analyzes analyze the user input and redacts all PII
-4. Returns the results to standard output in the specified JSON format
+## How it works
-### Expected Output
+- `pii-redaction.camel.yaml` holds two routes. The second is the plumbing:
`from` the `stream` component's
+ standard input, `to` the `direct` route that does the work, `to` standard
output.
+- The `direct` route is one `to` on the `openai` component with `operation:
chat-completion`. The
+ `systemMessage` tells the model what to redact and what to leave alone,
`temperature: 0.15` keeps it
+ predictable, and `jsonSchema` points at `pii.schema.json`, so the model must
answer in that shape and the
+ body that comes back is the JSON you see.
+- `pii.schema.json` is the contract with the model: a list of `detectedPII`
with the span, its type from a fixed
+ list, and the action, plus the `sanitizedText`.
+- `application.properties` sets the component's key, base URL and model from
environment variables, adds the
+ `camel-openai` dependency, and limits the run to one message.
-```json
-{
- "detectedPII": [
- {
- "span": "John Doe",
- "type": "PERSON",
- "action": "REDACTED"
- },
- {
- "span": "[email protected]",
- "type": "EMAIL",
- "action": "REDACTED"
- }
- ],
- "sanitizedText": "Customer [REDACTED] ([REDACTED]) requested a refund for
order #998877."
-}
-Analyzing text: I love this product! It's absolutely amazing...
-Sentiment: positive (Score: 0.95)
-Detected Language: en
-```
+## Build it step by step
+
+Ask your assistant, or type it yourself, one step at a time, and run after
each, with the environment
+variables set:
+
+1. A route from `stream:in` to `stream:out` that echoes what you pipe in; run
it with `echo hello | camel run *`
+ and `camel.main.durationMaxMessages=1`.
+2. Put a `to: openai` with `operation: chat-completion` between them, with the
key, URL and model in
+ `application.properties`; the model answers in free text.
+3. Add the `systemMessage` with the redaction rules.
+4. Write `pii.schema.json` and hand it to the endpoint with `jsonSchema`; the
answer is now JSON in that shape.
+5. Move the work to a `direct` route so another route could call it.
+
+## Try changing
+
+- Add `"IBAN"` to the `type` enum in the schema and pipe in a bank account.
+- Change the system message to mask instead of redact, `J*** D**`, and see the
`action` become `MASKED`.
+- Replace the `stream:in` route with a `file` consumer that redacts every text
file dropped in a directory.
+
+## Integration testing
+
+The example has no Citrus test, because it needs a language model behind an
API key; the `camel run` above
+is the test.
## Help and contributions
diff --git a/ai/openai-pii-redaction/metadata.json
b/ai/openai-pii-redaction/metadata.json
index 364a781..717670d 100644
--- a/ai/openai-pii-redaction/metadata.json
+++ b/ai/openai-pii-redaction/metadata.json
@@ -16,5 +16,7 @@
"privacy",
"security"
],
- "bundled": false
+ "bundled": false,
+ "needs": "an OpenAI-compatible API",
+ "ciSkip": true
}
diff --git a/ai/openai-pii-redaction/pii-redaction.camel.yaml
b/ai/openai-pii-redaction/pii-redaction.camel.yaml
index 14685bf..70e5c1c 100644
--- a/ai/openai-pii-redaction/pii-redaction.camel.yaml
+++ b/ai/openai-pii-redaction/pii-redaction.camel.yaml
@@ -9,12 +9,11 @@
parameters:
operation: chat-completion
jsonSchema: resource:classpath:pii.schema.json
- systemMessage: "You are a strict data privacy compliance
assistant.\
- \ Your goal is to analyze the user input, redact all PII, and
return\
- \ the results in the specified JSON format. RULES: 1. ONLY
redact\
- \ specific identifiers, 2. DO NOT redact generic titles,
roles,\
- \ or common nouns unless they are part of a proper noun. 3.
Preserve\
- \ the grammatical structure of the sentence."
+ systemMessage: >-
+ You are a strict data privacy compliance assistant. Your goal
is to analyze the user input,
+ redact all PII, and return the results in the specified JSON
format. RULES: 1. ONLY redact
+ specific identifiers, 2. DO NOT redact generic titles, roles,
or common nouns unless they
+ are part of a proper noun. 3. Preserve the grammatical
structure of the sentence.
temperature: 0.15
- route:
from:
diff --git a/camel-jbang-example-catalog.json b/camel-jbang-example-catalog.json
index 0434afa..253edf0 100644
--- a/camel-jbang-example-catalog.json
+++ b/camel-jbang-example-catalog.json
@@ -2,7 +2,7 @@
{
"name": "ai/docling-langchain4j-rag",
"title": "Document Analysis with Docling and LangChain4j RAG",
- "description": "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.",
+ "description": "Documents dropped in a directory are converted to
Markdown by a Docling service, analysed by a local Ollama model through
langchain4j-chat, and written as a report to an output directory; an HTTP
endpoint answers questions against the latest document.",
"level": "ai",
"teaches": {
"components": [
@@ -42,6 +42,8 @@
"docling",
"ollama"
],
+ "needs": "a local model",
+ "ciSkip": true,
"order": 4
},
{
@@ -88,6 +90,7 @@
"infraServices": [
"ollama"
],
+ "needs": "a local model",
"ciSkip": true,
"order": 1
},
@@ -164,6 +167,8 @@
"pii-redaction.camel.yaml",
"pii.schema.json"
],
+ "needs": "an OpenAI-compatible API",
+ "ciSkip": true,
"order": 3
},
{
@@ -827,8 +832,7 @@
"error-handling.camel.yaml",
"orders/order-1001.json",
"orders/order-1002.json",
- "orders/order-1003.json",
- "parked/order-1003.json"
+ "orders/order-1003.json"
],
"order": 1
},
@@ -856,7 +860,7 @@
],
"bundled": true,
"requiresDocker": false,
- "hasCitrusTests": false,
+ "hasCitrusTests": true,
"files": [
"README.md",
"application.properties",
@@ -885,7 +889,7 @@
],
"bundled": true,
"requiresDocker": false,
- "hasCitrusTests": false,
+ "hasCitrusTests": true,
"files": [
"Greeter.java",
"README.md",
@@ -920,7 +924,7 @@
],
"bundled": true,
"requiresDocker": false,
- "hasCitrusTests": false,
+ "hasCitrusTests": true,
"files": [
"README.md",
"splitter.camel.yaml"
@@ -948,7 +952,7 @@
],
"bundled": true,
"requiresDocker": false,
- "hasCitrusTests": false,
+ "hasCitrusTests": true,
"files": [
"README.md",
"application.properties",
diff --git a/cloud/README.md b/cloud/README.md
index 7fd4172..5abae25 100644
--- a/cloud/README.md
+++ b/cloud/README.md
@@ -9,5 +9,5 @@ A cloud service, run locally through LocalStack and switched to
the real thing b
Start with [AWS SQS](aws-sqs/).
-Every example has a README that says what you will see, how it works, how to
build it step by step, what to try changing, and how to run its test with
`camel test run`.
+Every example has a README that says what you will see, how it works, how to
build it step by step and what to try changing, and how to run its test with
`camel test run`.
<!-- group:end -->
diff --git a/cloud/aws-sqs/README.md b/cloud/aws-sqs/README.md
index 5e28424..91e3a7e 100644
--- a/cloud/aws-sqs/README.md
+++ b/cloud/aws-sqs/README.md
@@ -17,7 +17,8 @@ INFO ... aws-sqs.camel.yaml:35 : Courier picked up ORD-1003
for US: 3 line(s)
## Install Camel CLI
-<!-- see installation instructions in ../../install.adoc -->
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
## Run it
diff --git a/connect-service/README.md b/connect-service/README.md
index e1da701..a03f15c 100644
--- a/connect-service/README.md
+++ b/connect-service/README.md
@@ -14,5 +14,5 @@ SQL, JMS, MQTT, Kafka and FTP against a service the Camel CLI
starts for you wit
Start with [SQL database](sql/); the examples read best in the order above,
each one building on what the one before it set up.
-Every example has a README that says what you will see, how it works, how to
build it step by step, what to try changing, and how to run its test with
`camel test run`.
+Every example has a README that says what you will see, how it works, how to
build it step by step and what to try changing, and how to run its test with
`camel test run`.
<!-- group:end -->
diff --git a/connect-service/artemis/README.md
b/connect-service/artemis/README.md
index cad43b8..662051c 100644
--- a/connect-service/artemis/README.md
+++ b/connect-service/artemis/README.md
@@ -15,7 +15,8 @@ INFO ... artemis.camel.yaml:37 : Took ORD-1002 off the queue:
1 line(s) for cust
## Install Camel CLI
-<!-- see installation instructions in ../../install.adoc -->
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
## Run it
diff --git a/connect-service/camel-1-tribute/README.md
b/connect-service/camel-1-tribute/README.md
index 7ba2c5a..1ce81ee 100644
--- a/connect-service/camel-1-tribute/README.md
+++ b/connect-service/camel-1-tribute/README.md
@@ -1,16 +1,13 @@
-## Camel 1.0 Tribute - JMS to File
+# Camel 1.0 tribute
-A tribute to the very first Apache Camel example ever written.
+The very first Camel example of 2007, JMS to file, as it looks today: a timer
sends ten messages to a queue
+on an ActiveMQ Artemis broker started with `camel infra`, and a consumer route
writes each message to a file
+in `outbox`.
-When Apache Camel 1.0 was released in June 2007, the project shipped with just
two examples.
-The first and most iconic was `camel-example-jms-file` - a simple route that
consumed messages
-from a JMS queue and saved them to the file system.
-
-Back then, Camel was still part of the Apache ActiveMQ project. The README was
signed
-_"The Apache ActiveMQ team"_, the website lived at `activemq.apache.org/camel`,
-and classes like `CamelTemplate` (later renamed to `ProducerTemplate`) were
brand new.
-
-The original example looked like this:
+When Apache Camel 1.0 was released in June 2007, the project shipped with just
two examples. The first was
+`camel-example-jms-file`: a route that consumed messages from a JMS queue and
saved them to the file system.
+Camel was still part of the Apache ActiveMQ project, the README was signed
_"The Apache ActiveMQ team"_, and
+classes like `CamelTemplate` (later renamed `ProducerTemplate`) were brand
new. The original looked like this:
```java
CamelContext context = new DefaultCamelContext();
@@ -34,61 +31,107 @@ for (int i = 0; i < 10; i++) {
}
```
-That was 40+ lines of Java, a Maven project, ActiveMQ embedded in-process,
-and manual component wiring.
+Forty lines of Java, a Maven project, an embedded broker and manual component
wiring. Today it is one YAML
+file and one command.
+
+## What you will see
+
+```text
+INFO ... jms-to-file.camel.yaml:32 : Sending: Test Message: 1
+INFO ... jms-to-file.camel.yaml:10 : Received: Test Message: 1
+INFO ... jms-to-file.camel.yaml:32 : Sending: Test Message: 2
+INFO ... jms-to-file.camel.yaml:10 : Received: Test Message: 2
+...
+INFO ... jms-to-file.camel.yaml:32 : Sending: Test Message: 10
+INFO ... jms-to-file.camel.yaml:10 : Received: Test Message: 10
+```
+
+and ten files in `outbox`, one per message.
+
+## Install Camel CLI
-Today, the same example is a single YAML file and one command.
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
-### Running the example
+## Run it
The example needs a running ActiveMQ Artemis broker, which the Camel CLI
starts for you in a container
(Docker or Podman must be running). In one terminal:
-```sh
-$ camel infra run artemis
+```shell
+camel infra run artemis
```
-It prints the broker URL, user and password; they match
`application.properties`. In another terminal:
+It prints the connection details as JSON; they match `application.properties`.
In another terminal:
-```sh
-$ camel run *
+```shell
+camel run *
```
-Camel will send 10 test messages to the `test.queue` JMS queue (just like the
original)
-and consume them back, saving each message as a file in the `outbox` directory
-(the original wrote to a directory named `test`; here that name holds the
integration test).
-Stop the broker with `camel infra stop artemis`.
+Stop the example with `ctrl` + `c` and the service with `camel infra stop
artemis`.
-### What changed in 19 years
+## How it works
+
+- `jms-to-file.camel.yaml` holds two routes. `jms-to-file` is the original:
`from` the `jms` queue
+ `test.queue`, log, `to` the `file` directory `outbox`. `send-test-messages`
replaces the `for` loop: a
+ `timer` with `repeatCount: 10` sends "Test Message: N" to the same queue,
with `N` from the
+ `CamelTimerCounter` header the timer sets when `includeMetadata` is on.
+- `application.properties` declares the connection factory as a bean,
`camel.beans.artemisCF`, with the
+ broker URL, user and password `camel infra` printed, and hands it to the
`jms` component with
+ `camel.component.jms.connection-factory`. That is the `addComponent` of the
original, as properties.
+- The original wrote to a directory named `test`; here that name holds the
integration test, so the files go
+ to `outbox`.
+
+What changed in 19 years:
| | Camel 1.0 (2007) | Camel CLI (today) |
|---|---|---|
-| **Language** | Java (40+ lines) | YAML (25 lines) |
+| **Language** | Java (40+ lines) | YAML (35 lines) |
| **Build** | Maven project with pom.xml | No build needed |
| **Broker** | Embedded ActiveMQ (in-process) | Apache ActiveMQ Artemis
(container) |
-| **Component setup** | Manual `ConnectionFactory` wiring | Auto-configured
via properties |
+| **Component setup** | Manual `ConnectionFactory` wiring | Configured by
properties |
| **Run command** | `mvn camel:run` | `camel run *` |
-| **Dependencies** | Declared in pom.xml | Auto-downloaded |
+| **Dependencies** | Declared in pom.xml | Downloaded on first run |
-What stayed the same: `from("jms:queue:test.queue").to("file://test")` - the
core routing idea
-that made Camel what it is today.
+What stayed the same: `from("jms:queue:test.queue").to("file://test")`, the
routing idea that made Camel.
-### Help and contributions
+## Build it step by step
-If you hit any problem using Camel or have some feedback, then please
-[let us know](https://camel.apache.org/community/support/).
+Ask your assistant, or type it yourself, one step at a time, and run after
each, with the broker running:
-We also love contributors, so
-[get involved](https://camel.apache.org/community/contributing/) :-)
+1. A route from a timer, ten times, that logs "Test Message" with the timer
counter.
+2. Declare the Artemis connection factory in `application.properties` and send
the message to the `jms`
+ queue `test.queue` instead of logging it.
+3. A second route from the same queue that logs what it receives.
+4. Write each received message to a file in `outbox`.
-The Camel riders!
+## Try changing
+
+- Open the broker's console at http://localhost:8161 (user `artemis`, password
`artemis`) and watch the queue's
+ message counts while the example runs.
+- `fileName: "message-${header.CamelTimerCounter}.txt"` on the `file`
endpoint; the header survives the queue
+ because the JMS component carries headers as message properties.
+- Stop the `jms-to-file` route with `camel cmd stop-route camel-1-tribute
--id=jms-to-file` from another
+ terminal, and the messages queue up in the broker until you start it again.
-### Integration testing
+## Integration testing
The example comes with a test in the [Citrus](https://citrusframework.org/)
YAML DSL,
-`test/camel-1-tribute.citrus.it.yaml`, which the Camel CLI runs. The test
starts the broker itself
-with `camel infra`, so nothing must be running beforehand:
+`test/camel-1-tribute.citrus.it.yaml`, which the Camel CLI runs. The test
starts the broker itself with
+`camel infra`, so nothing must be running beforehand:
-```sh
-$ camel test run test/camel-1-tribute.citrus.it.yaml
+```shell
+camel test run test/camel-1-tribute.citrus.it.yaml
```
+
+The test verifies the tenth message is sent and received.
+
+## Help and contributions
+
+If you hit any problem using Camel or have some feedback, then please
+[let us know](https://camel.apache.org/community/support/).
+
+We also love contributors, so
+[get involved](https://camel.apache.org/community/contributing/) :-)
+
+The Camel riders!
diff --git a/connect-service/ftp/README.md b/connect-service/ftp/README.md
index 0bdc1d8..c6ce383 100644
--- a/connect-service/ftp/README.md
+++ b/connect-service/ftp/README.md
@@ -13,7 +13,8 @@ INFO ... ftp.camel.yaml:42 : Shipment ORD-1003 uploaded to
the courier as courie
## Install Camel CLI
-<!-- see installation instructions in ../../install.adoc -->
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
## Run it
diff --git a/connect-service/kafka-orders/README.md
b/connect-service/kafka-orders/README.md
index 63d1a68..0a28501 100644
--- a/connect-service/kafka-orders/README.md
+++ b/connect-service/kafka-orders/README.md
@@ -20,7 +20,8 @@ INFO ... kafka-orders.camel.yaml:103 : Notification: email to
customer C-482 abo
## Install Camel CLI
-<!-- see installation instructions in ../../install.adoc -->
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
## Run it
diff --git a/connect-service/mqtt/README.md b/connect-service/mqtt/README.md
index 5aef730..1faf8e4 100644
--- a/connect-service/mqtt/README.md
+++ b/connect-service/mqtt/README.md
@@ -14,7 +14,8 @@ WARN ... mqtt.camel.yaml:39 : cold-room-1: 9 °C, too warm,
alert the warehouse
## Install Camel CLI
-<!-- see installation instructions in ../../install.adoc -->
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
## Run it
diff --git a/connect-service/sql/README.md b/connect-service/sql/README.md
index bc6ee2c..e9509df 100644
--- a/connect-service/sql/README.md
+++ b/connect-service/sql/README.md
@@ -18,7 +18,8 @@ INFO ... sql.camel.yaml:61 : C-482 (DK): 1 order(s)
## Install Camel CLI
-<!-- see installation instructions in ../../install.adoc -->
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
## Run it
diff --git a/connect/README.md b/connect/README.md
index d45b787..e6cfdff 100644
--- a/connect/README.md
+++ b/connect/README.md
@@ -11,5 +11,5 @@ Files, an HTTP client and a REST server; everything runs
inside the example.
Start with [File processing](file-processing/); the examples read best in the
order above, each one building on what the one before it set up.
-Every example has a README that says what you will see, how it works, how to
build it step by step, what to try changing, and how to run its test with
`camel test run`.
+Every example has a README that says what you will see, how it works, how to
build it step by step and what to try changing, and how to run its test with
`camel test run`.
<!-- group:end -->
diff --git a/connect/file-processing/README.md
b/connect/file-processing/README.md
index a2d8717..3444ea6 100644
--- a/connect/file-processing/README.md
+++ b/connect/file-processing/README.md
@@ -17,7 +17,8 @@ INFO ... file-processing.camel.yaml:47 : Invoice INV-2004 for
ORD-1003: 53.45 EU
## Install Camel CLI
-<!-- see installation instructions in ../../install.adoc -->
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
## Run it
diff --git a/connect/http-client/README.md b/connect/http-client/README.md
index c24e51d..f035653 100644
--- a/connect/http-client/README.md
+++ b/connect/http-client/README.md
@@ -17,7 +17,8 @@ INFO ... http-client.camel.yaml:112 : ORD-1003: CAMEL-MUG x
2, 42 in stock, ok
## Install Camel CLI
-<!-- see installation instructions in ../../install.adoc -->
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
## Run it
diff --git a/connect/stock-api/README.md b/connect/stock-api/README.md
index eeb0e75..10cbd83 100644
--- a/connect/stock-api/README.md
+++ b/connect/stock-api/README.md
@@ -23,7 +23,8 @@ $ curl localhost:8080/stock
## Install Camel CLI
-<!-- see installation instructions in ../../install.adoc -->
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
## Run it
diff --git a/contracts/README.md b/contracts/README.md
index 827949a..4758ad6 100644
--- a/contracts/README.md
+++ b/contracts/README.md
@@ -11,5 +11,5 @@ An OpenAPI contract served and called, and an API protected
by Keycloak.
Start with [OpenAPI server](openapi-server/); the examples read best in the
order above, each one building on what the one before it set up.
-Every example has a README that says what you will see, how it works, how to
build it step by step, what to try changing, and how to run its test with
`camel test run`.
+Every example has a README that says what you will see, how it works, how to
build it step by step and what to try changing; the ones with a `test/`
directory also say how to run their test with `camel test run`.
<!-- group:end -->
diff --git a/contracts/keycloak-security-rest/README.md
b/contracts/keycloak-security-rest/README.md
index 739ed4b..e5ebabc 100644
--- a/contracts/keycloak-security-rest/README.md
+++ b/contracts/keycloak-security-rest/README.md
@@ -1,348 +1,118 @@
-# Keycloak Security REST API
+# Keycloak security
-This example demonstrates how to secure REST APIs using Apache Camel with
Keycloak authentication and authorization.
-It shows how to use the `platform-http` component to create REST endpoints
protected by Keycloak security policies.
+An API protected by Keycloak: two HTTP endpoints on port 8081, a public one
that answers everyone and a
+protected one that requires a bearer token from a Keycloak started with `camel
infra`, issued to a user with
+the `admin` role; anyone else gets 403.
-## Features
+## What you will see
-* Public endpoint accessible without authentication
-* Protected endpoint requiring admin role
-* Integration with Keycloak using OAuth2/OpenID Connect
-* JWT token validation
-* Role-based access control (RBAC)
+```text
+$ curl localhost:8081/api/public
+{"message": "This is a public endpoint, no authentication required",
"timestamp": "2026-09-20T10:30:00"}
-## Prerequisites
+$ curl -i -H "Authorization: Bearer $USER_TOKEN" localhost:8081/api/protected
+HTTP/1.1 403 Forbidden
+{"error": "Forbidden", "message": "Access denied. ...", "timestamp":
"2026-09-20T10:30:05", "status": 403}
-* JBang installed (https://www.jbang.dev)
-* Docker installed for running Keycloak
-* Basic understanding of OAuth2/OpenID Connect
-
-## Dependencies
-
-This example requires the `camel-keycloak` component.
-
-## 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:
-
-```sh
-$ jbang --version
-```
-
-This will output the version of JBang.
-
-To run this example you can either install Camel on JBang via:
-
-```sh
-$ jbang app install camel@apache/camel
-```
-
-Which allows to run Camel CLI with `camel` as shown below.
-
-## Running Keycloak
-
-The Camel CLI starts Keycloak for you in a container (Docker or Podman must be
running):
-
-```sh
-$ camel infra run keycloak
-```
-
-It prints the admin user (`admin`, password `admin`) and the URL,
http://localhost:8080. Wait for the
-container to finish starting before the configuration below. Stop it later
with `camel infra stop keycloak`.
-
-## Keycloak Configuration
-
-After Keycloak starts, you need to configure it:
-
-### 1. Access Keycloak Admin Console
-
-Open your browser and navigate to:
-* http://localhost:8080
-
-Login with:
-* Username: `admin`
-* Password: `admin`
-
-### 2. Create a Realm
-
-1. Click on the dropdown in the top left (says "master")
-2. Click "Create Realm"
-3. Enter realm name: `camel`
-4. Click "Create"
-
-### 3. Create a Client
-
-1. In the left menu, click "Clients"
-2. Click "Create client"
-3. Enter Client ID: `camel-client`
-4. Click "Next"
-5. Enable "Client authentication"
-6. Enable "Service accounts roles"
-7. Click "Next"
-8. Add Valid Redirect URIs: `http://localhost:8080/*`
-9. Click "Save"
-10. Go to the "Credentials" tab
-11. Copy the "Client Secret" value
-12. Update the `application.properties` file with this secret:
-
-```properties
-keycloak.client.secret=<your-client-secret>
-```
-
-### 4. Create an Admin Role
-
-1. In the left menu, click "Realm roles"
-2. Click "Create role"
-3. Enter role name: `admin`
-4. Click "Save"
-
-### 5. Create Users
-
-#### Create Regular User (without admin role)
-
-1. In the left menu, click "Users"
-2. Click "Add user"
-3. Enter username: `testuser`
-4. Enter email: `[email protected]`
-5. Enter first name: `Test`
-6. Enter last name: `User`
-7. Click "Create"
-8. Go to "Credentials" tab
-9. Click "Set password"
-10. Enter password: `password`
-11. Disable "Temporary" toggle
-12. Click "Save"
-
-#### Create Admin User (with admin role)
-
-1. In the left menu, click "Users"
-2. Click "Add user"
-3. Enter username: `admin-user`
-4. Enter email: `[email protected]`
-5. Enter first name: `Admin`
-6. Enter last name: `User`
-7. Click "Create"
-8. Go to "Credentials" tab
-9. Click "Set password"
-10. Enter password: `password`
-11. Disable "Temporary" toggle
-12. Click "Save"
-13. Go to "Role mapping" tab
-14. Click "Assign role"
-15. Select the `admin` role
-16. Click "Assign"
-
-## Running the Example
-
-After Keycloak is configured, start the Camel application:
-
-```sh
-$ camel run *
-```
-
-The application will start on port 8081 (Keycloak has 8080) with the following
endpoints:
-
-* `http://localhost:8081/api/public` - Public endpoint (no auth required)
-* `http://localhost:8081/api/protected` - Protected endpoint (admin role
required)
-
-## Testing the Endpoints
-
-### Test Public Endpoint (No Authentication)
-
-```sh
-$ curl http://localhost:8081/api/public
-```
-
-Expected response:
-```json
-{
- "message": "This is a public endpoint, no authentication required",
- "timestamp": "2024-10-06T10:30:00"
-}
-```
-
-### Test Protected Endpoint with Regular User (Should Fail)
-
-First, try to access the protected endpoint with a regular user who doesn't
have the admin role:
-
-```sh
-
-$ export ACCESS_TOKEN=$(curl -X POST
http://localhost:8080/realms/camel/protocol/openid-connect/token \
- -H "Content-Type: application/x-www-form-urlencoded" \
- -d "username=testuser" \
- -d "password=password" \
- -d "grant_type=password" \
- -d "client_id=camel-client" \
- -d "client_secret=<your-client-secret>" \
- | jq -r '.access_token')
-
-$ curl -H "Authorization: Bearer $ACCESS_TOKEN" \
- http://localhost:8081/api/protected
+$ curl -H "Authorization: Bearer $ADMIN_TOKEN" localhost:8081/api/protected
+{"message": "This is a protected endpoint, admin role required", "timestamp":
"2026-09-20T10:30:10"}
```
-This will return a **403 Forbidden** error because `testuser` does not have
the `admin` role.
-
-### Test Protected Endpoint with Admin User (Should Succeed)
-
-Now, obtain a token for the admin user and access the protected endpoint:
-
-```sh
-
-$ export ADMIN_TOKEN=$(curl -X POST
http://localhost:8080/realms/camel/protocol/openid-connect/token \
- -H "Content-Type: application/x-www-form-urlencoded" \
- -d "username=admin-user" \
- -d "password=password" \
- -d "grant_type=password" \
- -d "client_id=camel-client" \
- -d "client_secret=<your-client-secret>" \
- | jq -r '.access_token')
-
-$ curl -H "Authorization: Bearer $ADMIN_TOKEN" \
- http://localhost:8081/api/protected
-```
+and in the log:
-Expected response:
-```json
-{
- "message": "This is a protected endpoint, admin role required",
- "timestamp": "2024-10-06T10:30:00"
-}
+```text
+INFO ... rest-api.camel.yaml:80 : Public API called
+INFO ... rest-api.camel.yaml:50 : Authorization failed: ...
+INFO ... rest-api.camel.yaml:104 : Protected API called
```
-Replace `<your-client-secret>` with the actual client secret from Keycloak.
-
-## How It Works
-
-### Security Policies
-
-The example uses a Keycloak security policy to validate JWT tokens and enforce
role-based access control.
+## Install Camel CLI
-The policy is defined as a bean in the `rest-api.camel.yaml` file and requires
the `admin` role:
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
-```yaml
-- beans:
- - name: keycloakPolicy
- type: org.apache.camel.component.keycloak.security.KeycloakSecurityPolicy
- properties:
- serverUrl: "{{keycloak.server.url}}"
- realm: "{{keycloak.realm}}"
- clientId: "{{keycloak.client.id}}"
- clientSecret: "{{keycloak.client.secret}}"
- requiredRoles:
- - "admin"
-```
-
-The bean references configuration properties from `application.properties`:
+## Run it
-```properties
-keycloak.server.url=http://localhost:8080
-keycloak.realm=camel
-keycloak.client.id=camel-client
-keycloak.client.secret=<your-client-secret>
-```
+The example needs a running Keycloak, which the Camel CLI starts for you in a
container (Docker or Podman must
+be running). In one terminal:
-### Route Protection
-
-Routes are protected by adding a policy reference. The policy will validate
the JWT token and check that the user has the required `admin` role:
-
-```yaml
-- route:
- id: protected-api
- from:
- uri: "platform-http:/api/protected"
- steps:
- - policy:
- ref: keycloakPolicy
- - setBody:
- expression:
- simple:
- expression: |
- {
- "message": "This is a protected endpoint, admin role
required",
- "timestamp": "${date:now:yyyy-MM-dd'T'HH:mm:ss}"
- }
- - setHeader:
- name: Content-Type
- expression:
- constant:
- expression: application/json
- - log:
- message: "Protected API called"
+```shell
+camel infra run keycloak
```
-If a user without the `admin` role tries to access this endpoint, they will
receive a 403 Forbidden response.
+It prints the URL, http://localhost:8080, and the admin user (`admin`,
password `admin`). Keycloak knows
+nothing about the shop yet, so the realm, client, role and users are created
once in its console, in the
+browser at http://localhost:8080:
-## Developer Console
+1. **Realm**: the dropdown top left says `master`; *Create realm*, name
`camel`.
+2. **Client**: *Clients*, *Create client*, client ID `camel-client`; on the
next page enable *Client
+ authentication* and *Service accounts roles*; save. On the *Credentials*
tab copy the *Client Secret* into
+ `application.properties` as `keycloak.client.secret`.
+3. **Role**: *Realm roles*, *Create role*, name `admin`.
+4. **Users**: *Users*, *Add user*, username `testuser`; on the *Credentials*
tab set the password `password`
+ with *Temporary* off. The same for `admin-user`, and on its *Role mapping*
tab assign the `admin` role.
-You can enable the developer console via `--console` flag:
+Then, in another terminal:
-```sh
-$ camel run * --console
+```shell
+camel run *
```
-Then you can browse: http://localhost:8081/q/dev to introspect the running
Camel application.
-
-## Stopping
+The API starts on port 8081, because Keycloak has 8080. Get a token per user
from Keycloak, with `jq` to pick it
+out of the answer, and call the endpoints as above:
-To stop the Camel application, press `Ctrl+C`.
-
-To stop Keycloak:
-
-If you used Camel CLI infra:
-```sh
-$ camel infra stop keycloak
-```
-
-If you used Docker manually:
-```sh
-$ docker stop keycloak
-$ docker rm keycloak
+```shell
+export USER_TOKEN=$(curl -s -X POST
http://localhost:8080/realms/camel/protocol/openid-connect/token \
+ -d "grant_type=password" -d "client_id=camel-client" -d
"client_secret=<your-client-secret>" \
+ -d "username=testuser" -d "password=password" | jq -r '.access_token')
+export ADMIN_TOKEN=$(curl -s -X POST
http://localhost:8080/realms/camel/protocol/openid-connect/token \
+ -d "grant_type=password" -d "client_id=camel-client" -d
"client_secret=<your-client-secret>" \
+ -d "username=admin-user" -d "password=password" | jq -r '.access_token')
```
-## Troubleshooting
-
-### 401 Unauthorized
-
-* Verify the access token is valid and not expired
-* Check that the Authorization header is properly formatted: `Bearer <token>`
-* Ensure the client secret in `application.properties` matches Keycloak
-
-### 403 Forbidden
-
-* Verify the user has the required role (e.g., admin role for admin endpoints)
-* Check role assignments in Keycloak Admin Console
+Stop the example with `ctrl` + `c` and the service with `camel infra stop
keycloak`.
-### Connection Refused
+## How it works
-* Ensure Keycloak is running on port 8080 (`camel infra ps`) and the API on
8081
-* Verify the Keycloak server URL in `application.properties` matches your setup
+- `rest-api.camel.yaml` declares the bean `keycloakPolicy`, a
`KeycloakSecurityPolicy` from the `camel-keycloak`
+ component, with the server, realm, client and `requiredRoles: admin` from
`application.properties`. The
+ `# camel-k: dependency=camel:keycloak` line at the top makes the CLI
download the component, which it cannot
+ guess from a bean class.
+- Two routes from `platform-http`, the HTTP server built into the CLI.
`public-api` just answers. `protected-api`
+ starts with `policy: {ref: keycloakPolicy}`: the policy reads the bearer
token from the `Authorization`
+ header, validates it against Keycloak, checks the roles in it, and only then
lets the message through to the
+ steps that build the answer.
+- A token that is missing, invalid or without the role makes the policy throw
`CamelAuthorizationException`.
+ The `onException` at the top handles it for every route: status 403 in the
`CamelHttpResponseCode` header, a
+ JSON error body, and a log line.
+- `application.properties` holds the Keycloak details, the client secret you
copied, and `camel.server.port`.
-### Invalid Client Credentials
+## Build it step by step
-* Check that the client ID and secret in `application.properties` match the
Keycloak client configuration
-* Verify the realm name is correct
+Ask your assistant, or type it yourself, one step at a time, and run after
each, with Keycloak running and
+configured:
-## Architecture
+1. A route from `platform-http:/api/public` that answers a JSON message;
`curl` it.
+2. A second route on `/api/protected` that answers another message.
+3. The `keycloakPolicy` bean with the server, realm, client and secret from
`application.properties`, and a
+ `policy` step as the first step of the protected route; `curl` it without a
token and see the error.
+4. The `onException` for `CamelAuthorizationException` that answers 403 with a
JSON body.
+5. Get a token for `admin-user` and call the protected endpoint with it.
-This example demonstrates:
+## Try changing
-1. **Platform HTTP Component**: Provides HTTP server capabilities
-2. **Keycloak Security Policy**: Validates OAuth2 JWT tokens
-3. **Role-Based Access Control**: Restricts endpoints based on user roles
-4. **RESTful API Design**: Multiple endpoints with different security levels
+- `requiredRoles: "admin,manager"` on the policy and a `manager` role in
Keycloak: `allRolesRequired` decides
+ whether a user needs both or one of them.
+- Log `${header.Authorization}` in the protected route to see the raw token,
and paste it into
+ https://jwt.io to read the roles Keycloak put in it.
+- Protect the public endpoint too, with a second policy that has no
`requiredRoles`: any valid token passes.
-## Next Steps
+## Integration testing
-* Add more granular role-based access control
-* Implement refresh token handling
-* Add API documentation with OpenAPI/Swagger
-* Implement request/response logging
-* Add rate limiting
-* Implement CORS configuration for web applications
+The example has no Citrus test: the realm, client and users are created by
hand in the Keycloak console, so
+there is nothing a test could start from. Verify it with the `curl` calls
above.
-## Help and Contributions
+## Help and contributions
If you hit any problem using Camel or have some feedback, then please
[let us know](https://camel.apache.org/community/support/).
diff --git a/contracts/openapi-client/README.md
b/contracts/openapi-client/README.md
index d48c646..bee0624 100644
--- a/contracts/openapi-client/README.md
+++ b/contracts/openapi-client/README.md
@@ -17,7 +17,8 @@ INFO ... openapi-client.camel.yaml:63 : ORD-1003: reserved 2
x CAMEL-MUG, 42 lef
## Install Camel CLI
-<!-- see installation instructions in ../../install.adoc -->
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
## Run it
diff --git a/contracts/openapi-server/README.md
b/contracts/openapi-server/README.md
index 3e19b99..04fbb10 100644
--- a/contracts/openapi-server/README.md
+++ b/contracts/openapi-server/README.md
@@ -25,7 +25,8 @@ INFO ... openapi-server.camel.yaml:123 : Reserved 2 x
CAMEL-MUG for ORD-1001
## Install Camel CLI
-<!-- see installation instructions in ../../install.adoc -->
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
## Run it
diff --git a/fail-well/README.md b/fail-well/README.md
index b74f5b2..3612799 100644
--- a/fail-well/README.md
+++ b/fail-well/README.md
@@ -10,5 +10,5 @@ Retries, a dead letter channel, and a circuit breaker in
front of a flaky servic
Start with [Error handling](error-handling/); the examples read best in the
order above, each one building on what the one before it set up.
-Every example has a README that says what you will see, how it works, how to
build it step by step, what to try changing, and how to run its test with
`camel test run`.
+Every example has a README that says what you will see, how it works, how to
build it step by step and what to try changing, and how to run its test with
`camel test run`.
<!-- group:end -->
diff --git a/fail-well/circuit-breaker/README.md
b/fail-well/circuit-breaker/README.md
index f2a99dd..c38d159 100644
--- a/fail-well/circuit-breaker/README.md
+++ b/fail-well/circuit-breaker/README.md
@@ -19,7 +19,8 @@ INFO ... circuit-breaker.camel.yaml:30 : Stock check 15
(breaker CLOSED): CAMEL-
## Install Camel CLI
-<!-- see installation instructions in ../../install.adoc -->
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
## Run it
diff --git a/fail-well/error-handling/README.md
b/fail-well/error-handling/README.md
index 2a584e0..00b24b2 100644
--- a/fail-well/error-handling/README.md
+++ b/fail-well/error-handling/README.md
@@ -17,7 +17,8 @@ INFO ... error-handling.camel.yaml:75 : Order ORD-1003 parked
for manual review:
## Install Camel CLI
-<!-- see installation instructions in ../../install.adoc -->
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
## Run it
diff --git a/generate-catalog.sh b/generate-catalog.sh
index 18da957..da831c5 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 "needs" in meta:
+ entry["needs"] = meta["needs"]
if meta.get("ciSkip", False):
entry["ciSkip"] = True
if "order" in meta:
@@ -161,8 +163,8 @@ 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")
+ if e.get("needs"):
+ parts.append(e["needs"])
return ", ".join(parts) if parts else "nothing"
lines = []
@@ -194,8 +196,10 @@ for level, title, intro in GROUPS:
body.append(f"Start with [{first['title']}]({first_short}/); the
examples read best in the order above, "
f"each one building on what the one before it set up.")
body.append("")
- body.append("Every example has a README that says what you will see, how
it works, how to build it step by step, "
- "what to try changing, and how to run its test with `camel
test run`.")
+ body.append("Every example has a README that says what you will see, how
it works, how to build it step by step "
+ "and what to try changing"
+ + (", and how to run its test with `camel test run`." if
all(e["hasCitrusTests"] for e in entries)
+ else "; the ones with a `test/` directory also say how to
run their test with `camel test run`."))
if os.path.exists(group_readme):
g = open(group_readme).read()
if gstart in g and gend in g:
diff --git a/install.adoc b/install.adoc
deleted file mode 100644
index 826317e..0000000
--- a/install.adoc
+++ /dev/null
@@ -1,24 +0,0 @@
-First install JBang according to the https://www.jbang.dev/download/[JBang
installation guide]
-
-When JBang is installed you should be able to run from a shell:
-
-[source,shell]
-----
-jbang --version
-----
-
-This will output the version of JBang.
-
-To run this example you can install Camel on JBang via:
-
-[source,shell]
-----
-jbang app install camel@apache/camel
-----
-
-Which allows to run Camel with `camel` as shown below.
-
-[source,shell]
-----
-camel --version
-----
diff --git a/quick-start/README.md b/quick-start/README.md
index e8cbdca..2da983f 100644
--- a/quick-start/README.md
+++ b/quick-start/README.md
@@ -12,5 +12,5 @@ The first ten minutes: generic examples with no story and no
service, each runni
Start with [Timer Log](timer-log/); the others stand on their own, in any
order.
-Every example has a README that says what you will see, how it works, how to
build it step by step, what to try changing, and how to run its test with
`camel test run`.
+Every example has a README that says what you will see, how it works, how to
build it step by step and what to try changing, and how to run its test with
`camel test run`.
<!-- group:end -->
diff --git a/quick-start/rest-api/README.md b/quick-start/rest-api/README.md
index db3dac3..2fc82ee 100644
--- a/quick-start/rest-api/README.md
+++ b/quick-start/rest-api/README.md
@@ -1,12 +1,73 @@
-## REST API
+# REST API
-This example shows a REST API with hello endpoints.
+A REST API on port 8080, served by the HTTP server built into the Camel CLI:
`GET /api/hello` answers the
+greeting from `application.properties` and `GET /api/hello/{name}` answers a
greeting with the name.
-### How to run
+## What you will see
- camel run rest-api.camel.yaml
+```text
+$ curl localhost:8080/api/hello
+Hello from Camel REST API!
-### Try it
+$ curl localhost:8080/api/hello/World
+Hello World from Camel REST API!
+```
- curl http://localhost:8080/api/hello
- curl http://localhost:8080/api/hello/World
+## Install Camel CLI
+
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
+
+## Run it
+
+```shell
+camel run *
+```
+
+Then, in another terminal, call it with `curl` as above. Stop it with `ctrl` +
`c`.
+
+## How it works
+
+- `rest-api.camel.yaml` declares the API with the REST DSL: a `rest` with
`path: /api` and two `get`
+ operations, `/hello` and `/hello/{name}`. Each operation hands over to a
`direct` route with `to`.
+- The two `direct` routes set the body that becomes the HTTP response: a
constant with the greeting from
+ `application.properties`, and a simple expression in which the `{name}` path
parameter arrives as the
+ header `name`.
+- The CLI starts the HTTP server because the file uses the REST DSL;
`camel.server.port` in
+ `application.properties` sets the port.
+
+## Build it step by step
+
+Ask your assistant, or type it yourself, one step at a time, and run after
each:
+
+1. A `rest` with `path: /api` and one `get` on `/hello` that goes to a direct
route answering "Hello";
+ run it and `curl localhost:8080/api/hello`.
+2. Add the `/hello/{name}` operation and a second direct route that answers
with `${header.name}`.
+3. Move the greeting and the port to `application.properties`.
+
+## Try changing
+
+- Add `POST /api/hello` that answers with the request body: `curl -d 'Camel'
localhost:8080/api/hello`.
+- Set the header `Content-Type` to `application/json` and answer `{"greeting":
"${body}"}`.
+- Run with `--console` and open http://localhost:8080/q/dev to see the routes
and their statistics.
+
+## Integration testing
+
+The example comes with a test in the [Citrus](https://citrusframework.org/)
YAML DSL,
+`test/rest-api.citrus.it.yaml`, which the Camel CLI runs:
+
+```shell
+camel test run test/rest-api.citrus.it.yaml
+```
+
+The test starts the route and calls both operations over HTTP.
+
+## Help and contributions
+
+If you hit any problem using Camel or have some feedback, then please
+[let us know](https://camel.apache.org/community/support/).
+
+We also love contributors, so
+[get involved](https://camel.apache.org/community/contributing/) :-)
+
+The Camel riders!
diff --git a/quick-start/rest-api/application.properties
b/quick-start/rest-api/application.properties
index f3ee407..4abd98e 100644
--- a/quick-start/rest-api/application.properties
+++ b/quick-start/rest-api/application.properties
@@ -1,3 +1,20 @@
+# 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.
+
# HTTP server port
camel.server.port=8080
# Greeting message for the hello endpoint
diff --git a/quick-start/rest-api/test/rest-api.citrus.it.yaml
b/quick-start/rest-api/test/rest-api.citrus.it.yaml
new file mode 100644
index 0000000..160d16f
--- /dev/null
+++ b/quick-start/rest-api/test/rest-api.citrus.it.yaml
@@ -0,0 +1,40 @@
+name: rest-api-test
+description: The REST API answers the greeting, with and without a name
+actions:
+ - camel:
+ jbang:
+ run:
+ integration:
+ name: "rest-api"
+ file: "../rest-api.camel.yaml"
+ systemProperties:
+ file: "../application.properties"
+ - camel:
+ jbang:
+ verify:
+ integration: "rest-api"
+ logMessage: "(rest-api) started"
+ - http:
+ client: "http://localhost:8080"
+ sendRequest:
+ GET:
+ path: "/api/hello"
+ - http:
+ client: "http://localhost:8080"
+ receiveResponse:
+ response:
+ status: "200"
+ body:
+ data: "Hello from Camel REST API!"
+ - http:
+ client: "http://localhost:8080"
+ sendRequest:
+ GET:
+ path: "/api/hello/World"
+ - http:
+ client: "http://localhost:8080"
+ receiveResponse:
+ response:
+ status: "200"
+ body:
+ data: "Hello World from Camel REST API!"
diff --git a/quick-start/routes/Greeter.java b/quick-start/routes/Greeter.java
index bdb9a45..6053562 100644
--- a/quick-start/routes/Greeter.java
+++ b/quick-start/routes/Greeter.java
@@ -1,20 +1,34 @@
+/*
+ * 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;
+/**
+ * Builds the greeting. A plain Java class next to the route, with no Camel
API in it: beans.yaml declares it as the
+ * bean greeter and sets its greeting from a property, and the route calls its
greet method with the message body.
+ */
+public class Greeter {
-public class Greeter implements Processor {
+ private String greeting;
- private String message;
-
- public void setMessage(String message) {
- this.message = message;
+ public void setGreeting(String greeting) {
+ this.greeting = greeting;
}
- @Override
- public void process(Exchange exchange) throws Exception {
- String body = exchange.getIn().getBody(String.class);
- exchange.getIn().setBody(message + " " + body);
+ public String greet(String name) {
+ return greeting + ", " + name + "!";
}
-
-}
\ No newline at end of file
+}
diff --git a/quick-start/routes/README.md b/quick-start/routes/README.md
index 8157bcf..c8e1d74 100644
--- a/quick-start/routes/README.md
+++ b/quick-start/routes/README.md
@@ -1,67 +1,68 @@
-## Routes
+# Routes
-This example shows how routes are defined in Yaml.
+The first step from YAML into your own code: a timer route in YAML calls a
Java bean, `Greeter`, that builds
+the message, and logs what the bean returned.
-### Install JBang
+## What you will see
-First install JBang according to https://www.jbang.dev
-
-When JBang is installed then you should be able to run from a shell:
-
-```sh
-$ jbang --version
-```
-
-This will output the version of JBang.
-
-To run this example you can either install Camel on JBang via:
-
-```sh
-$ jbang app install camel@apache/camel
+```text
+INFO ... routes.camel.yaml:16 : Hello, Camel!
+INFO ... routes.camel.yaml:16 : Hello, Camel!
```
-Which allows to run Camel CLI with `camel` as shown below.
+## Install Camel CLI
-### How to run
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
-You can run this example using:
+## Run it
-```sh
-$ camel run *
+```shell
+camel run *
```
-Camel will start a route that periodically provides a greeting message.
+Stop it with `ctrl` + `c`, or from another terminal with `camel stop routes`.
-### Live reload
+## How it works
-You can run the example in dev mode which allows you to edit the example,
-and hot-reload when the file is saved.
+- `routes.camel.yaml` is the route: a `timer` fires every second, a `setBody`
puts the name `Camel` in the
+ message, a `bean` step calls the `greet` method of the bean `greeter` with
the body, and a `log` prints
+ what the method returned, which is now the body.
+- `Greeter.java` is a plain Java class next to the route, with no Camel API in
it: a `greeting` property and a
+ `greet(String name)` method. The CLI compiles it when the route starts.
+- `beans.yaml` declares the class as the bean `greeter` and sets its
`greeting` from a property.
+- `application.properties` holds the greeting and the timer period.
-```sh
-$ camel run * --dev
-```
+## Build it step by step
-### Run directly from GitHub
+Ask your assistant, or type it yourself, one step at a time, and run after
each:
-The example can also be run directly by referring to the GitHub URL as shown:
+1. A route from a timer that sets the body to "Camel" and logs it.
+2. A Java class `Greeter` in the package `camel.example` with a method
`greet(String name)` that returns
+ "Hello, " and the name, declared as the bean `greeter` in `beans.yaml`;
call it from the route with a
+ `bean` step before the log.
+3. Give the class a `greeting` property, set it in `beans.yaml`, and move its
value to
+ `application.properties`.
-```sh
-$ camel run https://github.com/apache/camel-jbang-examples/tree/main/routes
-```
+## Try changing
-### Developer Web Console
+- `greeter.greeting=Hej` in `application.properties`; the Java class and the
route do not change.
+- Add a second method `shout(String name)` that returns the greeting in upper
case and switch the `method` in
+ the route.
+- Leave out `method: greet` and see that Camel finds the single public method
by itself.
-You can enable the developer console via `--console` flag as show:
+## Integration testing
-```sh
-$ camel run * --console
-```
+The example comes with a test in the [Citrus](https://citrusframework.org/)
YAML DSL,
+`test/routes.citrus.it.yaml`, which the Camel CLI runs:
-Then you can browse: http://localhost:8080/q/dev to introspect the running
Camel Application.
-Under "beans" Camel should display bean `greeter`.
+```shell
+camel test run test/routes.citrus.it.yaml
+```
+The test starts the route, with the bean and the Java class, and verifies the
logged greeting.
-### Help and contributions
+## Help and contributions
If you hit any problem using Camel or have some feedback, then please
[let us know](https://camel.apache.org/community/support/).
diff --git a/quick-start/routes/application.properties
b/quick-start/routes/application.properties
index 9049d38..a6616c4 100644
--- a/quick-start/routes/application.properties
+++ b/quick-start/routes/application.properties
@@ -1,2 +1,21 @@
-# Greeting message used by the Greeter bean
-greeter.message=Hello!
+# 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.
+
+# The greeting the Greeter bean starts its message with
+greeter.greeting=Hello
+# How often the timer fires, in milliseconds
+greeter.period=1000
diff --git a/quick-start/routes/beans.yaml b/quick-start/routes/beans.yaml
index a984e77..ec44970 100644
--- a/quick-start/routes/beans.yaml
+++ b/quick-start/routes/beans.yaml
@@ -1,5 +1,5 @@
- beans:
- - name: "greeter"
- type: "camel.example.Greeter"
- properties:
- message: '{{greeter.message}}'
+ - name: greeter
+ type: "camel.example.Greeter"
+ properties:
+ greeting: "{{greeter.greeting}}"
diff --git a/quick-start/routes/routes.camel.yaml
b/quick-start/routes/routes.camel.yaml
index a15415f..76ae528 100644
--- a/quick-start/routes/routes.camel.yaml
+++ b/quick-start/routes/routes.camel.yaml
@@ -1,17 +1,17 @@
- route:
- id: greeting-route
+ id: greeting
from:
uri: timer
parameters:
- timerName: start
- period: 1000
+ timerName: greet
+ period: "{{greeter.period}}"
steps:
- setBody:
expression:
- simple:
- expression: "I'm ${routeId}"
+ constant:
+ expression: "Camel"
- bean:
ref: greeter
+ method: greet
- log:
message: "${body}"
-
diff --git a/quick-start/routes/test/routes.citrus.it.yaml
b/quick-start/routes/test/routes.citrus.it.yaml
new file mode 100644
index 0000000..b2f129e
--- /dev/null
+++ b/quick-start/routes/test/routes.citrus.it.yaml
@@ -0,0 +1,19 @@
+name: routes-test
+description: The route logs the greeting the Greeter bean built
+actions:
+ - camel:
+ jbang:
+ run:
+ integration:
+ name: "routes"
+ file: "../routes.camel.yaml"
+ systemProperties:
+ file: "../application.properties"
+ resources:
+ - "beans.yaml"
+ - "Greeter.java"
+ - camel:
+ jbang:
+ verify:
+ integration: "routes"
+ logMessage: "Hello, Camel!"
diff --git a/quick-start/splitter/README.md b/quick-start/splitter/README.md
index ea673cc..a07be02 100644
--- a/quick-start/splitter/README.md
+++ b/quick-start/splitter/README.md
@@ -1,42 +1,72 @@
-## Splitter
+# Splitter
-This example demonstrates the Splitter EIP.
+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.
-A batch of comma-separated product names is split into individual messages.
-Each item is logged with its split index.
+## What you will see
-The splitter processes each part of the message independently, which is useful
-for breaking down bulk data into individual records for downstream processing.
+```text
+INFO ... splitter.camel.yaml:14 : Received batch:
Laptop,Phone,Tablet,Monitor,Keyboard
+INFO ... splitter.camel.yaml:21 : Processing item 0: Laptop
+INFO ... splitter.camel.yaml:21 : Processing item 1: Phone
+INFO ... splitter.camel.yaml:21 : Processing item 2: Tablet
+INFO ... splitter.camel.yaml:21 : Processing item 3: Monitor
+INFO ... splitter.camel.yaml:21 : Processing item 4: Keyboard
+```
+
+The batch is sent three times, five seconds apart, and then the timer stops.
-### Install JBang
+## Install Camel CLI
-First install JBang according to https://www.jbang.dev
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
-When JBang is installed then you should be able to run from a shell:
+## Run it
-```sh
-$ jbang --version
+```shell
+camel run *
```
-This will output the version of JBang.
+Stop it with `ctrl` + `c`, or from another terminal with `camel stop splitter`.
-To run this example you can either install Camel on JBang via:
+## How it works
-```sh
-$ jbang app install camel@apache/camel
-```
+- `splitter.camel.yaml` is the route: a `timer` with `repeatCount: 3` fires
three times, a `setBody` puts the
+ batch in the message as one string, and a `log` prints it.
+- `split` with a `tokenize` expression on `,` turns the one message into five,
one per item. The steps under
+ the `split` run once for each of them: here a `log` with the item.
+- `CamelSplitIndex` is a header the splitter sets on each part, counting from
0; `CamelSplitSize` is the
+ total and `CamelSplitComplete` is true on the last part.
+- After the last part the route continues after the `split` with the original
batch as the body; there is
+ nothing after it here.
+
+## Build it step by step
-Which allows to run Camel CLI with `camel` as shown below.
+Ask your assistant, or type it yourself, one step at a time, and run after
each:
-### How to run
+1. A route from a timer that sets the body to "Laptop,Phone,Tablet" and logs
it.
+2. Split the body on the comma and log each part.
+3. Add the split index to the log line, and `repeatCount: 3` to the timer.
-You can run this example using:
+## Try changing
-```sh
-$ camel run *
+- Log `${header.CamelSplitIndex} of ${header.CamelSplitSize}` on each part.
+- Add a `log` after the `split` and see the original batch come back as the
body.
+- Split a JSON array instead: set the body to `["Laptop","Phone"]` and use
`jsonpath: {expression: "$[*]"}`
+ as the split expression.
+
+## Integration testing
+
+The example comes with a test in the [Citrus](https://citrusframework.org/)
YAML DSL,
+`test/splitter.citrus.it.yaml`, which the Camel CLI runs:
+
+```shell
+camel test run test/splitter.citrus.it.yaml
```
-### Help and contributions
+The test starts the route and verifies the batch and the last item are logged.
+
+## Help and contributions
If you hit any problem using Camel or have some feedback, then please
[let us know](https://camel.apache.org/community/support/).
diff --git a/quick-start/splitter/test/splitter.citrus.it.yaml
b/quick-start/splitter/test/splitter.citrus.it.yaml
new file mode 100644
index 0000000..d3ebde9
--- /dev/null
+++ b/quick-start/splitter/test/splitter.citrus.it.yaml
@@ -0,0 +1,19 @@
+name: splitter-test
+description: The splitter logs the batch and then one line per item
+actions:
+ - camel:
+ jbang:
+ run:
+ integration:
+ name: "splitter"
+ file: "../splitter.camel.yaml"
+ - camel:
+ jbang:
+ verify:
+ integration: "splitter"
+ logMessage: "Received batch: Laptop,Phone,Tablet,Monitor,Keyboard"
+ - camel:
+ jbang:
+ verify:
+ integration: "splitter"
+ logMessage: "Processing item 4: Keyboard"
diff --git a/quick-start/timer-log/README.md b/quick-start/timer-log/README.md
index f773ce1..5b44f20 100644
--- a/quick-start/timer-log/README.md
+++ b/quick-start/timer-log/README.md
@@ -1,7 +1,69 @@
-## Timer Log
+# Timer Log
-This example shows a simple timer that logs a hello message every second.
+The hello of Camel in one file: a timer fires every second and a log line
prints the greeting from
+`application.properties`.
-### How to run
+## What you will see
- camel run timer-log.camel.yaml
+```text
+INFO ... timer-log.camel.yaml:13 : Hello Camel!
+INFO ... timer-log.camel.yaml:13 : Hello Camel!
+INFO ... timer-log.camel.yaml:13 : Hello Camel!
+```
+
+## Install Camel CLI
+
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
+
+## Run it
+
+```shell
+camel run *
+```
+
+Stop it with `ctrl` + `c`, or from another terminal with `camel stop
timer-log`.
+
+## How it works
+
+- `timer-log.camel.yaml` is the route: `from` a `timer` that fires every
`timer.period` milliseconds, a
+ `setBody` puts the greeting in the message, and a `log` prints the body.
+- `application.properties` holds the period and the greeting.
`{{timer.period}}` and `{{greeting.message}}`
+ in the route are property placeholders, resolved when the route starts.
+- The Camel CLI reads every file in the directory: the route because it ends
in `.camel.yaml`, the properties
+ because of their name.
+
+## Build it step by step
+
+Ask your assistant, or type it yourself, one step at a time, and run after
each:
+
+1. A route from a timer that logs "Hello Camel!" every second.
+2. Move the greeting to `application.properties` and set the body from the
property.
+3. Move the period to a property too.
+
+## Try changing
+
+- `timer.period=5000` for a greeting every five seconds; the route file does
not change.
+- Add `repeatCount: 3` to the timer parameters and the timer stops after three
greetings.
+- Log `${date:now:HH:mm:ss} ${body}` to see when each line was written.
+
+## Integration testing
+
+The example comes with a test in the [Citrus](https://citrusframework.org/)
YAML DSL,
+`test/timer-log.citrus.it.yaml`, which the Camel CLI runs:
+
+```shell
+camel test run test/timer-log.citrus.it.yaml
+```
+
+The test starts the route and verifies the logged greeting.
+
+## Help and contributions
+
+If you hit any problem using Camel or have some feedback, then please
+[let us know](https://camel.apache.org/community/support/).
+
+We also love contributors, so
+[get involved](https://camel.apache.org/community/contributing/) :-)
+
+The Camel riders!
diff --git a/quick-start/timer-log/application.properties
b/quick-start/timer-log/application.properties
index 370577a..5cc0a54 100644
--- a/quick-start/timer-log/application.properties
+++ b/quick-start/timer-log/application.properties
@@ -1,3 +1,20 @@
+# 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.
+
# Timer period in milliseconds
timer.period=1000
# Greeting message to log
diff --git a/quick-start/timer-log/test/timer-log.citrus.it.yaml
b/quick-start/timer-log/test/timer-log.citrus.it.yaml
new file mode 100644
index 0000000..d2b2ebe
--- /dev/null
+++ b/quick-start/timer-log/test/timer-log.citrus.it.yaml
@@ -0,0 +1,16 @@
+name: timer-log-test
+description: The timer route logs the greeting from application.properties
+actions:
+ - camel:
+ jbang:
+ run:
+ integration:
+ name: "timer-log"
+ file: "../timer-log.camel.yaml"
+ systemProperties:
+ file: "../application.properties"
+ - camel:
+ jbang:
+ verify:
+ integration: "timer-log"
+ logMessage: "Hello Camel!"
diff --git a/route/README.md b/route/README.md
index ae08b32..d881b2e 100644
--- a/route/README.md
+++ b/route/README.md
@@ -12,5 +12,5 @@ The routing patterns: content-based router, splitter,
aggregator, filter and mul
Start with [Content-based router](content-based-router/); the examples read
best in the order above, each one building on what the one before it set up.
-Every example has a README that says what you will see, how it works, how to
build it step by step, what to try changing, and how to run its test with
`camel test run`.
+Every example has a README that says what you will see, how it works, how to
build it step by step and what to try changing, and how to run its test with
`camel test run`.
<!-- group:end -->
diff --git a/route/aggregator/README.md b/route/aggregator/README.md
index d67c6fb..56789ff 100644
--- a/route/aggregator/README.md
+++ b/route/aggregator/README.md
@@ -17,7 +17,8 @@ INFO ... aggregator.camel.yaml:51 : Shipment for ORD-1002
complete: [{"sku":"CAM
## Install Camel CLI
-<!-- see installation instructions in ../../install.adoc -->
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
## Run it
diff --git a/route/content-based-router/README.md
b/route/content-based-router/README.md
index ca66bc3..4bbb639 100644
--- a/route/content-based-router/README.md
+++ b/route/content-based-router/README.md
@@ -13,7 +13,8 @@ INFO ... content-based-router.camel.yaml:32 : Order ORD-1003
from US: export, cu
## Install Camel CLI
-<!-- see installation instructions in ../../install.adoc -->
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
## Run it
diff --git a/route/filter-and-multicast/README.md
b/route/filter-and-multicast/README.md
index 8418ed4..fd45f53 100644
--- a/route/filter-and-multicast/README.md
+++ b/route/filter-and-multicast/README.md
@@ -16,7 +16,8 @@ INFO ... filter-and-multicast.camel.yaml:16 : Order ORD-1003
received, status pe
## Install Camel CLI
-<!-- see installation instructions in ../../install.adoc -->
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
## Run it
diff --git a/route/order-lines/README.md b/route/order-lines/README.md
index 154e401..2659d8d 100644
--- a/route/order-lines/README.md
+++ b/route/order-lines/README.md
@@ -16,7 +16,8 @@ INFO ... order-lines.camel.yaml:20 : Order ORD-1002 with 1
line(s)
## Install Camel CLI
-<!-- see installation instructions in ../../install.adoc -->
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
## Run it
diff --git a/run/README.md b/run/README.md
index 3f9e7a8..58e80cc 100644
--- a/run/README.md
+++ b/run/README.md
@@ -11,5 +11,5 @@ Running Camel: timers and cron schedules, a bean in a route,
properties and prof
Start with [Order generator](order-generator/); the examples read best in the
order above, each one building on what the one before it set up.
-Every example has a README that says what you will see, how it works, how to
build it step by step, what to try changing, and how to run its test with
`camel test run`.
+Every example has a README that says what you will see, how it works, how to
build it step by step and what to try changing, and how to run its test with
`camel test run`.
<!-- group:end -->
diff --git a/run/nightly-report/README.md b/run/nightly-report/README.md
index ac608cd..5b02665 100644
--- a/run/nightly-report/README.md
+++ b/run/nightly-report/README.md
@@ -12,7 +12,8 @@ INFO ... nightly-report.camel.yaml:15 : Inventory report
2026-09-18 15:04:20: 12
## Install Camel CLI
-<!-- see installation instructions in ../../install.adoc -->
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
## Run it
diff --git a/run/order-generator/README.md b/run/order-generator/README.md
index e644321..18fc09f 100644
--- a/run/order-generator/README.md
+++ b/run/order-generator/README.md
@@ -13,7 +13,8 @@ INFO ... order-generator.camel.yaml:23 : New order ORD-1002:
{"orderId": "ORD-10
## Install Camel CLI
-<!-- see installation instructions in ../../install.adoc -->
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
## Run it
diff --git a/run/properties-and-profiles/README.md
b/run/properties-and-profiles/README.md
index e749ff1..a546167 100644
--- a/run/properties-and-profiles/README.md
+++ b/run/properties-and-profiles/README.md
@@ -15,7 +15,8 @@ INFO ... properties-and-profiles.camel.yaml:15 : Welcome to
Camel Shop, prices i
## Install Camel CLI
-<!-- see installation instructions in ../../install.adoc -->
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
## Run it
diff --git a/showcase/README.md b/showcase/README.md
index 2c5580e..9bc3925 100644
--- a/showcase/README.md
+++ b/showcase/README.md
@@ -12,5 +12,5 @@ Tooling demos outside the ladder: the TUI, a memory leak,
message sizes, log ana
Start with [TUI Hello World](tui-hello-world/); the others stand on their own,
in any order.
-Every example has a README that says what you will see, how it works, how to
build it step by step, what to try changing, and how to run its test with
`camel test run`.
+Every example has a README that says what you will see, how it works, how to
build it step by step and what to try changing; the ones with a `test/`
directory also say how to run their test with `camel test run`.
<!-- group:end -->
diff --git a/transform/README.md b/transform/README.md
index 3d8c69b..f87b719 100644
--- a/transform/README.md
+++ b/transform/README.md
@@ -14,5 +14,5 @@ JSON, XML and CSV in and out, field-by-field mapping, Groovy
and XSLT.
Start with [JSON transform](json-transform/); the examples read best in the
order above, each one building on what the one before it set up.
-Every example has a README that says what you will see, how it works, how to
build it step by step, what to try changing, and how to run its test with
`camel test run`.
+Every example has a README that says what you will see, how it works, how to
build it step by step and what to try changing, and how to run its test with
`camel test run`.
<!-- group:end -->
diff --git a/transform/csv-to-json/README.md b/transform/csv-to-json/README.md
index 6069d92..8ed95b6 100644
--- a/transform/csv-to-json/README.md
+++ b/transform/csv-to-json/README.md
@@ -16,7 +16,8 @@ INV-2001.json INV-2002.json INV-2003.json
## Install Camel CLI
-<!-- see installation instructions in ../../install.adoc -->
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
## Run it
diff --git a/transform/data-mapping/README.md b/transform/data-mapping/README.md
index ce49606..7ed0376 100644
--- a/transform/data-mapping/README.md
+++ b/transform/data-mapping/README.md
@@ -11,7 +11,8 @@ INFO ... data-mapping.camel.yaml:27 : Shipment for the
courier: {"shipmentRef":"
## Install Camel CLI
-<!-- see installation instructions in ../../install.adoc -->
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
## Run it
diff --git a/transform/groovy/README.md b/transform/groovy/README.md
index 35ba363..5a49097 100644
--- a/transform/groovy/README.md
+++ b/transform/groovy/README.md
@@ -13,7 +13,8 @@ INFO ... groovy.camel.yaml:32 : Order ORD-1002 rejected:
not-an-address is not a
## Install Camel CLI
-<!-- see installation instructions in ../../install.adoc -->
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
## Run it
diff --git a/transform/json-transform/README.md
b/transform/json-transform/README.md
index 9b79df3..f7e3d2c 100644
--- a/transform/json-transform/README.md
+++ b/transform/json-transform/README.md
@@ -16,7 +16,8 @@ INFO ... json-transform.camel.yaml:32 : Pick list for the
warehouse: {
## Install Camel CLI
-<!-- see installation instructions in ../../install.adoc -->
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
## Run it
diff --git a/transform/xml-to-json/README.md b/transform/xml-to-json/README.md
index 4784dcc..b7e7b72 100644
--- a/transform/xml-to-json/README.md
+++ b/transform/xml-to-json/README.md
@@ -12,7 +12,8 @@ INFO ... xml-to-json.camel.yaml:20 : The same order as JSON:
{"id":"ORD-1001","c
## Install Camel CLI
-<!-- see installation instructions in ../../install.adoc -->
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
## Run it
diff --git a/transform/xslt/README.md b/transform/xslt/README.md
index 47df8a3..4d79939 100644
--- a/transform/xslt/README.md
+++ b/transform/xslt/README.md
@@ -16,7 +16,8 @@ INFO ... xslt.camel.yaml:16 : Packing slip:
## Install Camel CLI
-<!-- see installation instructions in ../../install.adoc -->
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
## Run it