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 56016bb  CAMEL-24808: rung 3 of the ladder: route (#79)
56016bb is described below

commit 56016bb22783a4370e2090dfef78447788f6188f
Author: Claus Ibsen <[email protected]>
AuthorDate: Fri Sep 18 16:37:12 2026 +0200

    CAMEL-24808: rung 3 of the ladder: route (#79)
    
    Four examples under route/, all reading the same three web shop orders from 
an orders/ directory: content-based-router (choice by country, moved from 
eip/), order-lines (split with the order id in a header), aggregator (lines 
back into a shipment with GroupedBodyAggregationStrategy, moved from eip/) and 
filter-and-multicast (paid orders to warehouse and invoicing). Each has a 
README in the ladder shape, metadata with teaches, a Citrus YAML test run by 
camel test, and a CI matrix entry.
    
    Co-Authored-By: Claude Fable 5.1 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01Bp3538HRBPMQkb5ta9xRaj
---
 .github/workflows/build.yml                        |   8 +
 camel-jbang-example-catalog.json                   | 236 +++++++++++++++------
 eip/aggregator/README.md                           |  47 ----
 eip/aggregator/aggregator.camel.yaml               |  37 ----
 eip/aggregator/metadata.json                       |  25 ---
 eip/content-based-router/README.md                 |  48 -----
 .../content-based-router.camel.yaml                |  47 ----
 eip/content-based-router/metadata.json             |  25 ---
 route/aggregator/README.md                         |  72 +++++++
 route/aggregator/aggregator.camel.yaml             |  52 +++++
 route/aggregator/application.properties            |  16 ++
 route/aggregator/metadata.json                     |  32 +++
 route/aggregator/orders/order-1001.json            |   2 +
 route/aggregator/orders/order-1002.json            |   2 +
 route/aggregator/orders/order-1003.json            |   2 +
 route/aggregator/test/aggregator.citrus.it.yaml    |  25 +++
 route/content-based-router/README.md               |  66 ++++++
 route/content-based-router/application.properties  |  16 ++
 .../content-based-router.camel.yaml                |  33 +++
 route/content-based-router/metadata.json           |  28 +++
 route/content-based-router/orders/order-1001.json  |   2 +
 route/content-based-router/orders/order-1002.json  |   2 +
 route/content-based-router/orders/order-1003.json  |   2 +
 .../test/content-based-router.citrus.it.yaml       |  30 +++
 route/filter-and-multicast/README.md               |  68 ++++++
 route/filter-and-multicast/application.properties  |  16 ++
 .../filter-and-multicast.camel.yaml                |  50 +++++
 route/filter-and-multicast/metadata.json           |  30 +++
 route/filter-and-multicast/orders/order-1001.json  |   2 +
 route/filter-and-multicast/orders/order-1002.json  |   2 +
 route/filter-and-multicast/orders/order-1003.json  |   2 +
 .../test/filter-and-multicast.citrus.it.yaml       |  30 +++
 route/order-lines/README.md                        |  71 +++++++
 route/order-lines/application.properties           |  16 ++
 route/order-lines/metadata.json                    |  29 +++
 route/order-lines/order-lines.camel.yaml           |  30 +++
 route/order-lines/orders/order-1001.json           |   2 +
 route/order-lines/orders/order-1002.json           |   2 +
 route/order-lines/orders/order-1003.json           |   2 +
 route/order-lines/test/order-lines.citrus.it.yaml  |  25 +++
 40 files changed, 933 insertions(+), 299 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 2cb9c54..09d8559 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -50,6 +50,14 @@ jobs:
             path: transform/groovy
           - name: XSLT
             path: transform/xslt
+          - name: Content-based router
+            path: route/content-based-router
+          - name: Order lines
+            path: route/order-lines
+          - name: Aggregator
+            path: route/aggregator
+          - name: Filter and multicast
+            path: route/filter-and-multicast
           - name: OpenAPI server
             path: rest/openapi-server
           - name: OpenAPI client
diff --git a/camel-jbang-example-catalog.json b/camel-jbang-example-catalog.json
index 909c4b7..4a5e588 100644
--- a/camel-jbang-example-catalog.json
+++ b/camel-jbang-example-catalog.json
@@ -373,38 +373,6 @@
             "postgres"
         ]
     },
-    {
-        "name": "eip/aggregator",
-        "title": "Aggregator",
-        "description": "A timer sends individual messages that the aggregator 
collects into a batch of three by a correlation key, and one log line shows 
each completed batch.",
-        "level": "route",
-        "teaches": {
-            "components": [
-                "timer",
-                "direct",
-                "log"
-            ],
-            "eips": [
-                "aggregate",
-                "bean"
-            ],
-            "languages": [
-                "simple"
-            ]
-        },
-        "tags": [
-            "eip",
-            "aggregator",
-            "batch"
-        ],
-        "bundled": true,
-        "requiresDocker": false,
-        "hasCitrusTests": false,
-        "files": [
-            "README.md",
-            "aggregator.camel.yaml"
-        ]
-    },
     {
         "name": "eip/circuit-breaker",
         "title": "Circuit Breaker",
@@ -439,38 +407,6 @@
             "route.camel.yaml"
         ]
     },
-    {
-        "name": "eip/content-based-router",
-        "title": "Content Based Router",
-        "description": "A timer produces messages with different content and a 
choice routes each to a different branch by its content, so the log shows which 
branch handled which message.",
-        "level": "route",
-        "teaches": {
-            "components": [
-                "timer",
-                "log"
-            ],
-            "eips": [
-                "choice",
-                "setHeader",
-                "convertBodyTo"
-            ],
-            "languages": [
-                "simple"
-            ]
-        },
-        "tags": [
-            "eip",
-            "choice",
-            "routing"
-        ],
-        "bundled": true,
-        "requiresDocker": false,
-        "hasCitrusTests": false,
-        "files": [
-            "README.md",
-            "content-based-router.camel.yaml"
-        ]
-    },
     {
         "name": "eip/splitter",
         "title": "Splitter",
@@ -772,6 +708,169 @@
             "petstore.camel.yaml"
         ]
     },
+    {
+        "name": "route/aggregator",
+        "title": "Aggregator",
+        "description": "The warehouse reports each picked line on its own and 
the aggregator collects the lines of one order back into a shipment, correlated 
by the order id and complete when as many lines are in as the order had; each 
shipment is logged as JSON.",
+        "level": "route",
+        "teaches": {
+            "components": [
+                "file",
+                "direct",
+                "log"
+            ],
+            "eips": [
+                "aggregate",
+                "split",
+                "setHeader",
+                "marshal",
+                "unmarshal"
+            ],
+            "languages": [
+                "simple"
+            ],
+            "dataformats": [
+                "json"
+            ]
+        },
+        "tags": [
+            "shop",
+            "eip",
+            "aggregator",
+            "batch"
+        ],
+        "bundled": false,
+        "requiresDocker": false,
+        "hasCitrusTests": true,
+        "files": [
+            "README.md",
+            "aggregator.camel.yaml",
+            "application.properties",
+            "orders/order-1001.json",
+            "orders/order-1002.json",
+            "orders/order-1003.json"
+        ]
+    },
+    {
+        "name": "route/content-based-router",
+        "title": "Content-based router",
+        "description": "Three orders from three countries are read from the 
orders directory and a choice routes each by its country: the Danish order to 
local delivery, the German order to EU shipping without customs, the US order 
to export with a customs declaration; each branch logs what it did.",
+        "level": "route",
+        "teaches": {
+            "components": [
+                "file",
+                "log"
+            ],
+            "eips": [
+                "choice",
+                "unmarshal"
+            ],
+            "languages": [
+                "simple"
+            ],
+            "dataformats": [
+                "json"
+            ]
+        },
+        "tags": [
+            "shop",
+            "eip",
+            "choice",
+            "routing"
+        ],
+        "bundled": false,
+        "requiresDocker": false,
+        "hasCitrusTests": true,
+        "files": [
+            "README.md",
+            "application.properties",
+            "content-based-router.camel.yaml",
+            "orders/order-1001.json",
+            "orders/order-1002.json",
+            "orders/order-1003.json"
+        ]
+    },
+    {
+        "name": "route/filter-and-multicast",
+        "title": "Filter and multicast",
+        "description": "Three orders are read from the orders directory; a 
filter lets only the paid ones through and a multicast sends each paid order to 
both the warehouse route and the invoicing route, which log their part; the 
pending order is logged as received and goes no further.",
+        "level": "route",
+        "teaches": {
+            "components": [
+                "file",
+                "direct",
+                "log"
+            ],
+            "eips": [
+                "filter",
+                "multicast",
+                "unmarshal"
+            ],
+            "languages": [
+                "simple"
+            ],
+            "dataformats": [
+                "json"
+            ]
+        },
+        "tags": [
+            "shop",
+            "eip",
+            "filter",
+            "multicast"
+        ],
+        "bundled": false,
+        "requiresDocker": false,
+        "hasCitrusTests": true,
+        "files": [
+            "README.md",
+            "application.properties",
+            "filter-and-multicast.camel.yaml",
+            "orders/order-1001.json",
+            "orders/order-1002.json",
+            "orders/order-1003.json"
+        ]
+    },
+    {
+        "name": "route/order-lines",
+        "title": "Order lines",
+        "description": "Each order read from the orders directory is split 
into one message per line, the order id travels along in a header, and the log 
shows the order, one pick line per line, and the parent's confirmation that all 
lines went to picking.",
+        "level": "route",
+        "teaches": {
+            "components": [
+                "file",
+                "log"
+            ],
+            "eips": [
+                "split",
+                "setHeader",
+                "unmarshal"
+            ],
+            "languages": [
+                "simple"
+            ],
+            "dataformats": [
+                "json"
+            ]
+        },
+        "tags": [
+            "shop",
+            "eip",
+            "splitter",
+            "split"
+        ],
+        "bundled": false,
+        "requiresDocker": false,
+        "hasCitrusTests": true,
+        "files": [
+            "README.md",
+            "application.properties",
+            "order-lines.camel.yaml",
+            "orders/order-1001.json",
+            "orders/order-1002.json",
+            "orders/order-1003.json"
+        ]
+    },
     {
         "name": "run/nightly-report",
         "title": "Nightly report",
@@ -944,10 +1043,7 @@
             "README.md",
             "application.properties",
             "csv-to-json.camel.yaml",
-            "inbox/invoices.csv",
-            "outbox/INV-2001.json",
-            "outbox/INV-2002.json",
-            "outbox/INV-2003.json"
+            "inbox/invoices.csv"
         ]
     },
     {
@@ -1022,7 +1118,7 @@
         ],
         "bundled": false,
         "requiresDocker": false,
-        "hasCitrusTests": false,
+        "hasCitrusTests": true,
         "files": [
             "README.md",
             "application.properties",
@@ -1129,7 +1225,7 @@
         ],
         "bundled": false,
         "requiresDocker": false,
-        "hasCitrusTests": false,
+        "hasCitrusTests": true,
         "files": [
             "README.md",
             "application.properties",
diff --git a/eip/aggregator/README.md b/eip/aggregator/README.md
deleted file mode 100644
index 2f66737..0000000
--- a/eip/aggregator/README.md
+++ /dev/null
@@ -1,47 +0,0 @@
-## Aggregator
-
-This example demonstrates the Aggregator EIP.
-
-Individual order messages are collected into batches of 5 using the Aggregator 
EIP.
-The `StringAggregationStrategy` joins message bodies with a comma delimiter.
-
-A timer generates one order per second. The aggregator collects 5 orders before
-releasing the batch, which is then logged as a single combined message.
-
-### 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.
-
-### How to run
-
-You can run this example using:
-
-```sh
-$ camel run *
-```
-
-### 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/eip/aggregator/aggregator.camel.yaml 
b/eip/aggregator/aggregator.camel.yaml
deleted file mode 100644
index 45a750c..0000000
--- a/eip/aggregator/aggregator.camel.yaml
+++ /dev/null
@@ -1,37 +0,0 @@
-- beans:
-    - name: myAggregationStrategy
-      type: 
"#class:org.apache.camel.processor.aggregate.StringAggregationStrategy"
-      properties:
-        delimiter: ", "
-
-- route:
-    id: order-generator
-    from:
-      uri: timer
-      parameters:
-        timerName: orders
-        period: 1000
-      steps:
-        - setBody:
-            expression:
-              simple:
-                expression: "Order-${exchangeProperty.CamelTimerCounter}"
-        - log:
-            message: "New: ${body}"
-        - to:
-            uri: direct:aggregate
-
-- route:
-    id: order-aggregator
-    from:
-      uri: direct:aggregate
-      steps:
-        - aggregate:
-            aggregationStrategy: "#bean:myAggregationStrategy"
-            correlationExpression:
-              constant:
-                expression: "true"
-            completionSize: 5
-            steps:
-              - log:
-                  message: "Batch of 5: ${body}"
diff --git a/eip/aggregator/metadata.json b/eip/aggregator/metadata.json
deleted file mode 100644
index 485c8c6..0000000
--- a/eip/aggregator/metadata.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
-    "title": "Aggregator",
-    "description": "A timer sends individual messages that the aggregator 
collects into a batch of three by a correlation key, and one log line shows 
each completed batch.",
-    "level": "route",
-    "teaches": {
-        "components": [
-            "timer",
-            "direct",
-            "log"
-        ],
-        "eips": [
-            "aggregate",
-            "bean"
-        ],
-        "languages": [
-            "simple"
-        ]
-    },
-    "tags": [
-        "eip",
-        "aggregator",
-        "batch"
-    ],
-    "bundled": true
-}
diff --git a/eip/content-based-router/README.md 
b/eip/content-based-router/README.md
deleted file mode 100644
index d030d8d..0000000
--- a/eip/content-based-router/README.md
+++ /dev/null
@@ -1,48 +0,0 @@
-## Content Based Router
-
-This example demonstrates the Content Based Router EIP.
-
-A simulated temperature sensor generates random readings between 0 and 40.
-The route uses the `choice` EIP to classify each reading:
-
-- **Hot** (>= 30): logged as an alert
-- **Normal** (15-29): logged as normal
-- **Cold** (< 15): logged as an alert
-
-### 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.
-
-### How to run
-
-You can run this example using:
-
-```sh
-$ camel run *
-```
-
-### 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/eip/content-based-router/content-based-router.camel.yaml 
b/eip/content-based-router/content-based-router.camel.yaml
deleted file mode 100644
index d451fcf..0000000
--- a/eip/content-based-router/content-based-router.camel.yaml
+++ /dev/null
@@ -1,47 +0,0 @@
-- route:
-    id: sensor
-    from:
-      uri: timer
-      parameters:
-        timerName: sensor
-        period: 1000
-      steps:
-        - setBody:
-            expression:
-              simple:
-                expression: "${random(0,40)}"
-        - convertBodyTo:
-            type: int
-        - choice:
-            when:
-              - expression:
-                  simple:
-                    expression: "${body} >= 30"
-                steps:
-                  - setHeader:
-                      name: level
-                      expression:
-                        constant:
-                          expression: hot
-                  - log:
-                      message: "Hot alert: ${body} C"
-              - expression:
-                  simple:
-                    expression: "${body} >= 15"
-                steps:
-                  - setHeader:
-                      name: level
-                      expression:
-                        constant:
-                          expression: normal
-                  - log:
-                      message: "Normal: ${body} C"
-            otherwise:
-              steps:
-                - setHeader:
-                    name: level
-                    expression:
-                      constant:
-                        expression: cold
-                - log:
-                    message: "Cold alert: ${body} C"
diff --git a/eip/content-based-router/metadata.json 
b/eip/content-based-router/metadata.json
deleted file mode 100644
index 127d7b3..0000000
--- a/eip/content-based-router/metadata.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
-    "title": "Content Based Router",
-    "description": "A timer produces messages with different content and a 
choice routes each to a different branch by its content, so the log shows which 
branch handled which message.",
-    "level": "route",
-    "teaches": {
-        "components": [
-            "timer",
-            "log"
-        ],
-        "eips": [
-            "choice",
-            "setHeader",
-            "convertBodyTo"
-        ],
-        "languages": [
-            "simple"
-        ]
-    },
-    "tags": [
-        "eip",
-        "choice",
-        "routing"
-    ],
-    "bundled": true
-}
diff --git a/route/aggregator/README.md b/route/aggregator/README.md
new file mode 100644
index 0000000..d67c6fb
--- /dev/null
+++ b/route/aggregator/README.md
@@ -0,0 +1,72 @@
+# Aggregator
+
+The reverse of `order-lines`: the warehouse reports each picked line on its 
own, and the aggregator collects the
+lines of one order back into a shipment. Lines are correlated by the order id, 
and a shipment completes when all
+the lines the order had are in.
+
+## What you will see
+
+```text
+INFO ... aggregator.camel.yaml:26 : Picked 2 x CAMEL-TSHIRT for ORD-1001
+INFO ... aggregator.camel.yaml:26 : Picked 1 x CAMEL-MUG for ORD-1001
+INFO ... aggregator.camel.yaml:26 : Picked 3 x CAMEL-MUG for ORD-1002
+...
+INFO ... aggregator.camel.yaml:51 : Shipment for ORD-1001 complete: 
[{"sku":"CAMEL-TSHIRT","qty":2,"price":19.95},{"sku":"CAMEL-MUG","qty":1,"price":9.5}]
+INFO ... aggregator.camel.yaml:51 : Shipment for ORD-1002 complete: 
[{"sku":"CAMEL-MUG","qty":3,"price":9.5}]
+```
+
+## Install Camel CLI
+
+<!-- see installation instructions in ../../install.adoc -->
+
+## Run it
+
+```shell
+camel run *
+```
+
+The three orders in `orders/` are read once each (`noop: true` leaves the 
files in place), so the run
+can be repeated.
+
+## How it works
+
+- The first route reads the orders, keeps the order id in a header, splits 
them into lines, and sends every
+  line to `direct:shipment`, as a warehouse system would report picks one by 
one.
+- The second route aggregates: `correlationExpression` `${header.orderId}` 
groups lines of the same order,
+  `completionSizeExpression` `${header.CamelSplitSize}` says how many lines 
make the order complete, and the
+  `GroupedBodyAggregationStrategy` collects the bodies into a list.
+- When a shipment completes, the list of lines is written as JSON with 
`marshal` and logged.
+
+## Build it step by step
+
+1. Start from `order-lines` and send each line to `direct:shipment` instead of 
only logging it.
+2. Add a route from `direct:shipment` with an `aggregate` correlating on 
`${header.orderId}` and a fixed
+   `completionSize: 2`; only the two-line order completes.
+3. Replace the fixed size by `completionSizeExpression` with 
`${header.CamelSplitSize}`.
+4. Add `marshal` with `json` and log the shipment.
+
+## Try changing
+
+- Add `completionTimeout: 5000` so a shipment with a missing line still closes 
after five seconds.
+- Sum the pieces instead of listing the lines: the `data-mapping` example 
shows the Groovy for it.
+
+## Integration testing
+
+The example comes with a test in the [Citrus](https://citrusframework.org/) 
YAML DSL,
+`test/aggregator.citrus.it.yaml`, which the Camel CLI runs:
+
+```shell
+camel test run test/aggregator.citrus.it.yaml
+```
+
+The test starts the route and verifies that the shipments for the three orders 
complete.
+
+## 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/route/aggregator/aggregator.camel.yaml 
b/route/aggregator/aggregator.camel.yaml
new file mode 100644
index 0000000..5148ba5
--- /dev/null
+++ b/route/aggregator/aggregator.camel.yaml
@@ -0,0 +1,52 @@
+# The reverse of order-lines: the warehouse reports each picked line on its 
own, and the aggregator
+# collects the lines of one order back into a shipment. Lines are correlated 
by the order id, and a
+# shipment completes when all the lines the order had are in.
+- route:
+    id: picked-lines
+    from:
+      uri: file
+      parameters:
+        directoryName: orders
+        noop: true
+        sortBy: "file:name"
+      steps:
+        - unmarshal:
+            json:
+              library: Jackson
+        - setHeader:
+            name: orderId
+            expression:
+              simple:
+                expression: "${body[orderId]}"
+        - split:
+            expression:
+              simple:
+                expression: "${body[lines]}"
+            steps:
+              - log:
+                  message: "Picked ${body[qty]} x ${body[sku]} for 
${header.orderId}"
+              - to:
+                  uri: direct
+                  parameters:
+                    name: shipment
+- route:
+    id: aggregator
+    from:
+      uri: direct
+      parameters:
+        name: shipment
+      steps:
+        - aggregate:
+            aggregationStrategy: 
"#class:org.apache.camel.processor.aggregate.GroupedBodyAggregationStrategy"
+            correlationExpression:
+              simple:
+                expression: "${header.orderId}"
+            completionSizeExpression:
+              simple:
+                expression: "${header.CamelSplitSize}"
+            steps:
+              - marshal:
+                  json:
+                    library: Jackson
+              - log:
+                  message: "Shipment for ${header.orderId} complete: ${body}"
diff --git a/route/aggregator/application.properties 
b/route/aggregator/application.properties
new file mode 100644
index 0000000..13a8339
--- /dev/null
+++ b/route/aggregator/application.properties
@@ -0,0 +1,16 @@
+# 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.
diff --git a/route/aggregator/metadata.json b/route/aggregator/metadata.json
new file mode 100644
index 0000000..9fc6469
--- /dev/null
+++ b/route/aggregator/metadata.json
@@ -0,0 +1,32 @@
+{
+    "title": "Aggregator",
+    "description": "The warehouse reports each picked line on its own and the 
aggregator collects the lines of one order back into a shipment, correlated by 
the order id and complete when as many lines are in as the order had; each 
shipment is logged as JSON.",
+    "level": "route",
+    "teaches": {
+        "components": [
+            "file",
+            "direct",
+            "log"
+        ],
+        "eips": [
+            "aggregate",
+            "split",
+            "setHeader",
+            "marshal",
+            "unmarshal"
+        ],
+        "languages": [
+            "simple"
+        ],
+        "dataformats": [
+            "json"
+        ]
+    },
+    "tags": [
+        "shop",
+        "eip",
+        "aggregator",
+        "batch"
+    ],
+    "bundled": false
+}
diff --git a/route/aggregator/orders/order-1001.json 
b/route/aggregator/orders/order-1001.json
new file mode 100644
index 0000000..3eb5f03
--- /dev/null
+++ b/route/aggregator/orders/order-1001.json
@@ -0,0 +1,2 @@
+{"orderId": "ORD-1001", "customer": "C-482", "country": "DK", "status": "paid",
+ "lines": [{"sku": "CAMEL-TSHIRT", "qty": 2, "price": 19.95}, {"sku": 
"CAMEL-MUG", "qty": 1, "price": 9.50}]}
diff --git a/route/aggregator/orders/order-1002.json 
b/route/aggregator/orders/order-1002.json
new file mode 100644
index 0000000..bb7aa0e
--- /dev/null
+++ b/route/aggregator/orders/order-1002.json
@@ -0,0 +1,2 @@
+{"orderId": "ORD-1002", "customer": "C-207", "country": "DE", "status": "paid",
+ "lines": [{"sku": "CAMEL-MUG", "qty": 3, "price": 9.50}]}
diff --git a/route/aggregator/orders/order-1003.json 
b/route/aggregator/orders/order-1003.json
new file mode 100644
index 0000000..9edab76
--- /dev/null
+++ b/route/aggregator/orders/order-1003.json
@@ -0,0 +1,2 @@
+{"orderId": "ORD-1003", "customer": "C-134", "country": "US", "status": 
"pending",
+ "lines": [{"sku": "CAMEL-TSHIRT", "qty": 1, "price": 19.95}, {"sku": 
"CAMEL-CAP", "qty": 1, "price": 14.00}, {"sku": "CAMEL-MUG", "qty": 2, "price": 
9.50}]}
diff --git a/route/aggregator/test/aggregator.citrus.it.yaml 
b/route/aggregator/test/aggregator.citrus.it.yaml
new file mode 100644
index 0000000..9d7e783
--- /dev/null
+++ b/route/aggregator/test/aggregator.citrus.it.yaml
@@ -0,0 +1,25 @@
+name: aggregator-test
+description: The aggregator example logs what its README promises
+actions:
+  - camel:
+      jbang:
+        run:
+          integration:
+            name: "aggregator"
+            file: "../aggregator.camel.yaml"
+            systemProperties:
+              file: "../application.properties"
+          resources:
+            - "orders/order-1001.json"
+            - "orders/order-1002.json"
+            - "orders/order-1003.json"
+  - camel:
+      jbang:
+        verify:
+          integration: "aggregator"
+          logMessage: "Shipment for ORD-1001 complete"
+  - camel:
+      jbang:
+        verify:
+          integration: "aggregator"
+          logMessage: "Shipment for ORD-1003 complete"
diff --git a/route/content-based-router/README.md 
b/route/content-based-router/README.md
new file mode 100644
index 0000000..ca66bc3
--- /dev/null
+++ b/route/content-based-router/README.md
@@ -0,0 +1,66 @@
+# Content-based router
+
+Three orders from three countries. A `choice` routes each by its country: the 
Danish order to local delivery,
+EU orders to shipping without customs, everything else to export with a 
customs declaration.
+
+## What you will see
+
+```text
+INFO ... content-based-router.camel.yaml:22 : Order ORD-1001 from DK: local 
delivery from the Copenhagen warehouse
+INFO ... content-based-router.camel.yaml:28 : Order ORD-1002 from DE: EU 
shipping, no customs
+INFO ... content-based-router.camel.yaml:32 : Order ORD-1003 from US: export, 
customs declaration needed
+```
+
+## Install Camel CLI
+
+<!-- see installation instructions in ../../install.adoc -->
+
+## Run it
+
+```shell
+camel run *
+```
+
+The three orders in `orders/` are read once each (`noop: true` leaves the 
files in place), so the run
+can be repeated.
+
+## How it works
+
+- The `file` consumer reads the three orders from `orders/` in name order; 
`unmarshal` with `json` parses each,
+  so `${body[country]}` reads a field.
+- `choice` tests the country: `== 'DK'` first, then `in 'DE,SE,NL,FR'`, the 
simple language's list test, and
+  `otherwise` takes the rest. The first matching `when` wins.
+- Each branch only logs here; in a real shop each `to:` a different route or 
system.
+
+## Build it step by step
+
+1. A `file` route on `orders` that unmarshals each order and logs 
`${body[orderId]}` and `${body[country]}`.
+2. Add a `choice` with one `when` for `DK` and an `otherwise`, each logging a 
different line.
+3. Add a second `when` for the EU countries with `in 'DE,SE,NL,FR'`.
+4. Add a fourth order file for a Swedish customer and see it take the EU 
branch.
+
+## Try changing
+
+- Route on the order value instead: `${body[lines].size()} > 2` for big orders.
+- Replace the logs with `to: direct:...` routes, one per branch, as 
`filter-and-multicast` does.
+
+## Integration testing
+
+The example comes with a test in the [Citrus](https://citrusframework.org/) 
YAML DSL,
+`test/content-based-router.citrus.it.yaml`, which the Camel CLI runs:
+
+```shell
+camel test run test/content-based-router.citrus.it.yaml
+```
+
+The test starts the route and verifies that each of the three orders is logged 
from its branch.
+
+## 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/route/content-based-router/application.properties 
b/route/content-based-router/application.properties
new file mode 100644
index 0000000..13a8339
--- /dev/null
+++ b/route/content-based-router/application.properties
@@ -0,0 +1,16 @@
+# 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.
diff --git a/route/content-based-router/content-based-router.camel.yaml 
b/route/content-based-router/content-based-router.camel.yaml
new file mode 100644
index 0000000..017afee
--- /dev/null
+++ b/route/content-based-router/content-based-router.camel.yaml
@@ -0,0 +1,33 @@
+# Three orders from three countries. A choice routes each by its country: 
Danish orders go to the
+# local warehouse, EU orders ship without customs, everything else is an 
export with a customs
+# declaration. Each branch logs what it did.
+- route:
+    id: content-based-router
+    from:
+      uri: file
+      parameters:
+        directoryName: orders
+        noop: true
+        sortBy: "file:name"
+      steps:
+        - unmarshal:
+            json:
+              library: Jackson
+        - choice:
+            when:
+              - expression:
+                  simple:
+                    expression: "${body[country]} == 'DK'"
+                steps:
+                  - log:
+                      message: "Order ${body[orderId]} from ${body[country]}: 
local delivery from the Copenhagen warehouse"
+              - expression:
+                  simple:
+                    expression: "${body[country]} in 'DE,SE,NL,FR'"
+                steps:
+                  - log:
+                      message: "Order ${body[orderId]} from ${body[country]}: 
EU shipping, no customs"
+            otherwise:
+              steps:
+                - log:
+                    message: "Order ${body[orderId]} from ${body[country]}: 
export, customs declaration needed"
diff --git a/route/content-based-router/metadata.json 
b/route/content-based-router/metadata.json
new file mode 100644
index 0000000..5aba724
--- /dev/null
+++ b/route/content-based-router/metadata.json
@@ -0,0 +1,28 @@
+{
+    "title": "Content-based router",
+    "description": "Three orders from three countries are read from the orders 
directory and a choice routes each by its country: the Danish order to local 
delivery, the German order to EU shipping without customs, the US order to 
export with a customs declaration; each branch logs what it did.",
+    "level": "route",
+    "teaches": {
+        "components": [
+            "file",
+            "log"
+        ],
+        "eips": [
+            "choice",
+            "unmarshal"
+        ],
+        "languages": [
+            "simple"
+        ],
+        "dataformats": [
+            "json"
+        ]
+    },
+    "tags": [
+        "shop",
+        "eip",
+        "choice",
+        "routing"
+    ],
+    "bundled": false
+}
diff --git a/route/content-based-router/orders/order-1001.json 
b/route/content-based-router/orders/order-1001.json
new file mode 100644
index 0000000..3eb5f03
--- /dev/null
+++ b/route/content-based-router/orders/order-1001.json
@@ -0,0 +1,2 @@
+{"orderId": "ORD-1001", "customer": "C-482", "country": "DK", "status": "paid",
+ "lines": [{"sku": "CAMEL-TSHIRT", "qty": 2, "price": 19.95}, {"sku": 
"CAMEL-MUG", "qty": 1, "price": 9.50}]}
diff --git a/route/content-based-router/orders/order-1002.json 
b/route/content-based-router/orders/order-1002.json
new file mode 100644
index 0000000..bb7aa0e
--- /dev/null
+++ b/route/content-based-router/orders/order-1002.json
@@ -0,0 +1,2 @@
+{"orderId": "ORD-1002", "customer": "C-207", "country": "DE", "status": "paid",
+ "lines": [{"sku": "CAMEL-MUG", "qty": 3, "price": 9.50}]}
diff --git a/route/content-based-router/orders/order-1003.json 
b/route/content-based-router/orders/order-1003.json
new file mode 100644
index 0000000..9edab76
--- /dev/null
+++ b/route/content-based-router/orders/order-1003.json
@@ -0,0 +1,2 @@
+{"orderId": "ORD-1003", "customer": "C-134", "country": "US", "status": 
"pending",
+ "lines": [{"sku": "CAMEL-TSHIRT", "qty": 1, "price": 19.95}, {"sku": 
"CAMEL-CAP", "qty": 1, "price": 14.00}, {"sku": "CAMEL-MUG", "qty": 2, "price": 
9.50}]}
diff --git 
a/route/content-based-router/test/content-based-router.citrus.it.yaml 
b/route/content-based-router/test/content-based-router.citrus.it.yaml
new file mode 100644
index 0000000..508c901
--- /dev/null
+++ b/route/content-based-router/test/content-based-router.citrus.it.yaml
@@ -0,0 +1,30 @@
+name: content-based-router-test
+description: The content-based-router example logs what its README promises
+actions:
+  - camel:
+      jbang:
+        run:
+          integration:
+            name: "content-based-router"
+            file: "../content-based-router.camel.yaml"
+            systemProperties:
+              file: "../application.properties"
+          resources:
+            - "orders/order-1001.json"
+            - "orders/order-1002.json"
+            - "orders/order-1003.json"
+  - camel:
+      jbang:
+        verify:
+          integration: "content-based-router"
+          logMessage: "ORD-1001 from DK: local delivery"
+  - camel:
+      jbang:
+        verify:
+          integration: "content-based-router"
+          logMessage: "ORD-1002 from DE: EU shipping"
+  - camel:
+      jbang:
+        verify:
+          integration: "content-based-router"
+          logMessage: "ORD-1003 from US: export"
diff --git a/route/filter-and-multicast/README.md 
b/route/filter-and-multicast/README.md
new file mode 100644
index 0000000..8418ed4
--- /dev/null
+++ b/route/filter-and-multicast/README.md
@@ -0,0 +1,68 @@
+# Filter and multicast
+
+Only paid orders go further, and a paid order goes to two places at once: the 
warehouse to pick it and invoicing
+to bill it. The `filter` drops the unpaid order; the `multicast` sends the 
same message to both routes.
+
+## What you will see
+
+```text
+INFO ... filter-and-multicast.camel.yaml:16 : Order ORD-1001 received, status 
paid
+INFO ... filter-and-multicast.camel.yaml:40 : Warehouse: pick 2 line(s) for 
ORD-1001
+INFO ... filter-and-multicast.camel.yaml:49 : Invoicing: bill customer C-482 
for ORD-1001
+INFO ... filter-and-multicast.camel.yaml:16 : Order ORD-1002 received, status 
paid
+...
+INFO ... filter-and-multicast.camel.yaml:16 : Order ORD-1003 received, status 
pending
+```
+
+## Install Camel CLI
+
+<!-- see installation instructions in ../../install.adoc -->
+
+## Run it
+
+```shell
+camel run *
+```
+
+The three orders in `orders/` are read once each (`noop: true` leaves the 
files in place), so the run
+can be repeated.
+
+## How it works
+
+- `filter` with `${body[status]} == 'paid'` lets paid orders into its steps; 
the pending order is logged as
+  received and goes no further, which is the difference from `choice`: a 
filter has no otherwise.
+- `multicast` sends a copy of the message to each `to:` in turn, 
`direct:warehouse` then `direct:invoicing`.
+- The two `direct` routes are the two departments; each logs its part of the 
work.
+
+## Build it step by step
+
+1. A `file` route on `orders` that unmarshals each order and logs its id and 
status.
+2. Add a `filter` on the status and log inside it; the pending order 
disappears from the inner log.
+3. Add `direct:warehouse` and `direct:invoicing` routes and a `multicast` to 
both inside the filter.
+4. Add `parallelProcessing: true` to the multicast and see both departments 
log at the same moment.
+
+## Try changing
+
+- Add a third department, `direct:analytics`, that logs the order total.
+- Use `recipientList` with a header listing the departments instead of a fixed 
`multicast`.
+
+## Integration testing
+
+The example comes with a test in the [Citrus](https://citrusframework.org/) 
YAML DSL,
+`test/filter-and-multicast.citrus.it.yaml`, which the Camel CLI runs:
+
+```shell
+camel test run test/filter-and-multicast.citrus.it.yaml
+```
+
+The test starts the route and verifies that the warehouse and invoicing logs 
appear for the paid orders.
+
+## 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/route/filter-and-multicast/application.properties 
b/route/filter-and-multicast/application.properties
new file mode 100644
index 0000000..13a8339
--- /dev/null
+++ b/route/filter-and-multicast/application.properties
@@ -0,0 +1,16 @@
+# 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.
diff --git a/route/filter-and-multicast/filter-and-multicast.camel.yaml 
b/route/filter-and-multicast/filter-and-multicast.camel.yaml
new file mode 100644
index 0000000..1559e2b
--- /dev/null
+++ b/route/filter-and-multicast/filter-and-multicast.camel.yaml
@@ -0,0 +1,50 @@
+# Only paid orders go further, and a paid order goes to two places at once: 
the warehouse to pick
+# it and invoicing to bill it. The filter drops the unpaid order, the 
multicast sends the same
+# message to both.
+- route:
+    id: filter-and-multicast
+    from:
+      uri: file
+      parameters:
+        directoryName: orders
+        noop: true
+        sortBy: "file:name"
+      steps:
+        - unmarshal:
+            json:
+              library: Jackson
+        - log:
+            message: "Order ${body[orderId]} received, status ${body[status]}"
+        - filter:
+            expression:
+              simple:
+                expression: "${body[status]} == 'paid'"
+            steps:
+              - multicast:
+                  steps:
+                    - to:
+                        uri: direct
+                        parameters:
+                          name: warehouse
+                    - to:
+                        uri: direct
+                        parameters:
+                          name: invoicing
+- route:
+    id: warehouse
+    from:
+      uri: direct
+      parameters:
+        name: warehouse
+      steps:
+        - log:
+            message: "Warehouse: pick ${body[lines].size()} line(s) for 
${body[orderId]}"
+- route:
+    id: invoicing
+    from:
+      uri: direct
+      parameters:
+        name: invoicing
+      steps:
+        - log:
+            message: "Invoicing: bill customer ${body[customer]} for 
${body[orderId]}"
diff --git a/route/filter-and-multicast/metadata.json 
b/route/filter-and-multicast/metadata.json
new file mode 100644
index 0000000..148267e
--- /dev/null
+++ b/route/filter-and-multicast/metadata.json
@@ -0,0 +1,30 @@
+{
+    "title": "Filter and multicast",
+    "description": "Three orders are read from the orders directory; a filter 
lets only the paid ones through and a multicast sends each paid order to both 
the warehouse route and the invoicing route, which log their part; the pending 
order is logged as received and goes no further.",
+    "level": "route",
+    "teaches": {
+        "components": [
+            "file",
+            "direct",
+            "log"
+        ],
+        "eips": [
+            "filter",
+            "multicast",
+            "unmarshal"
+        ],
+        "languages": [
+            "simple"
+        ],
+        "dataformats": [
+            "json"
+        ]
+    },
+    "tags": [
+        "shop",
+        "eip",
+        "filter",
+        "multicast"
+    ],
+    "bundled": false
+}
diff --git a/route/filter-and-multicast/orders/order-1001.json 
b/route/filter-and-multicast/orders/order-1001.json
new file mode 100644
index 0000000..3eb5f03
--- /dev/null
+++ b/route/filter-and-multicast/orders/order-1001.json
@@ -0,0 +1,2 @@
+{"orderId": "ORD-1001", "customer": "C-482", "country": "DK", "status": "paid",
+ "lines": [{"sku": "CAMEL-TSHIRT", "qty": 2, "price": 19.95}, {"sku": 
"CAMEL-MUG", "qty": 1, "price": 9.50}]}
diff --git a/route/filter-and-multicast/orders/order-1002.json 
b/route/filter-and-multicast/orders/order-1002.json
new file mode 100644
index 0000000..bb7aa0e
--- /dev/null
+++ b/route/filter-and-multicast/orders/order-1002.json
@@ -0,0 +1,2 @@
+{"orderId": "ORD-1002", "customer": "C-207", "country": "DE", "status": "paid",
+ "lines": [{"sku": "CAMEL-MUG", "qty": 3, "price": 9.50}]}
diff --git a/route/filter-and-multicast/orders/order-1003.json 
b/route/filter-and-multicast/orders/order-1003.json
new file mode 100644
index 0000000..9edab76
--- /dev/null
+++ b/route/filter-and-multicast/orders/order-1003.json
@@ -0,0 +1,2 @@
+{"orderId": "ORD-1003", "customer": "C-134", "country": "US", "status": 
"pending",
+ "lines": [{"sku": "CAMEL-TSHIRT", "qty": 1, "price": 19.95}, {"sku": 
"CAMEL-CAP", "qty": 1, "price": 14.00}, {"sku": "CAMEL-MUG", "qty": 2, "price": 
9.50}]}
diff --git 
a/route/filter-and-multicast/test/filter-and-multicast.citrus.it.yaml 
b/route/filter-and-multicast/test/filter-and-multicast.citrus.it.yaml
new file mode 100644
index 0000000..dae997f
--- /dev/null
+++ b/route/filter-and-multicast/test/filter-and-multicast.citrus.it.yaml
@@ -0,0 +1,30 @@
+name: filter-and-multicast-test
+description: The filter-and-multicast example logs what its README promises
+actions:
+  - camel:
+      jbang:
+        run:
+          integration:
+            name: "filter-and-multicast"
+            file: "../filter-and-multicast.camel.yaml"
+            systemProperties:
+              file: "../application.properties"
+          resources:
+            - "orders/order-1001.json"
+            - "orders/order-1002.json"
+            - "orders/order-1003.json"
+  - camel:
+      jbang:
+        verify:
+          integration: "filter-and-multicast"
+          logMessage: "Warehouse: pick 2 line(s) for ORD-1001"
+  - camel:
+      jbang:
+        verify:
+          integration: "filter-and-multicast"
+          logMessage: "Invoicing: bill customer C-207 for ORD-1002"
+  - camel:
+      jbang:
+        verify:
+          integration: "filter-and-multicast"
+          logMessage: "Order ORD-1003 received, status pending"
diff --git a/route/order-lines/README.md b/route/order-lines/README.md
new file mode 100644
index 0000000..154e401
--- /dev/null
+++ b/route/order-lines/README.md
@@ -0,0 +1,71 @@
+# Order lines
+
+An order is one message with several lines; the warehouse picks one line at a 
time. The splitter turns the order
+into one message per line, and the order id travels along in a header.
+
+## What you will see
+
+```text
+INFO ... order-lines.camel.yaml:20 : Order ORD-1001 with 2 line(s)
+INFO ... order-lines.camel.yaml:27 :   pick 2 x CAMEL-TSHIRT for ORD-1001
+INFO ... order-lines.camel.yaml:27 :   pick 1 x CAMEL-MUG for ORD-1001
+INFO ... order-lines.camel.yaml:29 : Order ORD-1001: all 2 line(s) sent to 
picking
+INFO ... order-lines.camel.yaml:20 : Order ORD-1002 with 1 line(s)
+...
+```
+
+## Install Camel CLI
+
+<!-- see installation instructions in ../../install.adoc -->
+
+## Run it
+
+```shell
+camel run *
+```
+
+The three orders in `orders/` are read once each (`noop: true` leaves the 
files in place), so the run
+can be repeated.
+
+## How it works
+
+- The order is parsed with `unmarshal` and `json`, so `${body[lines]}` is a 
list and `${body[lines].size()}` its
+  length.
+- `setHeader` keeps the order id before the split: headers are copied to every 
split message, so each pick line
+  knows its order.
+- `split` with `${body[lines]}` runs its steps once per line with that line as 
the body; `CamelSplitIndex` and
+  `CamelSplitSize` are available inside if a count is needed.
+- After the split the route continues with the original order, which logs the 
confirmation.
+
+## Build it step by step
+
+1. A `file` route on `orders` that unmarshals each order and logs the number 
of lines.
+2. Add `split` over `${body[lines]}` and log `${body[sku]}` inside it.
+3. Keep the order id in a header before the split and use it in the pick line.
+4. Log the confirmation after the split and notice the body is the whole order 
again.
+
+## Try changing
+
+- Send each line to a `direct:pick` route instead of logging; the `aggregator` 
example collects them again.
+- Split in parallel with `parallelProcessing: true` and watch the pick lines 
interleave.
+
+## Integration testing
+
+The example comes with a test in the [Citrus](https://citrusframework.org/) 
YAML DSL,
+`test/order-lines.citrus.it.yaml`, which the Camel CLI runs:
+
+```shell
+camel test run test/order-lines.citrus.it.yaml
+```
+
+The test starts the route and verifies the pick lines and the confirmation.
+
+## 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/route/order-lines/application.properties 
b/route/order-lines/application.properties
new file mode 100644
index 0000000..13a8339
--- /dev/null
+++ b/route/order-lines/application.properties
@@ -0,0 +1,16 @@
+# 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.
diff --git a/route/order-lines/metadata.json b/route/order-lines/metadata.json
new file mode 100644
index 0000000..1466b46
--- /dev/null
+++ b/route/order-lines/metadata.json
@@ -0,0 +1,29 @@
+{
+    "title": "Order lines",
+    "description": "Each order read from the orders directory is split into 
one message per line, the order id travels along in a header, and the log shows 
the order, one pick line per line, and the parent's confirmation that all lines 
went to picking.",
+    "level": "route",
+    "teaches": {
+        "components": [
+            "file",
+            "log"
+        ],
+        "eips": [
+            "split",
+            "setHeader",
+            "unmarshal"
+        ],
+        "languages": [
+            "simple"
+        ],
+        "dataformats": [
+            "json"
+        ]
+    },
+    "tags": [
+        "shop",
+        "eip",
+        "splitter",
+        "split"
+    ],
+    "bundled": false
+}
diff --git a/route/order-lines/order-lines.camel.yaml 
b/route/order-lines/order-lines.camel.yaml
new file mode 100644
index 0000000..9f8c2ad
--- /dev/null
+++ b/route/order-lines/order-lines.camel.yaml
@@ -0,0 +1,30 @@
+# An order is one message with several lines; the warehouse picks one line at 
a time. The splitter
+# turns the order into one message per line, and the split messages keep the 
order id in a header.
+- route:
+    id: order-lines
+    from:
+      uri: file
+      parameters:
+        directoryName: orders
+        noop: true
+        sortBy: "file:name"
+      steps:
+        - unmarshal:
+            json:
+              library: Jackson
+        - setHeader:
+            name: orderId
+            expression:
+              simple:
+                expression: "${body[orderId]}"
+        - log:
+            message: "Order ${header.orderId} with ${body[lines].size()} 
line(s)"
+        - split:
+            expression:
+              simple:
+                expression: "${body[lines]}"
+            steps:
+              - log:
+                  message: "  pick ${body[qty]} x ${body[sku]} for 
${header.orderId}"
+        - log:
+            message: "Order ${header.orderId}: all ${body[lines].size()} 
line(s) sent to picking"
diff --git a/route/order-lines/orders/order-1001.json 
b/route/order-lines/orders/order-1001.json
new file mode 100644
index 0000000..3eb5f03
--- /dev/null
+++ b/route/order-lines/orders/order-1001.json
@@ -0,0 +1,2 @@
+{"orderId": "ORD-1001", "customer": "C-482", "country": "DK", "status": "paid",
+ "lines": [{"sku": "CAMEL-TSHIRT", "qty": 2, "price": 19.95}, {"sku": 
"CAMEL-MUG", "qty": 1, "price": 9.50}]}
diff --git a/route/order-lines/orders/order-1002.json 
b/route/order-lines/orders/order-1002.json
new file mode 100644
index 0000000..bb7aa0e
--- /dev/null
+++ b/route/order-lines/orders/order-1002.json
@@ -0,0 +1,2 @@
+{"orderId": "ORD-1002", "customer": "C-207", "country": "DE", "status": "paid",
+ "lines": [{"sku": "CAMEL-MUG", "qty": 3, "price": 9.50}]}
diff --git a/route/order-lines/orders/order-1003.json 
b/route/order-lines/orders/order-1003.json
new file mode 100644
index 0000000..9edab76
--- /dev/null
+++ b/route/order-lines/orders/order-1003.json
@@ -0,0 +1,2 @@
+{"orderId": "ORD-1003", "customer": "C-134", "country": "US", "status": 
"pending",
+ "lines": [{"sku": "CAMEL-TSHIRT", "qty": 1, "price": 19.95}, {"sku": 
"CAMEL-CAP", "qty": 1, "price": 14.00}, {"sku": "CAMEL-MUG", "qty": 2, "price": 
9.50}]}
diff --git a/route/order-lines/test/order-lines.citrus.it.yaml 
b/route/order-lines/test/order-lines.citrus.it.yaml
new file mode 100644
index 0000000..6bbfb54
--- /dev/null
+++ b/route/order-lines/test/order-lines.citrus.it.yaml
@@ -0,0 +1,25 @@
+name: order-lines-test
+description: The order-lines example logs what its README promises
+actions:
+  - camel:
+      jbang:
+        run:
+          integration:
+            name: "order-lines"
+            file: "../order-lines.camel.yaml"
+            systemProperties:
+              file: "../application.properties"
+          resources:
+            - "orders/order-1001.json"
+            - "orders/order-1002.json"
+            - "orders/order-1003.json"
+  - camel:
+      jbang:
+        verify:
+          integration: "order-lines"
+          logMessage: "pick 2 x CAMEL-TSHIRT for ORD-1001"
+  - camel:
+      jbang:
+        verify:
+          integration: "order-lines"
+          logMessage: "Order ORD-1003: all 3 line(s) sent to picking"

Reply via email to