diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/aggregate-eip.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/aggregate-eip.adoc
index db2ec5a92e58e..9a968b7654f7b 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/aggregate-eip.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/aggregate-eip.adoc
@@ -114,7 +114,10 @@ be used for creating commonly used aggregation strategies without having to crea
In the route below we group all the exchanges together using
`GroupedExchangeAggregationStrategy`:
-._Java-only: using GroupedExchangeAggregationStrategy_
+[tabs]
+====
+Java::
++
[source,java]
----
from("direct:start")
@@ -126,6 +129,44 @@ from("direct:start")
.completionTimeout(500L).to("mock:result");
----
+XML::
++
+[source,xml]
+----
+
+
+
+
+ true
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - aggregate:
+ aggregationStrategy: groupedExchangeStrategy
+ completionTimeout: 500
+ correlationExpression:
+ constant:
+ expression: "true"
+ steps:
+ - to:
+ uri: mock:result
+- beans:
+ - name: groupedExchangeStrategy
+ type: org.apache.camel.processor.aggregate.GroupedExchangeAggregationStrategy
+----
+====
+
As a result we have one outgoing `Exchange` being
routed to the `"mock:result"` endpoint. The exchange is a holder
containing all the incoming Exchanges.
@@ -238,6 +279,61 @@ be used at the same time.
Completion is mandatory and must be configured on the aggregation.
+For example, to collect the order lines of each order into a List, and send the order on when it has 10 lines, or when
+no new line has arrived for 5 seconds:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:lines")
+ .aggregate(header("orderId"), new GroupedBodyAggregationStrategy())
+ .completionSize(10)
+ .completionTimeout(5000)
+ .to("direct:order");
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:lines
+ steps:
+ - aggregate:
+ correlationExpression:
+ header:
+ expression: orderId
+ aggregationStrategy: groupedBodies
+ completionSize: 10
+ completionTimeout: 5000
+ steps:
+ - to:
+ uri: direct:order
+- beans:
+ - name: groupedBodies
+ type: org.apache.camel.processor.aggregate.GroupedBodyAggregationStrategy
+----
+====
+
=== Pre-completion mode
There can be use-cases where you want the incoming
@@ -397,7 +493,7 @@ from("direct:start")
.aggregate(header("id"), new MyAggregationStrategy())
.completionSize(10).id("myAggregator")
.aggregateController(controller)
- .to("mock:aggregated");
+ .to("mock:result");
----
XML::
@@ -409,7 +505,7 @@ XML::
-
@@ -429,6 +525,7 @@ YAML::
uri: direct:start
steps:
- aggregate:
+ id: myAggregator
aggregateController: myController
aggregationStrategy: myAppender
completionSize: 10
@@ -598,9 +695,14 @@ YAML::
----
====
-In Java DSL, you can also provide the bean class type directly:
+You can also provide the bean class type directly.
+In Java DSL you pass the class to `AggregationStrategies.bean`, and in XML or YAML DSL
+you specify the bean class in `aggregationStrategy` using the `#class:` syntax as shown:
-._Java-only: using AggregationStrategies.bean with class type_
+[tabs]
+====
+Java::
++
[source,java]
----
public void configure() throws Exception {
@@ -611,10 +713,49 @@ public void configure() throws Exception {
}
----
+XML::
++
+[source,xml]
+----
+
+
+
+
+ true
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - aggregate:
+ aggregationStrategy: "#class:com.foo.MyBodyAppender"
+ aggregationStrategyMethodName: append
+ completionSize: 3
+ correlationExpression:
+ constant:
+ expression: "true"
+ steps:
+ - to:
+ uri: mock:result
+----
+====
+
And if the bean has only one method, we do not need to specify the name
of the method:
-._Java-only: using AggregationStrategies.bean without method name_
+[tabs]
+====
+Java::
++
[source,java]
----
public void configure() throws Exception {
@@ -625,33 +766,13 @@ public void configure() throws Exception {
}
----
-And the `append` method could be static:
-
-._Java-only: bean class with static method_
-[source,java]
-----
-public class MyBodyAppender {
-
- public static String append(String existing, String next) {
- return next + existing;
- }
-
-}
-----
-
-When using XML or YAML DSL, you can also specify the bean class directly in `aggregationStrategy`
-using the `#class:` syntax as shown:
-
-[tabs]
-====
-
XML::
+
[source,xml]
----
-
+
true
@@ -670,7 +791,6 @@ YAML::
steps:
- aggregate:
aggregationStrategy: "#class:com.foo.MyBodyAppender"
- aggregationStrategyMethodName: append
completionSize: 3
correlationExpression:
constant:
@@ -681,17 +801,35 @@ YAML::
----
====
+And the `append` method could be static:
+
+._Java-only: bean class with static method_
+[source,java]
+----
+public class MyBodyAppender {
+
+ public static String append(String existing, String next) {
+ return next + existing;
+ }
+
+}
+----
+
=== Aggregating when no data
When using bean as `AggregationStrategy`, then the method is
*only* invoked when there is data to be aggregated, meaning that the message body
is not `null`. In cases where you want to have the method invoked, even when there are no data (message body is `null`),
-then set the `strategyMethodAllowNull` to `true`.
+then set the `aggregationStrategyMethodAllowNull` to `true`.
-When using beans, this can be configured a bit easier using the `beanAllowNull` method
-from `AggregationStrategies` as shown:
+When using beans in Java DSL, this can be configured a bit easier using the `beanAllowNull` method
+from `AggregationStrategies`. In XML and YAML DSL you set the `aggregationStrategyMethodAllowNull`
+option to `true`, as shown:
-._Java-only: using AggregationStrategies.beanAllowNull_
+[tabs]
+====
+Java::
++
[source,java]
----
public void configure() throws Exception {
@@ -701,6 +839,43 @@ public void configure() throws Exception {
}
----
+XML::
++
+[source,xml]
+----
+
+
+
+ seda:foo
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - pollEnrich:
+ expression:
+ constant:
+ expression: seda:foo
+ timeout: 1000
+ aggregationStrategy: myAppender
+ aggregationStrategyMethodName: append
+ aggregationStrategyMethodAllowNull: true
+ - to:
+ uri: mock:result
+----
+====
+
Then the `append` method in the bean would need to deal with the
situation that `newExchange` can be `null`:
@@ -730,11 +905,21 @@ If we didn't do this, then on timeout the append method would normally not be
invoked, meaning the xref:content-enricher.adoc[Content Enricher] did
not merge/change the message.
-In XML and YAML DSL you would configure the `strategyMethodAllowNull` option and
-set it to `true` as shown below:
+The same works with the aggregate EIP, as shown below:
[tabs]
====
+Java::
++
+[source,java]
+----
+public void configure() throws Exception {
+ from("direct:start")
+ .aggregate(constant(true), AggregationStrategies.beanAllowNull(appender, "append"))
+ .completionSize(3)
+ .to("mock:result");
+}
+----
XML::
+
@@ -780,7 +965,7 @@ YAML::
=== Aggregating with different body types
-When, for example, using `strategyMethodAllowNull` as `true`, then the
+When, for example, using `aggregationStrategyMethodAllowNull` as `true`, then the
parameter type of the message bodies does not have to be the same.
For example suppose we want to aggregate from a `com.foo.User` type to a
`List` that contains the name of the user. We could code a bean as follows:
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/cache-eip.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/cache-eip.adoc
index b9cca49139629..2a293a62150a7 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/cache-eip.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/cache-eip.adoc
@@ -21,6 +21,10 @@ management (bulk ops, invalidation, event listening).
The Cache *EIP* covers the most common use case — _"cache the result of this expensive block by key with a TTL"_ —
without manual plumbing. It is analogous to `@Cacheable` in Spring:
+[tabs]
+====
+Java::
++
[source,java]
----
// Without Cache EIP: 7+ lines of plumbing, tied to one cache technology
@@ -29,7 +33,7 @@ from("direct:start")
.setHeader("CamelCaffeineKey", header("productId"))
.to("caffeine-cache:products")
.choice()
- .when(body().isNull())
+ .when(header("CamelCaffeineActionHasResult").isEqualTo(false))
.to("http://expensive-service")
.setHeader("CamelCaffeineAction", constant("PUT"))
.to("caffeine-cache:products")
@@ -42,6 +46,95 @@ from("direct:start")
.end();
----
+XML::
++
+[source,xml]
+----
+
+
+
+
+ GET
+
+
+
+
+
+
+
+ ${header.CamelCaffeineActionHasResult} == false
+
+
+ PUT
+
+
+
+
+
+
+
+
+
+
+ ${header.productId}
+
+
+
+----
+
+YAML::
++
+// yaml-validator: skip
+[source,yaml]
+----
+# Without Cache EIP: manual plumbing, tied to one cache technology
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - setHeader:
+ name: CamelCaffeineAction
+ expression:
+ constant:
+ expression: GET
+ - setHeader:
+ name: CamelCaffeineKey
+ expression:
+ header:
+ expression: productId
+ - to:
+ uri: caffeine-cache:products
+ - choice:
+ when:
+ - expression:
+ simple:
+ expression: "${header.CamelCaffeineActionHasResult} == false"
+ steps:
+ - to:
+ uri: http://expensive-service
+ - setHeader:
+ name: CamelCaffeineAction
+ expression:
+ constant:
+ expression: PUT
+ - to:
+ uri: caffeine-cache:products
+
+# With Cache EIP: backend-agnostic
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - cache:
+ expression:
+ simple:
+ expression: "${header.productId}"
+ steps:
+ - to:
+ uri: http://expensive-service
+----
+====
+
== Options
// eip options: START
@@ -60,6 +153,10 @@ Cache errors are handled gracefully:
Cache with an auto-created in-memory store (zero config):
+[tabs]
+====
+Java::
++
[source,java]
----
from("direct:start")
@@ -70,6 +167,45 @@ from("direct:start")
.to("direct:continue");
----
+XML::
++
+[source,xml]
+----
+
+
+
+ ${header.productId}
+
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - cache:
+ expression:
+ simple:
+ expression: "${header.productId}"
+ steps:
+ - to:
+ uri: http://product-service/api/product
+ - unmarshal:
+ json: {}
+ - to:
+ uri: direct:continue
+----
+====
+
Cache with TTL and an explicit `KeyValueRepository`:
[tabs]
@@ -105,16 +241,21 @@ YAML::
+
[source,yaml]
----
-- from:
- uri: "direct:start"
- steps:
- - cache:
- simple: "${header.productId}"
- keyValueRepository: "myCache"
- ttl: "10m"
- steps:
- - to: "http://product-service/api/product"
- - to: "direct:continue"
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - cache:
+ expression:
+ simple:
+ expression: "${header.productId}"
+ keyValueRepository: myCache
+ ttl: 10m
+ steps:
+ - to:
+ uri: http://product-service/api/product
+ - to:
+ uri: direct:continue
----
====
@@ -151,6 +292,10 @@ public KeyValueRepository myCache() {
Then reference it in the route:
+[tabs]
+====
+Java::
++
[source,java]
----
from("direct:start")
@@ -160,6 +305,38 @@ from("direct:start")
.end();
----
+XML::
++
+[source,xml]
+----
+
+
+
+ ${header.key}
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - cache:
+ expression:
+ simple:
+ expression: "${header.key}"
+ keyValueRepository: myCache
+ steps:
+ - to:
+ uri: http://service
+----
+====
+
If a single `KeyValueRepository` bean exists in the registry, it is auto-discovered — no explicit reference needed.
NOTE: When no `KeyValueRepository` bean is registered, each `.cache()` block auto-creates its own isolated in-memory
@@ -187,6 +364,10 @@ public KeyValueRepository productCache() {
}
----
+[tabs]
+====
+Java::
++
[source,java]
----
// Route that caches product lookups
@@ -208,8 +389,8 @@ from("jms:topic:product.changes")
.log("Invalidated cache for product ${header.productId}");
----
-=== XML DSL
-
+XML::
++
[source,xml]
----
@@ -228,10 +409,45 @@ from("jms:topic:product.changes")
----
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:getProduct
+ steps:
+ - cache:
+ expression:
+ simple:
+ expression: "${header.productId}"
+ keyValueRepository: productCache
+ ttl: 30m
+ steps:
+ - to:
+ uri: http://product-service/api/product
+
+# Invalidation route
+- route:
+ from:
+ uri: jms:topic:product.changes
+ steps:
+ - bean:
+ ref: productCache
+ method: "delete(${header.productId})"
+ - log:
+ message: "Invalidated cache for product ${header.productId}"
+----
+====
+
=== Clearing the Entire Cache
To clear all entries from a `KeyValueRepository`, call `clear()`:
+[tabs]
+====
+Java::
++
[source,java]
----
from("direct:clearAll")
@@ -241,3 +457,27 @@ from("direct:clearAll")
cache.clear();
});
----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:clearAll
+ steps:
+ - bean:
+ ref: productCache
+ method: clear
+----
+====
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/choice-eip.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/choice-eip.adoc
index c9f0645698c2d..4a42cbb1ef27f 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/choice-eip.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/choice-eip.adoc
@@ -220,7 +220,7 @@ The Camel xref:languages:simple-language.adoc[Simple] language
is great to use with the Choice EIP in precondition mode to select a specific branch based
on xref:manual:ROOT:using-propertyplaceholder.adoc[property placeholders].
-Here we select from the Switch the first predicate that matches. So if there is
+Here we select from the Choice the first predicate that matches. So if there is
a property placeholder with the key `foo` then it is selected, and so on.
Notice how we can use `{{?foo}}` to mark the property placeholder as optional.
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/circuitBreaker-eip.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/circuitBreaker-eip.adoc
index 21d3a5f87e5af..afc040ed39e1b 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/circuitBreaker-eip.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/circuitBreaker-eip.adoc
@@ -49,38 +49,7 @@ what happened to the call itself:
`CamelCircuitBreakerState` is the state of the breaker after the call was recorded, so the call that opens the
breaker reports `OPEN` although it was attempted; use the rejected property to know whether a call was made.
The caught exception is available in the fallback, for example as `${exception.message}` in the Simple language.
-
-For example, a fallback that answers differently when the service is down rather than failing once:
-
-[source,yaml]
-----
-- route:
- from:
- uri: timer:stock?period=1000
- steps:
- - circuitBreaker:
- steps:
- - to:
- uri: direct:supplier
- onFallback:
- steps:
- - choice:
- when:
- - expression:
- simple:
- expression: "${exchangeProperty.CamelCircuitBreakerResponseRejected} == true"
- steps:
- - setBody:
- expression:
- constant:
- expression: "supplier is down, using the last known stock"
- otherwise:
- steps:
- - setBody:
- expression:
- constant:
- expression: "stock check failed, will try again"
-----
+The example under _Using the exchange properties in the fallback_ below uses these properties.
== Example
@@ -145,6 +114,89 @@ YAML::
----
====
+=== Using the exchange properties in the fallback
+
+A fallback that answers differently when the service is down rather than failing once:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("timer:stock?period=1000")
+ .circuitBreaker()
+ .to("direct:supplier")
+ .onFallback()
+ .choice()
+ .when(simple("${exchangeProperty.CamelCircuitBreakerResponseRejected} == true"))
+ .setBody(constant("supplier is down, using the last known stock"))
+ .otherwise()
+ .setBody(constant("stock check failed, will try again"))
+ .end()
+ .end();
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+
+ ${exchangeProperty.CamelCircuitBreakerResponseRejected} == true
+
+ supplier is down, using the last known stock
+
+
+
+
+ stock check failed, will try again
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: timer:stock?period=1000
+ steps:
+ - circuitBreaker:
+ steps:
+ - to:
+ uri: direct:supplier
+ onFallback:
+ steps:
+ - choice:
+ when:
+ - expression:
+ simple:
+ expression: "${exchangeProperty.CamelCircuitBreakerResponseRejected} == true"
+ steps:
+ - setBody:
+ expression:
+ constant:
+ expression: "supplier is down, using the last known stock"
+ otherwise:
+ steps:
+ - setBody:
+ expression:
+ constant:
+ expression: "stock check failed, will try again"
+----
+====
+
== Route lifecycle and breaker state
When a route is stopped and started, the circuit breaker state is reset (an OPEN breaker returns to CLOSED)
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/claimCheck-eip.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/claimCheck-eip.adoc
index da500600b3c0e..4582a9a513089 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/claimCheck-eip.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/claimCheck-eip.adoc
@@ -136,104 +136,6 @@ You can also remove headers when merging data back. For example, to remove all h
Note you cannot have both include (`+`) and exclude (`-`) `header:pattern` at the same time.
-== Dynamic keys
-
-The claim check keys are static, but you can use the `simple` language syntax to define dynamic keys.
-For example, to use a header from the message named `myKey`:
-
-[tabs]
-====
-Java::
-+
-[source,java]
-----
-from("direct:start")
- .to("mock:a")
- .claimCheck(ClaimCheckOperation.Set, "${header.myKey}")
- .transform().constant("Bye World")
- .to("mock:b")
- .claimCheck(ClaimCheckOperation.Get, "${header.myKey}")
- .to("mock:c")
- .transform().constant("Hi World")
- .to("mock:d")
- .claimCheck(ClaimCheckOperation.Get, "${header.myKey}")
- .to("mock:e");
-----
-
-XML::
-+
-[source,xml]
-----
-
-
-
-
-
- Bye World
-
-
-
-
-
- Hi World
-
-
-
-
-
-----
-
-YAML::
-+
-[source,yaml]
-----
-- route:
- from:
- uri: direct
- parameters:
- name: start
- steps:
- - to:
- uri: mock
- parameters:
- name: a
- - claimCheck:
- operation: Set
- key: "${header.myKey}"
- - transform:
- expression:
- constant:
- expression: Bye World
- - to:
- uri: mock
- parameters:
- name: b
- - claimCheck:
- operation: Get
- key: "${header.myKey}"
- - to:
- uri: mock
- parameters:
- name: c
- - transform:
- expression:
- constant:
- expression: Hi World
- - to:
- uri: mock
- parameters:
- name: d
- - claimCheck:
- operation: Get
- key: "${header.myKey}"
- - to:
- uri: mock
- parameters:
- name: e
-----
-
-====
-
== Example
The following example shows the `Push` and `Pop` operations in action:
@@ -451,3 +353,101 @@ YAML::
uri: mock:c
----
====
+
+== Dynamic keys
+
+The claim check keys are static, but you can use the `simple` language syntax to define dynamic keys.
+For example, to use a header from the message named `myKey`:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:start")
+ .to("mock:a")
+ .claimCheck(ClaimCheckOperation.Set, "${header.myKey}")
+ .transform().constant("Bye World")
+ .to("mock:b")
+ .claimCheck(ClaimCheckOperation.Get, "${header.myKey}")
+ .to("mock:c")
+ .transform().constant("Hi World")
+ .to("mock:d")
+ .claimCheck(ClaimCheckOperation.Get, "${header.myKey}")
+ .to("mock:e");
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+ Bye World
+
+
+
+
+
+ Hi World
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct
+ parameters:
+ name: start
+ steps:
+ - to:
+ uri: mock
+ parameters:
+ name: a
+ - claimCheck:
+ operation: Set
+ key: "${header.myKey}"
+ - transform:
+ expression:
+ constant:
+ expression: Bye World
+ - to:
+ uri: mock
+ parameters:
+ name: b
+ - claimCheck:
+ operation: Get
+ key: "${header.myKey}"
+ - to:
+ uri: mock
+ parameters:
+ name: c
+ - transform:
+ expression:
+ constant:
+ expression: Hi World
+ - to:
+ uri: mock
+ parameters:
+ name: d
+ - claimCheck:
+ operation: Get
+ key: "${header.myKey}"
+ - to:
+ uri: mock
+ parameters:
+ name: e
+----
+
+====
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/content-enricher.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/content-enricher.adoc
index ce81a8f04875a..e652a28767daa 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/content-enricher.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/content-enricher.adoc
@@ -108,18 +108,14 @@ YAML::
----
- route:
from:
- uri: direct
- parameters:
- name: start
+ uri: direct:start
steps:
- setBody:
expression:
simple:
expression: "${body} World!"
- to:
- uri: mock
- parameters:
- name: result
+ uri: mock:result
----
====
@@ -188,16 +184,12 @@ YAML::
----
- route:
from:
- uri: direct
- parameters:
- name: start
+ uri: direct:start
steps:
- process:
ref: myProcessor
- to:
- uri: mock
- parameters:
- name: result
+ uri: mock:result
----
====
@@ -223,9 +215,9 @@ XML::
[source,xml]
----
-
+
-
+
----
@@ -235,13 +227,13 @@ YAML::
----
- route:
from:
- uri: activemq:Input
+ uri: activemq:My.Queue
steps:
- bean:
ref: myBeanName
method: doTransform
- to:
- uri: activemq:Output
+ uri: activemq:Another.Queue
----
====
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/content-filter-eip.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/content-filter-eip.adoc
index 355a53ac163a6..ddaa880c2ba31 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/content-filter-eip.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/content-filter-eip.adoc
@@ -15,6 +15,54 @@ image::eip/ContentFilter.gif[image]
* Using a xref:manual::processor.adoc[Processor] with the filtering programmed in Java
* Using an xref:manual::expression.adoc[Expression]
+== Message Content filtering using expression
+
+Some languages like xref:languages:xpath-language.adoc[XPath], and xref:languages:xquery-language.adoc[XQuery]
+can be used to transform and filter content from messages.
+
+In the example we use xpath to filter a XML message to select all the `` elements:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("activemq:Input")
+ .setBody().xpath("//foo/bar")
+ .to("activemq:Output");
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+ //foo/bar
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: activemq:Input
+ steps:
+ - setBody:
+ expression:
+ xpath:
+ expression: //foo/bar
+ - to:
+ uri: activemq:Output
+----
+====
+
== Message Content filtering using a Processor
In this example, we add our own xref:manual::processor.adoc[Processor] using
@@ -84,16 +132,12 @@ YAML::
----
- route:
from:
- uri: direct
- parameters:
- name: start
+ uri: direct:start
steps:
- process:
ref: myProcessor
- to:
- uri: mock
- parameters:
- name: result
+ uri: mock:result
----
====
@@ -109,9 +153,9 @@ Java::
+
[source,java]
----
-from("activemq:My.Queue")
+from("activemq:Input")
.bean("myBeanName", "doFilter")
- .to("activemq:Another.Queue");
+ .to("activemq:Output");
----
XML::
@@ -140,50 +184,3 @@ YAML::
uri: activemq:Output
----
====
-
-== Message Content filtering using expression
-
-Some languages like xref:languages:xpath-language.adoc[XPath], and xref:languages:xquery-language.adoc[XQuery]
-can be used to transform and filter content from messages.
-
-In the example we use xpath to filter a XML message to select all the `` elements:
-
-[tabs]
-====
-Java::
-+
-[source,java]
-----
-from("activemq:Input")
- .setBody().xpath("//foo:bar")
- .to("activemq:Output");
-----
-
-XML::
-+
-[source,xml]
-----
-
-
-
- //foo:bar
-
-
-
-----
-
-YAML::
-+
-[source,yaml]
-----
-- route:
- from:
- uri: activemq:Input
- steps:
- - setBody:
- expression:
- xpath: //foo:bar
- - to:
- uri: activemq:Output
-----
-====
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/dead-letter-channel.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/dead-letter-channel.adoc
index 2df230324a93a..b6148e33bb4b4 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/dead-letter-channel.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/dead-letter-channel.adoc
@@ -61,9 +61,23 @@ Pay attention to Spring XML DSL. The type attribute is used to declare which err
And in XML the `` must be configured with an id.
The id must then be enabled on either the `` (global scope), or per route that should use this error handler.
-The following listing is an equivalent of the prior Java DSL listing:
+The following listing shows the error handler together with a route:
-._XML-only:_
+[tabs]
+====
+Java::
++
+[source,java]
+----
+errorHandler(deadLetterChannel("log:dead?level=ERROR"));
+
+from("direct:newOrder")
+ .bean("orderService", "validate")
+ .bean("orderService", "store");
+----
+
+Spring XML::
++
[source,xml]
----
@@ -77,9 +91,30 @@ The following listing is an equivalent of the prior Java DSL listing:
----
-Notice how we have enabled the error handler on the `` via `errorHandlerRef`
+YAML::
++
+[source,yaml]
+----
+- errorHandler:
+ deadLetterChannel:
+ deadLetterUri: "log:dead?level=ERROR"
+- route:
+ from:
+ uri: direct:newOrder
+ steps:
+ - bean:
+ ref: orderService
+ method: validate
+ - bean:
+ ref: orderService
+ method: store
+----
+====
+
+Notice how in Spring XML we have enabled the error handler on the `` via `errorHandlerRef`
which means this error handler is used globally. You can override this per route, and have
individual routes used another error handler.
+In Java and YAML, the error handler applies to the routes in the same `RouteBuilder` class or YAML file.
NOTE: The DSLs is planned to be improved in the near future to have a unified
way of configuring error handling across all DSLs with xref:manual::route-configuration.adoc[Route Configuration].
@@ -264,7 +299,7 @@ Java::
----
// will use the original message (body and headers)
errorHandler(deadLetterChannel("jms:queue:dead")
- .useOriginalMessage().maximumRedeliveries(5).redeliveryDelay(5000);
+ .useOriginalMessage().maximumRedeliveries(5).redeliveryDelay(5000));
----
Spring XML::
@@ -324,7 +359,7 @@ Java::
----
errorHandler(deadLetterChannel("jms:queue:dead")
.maximumRedeliveries(3)
- .onRedelivery(new MyOnRedeliveryProcessor()));
+ .onRedelivery(new MyRedeliveryProcessor()));
----
Spring XML::
@@ -396,8 +431,7 @@ Configuring this from Spring XML is done with the `onPrepareFailureRef` to refer
+
[source,xml]
----
-
+
----
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/delay-eip.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/delay-eip.adoc
index 53c147d95a68f..ad22500d3731a 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/delay-eip.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/delay-eip.adoc
@@ -69,9 +69,9 @@ Note that delay creates its own block, so some DSLs (including Java) require the
._Java-only: closing the delay block with end() in Java DSL_
[source,java]
----
-.from("direct:delayBlockExample")
+from("direct:delayBlockExample")
.to("direct:getJobState")
- .loopDoWhile(simple("${body.state} = 'NOT_DONE'"))
+ .loopDoWhile(simple("${body.state} == 'NOT_DONE'"))
.log(LoggingLevel.DEBUG, "Pausing")
.delay(10000)
.syncDelayed()
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/idempotentConsumer-eip.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/idempotentConsumer-eip.adoc
index 29c614a03d459..d4f2a23cbc95e 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/idempotentConsumer-eip.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/idempotentConsumer-eip.adoc
@@ -59,4 +59,140 @@ xref:ROOT:sql-component.adoc[JdbcOrphanLockAwareIdempotentRepository]
== Example
-For example, see the above implementations for more details.
+The idempotent consumer needs an expression that gives the id of each message, and a repository that remembers the ids
+it has seen. In the example below the id is the `messageId` header, and the repository is a `MemoryIdempotentRepository`,
+which keeps the last 1000 ids in memory. A message with an id that is already in the repository is skipped, so it does
+not reach `mock:result`.
+
+In XML and YAML the repository is a bean, and the idempotent consumer refers to it by name.
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:start")
+ .idempotentConsumer(header("messageId"), MemoryIdempotentRepository.memoryIdempotentRepository())
+ .to("mock:result");
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- beans:
+ - name: myRepo
+ type: org.apache.camel.support.processor.idempotent.MemoryIdempotentRepository
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - idempotentConsumer:
+ expression:
+ header:
+ expression: messageId
+ idempotentRepository: myRepo
+ steps:
+ - to:
+ uri: mock:result
+----
+====
+
+The memory repository forgets the ids when the application stops. To keep them, use `FileIdempotentRepository` or one
+of the repositories listed above.
+
+=== Handling the duplicates in the route
+
+By default, a duplicate is skipped. When `skipDuplicate` is `false`, a duplicate goes through the idempotent consumer
+too, and the exchange property `CamelDuplicateMessage` is `true`. The route can then decide what to do with it, for
+example log it:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:start")
+ .idempotentConsumer(header("messageId"), MemoryIdempotentRepository.memoryIdempotentRepository())
+ .skipDuplicate(false)
+ .choice()
+ .when(simple("${exchangeProperty.CamelDuplicateMessage} == true"))
+ .log("Message ${header.messageId} is a duplicate")
+ .otherwise()
+ .to("mock:result")
+ .end()
+ .end();
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+
+
+ ${exchangeProperty.CamelDuplicateMessage} == true
+
+
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- beans:
+ - name: myRepo
+ type: org.apache.camel.support.processor.idempotent.MemoryIdempotentRepository
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - idempotentConsumer:
+ expression:
+ header:
+ expression: messageId
+ idempotentRepository: myRepo
+ skipDuplicate: false
+ steps:
+ - choice:
+ when:
+ - expression:
+ simple:
+ expression: "${exchangeProperty.CamelDuplicateMessage} == true"
+ steps:
+ - log:
+ message: "Message ${header.messageId} is a duplicate"
+ otherwise:
+ steps:
+ - to:
+ uri: mock:result
+----
+====
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/kamelet-eip.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/kamelet-eip.adoc
index 16c97466223d1..d89489cc3fbdc 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/kamelet-eip.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/kamelet-eip.adoc
@@ -72,6 +72,7 @@ XML::
YAML::
+
+// yaml-validator: skip
[source,yaml]
----
- route:
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/log-eip.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/log-eip.adoc
index 41980bb8df0eb..faa35aa7cd9ba 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/log-eip.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/log-eip.adoc
@@ -276,7 +276,7 @@ Java::
Logger myLogger = org.slf4j.LoggerFactory.getLogger("com.mycompany.mylogger");
from("direct:moo")
- .log(myLogger, "Me Got ${body}")
+ .log(LoggingLevel.INFO, myLogger, "Me Got ${body}")
.to("bean:foo");
----
@@ -284,14 +284,16 @@ XML::
+
[source,xml]
----
-
-
+
+
+
+
-
+
-
+
----
@@ -300,7 +302,6 @@ YAML::
[source,yaml]
----
- route:
- id: moo
from:
uri: direct:moo
steps:
@@ -308,7 +309,13 @@ YAML::
logger: myLogger
message: "Me Got ${body}"
- to:
- uri: mock:baz
+ uri: bean:foo
+- beans:
+ - name: myLogger
+ type: org.slf4j.LoggerFactory
+ factoryMethod: getLogger
+ constructors:
+ 0: com.mycompany.mylogger
----
====
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/marshal-eip.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/marshal-eip.adoc
index 398f85725e39f..5583162409dbd 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/marshal-eip.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/marshal-eip.adoc
@@ -24,7 +24,48 @@ into a Java object; or some other representation according to the data format be
== Example
-The following example reads XML files from the inbox/xml directory.
+The following example sends an order as JSON to a JMS queue. The message body is an object, such as a Map or a POJO,
+and `marshal` turns it into JSON text with xref:dataformats:jackson2-dataformat.adoc[Jackson]:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:order")
+ .marshal().json(JsonLibrary.Jackson)
+ .to("jms:queue:order");
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:order
+ steps:
+ - marshal:
+ json:
+ library: Jackson
+ - to:
+ uri: jms:queue:order
+----
+====
+
+The next example reads XML files from the inbox/xml directory.
Each file is then transformed into Java Objects using xref:dataformats:jaxb-dataformat.adoc[JAXB].
Then a xref:ROOT:bean-component.adoc[Bean] is invoked that takes in the Java object.
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/pausable-eip.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/pausable-eip.adoc
index 20bc377177a84..cdbacc58be292 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/pausable-eip.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/pausable-eip.adoc
@@ -33,11 +33,49 @@ from("kafka:topic")
.process(exchange -> LOG.info("Received: {}", exchange.getMessage().getBody()))
.to("direct:destination");
----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: kafka:topic
+ steps:
+ - pausable:
+ consumerListener: kafkaConsumerListener
+ untilCheck: canContinue
+ - log:
+ message: "Received: ${body}"
+ - to:
+ uri: direct:destination
+- beans:
+ - name: kafkaConsumerListener
+ type: org.apache.camel.component.kafka.consumer.errorhandler.KafkaConsumerListener
+ - name: canContinue
+ type: com.foo.CanContinuePredicate
+----
====
You can also integrate the Pausable EIP with the xref:circuitBreaker-eip.adoc[Circuit Breaker EIP]
to pause consumption based on downstream system availability:
+[tabs]
+====
+Java::
++
[source,java]
----
from("kafka:topic")
@@ -48,6 +86,40 @@ from("kafka:topic")
.end();
----
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: kafka:topic
+ steps:
+ - pausable:
+ consumerListener: kafkaConsumerListener
+ untilCheck: canContinue
+ - circuitBreaker:
+ resilience4jConfiguration:
+ circuitBreaker: pausableCircuit
+ steps:
+ - to:
+ uri: direct:downstream
+----
+====
+
== See Also
* xref:resume-strategies.adoc[Resume Strategies]
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/pollEnrich-eip.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/pollEnrich-eip.adoc
index 3dfccbcd508e0..2ad4c0890e0a9 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/pollEnrich-eip.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/pollEnrich-eip.adoc
@@ -74,6 +74,100 @@ You can pass in a timeout value that determines which method to use:
The timeout values are in milliseconds.
+For example, to read the file `data.txt` from the inbox directory, waiting up to 10 seconds for it. Without an
+`AggregationStrategy`, the polled message is the result, so the content of the file becomes the message body:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:start")
+ .pollEnrich("file:inbox?fileName=data.txt", 10000)
+ .to("mock:result");
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+ file:inbox?fileName=data.txt
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - pollEnrich:
+ timeout: 10000
+ expression:
+ constant:
+ expression: file:inbox?fileName=data.txt
+ - to:
+ uri: mock:result
+----
+====
+
+=== Poll Enrich with Dynamic Endpoints
+
+Both `enrich` and `pollEnrich` supports using dynamic uris computed based on information from the current `Exchange`.
+
+For example to `pollEnrich` from an endpoint that uses a header to indicate a SEDA queue name:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:start")
+ .pollEnrich().simple("seda:${header.queueName}")
+ .to("direct:result");
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+ seda:${header.queueName}
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - pollEnrich:
+ expression:
+ simple:
+ expression: "seda:${header.queueName}"
+ - to:
+ uri: direct:result
+----
+====
+
+TIP: See the `cacheSize` option for more details on _how much cache_ to use depending on how many or few unique endpoints are used.
+
=== Using Poll Enrich
The content enricher (`pollEnrich`) retrieves additional data from a _resource endpoint_ in order to enrich an incoming message (contained in the _original exchange_).
@@ -218,55 +312,6 @@ YAML::
Notice that the enriched endpoint is a constant, however, Camel also supports dynamic endpoints which is covered next.
-=== Poll Enrich with Dynamic Endpoints
-
-Both `enrich` and `pollEnrich` supports using dynamic uris computed based on information from the current `Exchange`.
-
-For example to `pollEnrich` from an endpoint that uses a header to indicate a SEDA queue name:
-
-[tabs]
-====
-Java::
-+
-[source,java]
-----
-from("direct:start")
- .pollEnrich().simple("seda:${header.queueName}")
- .to("direct:result");
-----
-
-XML::
-+
-[source,xml]
-----
-
-
-
- seda:${header.queueName}
-
-
-
-----
-
-YAML::
-+
-[source,yaml]
-----
-- route:
- from:
- uri: direct:start
- steps:
- - pollEnrich:
- expression:
- simple:
- expression: "seda:${header.queueName}"
- - to:
- uri: direct:result
-----
-====
-
-TIP: See the `cacheSize` option for more details on _how much cache_ to use depending on how many or few unique endpoints are used.
-
=== Using Poll Enrich with file based components
When using `poll` or `pollEnrich` with the file based components, then the `eagerLimitMaxMessagesPerPoll` option
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/polling-consumer.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/polling-consumer.adoc
index d06b471755975..152e13341cbb5 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/polling-consumer.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/polling-consumer.adoc
@@ -44,6 +44,7 @@ You can programmatically create an instance of `PollingConsumer` from any endpoi
----
Endpoint endpoint = context.getEndpoint("activemq:my.queue");
PollingConsumer consumer = endpoint.createPollingConsumer();
+consumer.start();
Exchange exchange = consumer.receive();
----
@@ -96,6 +97,7 @@ as shown below:
Endpoint endpoint =
context.getEndpoint("file:inbox?pollingConsumerQueueSize=50");
PollingConsumer consumer = endpoint.createPollingConsumer();
+consumer.start();
Exchange exchange = consumer.receive(5000);
----
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/process-eip.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/process-eip.adoc
index 654b09187ac4b..d4acf724f9f2f 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/process-eip.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/process-eip.adoc
@@ -66,13 +66,44 @@ public class MyProcessor implements Processor {
Then in Camel you can call this processor:
-._Java-only: calling a Processor by instantiating it directly_
+[tabs]
+====
+Java::
++
[source,java]
----
from("activemq:myQueue")
.process(new MyProcessor());
----
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: activemq:myQueue
+ steps:
+ - process:
+ ref: myProcessor
+- beans:
+ - name: myProcessor
+ type: com.foo.MyProcessor
+----
+====
+
You can also call a processor by its bean id, if the processor
has been enlisted in the xref:manual::registry.adoc[Registry], such as with the id `myProcessor`:
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/recipientList-eip.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/recipientList-eip.adoc
index f0efa65b63678..5a734e2c8e7a5 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/recipientList-eip.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/recipientList-eip.adoc
@@ -143,10 +143,23 @@ The dynamic list of recipients that are defined in the header must be iterable s
=== Configuring delimiter for dynamic destinations
-In XML DSL you can set the delimiter attribute for setting a delimiter to be used if the header value is a single `String` with multiple separated endpoints.
+You can set the `delimiter` option for setting a delimiter to be used if the header value is a single `String` with multiple separated endpoints.
By default, Camel uses comma as delimiter, but this option lets you specify a custom delimiter to use instead.
-._XML-only:_
+[tabs]
+====
+Java::
++
+In Java DSL, you specify the delimiter as second parameter:
++
+[source,java]
+----
+from("direct:a")
+ .recipientList(header("myHeader"), ";");
+----
+
+XML::
++
[source,xml]
----
@@ -158,8 +171,8 @@ By default, Camel uses comma as delimiter, but this option lets you specify a cu
----
-And in YAML DSL you set the `delimiter` parameter:
-
+YAML::
++
[source,yaml]
----
- route:
@@ -172,21 +185,13 @@ And in YAML DSL you set the `delimiter` parameter:
header:
expression: myHeader
----
+====
So if *myHeader* contains a `String` with the value `"activemq:queue:foo;activemq:topic:hello ; log:bar"`
-then Camel will split the `String` using the delimiter given in the XML that was comma,
+then Camel will split the `String` using the delimiter, which is a semicolon,
resulting into three endpoints to send to.
You can use spaces between the endpoints as Camel will trim the value when it looks up the endpoint to send to.
-In Java DSL, you specify the delimiter as second parameter as shown below:
-
-._Java-only: the delimiter is passed as a second parameter to recipientList_
-[source,java]
-----
-from("direct:a")
- .recipientList(header("myHeader"), ";");
-----
-
=== Using parallel processing
The Recipient List supports `parallelProcessing` similar to what xref:multicast-eip.adoc[Multicast] and xref:split-eip.adoc[Split] EIPs have as well.
@@ -415,10 +420,13 @@ So use this option with a bit of care.
====
For example, in the unit test below, you can see that we multicast the message to three destinations.
-We have a timeout of 2 seconds, which means only the last two messages can be completed within the timeframe.
+We have a timeout of 250 milliseconds, which means only the last two messages can be completed within the timeframe.
This means we will only aggregate the last two which yields a result aggregation which outputs "BC".
-._Java-only: multicast with inline AggregationStrategy and timeout_
+[tabs]
+====
+Java::
++
[source,java]
----
from("direct:start")
@@ -445,6 +453,111 @@ from("direct:b").to("mock:B").setBody(constant("B"));
from("direct:c").to("mock:C").setBody(constant("C"));
----
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1000
+
+
+
+ A
+
+
+
+
+
+
+
+ B
+
+
+
+
+
+
+
+ C
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - multicast:
+ aggregationStrategy: myStrategy
+ parallelProcessing: true
+ timeout: 250
+ steps:
+ - to:
+ uri: direct:a
+ - to:
+ uri: direct:b
+ - to:
+ uri: direct:c
+ - to:
+ uri: mock:result
+- route:
+ from:
+ uri: direct:a
+ steps:
+ - delay:
+ expression:
+ constant:
+ expression: "1000"
+ - to:
+ uri: mock:A
+ - setBody:
+ expression:
+ constant:
+ expression: A
+- route:
+ from:
+ uri: direct:b
+ steps:
+ - to:
+ uri: mock:B
+ - setBody:
+ expression:
+ constant:
+ expression: B
+- route:
+ from:
+ uri: direct:c
+ steps:
+ - to:
+ uri: mock:C
+ - setBody:
+ expression:
+ constant:
+ expression: C
+- beans:
+ - name: myStrategy
+ type: com.foo.MyAggregationStrategy
+----
+====
+
By default, if a timeout occurs the `AggregationStrategy` is not invoked. However, you can implement the `timeout` method:
This allows you to deal with the timeout in the `AggregationStrategy` if you really need to.
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/resumable-eip.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/resumable-eip.adoc
index cd2c2c679afbe..b1a8191f2aa73 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/resumable-eip.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/resumable-eip.adoc
@@ -37,12 +37,45 @@ from("some:component")
.resumable().configuration(kafkaConfigurationBuilder)
.process(this::process);
----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: some:component
+ steps:
+ - resumable:
+ resumeStrategy: kafkaResumeStrategy
+ - process:
+ ref: myProcessor
+----
====
+NOTE: The configuration builder is only available in the Java DSL.
+In XML and YAML, the `resumeStrategy` option refers to a resume strategy bean by its name.
+In this example, `kafkaResumeStrategy` is a `SingleNodeKafkaResumeStrategy` bean set up with the same configuration.
+
=== Intermittent Mode
Enable intermittent mode to avoid updating the offset for every exchange:
+[tabs]
+====
+Java::
++
[source,java]
----
from("some:component")
@@ -50,6 +83,36 @@ from("some:component")
.process(this::process);
----
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: some:component
+ steps:
+ - resumable:
+ resumeStrategy: testResumeStrategy
+ intermittent: true
+ - process:
+ ref: myProcessor
+- beans:
+ - name: testResumeStrategy
+ type: com.foo.MyTestResumeStrategy
+----
+====
+
== See Also
* xref:resume-strategies.adoc[Resume Strategies]
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/resume-strategies.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/resume-strategies.adoc
index 569e0b61619e9..a29e649658209 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/resume-strategies.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/resume-strategies.adoc
@@ -4,6 +4,7 @@
:description: Provide strategies to allow consuming data from specific offsets
:since: 3.16.0
:supportlevel: Experimental
+:tabs-sync-option:
The resume strategies allow users to implement strategies that point the consumer part of the routes to the last point of consumption. This allows Camel to skip reading and processing data that has already been consumed.
@@ -35,7 +36,7 @@ The resume strategies comes in three parts:
== The DSL method
The route needs to use the `resumable()` method followed by passing a strategy configuration using `configuration`.
-It is also possible to use the `resumableStrategy` to point to an instance of the resume strategy in use, although
+It is also possible to use the `resumeStrategy` to point to an instance of the resume strategy in use, although
this is much more complex. The vast majority of the cases should use a `configuration`, in which case Camel will do
the heavy-lifting for you.
@@ -60,7 +61,10 @@ from("some:component")
This instance can be bound in the Context registry as follows:
-._Java-only: binding resume strategy to the registry_
+[tabs]
+====
+Java::
++
[source,java]
----
getCamelContext().getRegistry().bind("testResumeStrategy", new MyTestResumeStrategy());
@@ -71,17 +75,97 @@ from("some:component")
.process(this::process);
----
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- beans:
+ - name: testResumeStrategy
+ type: com.foo.MyTestResumeStrategy
+ - name: resumeCache
+ type: com.foo.MyChoiceOfResumeCache
+ constructors:
+ 0: 100
+- route:
+ from:
+ uri: some:component
+ steps:
+ - resumable:
+ resumeStrategy: testResumeStrategy
+ - process:
+ ref: myProcessor
+----
+====
+
Or the instance can be constructed as follows:
-._Java-only: constructing resume strategy inline_
+[tabs]
+====
+Java::
++
[source,java]
----
getCamelContext().getRegistry().bind("resumeCache", new MyChoiceOfResumeCache<>(100));
from("some:component")
.resumable(new MyTestResumeStrategy())
- .process(this::process)
+ .process(this::process);
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
----
+- beans:
+ - name: resumeCache
+ type: com.foo.MyChoiceOfResumeCache
+ constructors:
+ 0: 100
+- route:
+ from:
+ uri: some:component
+ steps:
+ - resumable:
+ resumeStrategy: "#class:com.foo.MyTestResumeStrategy"
+ - process:
+ ref: myProcessor
+----
+====
In some circumstances, such as when dealing with File I/O, it may be necessary to set the offset manually. There are
*supporting classes* that can help work with resumables:
@@ -94,13 +178,46 @@ In some circumstances, such as when dealing with File I/O, it may be necessary t
In some cases, it may be necessary to avoid updating the offset for every exchange.
You can enable the intermittent mode to modify the route behavior so that missing offsets will not cause an exception:
-._Java-only: enabling intermittent mode on resumable_
+[tabs]
+====
+Java::
++
[source,java]
----
from("some:component")
-.resumable().resumeStrategy(new MyTestResumeStrategy()).intermittent(true)
-.process(this::process)
+ .resumable().resumeStrategy(new MyTestResumeStrategy()).intermittent(true)
+ .process(this::process);
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: some:component
+ steps:
+ - resumable:
+ resumeStrategy: testResumeStrategy
+ intermittent: true
+ - process:
+ ref: myProcessor
+- beans:
+ - name: testResumeStrategy
+ type: com.foo.MyTestResumeStrategy
----
+====
== Builtin Resume Strategies
@@ -140,7 +257,10 @@ When using the converters with the file component, beware of the differences in
For instance, the behavior of:
-._Java-only: resumable with Reader body type_
+[tabs]
+====
+Java::
++
[source,java]
----
from("file:{{input.dir}}?noop=true&fileName={{input.file}}")
@@ -149,9 +269,44 @@ from("file:{{input.dir}}?noop=true&fileName={{input.file}}")
.process(this::process);
----
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: "file:{{input.dir}}"
+ parameters:
+ noop: true
+ fileName: "{{input.file}}"
+ steps:
+ - resumable:
+ resumeStrategy: testResumeStrategy
+ - convertBodyTo:
+ type: java.io.Reader
+ - process:
+ ref: myProcessor
+----
+====
+
It is different from the behavior of:
-._Java-only: resumable with InputStream body type_
+[tabs]
+====
+Java::
++
[source,java]
----
from("file:{{input.dir}}?noop=true&fileName={{input.file}}")
@@ -160,6 +315,38 @@ from("file:{{input.dir}}?noop=true&fileName={{input.file}}")
.process(this::process);
----
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: "file:{{input.dir}}"
+ parameters:
+ noop: true
+ fileName: "{{input.file}}"
+ steps:
+ - resumable:
+ resumeStrategy: testResumeStrategy
+ - convertBodyTo:
+ type: java.io.InputStream
+ - process:
+ ref: myProcessor
+----
+====
+
*Reason*: the `skip` method in the Reader will skip characters, whereas the same method on the InputStream will skip bytes.
@@ -180,14 +367,52 @@ To use the API, it needs an instance of a Consumer listener along with a predica
Usage example:
-._Java-only: using pausable consumer API_
+[tabs]
+====
+Java::
++
[source,java]
----
-from(from)
+from("kafka:topic")
.pausable(new KafkaConsumerListener(), o -> canContinue())
.process(exchange -> LOG.info("Received an exchange: {}", exchange.getMessage().getBody()))
- .to(destination);
+ .to("direct:destination");
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: kafka:topic
+ steps:
+ - pausable:
+ consumerListener: kafkaConsumerListener
+ untilCheck: canContinue
+ - log:
+ message: "Received an exchange: ${body}"
+ - to:
+ uri: direct:destination
+- beans:
+ - name: kafkaConsumerListener
+ type: org.apache.camel.component.kafka.consumer.errorhandler.KafkaConsumerListener
+ - name: canContinue
+ type: com.foo.CanContinuePredicate
----
+====
You can also integrate the pausable API and the consumer listener with the circuit breaker EIP. For instance, it's
possible to configure the circuit breaker so that it can manipulate the state of the listener based on success or on
@@ -199,7 +424,7 @@ On success, it shuts down the scheduled check.
An example implementation of this approach would be similar to this:
-._Java-only: integrating pausable consumer with circuit breaker_
+._Java-only: creating the circuit breaker and binding it to the registry_
[source,java]
----
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("pausable");
@@ -218,21 +443,68 @@ circuitBreaker.getEventPublisher()
if (executorService == null) {
executorService = Executors.newSingleThreadScheduledExecutor();
- // In a real world scenario, instead of incrementing, it could be pinging a remote system or
- // running a similar check to determine whether it's available. That
+ // In a real world scenario, it could be pinging a remote system or
+ // running a similar check to determine whether it's available.
executorService.scheduleAtFixedRate(() -> someCheckMethod(), 1, 1, TimeUnit.SECONDS);
}
});
// Binds the configuration to the registry
- getCamelContext().getRegistry().bind("pausableCircuit", circuitBreaker);
+getCamelContext().getRegistry().bind("pausableCircuit", circuitBreaker);
+----
-from(from)
+The route then uses the circuit breaker by its name in the registry, `pausableCircuit`:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("kafka:topic")
.pausable(new KafkaConsumerListener(), o -> canContinue())
.routeId("pausable-it")
.process(exchange -> LOG.info("Got record from Kafka: {}", exchange.getMessage().getBody()))
.circuitBreaker()
.resilience4jConfiguration().circuitBreaker("pausableCircuit").end()
- .to(to)
+ .to("direct:downstream")
.end();
----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ id: pausable-it
+ from:
+ uri: kafka:topic
+ steps:
+ - pausable:
+ consumerListener: kafkaConsumerListener
+ untilCheck: canContinue
+ - log:
+ message: "Got record from Kafka: ${body}"
+ - circuitBreaker:
+ resilience4jConfiguration:
+ circuitBreaker: pausableCircuit
+ steps:
+ - to:
+ uri: direct:downstream
+----
+====
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/setHeaders-eip.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/setHeaders-eip.adoc
index 2be3b1e8be27d..75cff6e97242e 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/setHeaders-eip.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/setHeaders-eip.adoc
@@ -152,13 +152,13 @@ XML::
-
- ${body}
-
-
- ${header.foo} > 10
-
-
+
+ ${body}
+
+
+ ${header.foo} > 10
+
+
----
@@ -195,7 +195,7 @@ If the order in which the headers should be set is important, use a `LinkedHashM
._Java-only: using a LinkedHashMap to set headers in order_
[source,java]
----
-private Map headerMap = new java.util.LinkedHashMap<>();
+Map headerMap = new java.util.LinkedHashMap<>();
headerMap.put("foo", ConstantLanguage.constant("ABC"));
headerMap.put("bar", ConstantLanguage.constant("XYZ"));
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/setVariables-eip.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/setVariables-eip.adoc
index 40c565ae81043..3540038701fcb 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/setVariables-eip.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/setVariables-eip.adoc
@@ -152,13 +152,13 @@ XML::
-
- ${body}
-
-
- ${variable.foo} > 10
-
-
+
+ ${body}
+
+
+ ${variable.foo} > 10
+
+
----
@@ -195,7 +195,7 @@ If the order in which the variables should be set is important, use a `LinkedHas
._Java-only: using a LinkedHashMap to set variables in order_
[source,java]
----
-private Map variableMap = new java.util.LinkedHashMap<>();
+Map variableMap = new java.util.LinkedHashMap<>();
variableMap.put("foo", ConstantLanguage.constant("ABC"));
variableMap.put("bar", ConstantLanguage.constant("XYZ"));
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/split-eip.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/split-eip.adoc
index e64c8ef3748e1..df5289871bbb1 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/split-eip.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/split-eip.adoc
@@ -77,69 +77,89 @@ YAML::
----
====
-The Split EIP has special support for splitting using a delimiter, instead of using
-xref:components:languages:tokenize-language.adoc[Tokenize] language.
+=== Splitting the message body
+
+A common use case is to split a list/set/collection/map, array, or something that is iterable
+from the message body.
-The previous example can also be done as follows:
+The Split EIP will by default split the message body based on the value type:
+
+- `java.util.Collection`: splits by each element from the collection/list/set.
+- `java.util.Map`: splits by each `Map.Entry` from the map.
+- `Object[]`: splits the array by each element
+- `Iterator`: splits by the iterator
+- `Iterable`: splits by the iterable
+- `org.w3c.dom.NodeList`: splits the XML document by each element from the list
+- `String`: splits the string value by comma as separator
+
+For any other type, the message body is not split, and instead used _as-is_, meaning that
+the Split EIP will be split into a single message (the same).
+
+To use this with the splitter, you should _just_ use body as the expression:
[tabs]
====
-
Java::
+
[source,java]
----
-from("direct:a")
- .split(body()).delimiter("\n")
- .to("direct:b");
+from("direct:splitUsingBody")
+ .split(body())
+ .to("mock:result");
----
XML::
+
+In XML, you use xref:components:languages:simple-language.adoc[Simple] to refer to the message body:
++
[source,xml]
----
-
-
- ${body}
-
+
+
+ ${body}
+
----
YAML::
+
+In YAML, you use xref:components:languages:simple-language.adoc[Simple] to refer to the message body:
++
[source,yaml]
----
- route:
from:
- uri: direct:a
+ uri: direct:splitUsingBody
steps:
- split:
- delimiter: "\n"
expression:
simple:
expression: "${body}"
steps:
- to:
- uri: direct:b
+ uri: mock:result
----
====
-The splitter can use any xref:manual:ROOT:expression.adoc[Expression],
-so you could use any of the supported languages such as
-xref:components:languages:simple-language.adoc[Simple], xref:components:languages:xpath-language.adoc[XPath],
-xref:components:languages:jsonpath-language.adoc[JSonPath], xref:components:languages:groovy-language.adoc[Groovy] to perform the split.
+=== Splitting with a delimiter
+
+The Split EIP has special support for splitting using a delimiter, instead of using
+xref:components:languages:tokenize-language.adoc[Tokenize] language.
+
+The first example can also be done as follows:
[tabs]
====
+
Java::
+
[source,java]
----
-from("activemq:my.queue")
- .split(xpath("//foo/bar"))
- .to("file:some/directory");
+from("direct:a")
+ .split(body()).delimiter("\n")
+ .to("direct:b");
----
XML::
@@ -147,11 +167,11 @@ XML::
[source,xml]
----
-
-
- //foo/bar
-
-
+
+
+ ${body}
+
+
----
@@ -161,37 +181,25 @@ YAML::
----
- route:
from:
- uri: activemq:my.queue
+ uri: direct:a
steps:
- split:
+ delimiter: "\n"
expression:
- xpath:
- expression: //foo/bar
+ simple:
+ expression: "${body}"
steps:
- to:
- uri: file:some/directory
+ uri: direct:b
----
====
-=== Splitting the message body
-
-A common use case is to split a list/set/collection/map, array, or something that is iterable
-from the message body.
-
-The Split EIP will by default split the message body based on the value type:
-
-- `java.util.Collection`: splits by each element from the collection/list/set.
-- `java.util.Map`: splits by each `Map.Entry` from the map.
-- `Object[]`: splits the array by each element
-- `Iterator`: splits by the iterator
-- `Iterable`: splits by the iterable
-- `org.w3c.dom.NodeList`: splits the XML document by each element from the list
-- `String`: splits the string value by comma as separator
-
-For any other type, the message body is not split, and instead used _as-is_, meaning that
-the Split EIP will be split into a single message (the same).
+=== Splitting with an expression
-To use this with the splitter, you should _just_ use body as the expression:
+The splitter can use any xref:manual:ROOT:expression.adoc[Expression],
+so you could use any of the supported languages such as
+xref:components:languages:simple-language.adoc[Simple], xref:components:languages:xpath-language.adoc[XPath],
+xref:components:languages:jsonpath-language.adoc[JSonPath], xref:components:languages:groovy-language.adoc[Groovy] to perform the split.
[tabs]
====
@@ -199,43 +207,39 @@ Java::
+
[source,java]
----
-from("direct:splitUsingBody")
- .split(body())
- .to("mock:result");
+from("activemq:my.queue")
+ .split(xpath("//foo/bar"))
+ .to("file:some/directory");
----
XML::
+
-In XML, you use xref:components:languages:simple-language.adoc[Simple] to refer to the message body:
-+
[source,xml]
----
-
-
- ${body}
-
-
+
+
+ //foo/bar
+
+
----
YAML::
+
-In YAML, you use xref:components:languages:simple-language.adoc[Simple] to refer to the message body:
-+
[source,yaml]
----
- route:
from:
- uri: direct:splitUsingBody
+ uri: activemq:my.queue
steps:
- split:
expression:
- simple:
- expression: "${body}"
+ xpath:
+ expression: //foo/bar
steps:
- to:
- uri: mock:result
+ uri: file:some/directory
----
====
@@ -254,9 +258,9 @@ Java::
----
from("direct:a")
.split(body()).parallelProcessing()
- .to("direct:x")
- .to("direct:y")
- .to("direct:z");
+ .to("direct:b")
+ .to("direct:c")
+ .to("direct:d");
----
XML::
@@ -309,7 +313,7 @@ thread that called the splitter, then make sure to enable the synchronous option
You may want to continue routing the exchange after the Split EIP. In Java DSL you need to use `end()`
to mark where split ends, and where other EIPs can be added to continue the route.
-In the example above then sending to mock:result happens after the Split EIP has finished.
+In the example below then sending to mock:result happens after the Split EIP has finished.
In other words, the message should finish splitting the entire message before the message
continues.
@@ -321,9 +325,9 @@ Java::
----
from("direct:a")
.split(body()).parallelProcessing()
- .to("direct:x")
- .to("direct:y")
- .to("direct:z")
+ .to("direct:b")
+ .to("direct:c")
+ .to("direct:d")
.end()
.to("mock:result");
----
@@ -390,11 +394,11 @@ Java::
+
[source,java]
----
-from("direct:start")
+from("direct:a")
.split(body(), new MyAggregationStrategy())
- .to("direct:x")
- .to("direct:y")
- .to("direct:z")
+ .to("direct:b")
+ .to("direct:c")
+ .to("direct:d")
.end()
.to("mock:result");
----
@@ -515,7 +519,7 @@ Java::
----
from("direct:streaming")
.split(method(new MyCustomSplitter(), "splitMe")).streaming()
- .to("activemq:my.parts")
+ .to("activemq:my.parts");
----
XML::
@@ -729,7 +733,7 @@ YAML::
----
====
-You can set `inheritNamsepaceTagName` property to `*` to include the preceding context in each token (i.e., generating each token enclosed in its ancestor elements). It is noted that each token must share the same ancestor elements in this case.
+You can set `inheritNamespaceTagName` property to `*` to include the preceding context in each token (i.e., generating each token enclosed in its ancestor elements). It is noted that each token must share the same ancestor elements in this case.
The above tokenizer works well on simple structures but has some inherent limitations in handling more complex XML structures.
=== Streaming big XML payloads using XML Tokenize language
@@ -957,7 +961,7 @@ from("direct:start")
// after we have split and handled each message, we want to send a single combined
// response back to the original caller, so we let this bean build it for us,
// this bean will receive the result of the aggregate strategy: MyOrderStrategy
- .to("bean:MyOrderService?method=buildCombinedResponse")
+ .to("bean:MyOrderService?method=buildCombinedResponse");
----
XML::
@@ -1541,6 +1545,42 @@ from("direct:line")
.to("mock:line");
----
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+----
+
Spring XML::
+
[source,xml]
@@ -1583,6 +1623,13 @@ YAML::
+
[source,yaml]
----
+- errorHandler:
+ deadLetterChannel:
+ deadLetterUri: mock:dead
+ useOriginalMessage: true
+ redeliveryPolicy:
+ maximumRedeliveries: 3
+ redeliveryDelay: "0"
- route:
from:
uri: direct:start
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/switch-eip.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/switch-eip.adoc
index ce0d869da7445..5e396c9496b38 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/switch-eip.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/switch-eip.adoc
@@ -166,6 +166,58 @@ reference, `ref:department`, to evaluate the message body once and route the res
Load it alongside the `direct:billing`, `direct:technical`, and `direct:review`
handling routes shown above.
+[tabs]
+====
+Java::
++
+[source,java]
+----
+import static org.apache.camel.semantic.SemanticEvaluationsBuilder.semanticEvaluations;
+
+semanticEvaluations(this)
+ .evaluation("department").expert("decisions")
+ .type("choice")
+ .instructions("Which department should handle this message?")
+ .criterion("billing", "Invoices, payments and refunds")
+ .criterion("technical", "Bugs, outages and technical problems")
+ .criterion("other", "Any other request")
+ .register();
+
+from("direct:classify")
+ .doSwitch().language("semantic", "ref:department")
+ .doCase("billing", "direct:billing")
+ .doCase("technical", "direct:technical")
+ .otherwise("direct:review")
+ .end();
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+ Which department should handle this message?
+
+
+
+
+
+
+
+
+ ref:department
+
+
+
+
+
+
+----
+
+YAML::
++
[source,yaml]
----
- semantic:
@@ -195,6 +247,7 @@ handling routes shown above.
otherwise:
uri: direct:review
----
+====
== Destinations and management
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/toD-eip.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/toD-eip.adoc
index 1f9fb853484e5..e28f6642e4cf6 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/toD-eip.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/toD-eip.adoc
@@ -426,6 +426,10 @@ Kamelet-style deployments you may want to constrain this to a fixed set of compo
option takes a comma-separated allow-list of component schemes; a resolved recipient whose scheme is not in the list
is rejected (independently of `ignoreInvalidEndpoint`). By default the option is unset and any scheme is allowed.
+[tabs]
+====
+Java::
++
[source,java]
----
from("direct:start")
@@ -433,8 +437,8 @@ from("direct:start")
.toD().allowedSchemes("http,https").uri("${header.target}");
----
-And in XML:
-
+XML::
++
[source,xml]
----
@@ -443,16 +447,18 @@ And in XML:
----
-And in YAML:
-
+YAML::
++
[source,yaml]
----
-- from:
- uri: direct:start
- steps:
- - toD:
- uri: "${header.target}"
- allowedSchemes: "http,https"
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - toD:
+ uri: "${header.target}"
+ allowedSchemes: "http,https"
----
+====
NOTE: `wireTap` extends `toD` and therefore honours the same `allowedSchemes` option.
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/tokenizer-eip.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/tokenizer-eip.adoc
index db34196f5afa0..050f8e448224b 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/tokenizer-eip.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/tokenizer-eip.adoc
@@ -59,6 +59,44 @@ from("direct:start")
.split().body()
.to("mock:result");
----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+ ${body}
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - tokenizer:
+ langChain4jParagraphTokenizer:
+ maxTokens: 1024
+ maxOverlap: 10
+ tokenizerType: OPEN_AI
+ - split:
+ expression:
+ simple:
+ expression: "${body}"
+ steps:
+ - to:
+ uri: mock:result
+----
====
=== Tokenize by tokens
@@ -82,6 +120,45 @@ from("direct:start")
.split().body()
.to("mock:result");
----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+ ${body}
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - tokenizer:
+ langChain4jParagraphTokenizer:
+ maxTokens: 1024
+ modelName: gpt-4o-mini
+ maxOverlap: 10
+ tokenizerType: OPEN_AI
+ - split:
+ expression:
+ simple:
+ expression: "${body}"
+ steps:
+ - to:
+ uri: mock:result
+----
====
== Dependencies
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/transactional-client.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/transactional-client.adoc
index 3fc4990987a36..8ec329fb2c668 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/transactional-client.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/transactional-client.adoc
@@ -1,4 +1,5 @@
= Transactional Client
+:tabs-sync-option:
Camel supports the
http://www.enterpriseintegrationpatterns.com/TransactionalClient.html[Transactional
@@ -225,7 +226,7 @@ Spring XML::
-
+
----
====
@@ -235,31 +236,48 @@ Then we have routes where each of the routes uses their different policy:
[tabs]
====
-Spring XML::
+Java::
++
+[source,java]
+----
+from("activemq:queue:inbox")
+ .transacted("txRequired")
+ .to("direct:audit")
+ .to("direct:order")
+ .to("activemq:queue:order");
+
+from("direct:audit")
+ .transacted("txRequiresNew")
+ .bean("auditLogService", "insertAuditLog");
+
+from("direct:order")
+ .transacted("txMandatory")
+ .bean("orderService", "insertOrder");
+----
+
+XML::
+
[source,xml]
----
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
----
YAML::
@@ -315,7 +333,7 @@ The sample is in its full as a
https://github.com/apache/camel/blob/main/components/camel-spring-parent/camel-spring-xml/src/test/java/org/apache/camel/spring/interceptor/TransactionalClientDataSourceWithOnExceptionMinimalConfigurationTest.java[unit test].
First, we set up the usual spring stuff in its configuration file.
-Here we have defined a DataSource to the HSQLDB and most
+Here we have defined a DataSource to the H2 database and most
importantly the Spring `DataSourceTransactionManager` that is doing the
heavy lifting of ensuring our transactional policies.
@@ -359,45 +377,62 @@ as transacted using the `` XML tag.
[tabs]
====
-Spring XML::
+Java::
++
+[source,java]
+----
+from("direct:okay")
+ // mark this route as transacted
+ .transacted()
+ .setBody(constant("Tiger in Action"))
+ .bean("bookService")
+ .setBody(constant("Elephant in Action"))
+ .bean("bookService");
+
+from("direct:fail")
+ // mark this route as transacted
+ .transacted()
+ .setBody(constant("Tiger in Action"))
+ .bean("bookService")
+ .setBody(constant("Donkey in Action"))
+ .bean("bookService");
+----
+
+XML::
+
[source,xml]
----
-
-
-
-
-
-
-
- Tiger in Action
-
-
-
- Elephant in Action
-
-
-
-
-
-
-
-
-
- Tiger in Action
-
-
-
- Donkey in Action
-
-
-
-
-
+
+
+
+
+
+ Tiger in Action
+
+
+
+ Elephant in Action
+
+
+
+
+
+
+
+
+
+ Tiger in Action
+
+
+
+ Donkey in Action
+
+
+
----
YAML::
@@ -449,7 +484,7 @@ Remember to use ``. The rest is standard Spring XML to set up the t
In this sample, we want to listen for messages in a queue and process the
messages with our business logic java code and send them along. Since
it is based on a
-https://github.com/apache/camel/tree/main/components/camel-jms/src/test/java/org/apache/camel/component/jms/tx/TransactionMinimalConfigurationTest.java[unit test],
+https://github.com/apache/camel/blob/main/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/spring/tx/TransactionMinimalConfigurationIT.java[unit test],
the destination is a mock endpoint.
First, we configure the standard Spring XML to declare a JMS connection
@@ -479,7 +514,7 @@ Spring XML::
-
+
@@ -497,22 +532,34 @@ route as transacted using the `` XML tag.
[tabs]
====
-Spring XML::
+Java::
++
+[source,java]
+----
+// 1: from the jms queue
+from("activemq:queue:okay")
+ // 2: mark this route as transacted
+ .transacted()
+ // 3: call our business logic that is myProcessor
+ .process("myProcessor")
+ // 4: if success then send it to the mock
+ .to("mock:result");
+----
+
+XML::
+
[source,xml]
----
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
----
YAML::
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/unmarshal-eip.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/unmarshal-eip.adoc
index a1ef60ca72415..c3556e98eaa25 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/unmarshal-eip.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/unmarshal-eip.adoc
@@ -24,7 +24,53 @@ into a Java object; or some other representation according to the data format be
== Example
-The following example reads XML files from the inbox/xml directory.
+The following example reads JSON files from the inbox/json directory. `unmarshal` turns each file into a Map with
+xref:dataformats:jackson2-dataformat.adoc[Jackson] (a JSON object becomes a Map, a JSON array a List), so the route can
+read its fields, such as `${body[orderId]}`:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("file:inbox/json")
+ .unmarshal().json(JsonLibrary.Jackson)
+ .log("Order ${body[orderId]} for customer ${body[customer]}")
+ .to("bean:validateOrder");
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: file:inbox/json
+ steps:
+ - unmarshal:
+ json:
+ library: Jackson
+ - log:
+ message: "Order ${body[orderId]} for customer ${body[customer]}"
+ - to:
+ uri: bean:validateOrder
+----
+====
+
+The next example reads XML files from the inbox/xml directory.
Each file is then transformed into Java Objects using xref:dataformats:jaxb-dataformat.adoc[JAXB].
Then a xref:ROOT:bean-component.adoc[Bean] is invoked that takes in the Java object.
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/langChain4jCharacterTokenizer.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/langChain4jCharacterTokenizer.json
index d968a5b47359b..3baf8ac1bc842 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/langChain4jCharacterTokenizer.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/langChain4jCharacterTokenizer.json
@@ -17,6 +17,6 @@
"tokenizerType": { "index": 1, "kind": "attribute", "displayName": "Tokenizer Type", "group": "common", "required": true, "type": "enum", "javaType": "org.apache.camel.model.tokenizer.TokenizerType", "enum": [ "OPEN_AI", "AZURE", "QWEN" ], "deprecated": false, "autowired": false, "secret": false, "description": "The tokenizer type. Must be one of OPEN_AI, AZURE or QWEN." },
"maxTokens": { "index": 2, "kind": "attribute", "displayName": "Max Tokens", "group": "common", "required": true, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "The maximum number of tokens on each segment." },
"maxOverlap": { "index": 3, "kind": "attribute", "displayName": "Max Overlap", "group": "common", "required": true, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "The maximum number of tokens that can overlap in each segment." },
- "modelName": { "index": 4, "kind": "attribute", "displayName": "Model Name", "group": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The underlying model name used by the tokenizer. Providing this switches to computing segment sizes in terms of tokens." }
+ "modelName": { "index": 4, "kind": "attribute", "displayName": "Model Name", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The underlying model name used by the tokenizer. Providing this switches to computing segment sizes in terms of tokens." }
}
}
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/langChain4jLineTokenizer.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/langChain4jLineTokenizer.json
index 843cf5419594b..364d2590da983 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/langChain4jLineTokenizer.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/langChain4jLineTokenizer.json
@@ -17,6 +17,6 @@
"tokenizerType": { "index": 1, "kind": "attribute", "displayName": "Tokenizer Type", "group": "common", "required": true, "type": "enum", "javaType": "org.apache.camel.model.tokenizer.TokenizerType", "enum": [ "OPEN_AI", "AZURE", "QWEN" ], "deprecated": false, "autowired": false, "secret": false, "description": "The tokenizer type. Must be one of OPEN_AI, AZURE or QWEN." },
"maxTokens": { "index": 2, "kind": "attribute", "displayName": "Max Tokens", "group": "common", "required": true, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "The maximum number of tokens on each segment." },
"maxOverlap": { "index": 3, "kind": "attribute", "displayName": "Max Overlap", "group": "common", "required": true, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "The maximum number of tokens that can overlap in each segment." },
- "modelName": { "index": 4, "kind": "attribute", "displayName": "Model Name", "group": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The underlying model name used by the tokenizer. Providing this switches to computing segment sizes in terms of tokens." }
+ "modelName": { "index": 4, "kind": "attribute", "displayName": "Model Name", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The underlying model name used by the tokenizer. Providing this switches to computing segment sizes in terms of tokens." }
}
}
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/langChain4jParagraphTokenizer.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/langChain4jParagraphTokenizer.json
index 4118c4afeb50c..c252e73498333 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/langChain4jParagraphTokenizer.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/langChain4jParagraphTokenizer.json
@@ -17,6 +17,6 @@
"tokenizerType": { "index": 1, "kind": "attribute", "displayName": "Tokenizer Type", "group": "common", "required": true, "type": "enum", "javaType": "org.apache.camel.model.tokenizer.TokenizerType", "enum": [ "OPEN_AI", "AZURE", "QWEN" ], "deprecated": false, "autowired": false, "secret": false, "description": "The tokenizer type. Must be one of OPEN_AI, AZURE or QWEN." },
"maxTokens": { "index": 2, "kind": "attribute", "displayName": "Max Tokens", "group": "common", "required": true, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "The maximum number of tokens on each segment." },
"maxOverlap": { "index": 3, "kind": "attribute", "displayName": "Max Overlap", "group": "common", "required": true, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "The maximum number of tokens that can overlap in each segment." },
- "modelName": { "index": 4, "kind": "attribute", "displayName": "Model Name", "group": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The underlying model name used by the tokenizer. Providing this switches to computing segment sizes in terms of tokens." }
+ "modelName": { "index": 4, "kind": "attribute", "displayName": "Model Name", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The underlying model name used by the tokenizer. Providing this switches to computing segment sizes in terms of tokens." }
}
}
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/langChain4jSentenceTokenizer.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/langChain4jSentenceTokenizer.json
index 1d8328d7fc916..869f54bc15785 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/langChain4jSentenceTokenizer.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/langChain4jSentenceTokenizer.json
@@ -17,6 +17,6 @@
"tokenizerType": { "index": 1, "kind": "attribute", "displayName": "Tokenizer Type", "group": "common", "required": true, "type": "enum", "javaType": "org.apache.camel.model.tokenizer.TokenizerType", "enum": [ "OPEN_AI", "AZURE", "QWEN" ], "deprecated": false, "autowired": false, "secret": false, "description": "The tokenizer type. Must be one of OPEN_AI, AZURE or QWEN." },
"maxTokens": { "index": 2, "kind": "attribute", "displayName": "Max Tokens", "group": "common", "required": true, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "The maximum number of tokens on each segment." },
"maxOverlap": { "index": 3, "kind": "attribute", "displayName": "Max Overlap", "group": "common", "required": true, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "The maximum number of tokens that can overlap in each segment." },
- "modelName": { "index": 4, "kind": "attribute", "displayName": "Model Name", "group": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The underlying model name used by the tokenizer. Providing this switches to computing segment sizes in terms of tokens." }
+ "modelName": { "index": 4, "kind": "attribute", "displayName": "Model Name", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The underlying model name used by the tokenizer. Providing this switches to computing segment sizes in terms of tokens." }
}
}
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/langChain4jWordTokenizer.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/langChain4jWordTokenizer.json
index 57e842b9d8145..b5d89c3e823b2 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/langChain4jWordTokenizer.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/langChain4jWordTokenizer.json
@@ -17,6 +17,6 @@
"tokenizerType": { "index": 1, "kind": "attribute", "displayName": "Tokenizer Type", "group": "common", "required": true, "type": "enum", "javaType": "org.apache.camel.model.tokenizer.TokenizerType", "enum": [ "OPEN_AI", "AZURE", "QWEN" ], "deprecated": false, "autowired": false, "secret": false, "description": "The tokenizer type. Must be one of OPEN_AI, AZURE or QWEN." },
"maxTokens": { "index": 2, "kind": "attribute", "displayName": "Max Tokens", "group": "common", "required": true, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "The maximum number of tokens on each segment." },
"maxOverlap": { "index": 3, "kind": "attribute", "displayName": "Max Overlap", "group": "common", "required": true, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "The maximum number of tokens that can overlap in each segment." },
- "modelName": { "index": 4, "kind": "attribute", "displayName": "Model Name", "group": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The underlying model name used by the tokenizer. Providing this switches to computing segment sizes in terms of tokens." }
+ "modelName": { "index": 4, "kind": "attribute", "displayName": "Model Name", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The underlying model name used by the tokenizer. Providing this switches to computing segment sizes in terms of tokens." }
}
}
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
index 278dd6cd0df9f..8edbc97f545fd 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
@@ -14551,7 +14551,7 @@ The maximum number of tokens that can overlap in each segment.
-
+
-
+
");
+ "semantic-language", "",
+ "switch-eip", "");
private static CamelCatalog catalog;
private static Schema springSchema;
diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/aggregate-eip.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/aggregate-eip.adoc
index db2ec5a92e58e..9a968b7654f7b 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/aggregate-eip.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/aggregate-eip.adoc
@@ -114,7 +114,10 @@ be used for creating commonly used aggregation strategies without having to crea
In the route below we group all the exchanges together using
`GroupedExchangeAggregationStrategy`:
-._Java-only: using GroupedExchangeAggregationStrategy_
+[tabs]
+====
+Java::
++
[source,java]
----
from("direct:start")
@@ -126,6 +129,44 @@ from("direct:start")
.completionTimeout(500L).to("mock:result");
----
+XML::
++
+[source,xml]
+----
+
+
+
+
+ true
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - aggregate:
+ aggregationStrategy: groupedExchangeStrategy
+ completionTimeout: 500
+ correlationExpression:
+ constant:
+ expression: "true"
+ steps:
+ - to:
+ uri: mock:result
+- beans:
+ - name: groupedExchangeStrategy
+ type: org.apache.camel.processor.aggregate.GroupedExchangeAggregationStrategy
+----
+====
+
As a result we have one outgoing `Exchange` being
routed to the `"mock:result"` endpoint. The exchange is a holder
containing all the incoming Exchanges.
@@ -238,6 +279,61 @@ be used at the same time.
Completion is mandatory and must be configured on the aggregation.
+For example, to collect the order lines of each order into a List, and send the order on when it has 10 lines, or when
+no new line has arrived for 5 seconds:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:lines")
+ .aggregate(header("orderId"), new GroupedBodyAggregationStrategy())
+ .completionSize(10)
+ .completionTimeout(5000)
+ .to("direct:order");
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:lines
+ steps:
+ - aggregate:
+ correlationExpression:
+ header:
+ expression: orderId
+ aggregationStrategy: groupedBodies
+ completionSize: 10
+ completionTimeout: 5000
+ steps:
+ - to:
+ uri: direct:order
+- beans:
+ - name: groupedBodies
+ type: org.apache.camel.processor.aggregate.GroupedBodyAggregationStrategy
+----
+====
+
=== Pre-completion mode
There can be use-cases where you want the incoming
@@ -397,7 +493,7 @@ from("direct:start")
.aggregate(header("id"), new MyAggregationStrategy())
.completionSize(10).id("myAggregator")
.aggregateController(controller)
- .to("mock:aggregated");
+ .to("mock:result");
----
XML::
@@ -409,7 +505,7 @@ XML::
-
@@ -429,6 +525,7 @@ YAML::
uri: direct:start
steps:
- aggregate:
+ id: myAggregator
aggregateController: myController
aggregationStrategy: myAppender
completionSize: 10
@@ -598,9 +695,14 @@ YAML::
----
====
-In Java DSL, you can also provide the bean class type directly:
+You can also provide the bean class type directly.
+In Java DSL you pass the class to `AggregationStrategies.bean`, and in XML or YAML DSL
+you specify the bean class in `aggregationStrategy` using the `#class:` syntax as shown:
-._Java-only: using AggregationStrategies.bean with class type_
+[tabs]
+====
+Java::
++
[source,java]
----
public void configure() throws Exception {
@@ -611,10 +713,49 @@ public void configure() throws Exception {
}
----
+XML::
++
+[source,xml]
+----
+
+
+
+
+ true
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - aggregate:
+ aggregationStrategy: "#class:com.foo.MyBodyAppender"
+ aggregationStrategyMethodName: append
+ completionSize: 3
+ correlationExpression:
+ constant:
+ expression: "true"
+ steps:
+ - to:
+ uri: mock:result
+----
+====
+
And if the bean has only one method, we do not need to specify the name
of the method:
-._Java-only: using AggregationStrategies.bean without method name_
+[tabs]
+====
+Java::
++
[source,java]
----
public void configure() throws Exception {
@@ -625,33 +766,13 @@ public void configure() throws Exception {
}
----
-And the `append` method could be static:
-
-._Java-only: bean class with static method_
-[source,java]
-----
-public class MyBodyAppender {
-
- public static String append(String existing, String next) {
- return next + existing;
- }
-
-}
-----
-
-When using XML or YAML DSL, you can also specify the bean class directly in `aggregationStrategy`
-using the `#class:` syntax as shown:
-
-[tabs]
-====
-
XML::
+
[source,xml]
----
-
+
true
@@ -670,7 +791,6 @@ YAML::
steps:
- aggregate:
aggregationStrategy: "#class:com.foo.MyBodyAppender"
- aggregationStrategyMethodName: append
completionSize: 3
correlationExpression:
constant:
@@ -681,17 +801,35 @@ YAML::
----
====
+And the `append` method could be static:
+
+._Java-only: bean class with static method_
+[source,java]
+----
+public class MyBodyAppender {
+
+ public static String append(String existing, String next) {
+ return next + existing;
+ }
+
+}
+----
+
=== Aggregating when no data
When using bean as `AggregationStrategy`, then the method is
*only* invoked when there is data to be aggregated, meaning that the message body
is not `null`. In cases where you want to have the method invoked, even when there are no data (message body is `null`),
-then set the `strategyMethodAllowNull` to `true`.
+then set the `aggregationStrategyMethodAllowNull` to `true`.
-When using beans, this can be configured a bit easier using the `beanAllowNull` method
-from `AggregationStrategies` as shown:
+When using beans in Java DSL, this can be configured a bit easier using the `beanAllowNull` method
+from `AggregationStrategies`. In XML and YAML DSL you set the `aggregationStrategyMethodAllowNull`
+option to `true`, as shown:
-._Java-only: using AggregationStrategies.beanAllowNull_
+[tabs]
+====
+Java::
++
[source,java]
----
public void configure() throws Exception {
@@ -701,6 +839,43 @@ public void configure() throws Exception {
}
----
+XML::
++
+[source,xml]
+----
+
+
+
+ seda:foo
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - pollEnrich:
+ expression:
+ constant:
+ expression: seda:foo
+ timeout: 1000
+ aggregationStrategy: myAppender
+ aggregationStrategyMethodName: append
+ aggregationStrategyMethodAllowNull: true
+ - to:
+ uri: mock:result
+----
+====
+
Then the `append` method in the bean would need to deal with the
situation that `newExchange` can be `null`:
@@ -730,11 +905,21 @@ If we didn't do this, then on timeout the append method would normally not be
invoked, meaning the xref:content-enricher.adoc[Content Enricher] did
not merge/change the message.
-In XML and YAML DSL you would configure the `strategyMethodAllowNull` option and
-set it to `true` as shown below:
+The same works with the aggregate EIP, as shown below:
[tabs]
====
+Java::
++
+[source,java]
+----
+public void configure() throws Exception {
+ from("direct:start")
+ .aggregate(constant(true), AggregationStrategies.beanAllowNull(appender, "append"))
+ .completionSize(3)
+ .to("mock:result");
+}
+----
XML::
+
@@ -780,7 +965,7 @@ YAML::
=== Aggregating with different body types
-When, for example, using `strategyMethodAllowNull` as `true`, then the
+When, for example, using `aggregationStrategyMethodAllowNull` as `true`, then the
parameter type of the message bodies does not have to be the same.
For example suppose we want to aggregate from a `com.foo.User` type to a
`List` that contains the name of the user. We could code a bean as follows:
diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/cache-eip.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/cache-eip.adoc
index b9cca49139629..2a293a62150a7 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/cache-eip.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/cache-eip.adoc
@@ -21,6 +21,10 @@ management (bulk ops, invalidation, event listening).
The Cache *EIP* covers the most common use case — _"cache the result of this expensive block by key with a TTL"_ —
without manual plumbing. It is analogous to `@Cacheable` in Spring:
+[tabs]
+====
+Java::
++
[source,java]
----
// Without Cache EIP: 7+ lines of plumbing, tied to one cache technology
@@ -29,7 +33,7 @@ from("direct:start")
.setHeader("CamelCaffeineKey", header("productId"))
.to("caffeine-cache:products")
.choice()
- .when(body().isNull())
+ .when(header("CamelCaffeineActionHasResult").isEqualTo(false))
.to("http://expensive-service")
.setHeader("CamelCaffeineAction", constant("PUT"))
.to("caffeine-cache:products")
@@ -42,6 +46,95 @@ from("direct:start")
.end();
----
+XML::
++
+[source,xml]
+----
+
+
+
+
+ GET
+
+
+
+
+
+
+
+ ${header.CamelCaffeineActionHasResult} == false
+
+
+ PUT
+
+
+
+
+
+
+
+
+
+
+ ${header.productId}
+
+
+
+----
+
+YAML::
++
+// yaml-validator: skip
+[source,yaml]
+----
+# Without Cache EIP: manual plumbing, tied to one cache technology
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - setHeader:
+ name: CamelCaffeineAction
+ expression:
+ constant:
+ expression: GET
+ - setHeader:
+ name: CamelCaffeineKey
+ expression:
+ header:
+ expression: productId
+ - to:
+ uri: caffeine-cache:products
+ - choice:
+ when:
+ - expression:
+ simple:
+ expression: "${header.CamelCaffeineActionHasResult} == false"
+ steps:
+ - to:
+ uri: http://expensive-service
+ - setHeader:
+ name: CamelCaffeineAction
+ expression:
+ constant:
+ expression: PUT
+ - to:
+ uri: caffeine-cache:products
+
+# With Cache EIP: backend-agnostic
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - cache:
+ expression:
+ simple:
+ expression: "${header.productId}"
+ steps:
+ - to:
+ uri: http://expensive-service
+----
+====
+
== Options
// eip options: START
@@ -60,6 +153,10 @@ Cache errors are handled gracefully:
Cache with an auto-created in-memory store (zero config):
+[tabs]
+====
+Java::
++
[source,java]
----
from("direct:start")
@@ -70,6 +167,45 @@ from("direct:start")
.to("direct:continue");
----
+XML::
++
+[source,xml]
+----
+
+
+
+ ${header.productId}
+
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - cache:
+ expression:
+ simple:
+ expression: "${header.productId}"
+ steps:
+ - to:
+ uri: http://product-service/api/product
+ - unmarshal:
+ json: {}
+ - to:
+ uri: direct:continue
+----
+====
+
Cache with TTL and an explicit `KeyValueRepository`:
[tabs]
@@ -105,16 +241,21 @@ YAML::
+
[source,yaml]
----
-- from:
- uri: "direct:start"
- steps:
- - cache:
- simple: "${header.productId}"
- keyValueRepository: "myCache"
- ttl: "10m"
- steps:
- - to: "http://product-service/api/product"
- - to: "direct:continue"
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - cache:
+ expression:
+ simple:
+ expression: "${header.productId}"
+ keyValueRepository: myCache
+ ttl: 10m
+ steps:
+ - to:
+ uri: http://product-service/api/product
+ - to:
+ uri: direct:continue
----
====
@@ -151,6 +292,10 @@ public KeyValueRepository myCache() {
Then reference it in the route:
+[tabs]
+====
+Java::
++
[source,java]
----
from("direct:start")
@@ -160,6 +305,38 @@ from("direct:start")
.end();
----
+XML::
++
+[source,xml]
+----
+
+
+
+ ${header.key}
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - cache:
+ expression:
+ simple:
+ expression: "${header.key}"
+ keyValueRepository: myCache
+ steps:
+ - to:
+ uri: http://service
+----
+====
+
If a single `KeyValueRepository` bean exists in the registry, it is auto-discovered — no explicit reference needed.
NOTE: When no `KeyValueRepository` bean is registered, each `.cache()` block auto-creates its own isolated in-memory
@@ -187,6 +364,10 @@ public KeyValueRepository productCache() {
}
----
+[tabs]
+====
+Java::
++
[source,java]
----
// Route that caches product lookups
@@ -208,8 +389,8 @@ from("jms:topic:product.changes")
.log("Invalidated cache for product ${header.productId}");
----
-=== XML DSL
-
+XML::
++
[source,xml]
----
@@ -228,10 +409,45 @@ from("jms:topic:product.changes")
----
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:getProduct
+ steps:
+ - cache:
+ expression:
+ simple:
+ expression: "${header.productId}"
+ keyValueRepository: productCache
+ ttl: 30m
+ steps:
+ - to:
+ uri: http://product-service/api/product
+
+# Invalidation route
+- route:
+ from:
+ uri: jms:topic:product.changes
+ steps:
+ - bean:
+ ref: productCache
+ method: "delete(${header.productId})"
+ - log:
+ message: "Invalidated cache for product ${header.productId}"
+----
+====
+
=== Clearing the Entire Cache
To clear all entries from a `KeyValueRepository`, call `clear()`:
+[tabs]
+====
+Java::
++
[source,java]
----
from("direct:clearAll")
@@ -241,3 +457,27 @@ from("direct:clearAll")
cache.clear();
});
----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:clearAll
+ steps:
+ - bean:
+ ref: productCache
+ method: clear
+----
+====
diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/choice-eip.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/choice-eip.adoc
index c9f0645698c2d..4a42cbb1ef27f 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/choice-eip.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/choice-eip.adoc
@@ -220,7 +220,7 @@ The Camel xref:languages:simple-language.adoc[Simple] language
is great to use with the Choice EIP in precondition mode to select a specific branch based
on xref:manual:ROOT:using-propertyplaceholder.adoc[property placeholders].
-Here we select from the Switch the first predicate that matches. So if there is
+Here we select from the Choice the first predicate that matches. So if there is
a property placeholder with the key `foo` then it is selected, and so on.
Notice how we can use `{{?foo}}` to mark the property placeholder as optional.
diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/circuitBreaker-eip.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/circuitBreaker-eip.adoc
index 21d3a5f87e5af..afc040ed39e1b 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/circuitBreaker-eip.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/circuitBreaker-eip.adoc
@@ -49,38 +49,7 @@ what happened to the call itself:
`CamelCircuitBreakerState` is the state of the breaker after the call was recorded, so the call that opens the
breaker reports `OPEN` although it was attempted; use the rejected property to know whether a call was made.
The caught exception is available in the fallback, for example as `${exception.message}` in the Simple language.
-
-For example, a fallback that answers differently when the service is down rather than failing once:
-
-[source,yaml]
-----
-- route:
- from:
- uri: timer:stock?period=1000
- steps:
- - circuitBreaker:
- steps:
- - to:
- uri: direct:supplier
- onFallback:
- steps:
- - choice:
- when:
- - expression:
- simple:
- expression: "${exchangeProperty.CamelCircuitBreakerResponseRejected} == true"
- steps:
- - setBody:
- expression:
- constant:
- expression: "supplier is down, using the last known stock"
- otherwise:
- steps:
- - setBody:
- expression:
- constant:
- expression: "stock check failed, will try again"
-----
+The example under _Using the exchange properties in the fallback_ below uses these properties.
== Example
@@ -145,6 +114,89 @@ YAML::
----
====
+=== Using the exchange properties in the fallback
+
+A fallback that answers differently when the service is down rather than failing once:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("timer:stock?period=1000")
+ .circuitBreaker()
+ .to("direct:supplier")
+ .onFallback()
+ .choice()
+ .when(simple("${exchangeProperty.CamelCircuitBreakerResponseRejected} == true"))
+ .setBody(constant("supplier is down, using the last known stock"))
+ .otherwise()
+ .setBody(constant("stock check failed, will try again"))
+ .end()
+ .end();
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+
+ ${exchangeProperty.CamelCircuitBreakerResponseRejected} == true
+
+ supplier is down, using the last known stock
+
+
+
+
+ stock check failed, will try again
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: timer:stock?period=1000
+ steps:
+ - circuitBreaker:
+ steps:
+ - to:
+ uri: direct:supplier
+ onFallback:
+ steps:
+ - choice:
+ when:
+ - expression:
+ simple:
+ expression: "${exchangeProperty.CamelCircuitBreakerResponseRejected} == true"
+ steps:
+ - setBody:
+ expression:
+ constant:
+ expression: "supplier is down, using the last known stock"
+ otherwise:
+ steps:
+ - setBody:
+ expression:
+ constant:
+ expression: "stock check failed, will try again"
+----
+====
+
== Route lifecycle and breaker state
When a route is stopped and started, the circuit breaker state is reset (an OPEN breaker returns to CLOSED)
diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/claimCheck-eip.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/claimCheck-eip.adoc
index da500600b3c0e..4582a9a513089 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/claimCheck-eip.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/claimCheck-eip.adoc
@@ -136,104 +136,6 @@ You can also remove headers when merging data back. For example, to remove all h
Note you cannot have both include (`+`) and exclude (`-`) `header:pattern` at the same time.
-== Dynamic keys
-
-The claim check keys are static, but you can use the `simple` language syntax to define dynamic keys.
-For example, to use a header from the message named `myKey`:
-
-[tabs]
-====
-Java::
-+
-[source,java]
-----
-from("direct:start")
- .to("mock:a")
- .claimCheck(ClaimCheckOperation.Set, "${header.myKey}")
- .transform().constant("Bye World")
- .to("mock:b")
- .claimCheck(ClaimCheckOperation.Get, "${header.myKey}")
- .to("mock:c")
- .transform().constant("Hi World")
- .to("mock:d")
- .claimCheck(ClaimCheckOperation.Get, "${header.myKey}")
- .to("mock:e");
-----
-
-XML::
-+
-[source,xml]
-----
-
-
-
-
-
- Bye World
-
-
-
-
-
- Hi World
-
-
-
-
-
-----
-
-YAML::
-+
-[source,yaml]
-----
-- route:
- from:
- uri: direct
- parameters:
- name: start
- steps:
- - to:
- uri: mock
- parameters:
- name: a
- - claimCheck:
- operation: Set
- key: "${header.myKey}"
- - transform:
- expression:
- constant:
- expression: Bye World
- - to:
- uri: mock
- parameters:
- name: b
- - claimCheck:
- operation: Get
- key: "${header.myKey}"
- - to:
- uri: mock
- parameters:
- name: c
- - transform:
- expression:
- constant:
- expression: Hi World
- - to:
- uri: mock
- parameters:
- name: d
- - claimCheck:
- operation: Get
- key: "${header.myKey}"
- - to:
- uri: mock
- parameters:
- name: e
-----
-
-====
-
== Example
The following example shows the `Push` and `Pop` operations in action:
@@ -451,3 +353,101 @@ YAML::
uri: mock:c
----
====
+
+== Dynamic keys
+
+The claim check keys are static, but you can use the `simple` language syntax to define dynamic keys.
+For example, to use a header from the message named `myKey`:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:start")
+ .to("mock:a")
+ .claimCheck(ClaimCheckOperation.Set, "${header.myKey}")
+ .transform().constant("Bye World")
+ .to("mock:b")
+ .claimCheck(ClaimCheckOperation.Get, "${header.myKey}")
+ .to("mock:c")
+ .transform().constant("Hi World")
+ .to("mock:d")
+ .claimCheck(ClaimCheckOperation.Get, "${header.myKey}")
+ .to("mock:e");
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+ Bye World
+
+
+
+
+
+ Hi World
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct
+ parameters:
+ name: start
+ steps:
+ - to:
+ uri: mock
+ parameters:
+ name: a
+ - claimCheck:
+ operation: Set
+ key: "${header.myKey}"
+ - transform:
+ expression:
+ constant:
+ expression: Bye World
+ - to:
+ uri: mock
+ parameters:
+ name: b
+ - claimCheck:
+ operation: Get
+ key: "${header.myKey}"
+ - to:
+ uri: mock
+ parameters:
+ name: c
+ - transform:
+ expression:
+ constant:
+ expression: Hi World
+ - to:
+ uri: mock
+ parameters:
+ name: d
+ - claimCheck:
+ operation: Get
+ key: "${header.myKey}"
+ - to:
+ uri: mock
+ parameters:
+ name: e
+----
+
+====
diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/content-enricher.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/content-enricher.adoc
index ce81a8f04875a..e652a28767daa 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/content-enricher.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/content-enricher.adoc
@@ -108,18 +108,14 @@ YAML::
----
- route:
from:
- uri: direct
- parameters:
- name: start
+ uri: direct:start
steps:
- setBody:
expression:
simple:
expression: "${body} World!"
- to:
- uri: mock
- parameters:
- name: result
+ uri: mock:result
----
====
@@ -188,16 +184,12 @@ YAML::
----
- route:
from:
- uri: direct
- parameters:
- name: start
+ uri: direct:start
steps:
- process:
ref: myProcessor
- to:
- uri: mock
- parameters:
- name: result
+ uri: mock:result
----
====
@@ -223,9 +215,9 @@ XML::
[source,xml]
----
-
+
-
+
----
@@ -235,13 +227,13 @@ YAML::
----
- route:
from:
- uri: activemq:Input
+ uri: activemq:My.Queue
steps:
- bean:
ref: myBeanName
method: doTransform
- to:
- uri: activemq:Output
+ uri: activemq:Another.Queue
----
====
diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/content-filter-eip.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/content-filter-eip.adoc
index 355a53ac163a6..ddaa880c2ba31 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/content-filter-eip.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/content-filter-eip.adoc
@@ -15,6 +15,54 @@ image::eip/ContentFilter.gif[image]
* Using a xref:manual::processor.adoc[Processor] with the filtering programmed in Java
* Using an xref:manual::expression.adoc[Expression]
+== Message Content filtering using expression
+
+Some languages like xref:languages:xpath-language.adoc[XPath], and xref:languages:xquery-language.adoc[XQuery]
+can be used to transform and filter content from messages.
+
+In the example we use xpath to filter a XML message to select all the `` elements:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("activemq:Input")
+ .setBody().xpath("//foo/bar")
+ .to("activemq:Output");
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+ //foo/bar
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: activemq:Input
+ steps:
+ - setBody:
+ expression:
+ xpath:
+ expression: //foo/bar
+ - to:
+ uri: activemq:Output
+----
+====
+
== Message Content filtering using a Processor
In this example, we add our own xref:manual::processor.adoc[Processor] using
@@ -84,16 +132,12 @@ YAML::
----
- route:
from:
- uri: direct
- parameters:
- name: start
+ uri: direct:start
steps:
- process:
ref: myProcessor
- to:
- uri: mock
- parameters:
- name: result
+ uri: mock:result
----
====
@@ -109,9 +153,9 @@ Java::
+
[source,java]
----
-from("activemq:My.Queue")
+from("activemq:Input")
.bean("myBeanName", "doFilter")
- .to("activemq:Another.Queue");
+ .to("activemq:Output");
----
XML::
@@ -140,50 +184,3 @@ YAML::
uri: activemq:Output
----
====
-
-== Message Content filtering using expression
-
-Some languages like xref:languages:xpath-language.adoc[XPath], and xref:languages:xquery-language.adoc[XQuery]
-can be used to transform and filter content from messages.
-
-In the example we use xpath to filter a XML message to select all the `` elements:
-
-[tabs]
-====
-Java::
-+
-[source,java]
-----
-from("activemq:Input")
- .setBody().xpath("//foo:bar")
- .to("activemq:Output");
-----
-
-XML::
-+
-[source,xml]
-----
-
-
-
- //foo:bar
-
-
-
-----
-
-YAML::
-+
-[source,yaml]
-----
-- route:
- from:
- uri: activemq:Input
- steps:
- - setBody:
- expression:
- xpath: //foo:bar
- - to:
- uri: activemq:Output
-----
-====
diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/dead-letter-channel.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/dead-letter-channel.adoc
index 2df230324a93a..b6148e33bb4b4 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/dead-letter-channel.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/dead-letter-channel.adoc
@@ -61,9 +61,23 @@ Pay attention to Spring XML DSL. The type attribute is used to declare which err
And in XML the `` must be configured with an id.
The id must then be enabled on either the `` (global scope), or per route that should use this error handler.
-The following listing is an equivalent of the prior Java DSL listing:
+The following listing shows the error handler together with a route:
-._XML-only:_
+[tabs]
+====
+Java::
++
+[source,java]
+----
+errorHandler(deadLetterChannel("log:dead?level=ERROR"));
+
+from("direct:newOrder")
+ .bean("orderService", "validate")
+ .bean("orderService", "store");
+----
+
+Spring XML::
++
[source,xml]
----
@@ -77,9 +91,30 @@ The following listing is an equivalent of the prior Java DSL listing:
----
-Notice how we have enabled the error handler on the `` via `errorHandlerRef`
+YAML::
++
+[source,yaml]
+----
+- errorHandler:
+ deadLetterChannel:
+ deadLetterUri: "log:dead?level=ERROR"
+- route:
+ from:
+ uri: direct:newOrder
+ steps:
+ - bean:
+ ref: orderService
+ method: validate
+ - bean:
+ ref: orderService
+ method: store
+----
+====
+
+Notice how in Spring XML we have enabled the error handler on the `` via `errorHandlerRef`
which means this error handler is used globally. You can override this per route, and have
individual routes used another error handler.
+In Java and YAML, the error handler applies to the routes in the same `RouteBuilder` class or YAML file.
NOTE: The DSLs is planned to be improved in the near future to have a unified
way of configuring error handling across all DSLs with xref:manual::route-configuration.adoc[Route Configuration].
@@ -264,7 +299,7 @@ Java::
----
// will use the original message (body and headers)
errorHandler(deadLetterChannel("jms:queue:dead")
- .useOriginalMessage().maximumRedeliveries(5).redeliveryDelay(5000);
+ .useOriginalMessage().maximumRedeliveries(5).redeliveryDelay(5000));
----
Spring XML::
@@ -324,7 +359,7 @@ Java::
----
errorHandler(deadLetterChannel("jms:queue:dead")
.maximumRedeliveries(3)
- .onRedelivery(new MyOnRedeliveryProcessor()));
+ .onRedelivery(new MyRedeliveryProcessor()));
----
Spring XML::
@@ -396,8 +431,7 @@ Configuring this from Spring XML is done with the `onPrepareFailureRef` to refer
+
[source,xml]
----
-
+
----
diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/delay-eip.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/delay-eip.adoc
index 53c147d95a68f..ad22500d3731a 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/delay-eip.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/delay-eip.adoc
@@ -69,9 +69,9 @@ Note that delay creates its own block, so some DSLs (including Java) require the
._Java-only: closing the delay block with end() in Java DSL_
[source,java]
----
-.from("direct:delayBlockExample")
+from("direct:delayBlockExample")
.to("direct:getJobState")
- .loopDoWhile(simple("${body.state} = 'NOT_DONE'"))
+ .loopDoWhile(simple("${body.state} == 'NOT_DONE'"))
.log(LoggingLevel.DEBUG, "Pausing")
.delay(10000)
.syncDelayed()
diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/idempotentConsumer-eip.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/idempotentConsumer-eip.adoc
index 29c614a03d459..d4f2a23cbc95e 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/idempotentConsumer-eip.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/idempotentConsumer-eip.adoc
@@ -59,4 +59,140 @@ xref:ROOT:sql-component.adoc[JdbcOrphanLockAwareIdempotentRepository]
== Example
-For example, see the above implementations for more details.
+The idempotent consumer needs an expression that gives the id of each message, and a repository that remembers the ids
+it has seen. In the example below the id is the `messageId` header, and the repository is a `MemoryIdempotentRepository`,
+which keeps the last 1000 ids in memory. A message with an id that is already in the repository is skipped, so it does
+not reach `mock:result`.
+
+In XML and YAML the repository is a bean, and the idempotent consumer refers to it by name.
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:start")
+ .idempotentConsumer(header("messageId"), MemoryIdempotentRepository.memoryIdempotentRepository())
+ .to("mock:result");
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- beans:
+ - name: myRepo
+ type: org.apache.camel.support.processor.idempotent.MemoryIdempotentRepository
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - idempotentConsumer:
+ expression:
+ header:
+ expression: messageId
+ idempotentRepository: myRepo
+ steps:
+ - to:
+ uri: mock:result
+----
+====
+
+The memory repository forgets the ids when the application stops. To keep them, use `FileIdempotentRepository` or one
+of the repositories listed above.
+
+=== Handling the duplicates in the route
+
+By default, a duplicate is skipped. When `skipDuplicate` is `false`, a duplicate goes through the idempotent consumer
+too, and the exchange property `CamelDuplicateMessage` is `true`. The route can then decide what to do with it, for
+example log it:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:start")
+ .idempotentConsumer(header("messageId"), MemoryIdempotentRepository.memoryIdempotentRepository())
+ .skipDuplicate(false)
+ .choice()
+ .when(simple("${exchangeProperty.CamelDuplicateMessage} == true"))
+ .log("Message ${header.messageId} is a duplicate")
+ .otherwise()
+ .to("mock:result")
+ .end()
+ .end();
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+
+
+ ${exchangeProperty.CamelDuplicateMessage} == true
+
+
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- beans:
+ - name: myRepo
+ type: org.apache.camel.support.processor.idempotent.MemoryIdempotentRepository
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - idempotentConsumer:
+ expression:
+ header:
+ expression: messageId
+ idempotentRepository: myRepo
+ skipDuplicate: false
+ steps:
+ - choice:
+ when:
+ - expression:
+ simple:
+ expression: "${exchangeProperty.CamelDuplicateMessage} == true"
+ steps:
+ - log:
+ message: "Message ${header.messageId} is a duplicate"
+ otherwise:
+ steps:
+ - to:
+ uri: mock:result
+----
+====
diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/kamelet-eip.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/kamelet-eip.adoc
index 16c97466223d1..d89489cc3fbdc 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/kamelet-eip.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/kamelet-eip.adoc
@@ -72,6 +72,7 @@ XML::
YAML::
+
+// yaml-validator: skip
[source,yaml]
----
- route:
diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/log-eip.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/log-eip.adoc
index 41980bb8df0eb..faa35aa7cd9ba 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/log-eip.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/log-eip.adoc
@@ -276,7 +276,7 @@ Java::
Logger myLogger = org.slf4j.LoggerFactory.getLogger("com.mycompany.mylogger");
from("direct:moo")
- .log(myLogger, "Me Got ${body}")
+ .log(LoggingLevel.INFO, myLogger, "Me Got ${body}")
.to("bean:foo");
----
@@ -284,14 +284,16 @@ XML::
+
[source,xml]
----
-
-
+
+
+
+
-
+
-
+
----
@@ -300,7 +302,6 @@ YAML::
[source,yaml]
----
- route:
- id: moo
from:
uri: direct:moo
steps:
@@ -308,7 +309,13 @@ YAML::
logger: myLogger
message: "Me Got ${body}"
- to:
- uri: mock:baz
+ uri: bean:foo
+- beans:
+ - name: myLogger
+ type: org.slf4j.LoggerFactory
+ factoryMethod: getLogger
+ constructors:
+ 0: com.mycompany.mylogger
----
====
diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/marshal-eip.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/marshal-eip.adoc
index 398f85725e39f..5583162409dbd 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/marshal-eip.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/marshal-eip.adoc
@@ -24,7 +24,48 @@ into a Java object; or some other representation according to the data format be
== Example
-The following example reads XML files from the inbox/xml directory.
+The following example sends an order as JSON to a JMS queue. The message body is an object, such as a Map or a POJO,
+and `marshal` turns it into JSON text with xref:dataformats:jackson2-dataformat.adoc[Jackson]:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:order")
+ .marshal().json(JsonLibrary.Jackson)
+ .to("jms:queue:order");
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:order
+ steps:
+ - marshal:
+ json:
+ library: Jackson
+ - to:
+ uri: jms:queue:order
+----
+====
+
+The next example reads XML files from the inbox/xml directory.
Each file is then transformed into Java Objects using xref:dataformats:jaxb-dataformat.adoc[JAXB].
Then a xref:ROOT:bean-component.adoc[Bean] is invoked that takes in the Java object.
diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/pausable-eip.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/pausable-eip.adoc
index 20bc377177a84..cdbacc58be292 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/pausable-eip.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/pausable-eip.adoc
@@ -33,11 +33,49 @@ from("kafka:topic")
.process(exchange -> LOG.info("Received: {}", exchange.getMessage().getBody()))
.to("direct:destination");
----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: kafka:topic
+ steps:
+ - pausable:
+ consumerListener: kafkaConsumerListener
+ untilCheck: canContinue
+ - log:
+ message: "Received: ${body}"
+ - to:
+ uri: direct:destination
+- beans:
+ - name: kafkaConsumerListener
+ type: org.apache.camel.component.kafka.consumer.errorhandler.KafkaConsumerListener
+ - name: canContinue
+ type: com.foo.CanContinuePredicate
+----
====
You can also integrate the Pausable EIP with the xref:circuitBreaker-eip.adoc[Circuit Breaker EIP]
to pause consumption based on downstream system availability:
+[tabs]
+====
+Java::
++
[source,java]
----
from("kafka:topic")
@@ -48,6 +86,40 @@ from("kafka:topic")
.end();
----
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: kafka:topic
+ steps:
+ - pausable:
+ consumerListener: kafkaConsumerListener
+ untilCheck: canContinue
+ - circuitBreaker:
+ resilience4jConfiguration:
+ circuitBreaker: pausableCircuit
+ steps:
+ - to:
+ uri: direct:downstream
+----
+====
+
== See Also
* xref:resume-strategies.adoc[Resume Strategies]
diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/pollEnrich-eip.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/pollEnrich-eip.adoc
index 3dfccbcd508e0..2ad4c0890e0a9 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/pollEnrich-eip.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/pollEnrich-eip.adoc
@@ -74,6 +74,100 @@ You can pass in a timeout value that determines which method to use:
The timeout values are in milliseconds.
+For example, to read the file `data.txt` from the inbox directory, waiting up to 10 seconds for it. Without an
+`AggregationStrategy`, the polled message is the result, so the content of the file becomes the message body:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:start")
+ .pollEnrich("file:inbox?fileName=data.txt", 10000)
+ .to("mock:result");
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+ file:inbox?fileName=data.txt
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - pollEnrich:
+ timeout: 10000
+ expression:
+ constant:
+ expression: file:inbox?fileName=data.txt
+ - to:
+ uri: mock:result
+----
+====
+
+=== Poll Enrich with Dynamic Endpoints
+
+Both `enrich` and `pollEnrich` supports using dynamic uris computed based on information from the current `Exchange`.
+
+For example to `pollEnrich` from an endpoint that uses a header to indicate a SEDA queue name:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:start")
+ .pollEnrich().simple("seda:${header.queueName}")
+ .to("direct:result");
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+ seda:${header.queueName}
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - pollEnrich:
+ expression:
+ simple:
+ expression: "seda:${header.queueName}"
+ - to:
+ uri: direct:result
+----
+====
+
+TIP: See the `cacheSize` option for more details on _how much cache_ to use depending on how many or few unique endpoints are used.
+
=== Using Poll Enrich
The content enricher (`pollEnrich`) retrieves additional data from a _resource endpoint_ in order to enrich an incoming message (contained in the _original exchange_).
@@ -218,55 +312,6 @@ YAML::
Notice that the enriched endpoint is a constant, however, Camel also supports dynamic endpoints which is covered next.
-=== Poll Enrich with Dynamic Endpoints
-
-Both `enrich` and `pollEnrich` supports using dynamic uris computed based on information from the current `Exchange`.
-
-For example to `pollEnrich` from an endpoint that uses a header to indicate a SEDA queue name:
-
-[tabs]
-====
-Java::
-+
-[source,java]
-----
-from("direct:start")
- .pollEnrich().simple("seda:${header.queueName}")
- .to("direct:result");
-----
-
-XML::
-+
-[source,xml]
-----
-
-
-
- seda:${header.queueName}
-
-
-
-----
-
-YAML::
-+
-[source,yaml]
-----
-- route:
- from:
- uri: direct:start
- steps:
- - pollEnrich:
- expression:
- simple:
- expression: "seda:${header.queueName}"
- - to:
- uri: direct:result
-----
-====
-
-TIP: See the `cacheSize` option for more details on _how much cache_ to use depending on how many or few unique endpoints are used.
-
=== Using Poll Enrich with file based components
When using `poll` or `pollEnrich` with the file based components, then the `eagerLimitMaxMessagesPerPoll` option
diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/polling-consumer.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/polling-consumer.adoc
index d06b471755975..152e13341cbb5 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/polling-consumer.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/polling-consumer.adoc
@@ -44,6 +44,7 @@ You can programmatically create an instance of `PollingConsumer` from any endpoi
----
Endpoint endpoint = context.getEndpoint("activemq:my.queue");
PollingConsumer consumer = endpoint.createPollingConsumer();
+consumer.start();
Exchange exchange = consumer.receive();
----
@@ -96,6 +97,7 @@ as shown below:
Endpoint endpoint =
context.getEndpoint("file:inbox?pollingConsumerQueueSize=50");
PollingConsumer consumer = endpoint.createPollingConsumer();
+consumer.start();
Exchange exchange = consumer.receive(5000);
----
diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/process-eip.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/process-eip.adoc
index 654b09187ac4b..d4acf724f9f2f 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/process-eip.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/process-eip.adoc
@@ -66,13 +66,44 @@ public class MyProcessor implements Processor {
Then in Camel you can call this processor:
-._Java-only: calling a Processor by instantiating it directly_
+[tabs]
+====
+Java::
++
[source,java]
----
from("activemq:myQueue")
.process(new MyProcessor());
----
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: activemq:myQueue
+ steps:
+ - process:
+ ref: myProcessor
+- beans:
+ - name: myProcessor
+ type: com.foo.MyProcessor
+----
+====
+
You can also call a processor by its bean id, if the processor
has been enlisted in the xref:manual::registry.adoc[Registry], such as with the id `myProcessor`:
diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/recipientList-eip.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/recipientList-eip.adoc
index f0efa65b63678..5a734e2c8e7a5 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/recipientList-eip.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/recipientList-eip.adoc
@@ -143,10 +143,23 @@ The dynamic list of recipients that are defined in the header must be iterable s
=== Configuring delimiter for dynamic destinations
-In XML DSL you can set the delimiter attribute for setting a delimiter to be used if the header value is a single `String` with multiple separated endpoints.
+You can set the `delimiter` option for setting a delimiter to be used if the header value is a single `String` with multiple separated endpoints.
By default, Camel uses comma as delimiter, but this option lets you specify a custom delimiter to use instead.
-._XML-only:_
+[tabs]
+====
+Java::
++
+In Java DSL, you specify the delimiter as second parameter:
++
+[source,java]
+----
+from("direct:a")
+ .recipientList(header("myHeader"), ";");
+----
+
+XML::
++
[source,xml]
----
@@ -158,8 +171,8 @@ By default, Camel uses comma as delimiter, but this option lets you specify a cu
----
-And in YAML DSL you set the `delimiter` parameter:
-
+YAML::
++
[source,yaml]
----
- route:
@@ -172,21 +185,13 @@ And in YAML DSL you set the `delimiter` parameter:
header:
expression: myHeader
----
+====
So if *myHeader* contains a `String` with the value `"activemq:queue:foo;activemq:topic:hello ; log:bar"`
-then Camel will split the `String` using the delimiter given in the XML that was comma,
+then Camel will split the `String` using the delimiter, which is a semicolon,
resulting into three endpoints to send to.
You can use spaces between the endpoints as Camel will trim the value when it looks up the endpoint to send to.
-In Java DSL, you specify the delimiter as second parameter as shown below:
-
-._Java-only: the delimiter is passed as a second parameter to recipientList_
-[source,java]
-----
-from("direct:a")
- .recipientList(header("myHeader"), ";");
-----
-
=== Using parallel processing
The Recipient List supports `parallelProcessing` similar to what xref:multicast-eip.adoc[Multicast] and xref:split-eip.adoc[Split] EIPs have as well.
@@ -415,10 +420,13 @@ So use this option with a bit of care.
====
For example, in the unit test below, you can see that we multicast the message to three destinations.
-We have a timeout of 2 seconds, which means only the last two messages can be completed within the timeframe.
+We have a timeout of 250 milliseconds, which means only the last two messages can be completed within the timeframe.
This means we will only aggregate the last two which yields a result aggregation which outputs "BC".
-._Java-only: multicast with inline AggregationStrategy and timeout_
+[tabs]
+====
+Java::
++
[source,java]
----
from("direct:start")
@@ -445,6 +453,111 @@ from("direct:b").to("mock:B").setBody(constant("B"));
from("direct:c").to("mock:C").setBody(constant("C"));
----
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1000
+
+
+
+ A
+
+
+
+
+
+
+
+ B
+
+
+
+
+
+
+
+ C
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - multicast:
+ aggregationStrategy: myStrategy
+ parallelProcessing: true
+ timeout: 250
+ steps:
+ - to:
+ uri: direct:a
+ - to:
+ uri: direct:b
+ - to:
+ uri: direct:c
+ - to:
+ uri: mock:result
+- route:
+ from:
+ uri: direct:a
+ steps:
+ - delay:
+ expression:
+ constant:
+ expression: "1000"
+ - to:
+ uri: mock:A
+ - setBody:
+ expression:
+ constant:
+ expression: A
+- route:
+ from:
+ uri: direct:b
+ steps:
+ - to:
+ uri: mock:B
+ - setBody:
+ expression:
+ constant:
+ expression: B
+- route:
+ from:
+ uri: direct:c
+ steps:
+ - to:
+ uri: mock:C
+ - setBody:
+ expression:
+ constant:
+ expression: C
+- beans:
+ - name: myStrategy
+ type: com.foo.MyAggregationStrategy
+----
+====
+
By default, if a timeout occurs the `AggregationStrategy` is not invoked. However, you can implement the `timeout` method:
This allows you to deal with the timeout in the `AggregationStrategy` if you really need to.
diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/resumable-eip.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/resumable-eip.adoc
index cd2c2c679afbe..b1a8191f2aa73 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/resumable-eip.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/resumable-eip.adoc
@@ -37,12 +37,45 @@ from("some:component")
.resumable().configuration(kafkaConfigurationBuilder)
.process(this::process);
----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: some:component
+ steps:
+ - resumable:
+ resumeStrategy: kafkaResumeStrategy
+ - process:
+ ref: myProcessor
+----
====
+NOTE: The configuration builder is only available in the Java DSL.
+In XML and YAML, the `resumeStrategy` option refers to a resume strategy bean by its name.
+In this example, `kafkaResumeStrategy` is a `SingleNodeKafkaResumeStrategy` bean set up with the same configuration.
+
=== Intermittent Mode
Enable intermittent mode to avoid updating the offset for every exchange:
+[tabs]
+====
+Java::
++
[source,java]
----
from("some:component")
@@ -50,6 +83,36 @@ from("some:component")
.process(this::process);
----
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: some:component
+ steps:
+ - resumable:
+ resumeStrategy: testResumeStrategy
+ intermittent: true
+ - process:
+ ref: myProcessor
+- beans:
+ - name: testResumeStrategy
+ type: com.foo.MyTestResumeStrategy
+----
+====
+
== See Also
* xref:resume-strategies.adoc[Resume Strategies]
diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/resume-strategies.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/resume-strategies.adoc
index 569e0b61619e9..a29e649658209 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/resume-strategies.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/resume-strategies.adoc
@@ -4,6 +4,7 @@
:description: Provide strategies to allow consuming data from specific offsets
:since: 3.16.0
:supportlevel: Experimental
+:tabs-sync-option:
The resume strategies allow users to implement strategies that point the consumer part of the routes to the last point of consumption. This allows Camel to skip reading and processing data that has already been consumed.
@@ -35,7 +36,7 @@ The resume strategies comes in three parts:
== The DSL method
The route needs to use the `resumable()` method followed by passing a strategy configuration using `configuration`.
-It is also possible to use the `resumableStrategy` to point to an instance of the resume strategy in use, although
+It is also possible to use the `resumeStrategy` to point to an instance of the resume strategy in use, although
this is much more complex. The vast majority of the cases should use a `configuration`, in which case Camel will do
the heavy-lifting for you.
@@ -60,7 +61,10 @@ from("some:component")
This instance can be bound in the Context registry as follows:
-._Java-only: binding resume strategy to the registry_
+[tabs]
+====
+Java::
++
[source,java]
----
getCamelContext().getRegistry().bind("testResumeStrategy", new MyTestResumeStrategy());
@@ -71,17 +75,97 @@ from("some:component")
.process(this::process);
----
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- beans:
+ - name: testResumeStrategy
+ type: com.foo.MyTestResumeStrategy
+ - name: resumeCache
+ type: com.foo.MyChoiceOfResumeCache
+ constructors:
+ 0: 100
+- route:
+ from:
+ uri: some:component
+ steps:
+ - resumable:
+ resumeStrategy: testResumeStrategy
+ - process:
+ ref: myProcessor
+----
+====
+
Or the instance can be constructed as follows:
-._Java-only: constructing resume strategy inline_
+[tabs]
+====
+Java::
++
[source,java]
----
getCamelContext().getRegistry().bind("resumeCache", new MyChoiceOfResumeCache<>(100));
from("some:component")
.resumable(new MyTestResumeStrategy())
- .process(this::process)
+ .process(this::process);
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
----
+- beans:
+ - name: resumeCache
+ type: com.foo.MyChoiceOfResumeCache
+ constructors:
+ 0: 100
+- route:
+ from:
+ uri: some:component
+ steps:
+ - resumable:
+ resumeStrategy: "#class:com.foo.MyTestResumeStrategy"
+ - process:
+ ref: myProcessor
+----
+====
In some circumstances, such as when dealing with File I/O, it may be necessary to set the offset manually. There are
*supporting classes* that can help work with resumables:
@@ -94,13 +178,46 @@ In some circumstances, such as when dealing with File I/O, it may be necessary t
In some cases, it may be necessary to avoid updating the offset for every exchange.
You can enable the intermittent mode to modify the route behavior so that missing offsets will not cause an exception:
-._Java-only: enabling intermittent mode on resumable_
+[tabs]
+====
+Java::
++
[source,java]
----
from("some:component")
-.resumable().resumeStrategy(new MyTestResumeStrategy()).intermittent(true)
-.process(this::process)
+ .resumable().resumeStrategy(new MyTestResumeStrategy()).intermittent(true)
+ .process(this::process);
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: some:component
+ steps:
+ - resumable:
+ resumeStrategy: testResumeStrategy
+ intermittent: true
+ - process:
+ ref: myProcessor
+- beans:
+ - name: testResumeStrategy
+ type: com.foo.MyTestResumeStrategy
----
+====
== Builtin Resume Strategies
@@ -140,7 +257,10 @@ When using the converters with the file component, beware of the differences in
For instance, the behavior of:
-._Java-only: resumable with Reader body type_
+[tabs]
+====
+Java::
++
[source,java]
----
from("file:{{input.dir}}?noop=true&fileName={{input.file}}")
@@ -149,9 +269,44 @@ from("file:{{input.dir}}?noop=true&fileName={{input.file}}")
.process(this::process);
----
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: "file:{{input.dir}}"
+ parameters:
+ noop: true
+ fileName: "{{input.file}}"
+ steps:
+ - resumable:
+ resumeStrategy: testResumeStrategy
+ - convertBodyTo:
+ type: java.io.Reader
+ - process:
+ ref: myProcessor
+----
+====
+
It is different from the behavior of:
-._Java-only: resumable with InputStream body type_
+[tabs]
+====
+Java::
++
[source,java]
----
from("file:{{input.dir}}?noop=true&fileName={{input.file}}")
@@ -160,6 +315,38 @@ from("file:{{input.dir}}?noop=true&fileName={{input.file}}")
.process(this::process);
----
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: "file:{{input.dir}}"
+ parameters:
+ noop: true
+ fileName: "{{input.file}}"
+ steps:
+ - resumable:
+ resumeStrategy: testResumeStrategy
+ - convertBodyTo:
+ type: java.io.InputStream
+ - process:
+ ref: myProcessor
+----
+====
+
*Reason*: the `skip` method in the Reader will skip characters, whereas the same method on the InputStream will skip bytes.
@@ -180,14 +367,52 @@ To use the API, it needs an instance of a Consumer listener along with a predica
Usage example:
-._Java-only: using pausable consumer API_
+[tabs]
+====
+Java::
++
[source,java]
----
-from(from)
+from("kafka:topic")
.pausable(new KafkaConsumerListener(), o -> canContinue())
.process(exchange -> LOG.info("Received an exchange: {}", exchange.getMessage().getBody()))
- .to(destination);
+ .to("direct:destination");
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: kafka:topic
+ steps:
+ - pausable:
+ consumerListener: kafkaConsumerListener
+ untilCheck: canContinue
+ - log:
+ message: "Received an exchange: ${body}"
+ - to:
+ uri: direct:destination
+- beans:
+ - name: kafkaConsumerListener
+ type: org.apache.camel.component.kafka.consumer.errorhandler.KafkaConsumerListener
+ - name: canContinue
+ type: com.foo.CanContinuePredicate
----
+====
You can also integrate the pausable API and the consumer listener with the circuit breaker EIP. For instance, it's
possible to configure the circuit breaker so that it can manipulate the state of the listener based on success or on
@@ -199,7 +424,7 @@ On success, it shuts down the scheduled check.
An example implementation of this approach would be similar to this:
-._Java-only: integrating pausable consumer with circuit breaker_
+._Java-only: creating the circuit breaker and binding it to the registry_
[source,java]
----
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("pausable");
@@ -218,21 +443,68 @@ circuitBreaker.getEventPublisher()
if (executorService == null) {
executorService = Executors.newSingleThreadScheduledExecutor();
- // In a real world scenario, instead of incrementing, it could be pinging a remote system or
- // running a similar check to determine whether it's available. That
+ // In a real world scenario, it could be pinging a remote system or
+ // running a similar check to determine whether it's available.
executorService.scheduleAtFixedRate(() -> someCheckMethod(), 1, 1, TimeUnit.SECONDS);
}
});
// Binds the configuration to the registry
- getCamelContext().getRegistry().bind("pausableCircuit", circuitBreaker);
+getCamelContext().getRegistry().bind("pausableCircuit", circuitBreaker);
+----
-from(from)
+The route then uses the circuit breaker by its name in the registry, `pausableCircuit`:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("kafka:topic")
.pausable(new KafkaConsumerListener(), o -> canContinue())
.routeId("pausable-it")
.process(exchange -> LOG.info("Got record from Kafka: {}", exchange.getMessage().getBody()))
.circuitBreaker()
.resilience4jConfiguration().circuitBreaker("pausableCircuit").end()
- .to(to)
+ .to("direct:downstream")
.end();
----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ id: pausable-it
+ from:
+ uri: kafka:topic
+ steps:
+ - pausable:
+ consumerListener: kafkaConsumerListener
+ untilCheck: canContinue
+ - log:
+ message: "Got record from Kafka: ${body}"
+ - circuitBreaker:
+ resilience4jConfiguration:
+ circuitBreaker: pausableCircuit
+ steps:
+ - to:
+ uri: direct:downstream
+----
+====
diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/setHeaders-eip.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/setHeaders-eip.adoc
index 2be3b1e8be27d..75cff6e97242e 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/setHeaders-eip.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/setHeaders-eip.adoc
@@ -152,13 +152,13 @@ XML::
-
- ${body}
-
-
- ${header.foo} > 10
-
-
+
+ ${body}
+
+
+ ${header.foo} > 10
+
+
----
@@ -195,7 +195,7 @@ If the order in which the headers should be set is important, use a `LinkedHashM
._Java-only: using a LinkedHashMap to set headers in order_
[source,java]
----
-private Map headerMap = new java.util.LinkedHashMap<>();
+Map headerMap = new java.util.LinkedHashMap<>();
headerMap.put("foo", ConstantLanguage.constant("ABC"));
headerMap.put("bar", ConstantLanguage.constant("XYZ"));
diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/setVariables-eip.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/setVariables-eip.adoc
index 40c565ae81043..3540038701fcb 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/setVariables-eip.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/setVariables-eip.adoc
@@ -152,13 +152,13 @@ XML::
-
- ${body}
-
-
- ${variable.foo} > 10
-
-
+
+ ${body}
+
+
+ ${variable.foo} > 10
+
+
----
@@ -195,7 +195,7 @@ If the order in which the variables should be set is important, use a `LinkedHas
._Java-only: using a LinkedHashMap to set variables in order_
[source,java]
----
-private Map variableMap = new java.util.LinkedHashMap<>();
+Map variableMap = new java.util.LinkedHashMap<>();
variableMap.put("foo", ConstantLanguage.constant("ABC"));
variableMap.put("bar", ConstantLanguage.constant("XYZ"));
diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/split-eip.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/split-eip.adoc
index e64c8ef3748e1..df5289871bbb1 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/split-eip.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/split-eip.adoc
@@ -77,69 +77,89 @@ YAML::
----
====
-The Split EIP has special support for splitting using a delimiter, instead of using
-xref:components:languages:tokenize-language.adoc[Tokenize] language.
+=== Splitting the message body
+
+A common use case is to split a list/set/collection/map, array, or something that is iterable
+from the message body.
-The previous example can also be done as follows:
+The Split EIP will by default split the message body based on the value type:
+
+- `java.util.Collection`: splits by each element from the collection/list/set.
+- `java.util.Map`: splits by each `Map.Entry` from the map.
+- `Object[]`: splits the array by each element
+- `Iterator`: splits by the iterator
+- `Iterable`: splits by the iterable
+- `org.w3c.dom.NodeList`: splits the XML document by each element from the list
+- `String`: splits the string value by comma as separator
+
+For any other type, the message body is not split, and instead used _as-is_, meaning that
+the Split EIP will be split into a single message (the same).
+
+To use this with the splitter, you should _just_ use body as the expression:
[tabs]
====
-
Java::
+
[source,java]
----
-from("direct:a")
- .split(body()).delimiter("\n")
- .to("direct:b");
+from("direct:splitUsingBody")
+ .split(body())
+ .to("mock:result");
----
XML::
+
+In XML, you use xref:components:languages:simple-language.adoc[Simple] to refer to the message body:
++
[source,xml]
----
-
-
- ${body}
-
+
+
+ ${body}
+
----
YAML::
+
+In YAML, you use xref:components:languages:simple-language.adoc[Simple] to refer to the message body:
++
[source,yaml]
----
- route:
from:
- uri: direct:a
+ uri: direct:splitUsingBody
steps:
- split:
- delimiter: "\n"
expression:
simple:
expression: "${body}"
steps:
- to:
- uri: direct:b
+ uri: mock:result
----
====
-The splitter can use any xref:manual:ROOT:expression.adoc[Expression],
-so you could use any of the supported languages such as
-xref:components:languages:simple-language.adoc[Simple], xref:components:languages:xpath-language.adoc[XPath],
-xref:components:languages:jsonpath-language.adoc[JSonPath], xref:components:languages:groovy-language.adoc[Groovy] to perform the split.
+=== Splitting with a delimiter
+
+The Split EIP has special support for splitting using a delimiter, instead of using
+xref:components:languages:tokenize-language.adoc[Tokenize] language.
+
+The first example can also be done as follows:
[tabs]
====
+
Java::
+
[source,java]
----
-from("activemq:my.queue")
- .split(xpath("//foo/bar"))
- .to("file:some/directory");
+from("direct:a")
+ .split(body()).delimiter("\n")
+ .to("direct:b");
----
XML::
@@ -147,11 +167,11 @@ XML::
[source,xml]
----
-
-
- //foo/bar
-
-
+
+
+ ${body}
+
+
----
@@ -161,37 +181,25 @@ YAML::
----
- route:
from:
- uri: activemq:my.queue
+ uri: direct:a
steps:
- split:
+ delimiter: "\n"
expression:
- xpath:
- expression: //foo/bar
+ simple:
+ expression: "${body}"
steps:
- to:
- uri: file:some/directory
+ uri: direct:b
----
====
-=== Splitting the message body
-
-A common use case is to split a list/set/collection/map, array, or something that is iterable
-from the message body.
-
-The Split EIP will by default split the message body based on the value type:
-
-- `java.util.Collection`: splits by each element from the collection/list/set.
-- `java.util.Map`: splits by each `Map.Entry` from the map.
-- `Object[]`: splits the array by each element
-- `Iterator`: splits by the iterator
-- `Iterable`: splits by the iterable
-- `org.w3c.dom.NodeList`: splits the XML document by each element from the list
-- `String`: splits the string value by comma as separator
-
-For any other type, the message body is not split, and instead used _as-is_, meaning that
-the Split EIP will be split into a single message (the same).
+=== Splitting with an expression
-To use this with the splitter, you should _just_ use body as the expression:
+The splitter can use any xref:manual:ROOT:expression.adoc[Expression],
+so you could use any of the supported languages such as
+xref:components:languages:simple-language.adoc[Simple], xref:components:languages:xpath-language.adoc[XPath],
+xref:components:languages:jsonpath-language.adoc[JSonPath], xref:components:languages:groovy-language.adoc[Groovy] to perform the split.
[tabs]
====
@@ -199,43 +207,39 @@ Java::
+
[source,java]
----
-from("direct:splitUsingBody")
- .split(body())
- .to("mock:result");
+from("activemq:my.queue")
+ .split(xpath("//foo/bar"))
+ .to("file:some/directory");
----
XML::
+
-In XML, you use xref:components:languages:simple-language.adoc[Simple] to refer to the message body:
-+
[source,xml]
----
-
-
- ${body}
-
-
+
+
+ //foo/bar
+
+
----
YAML::
+
-In YAML, you use xref:components:languages:simple-language.adoc[Simple] to refer to the message body:
-+
[source,yaml]
----
- route:
from:
- uri: direct:splitUsingBody
+ uri: activemq:my.queue
steps:
- split:
expression:
- simple:
- expression: "${body}"
+ xpath:
+ expression: //foo/bar
steps:
- to:
- uri: mock:result
+ uri: file:some/directory
----
====
@@ -254,9 +258,9 @@ Java::
----
from("direct:a")
.split(body()).parallelProcessing()
- .to("direct:x")
- .to("direct:y")
- .to("direct:z");
+ .to("direct:b")
+ .to("direct:c")
+ .to("direct:d");
----
XML::
@@ -309,7 +313,7 @@ thread that called the splitter, then make sure to enable the synchronous option
You may want to continue routing the exchange after the Split EIP. In Java DSL you need to use `end()`
to mark where split ends, and where other EIPs can be added to continue the route.
-In the example above then sending to mock:result happens after the Split EIP has finished.
+In the example below then sending to mock:result happens after the Split EIP has finished.
In other words, the message should finish splitting the entire message before the message
continues.
@@ -321,9 +325,9 @@ Java::
----
from("direct:a")
.split(body()).parallelProcessing()
- .to("direct:x")
- .to("direct:y")
- .to("direct:z")
+ .to("direct:b")
+ .to("direct:c")
+ .to("direct:d")
.end()
.to("mock:result");
----
@@ -390,11 +394,11 @@ Java::
+
[source,java]
----
-from("direct:start")
+from("direct:a")
.split(body(), new MyAggregationStrategy())
- .to("direct:x")
- .to("direct:y")
- .to("direct:z")
+ .to("direct:b")
+ .to("direct:c")
+ .to("direct:d")
.end()
.to("mock:result");
----
@@ -515,7 +519,7 @@ Java::
----
from("direct:streaming")
.split(method(new MyCustomSplitter(), "splitMe")).streaming()
- .to("activemq:my.parts")
+ .to("activemq:my.parts");
----
XML::
@@ -729,7 +733,7 @@ YAML::
----
====
-You can set `inheritNamsepaceTagName` property to `*` to include the preceding context in each token (i.e., generating each token enclosed in its ancestor elements). It is noted that each token must share the same ancestor elements in this case.
+You can set `inheritNamespaceTagName` property to `*` to include the preceding context in each token (i.e., generating each token enclosed in its ancestor elements). It is noted that each token must share the same ancestor elements in this case.
The above tokenizer works well on simple structures but has some inherent limitations in handling more complex XML structures.
=== Streaming big XML payloads using XML Tokenize language
@@ -957,7 +961,7 @@ from("direct:start")
// after we have split and handled each message, we want to send a single combined
// response back to the original caller, so we let this bean build it for us,
// this bean will receive the result of the aggregate strategy: MyOrderStrategy
- .to("bean:MyOrderService?method=buildCombinedResponse")
+ .to("bean:MyOrderService?method=buildCombinedResponse");
----
XML::
@@ -1541,6 +1545,42 @@ from("direct:line")
.to("mock:line");
----
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+----
+
Spring XML::
+
[source,xml]
@@ -1583,6 +1623,13 @@ YAML::
+
[source,yaml]
----
+- errorHandler:
+ deadLetterChannel:
+ deadLetterUri: mock:dead
+ useOriginalMessage: true
+ redeliveryPolicy:
+ maximumRedeliveries: 3
+ redeliveryDelay: "0"
- route:
from:
uri: direct:start
diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/switch-eip.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/switch-eip.adoc
index ce0d869da7445..5e396c9496b38 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/switch-eip.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/switch-eip.adoc
@@ -166,6 +166,58 @@ reference, `ref:department`, to evaluate the message body once and route the res
Load it alongside the `direct:billing`, `direct:technical`, and `direct:review`
handling routes shown above.
+[tabs]
+====
+Java::
++
+[source,java]
+----
+import static org.apache.camel.semantic.SemanticEvaluationsBuilder.semanticEvaluations;
+
+semanticEvaluations(this)
+ .evaluation("department").expert("decisions")
+ .type("choice")
+ .instructions("Which department should handle this message?")
+ .criterion("billing", "Invoices, payments and refunds")
+ .criterion("technical", "Bugs, outages and technical problems")
+ .criterion("other", "Any other request")
+ .register();
+
+from("direct:classify")
+ .doSwitch().language("semantic", "ref:department")
+ .doCase("billing", "direct:billing")
+ .doCase("technical", "direct:technical")
+ .otherwise("direct:review")
+ .end();
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+ Which department should handle this message?
+
+
+
+
+
+
+
+
+ ref:department
+
+
+
+
+
+
+----
+
+YAML::
++
[source,yaml]
----
- semantic:
@@ -195,6 +247,7 @@ handling routes shown above.
otherwise:
uri: direct:review
----
+====
== Destinations and management
diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/toD-eip.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/toD-eip.adoc
index 1f9fb853484e5..e28f6642e4cf6 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/toD-eip.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/toD-eip.adoc
@@ -426,6 +426,10 @@ Kamelet-style deployments you may want to constrain this to a fixed set of compo
option takes a comma-separated allow-list of component schemes; a resolved recipient whose scheme is not in the list
is rejected (independently of `ignoreInvalidEndpoint`). By default the option is unset and any scheme is allowed.
+[tabs]
+====
+Java::
++
[source,java]
----
from("direct:start")
@@ -433,8 +437,8 @@ from("direct:start")
.toD().allowedSchemes("http,https").uri("${header.target}");
----
-And in XML:
-
+XML::
++
[source,xml]
----
@@ -443,16 +447,18 @@ And in XML:
----
-And in YAML:
-
+YAML::
++
[source,yaml]
----
-- from:
- uri: direct:start
- steps:
- - toD:
- uri: "${header.target}"
- allowedSchemes: "http,https"
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - toD:
+ uri: "${header.target}"
+ allowedSchemes: "http,https"
----
+====
NOTE: `wireTap` extends `toD` and therefore honours the same `allowedSchemes` option.
diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/tokenizer-eip.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/tokenizer-eip.adoc
index db34196f5afa0..050f8e448224b 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/tokenizer-eip.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/tokenizer-eip.adoc
@@ -59,6 +59,44 @@ from("direct:start")
.split().body()
.to("mock:result");
----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+ ${body}
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - tokenizer:
+ langChain4jParagraphTokenizer:
+ maxTokens: 1024
+ maxOverlap: 10
+ tokenizerType: OPEN_AI
+ - split:
+ expression:
+ simple:
+ expression: "${body}"
+ steps:
+ - to:
+ uri: mock:result
+----
====
=== Tokenize by tokens
@@ -82,6 +120,45 @@ from("direct:start")
.split().body()
.to("mock:result");
----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+ ${body}
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:start
+ steps:
+ - tokenizer:
+ langChain4jParagraphTokenizer:
+ maxTokens: 1024
+ modelName: gpt-4o-mini
+ maxOverlap: 10
+ tokenizerType: OPEN_AI
+ - split:
+ expression:
+ simple:
+ expression: "${body}"
+ steps:
+ - to:
+ uri: mock:result
+----
====
== Dependencies
diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/transactional-client.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/transactional-client.adoc
index 3fc4990987a36..8ec329fb2c668 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/transactional-client.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/transactional-client.adoc
@@ -1,4 +1,5 @@
= Transactional Client
+:tabs-sync-option:
Camel supports the
http://www.enterpriseintegrationpatterns.com/TransactionalClient.html[Transactional
@@ -225,7 +226,7 @@ Spring XML::
-
+
----
====
@@ -235,31 +236,48 @@ Then we have routes where each of the routes uses their different policy:
[tabs]
====
-Spring XML::
+Java::
++
+[source,java]
+----
+from("activemq:queue:inbox")
+ .transacted("txRequired")
+ .to("direct:audit")
+ .to("direct:order")
+ .to("activemq:queue:order");
+
+from("direct:audit")
+ .transacted("txRequiresNew")
+ .bean("auditLogService", "insertAuditLog");
+
+from("direct:order")
+ .transacted("txMandatory")
+ .bean("orderService", "insertOrder");
+----
+
+XML::
+
[source,xml]
----
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
----
YAML::
@@ -315,7 +333,7 @@ The sample is in its full as a
https://github.com/apache/camel/blob/main/components/camel-spring-parent/camel-spring-xml/src/test/java/org/apache/camel/spring/interceptor/TransactionalClientDataSourceWithOnExceptionMinimalConfigurationTest.java[unit test].
First, we set up the usual spring stuff in its configuration file.
-Here we have defined a DataSource to the HSQLDB and most
+Here we have defined a DataSource to the H2 database and most
importantly the Spring `DataSourceTransactionManager` that is doing the
heavy lifting of ensuring our transactional policies.
@@ -359,45 +377,62 @@ as transacted using the `` XML tag.
[tabs]
====
-Spring XML::
+Java::
++
+[source,java]
+----
+from("direct:okay")
+ // mark this route as transacted
+ .transacted()
+ .setBody(constant("Tiger in Action"))
+ .bean("bookService")
+ .setBody(constant("Elephant in Action"))
+ .bean("bookService");
+
+from("direct:fail")
+ // mark this route as transacted
+ .transacted()
+ .setBody(constant("Tiger in Action"))
+ .bean("bookService")
+ .setBody(constant("Donkey in Action"))
+ .bean("bookService");
+----
+
+XML::
+
[source,xml]
----
-
-
-
-
-
-
-
- Tiger in Action
-
-
-
- Elephant in Action
-
-
-
-
-
-
-
-
-
- Tiger in Action
-
-
-
- Donkey in Action
-
-
-
-
-
+
+
+
+
+
+ Tiger in Action
+
+
+
+ Elephant in Action
+
+
+
+
+
+
+
+
+
+ Tiger in Action
+
+
+
+ Donkey in Action
+
+
+
----
YAML::
@@ -449,7 +484,7 @@ Remember to use ``. The rest is standard Spring XML to set up the t
In this sample, we want to listen for messages in a queue and process the
messages with our business logic java code and send them along. Since
it is based on a
-https://github.com/apache/camel/tree/main/components/camel-jms/src/test/java/org/apache/camel/component/jms/tx/TransactionMinimalConfigurationTest.java[unit test],
+https://github.com/apache/camel/blob/main/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/spring/tx/TransactionMinimalConfigurationIT.java[unit test],
the destination is a mock endpoint.
First, we configure the standard Spring XML to declare a JMS connection
@@ -479,7 +514,7 @@ Spring XML::
-
+
@@ -497,22 +532,34 @@ route as transacted using the `` XML tag.
[tabs]
====
-Spring XML::
+Java::
++
+[source,java]
+----
+// 1: from the jms queue
+from("activemq:queue:okay")
+ // 2: mark this route as transacted
+ .transacted()
+ // 3: call our business logic that is myProcessor
+ .process("myProcessor")
+ // 4: if success then send it to the mock
+ .to("mock:result");
+----
+
+XML::
+
[source,xml]
----
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
----
YAML::
diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/unmarshal-eip.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/unmarshal-eip.adoc
index a1ef60ca72415..c3556e98eaa25 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/unmarshal-eip.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/unmarshal-eip.adoc
@@ -24,7 +24,53 @@ into a Java object; or some other representation according to the data format be
== Example
-The following example reads XML files from the inbox/xml directory.
+The following example reads JSON files from the inbox/json directory. `unmarshal` turns each file into a Map with
+xref:dataformats:jackson2-dataformat.adoc[Jackson] (a JSON object becomes a Map, a JSON array a List), so the route can
+read its fields, such as `${body[orderId]}`:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("file:inbox/json")
+ .unmarshal().json(JsonLibrary.Jackson)
+ .log("Order ${body[orderId]} for customer ${body[customer]}")
+ .to("bean:validateOrder");
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: file:inbox/json
+ steps:
+ - unmarshal:
+ json:
+ library: Jackson
+ - log:
+ message: "Order ${body[orderId]} for customer ${body[customer]}"
+ - to:
+ uri: bean:validateOrder
+----
+====
+
+The next example reads XML files from the inbox/xml directory.
Each file is then transformed into Java Objects using xref:dataformats:jaxb-dataformat.adoc[JAXB].
Then a xref:ROOT:bean-component.adoc[Bean] is invoked that takes in the Java object.
diff --git a/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/tokenizer/langChain4jCharacterTokenizer.json b/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/tokenizer/langChain4jCharacterTokenizer.json
index d968a5b47359b..3baf8ac1bc842 100644
--- a/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/tokenizer/langChain4jCharacterTokenizer.json
+++ b/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/tokenizer/langChain4jCharacterTokenizer.json
@@ -17,6 +17,6 @@
"tokenizerType": { "index": 1, "kind": "attribute", "displayName": "Tokenizer Type", "group": "common", "required": true, "type": "enum", "javaType": "org.apache.camel.model.tokenizer.TokenizerType", "enum": [ "OPEN_AI", "AZURE", "QWEN" ], "deprecated": false, "autowired": false, "secret": false, "description": "The tokenizer type. Must be one of OPEN_AI, AZURE or QWEN." },
"maxTokens": { "index": 2, "kind": "attribute", "displayName": "Max Tokens", "group": "common", "required": true, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "The maximum number of tokens on each segment." },
"maxOverlap": { "index": 3, "kind": "attribute", "displayName": "Max Overlap", "group": "common", "required": true, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "The maximum number of tokens that can overlap in each segment." },
- "modelName": { "index": 4, "kind": "attribute", "displayName": "Model Name", "group": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The underlying model name used by the tokenizer. Providing this switches to computing segment sizes in terms of tokens." }
+ "modelName": { "index": 4, "kind": "attribute", "displayName": "Model Name", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The underlying model name used by the tokenizer. Providing this switches to computing segment sizes in terms of tokens." }
}
}
diff --git a/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/tokenizer/langChain4jLineTokenizer.json b/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/tokenizer/langChain4jLineTokenizer.json
index 843cf5419594b..364d2590da983 100644
--- a/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/tokenizer/langChain4jLineTokenizer.json
+++ b/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/tokenizer/langChain4jLineTokenizer.json
@@ -17,6 +17,6 @@
"tokenizerType": { "index": 1, "kind": "attribute", "displayName": "Tokenizer Type", "group": "common", "required": true, "type": "enum", "javaType": "org.apache.camel.model.tokenizer.TokenizerType", "enum": [ "OPEN_AI", "AZURE", "QWEN" ], "deprecated": false, "autowired": false, "secret": false, "description": "The tokenizer type. Must be one of OPEN_AI, AZURE or QWEN." },
"maxTokens": { "index": 2, "kind": "attribute", "displayName": "Max Tokens", "group": "common", "required": true, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "The maximum number of tokens on each segment." },
"maxOverlap": { "index": 3, "kind": "attribute", "displayName": "Max Overlap", "group": "common", "required": true, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "The maximum number of tokens that can overlap in each segment." },
- "modelName": { "index": 4, "kind": "attribute", "displayName": "Model Name", "group": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The underlying model name used by the tokenizer. Providing this switches to computing segment sizes in terms of tokens." }
+ "modelName": { "index": 4, "kind": "attribute", "displayName": "Model Name", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The underlying model name used by the tokenizer. Providing this switches to computing segment sizes in terms of tokens." }
}
}
diff --git a/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/tokenizer/langChain4jParagraphTokenizer.json b/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/tokenizer/langChain4jParagraphTokenizer.json
index 4118c4afeb50c..c252e73498333 100644
--- a/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/tokenizer/langChain4jParagraphTokenizer.json
+++ b/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/tokenizer/langChain4jParagraphTokenizer.json
@@ -17,6 +17,6 @@
"tokenizerType": { "index": 1, "kind": "attribute", "displayName": "Tokenizer Type", "group": "common", "required": true, "type": "enum", "javaType": "org.apache.camel.model.tokenizer.TokenizerType", "enum": [ "OPEN_AI", "AZURE", "QWEN" ], "deprecated": false, "autowired": false, "secret": false, "description": "The tokenizer type. Must be one of OPEN_AI, AZURE or QWEN." },
"maxTokens": { "index": 2, "kind": "attribute", "displayName": "Max Tokens", "group": "common", "required": true, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "The maximum number of tokens on each segment." },
"maxOverlap": { "index": 3, "kind": "attribute", "displayName": "Max Overlap", "group": "common", "required": true, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "The maximum number of tokens that can overlap in each segment." },
- "modelName": { "index": 4, "kind": "attribute", "displayName": "Model Name", "group": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The underlying model name used by the tokenizer. Providing this switches to computing segment sizes in terms of tokens." }
+ "modelName": { "index": 4, "kind": "attribute", "displayName": "Model Name", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The underlying model name used by the tokenizer. Providing this switches to computing segment sizes in terms of tokens." }
}
}
diff --git a/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/tokenizer/langChain4jSentenceTokenizer.json b/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/tokenizer/langChain4jSentenceTokenizer.json
index 1d8328d7fc916..869f54bc15785 100644
--- a/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/tokenizer/langChain4jSentenceTokenizer.json
+++ b/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/tokenizer/langChain4jSentenceTokenizer.json
@@ -17,6 +17,6 @@
"tokenizerType": { "index": 1, "kind": "attribute", "displayName": "Tokenizer Type", "group": "common", "required": true, "type": "enum", "javaType": "org.apache.camel.model.tokenizer.TokenizerType", "enum": [ "OPEN_AI", "AZURE", "QWEN" ], "deprecated": false, "autowired": false, "secret": false, "description": "The tokenizer type. Must be one of OPEN_AI, AZURE or QWEN." },
"maxTokens": { "index": 2, "kind": "attribute", "displayName": "Max Tokens", "group": "common", "required": true, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "The maximum number of tokens on each segment." },
"maxOverlap": { "index": 3, "kind": "attribute", "displayName": "Max Overlap", "group": "common", "required": true, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "The maximum number of tokens that can overlap in each segment." },
- "modelName": { "index": 4, "kind": "attribute", "displayName": "Model Name", "group": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The underlying model name used by the tokenizer. Providing this switches to computing segment sizes in terms of tokens." }
+ "modelName": { "index": 4, "kind": "attribute", "displayName": "Model Name", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The underlying model name used by the tokenizer. Providing this switches to computing segment sizes in terms of tokens." }
}
}
diff --git a/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/tokenizer/langChain4jWordTokenizer.json b/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/tokenizer/langChain4jWordTokenizer.json
index 57e842b9d8145..b5d89c3e823b2 100644
--- a/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/tokenizer/langChain4jWordTokenizer.json
+++ b/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/tokenizer/langChain4jWordTokenizer.json
@@ -17,6 +17,6 @@
"tokenizerType": { "index": 1, "kind": "attribute", "displayName": "Tokenizer Type", "group": "common", "required": true, "type": "enum", "javaType": "org.apache.camel.model.tokenizer.TokenizerType", "enum": [ "OPEN_AI", "AZURE", "QWEN" ], "deprecated": false, "autowired": false, "secret": false, "description": "The tokenizer type. Must be one of OPEN_AI, AZURE or QWEN." },
"maxTokens": { "index": 2, "kind": "attribute", "displayName": "Max Tokens", "group": "common", "required": true, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "The maximum number of tokens on each segment." },
"maxOverlap": { "index": 3, "kind": "attribute", "displayName": "Max Overlap", "group": "common", "required": true, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "The maximum number of tokens that can overlap in each segment." },
- "modelName": { "index": 4, "kind": "attribute", "displayName": "Model Name", "group": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The underlying model name used by the tokenizer. Providing this switches to computing segment sizes in terms of tokens." }
+ "modelName": { "index": 4, "kind": "attribute", "displayName": "Model Name", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The underlying model name used by the tokenizer. Providing this switches to computing segment sizes in terms of tokens." }
}
}
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/tokenizer/LangChain4jTokenizerDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/tokenizer/LangChain4jTokenizerDefinition.java
index 596bf1e8d6599..93410763536ed 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/tokenizer/LangChain4jTokenizerDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/tokenizer/LangChain4jTokenizerDefinition.java
@@ -52,7 +52,7 @@ public class LangChain4jTokenizerDefinition extends TokenizerImplementationDefin
description = "The maximum number of tokens that can overlap in each segment.")
private String maxOverlap;
- @XmlAttribute(required = true)
+ @XmlAttribute
@Metadata(javaType = "java.lang.String", required = false,
description = "The underlying model name used by the tokenizer."
+ " Providing this switches to computing segment sizes in terms of tokens.")
diff --git a/dsl/camel-jbang/camel-jbang-core/src/generated/resources/org/apache/camel/dsl/jbang/core/commands/ai/eip-samples.json b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/org/apache/camel/dsl/jbang/core/commands/ai/eip-samples.json
index 90d37e636eb8b..c8a033a3242ad 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/generated/resources/org/apache/camel/dsl/jbang/core/commands/ai/eip-samples.json
+++ b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/org/apache/camel/dsl/jbang/core/commands/ai/eip-samples.json
@@ -8,7 +8,15 @@
"aggregate": [
{
"source": "aggregate-eip.adoc",
- "yaml": "- route:\n from:\n uri: direct:start\n steps:\n - aggregate:\n aggregateController: myController\n aggregationStrategy: myAppender\n completionSize: 10\n correlationExpression:\n header:\n expression: id\n steps:\n - to:\n uri: mock:result\n"
+ "yaml": "- route:\n from:\n uri: direct:start\n steps:\n - aggregate:\n aggregationStrategy: groupedExchangeStrategy\n completionTimeout: 500\n correlationExpression:\n constant:\n expression: \"true\"\n steps:\n - to:\n uri: mock:result\n- beans:\n - name: groupedExchangeStrategy\n type: org.apache.camel.processor.aggregate.GroupedExchangeAggregationStrategy\n"
+ },
+ {
+ "source": "aggregate-eip.adoc",
+ "yaml": "- route:\n from:\n uri: direct:lines\n steps:\n - aggregate:\n correlationExpression:\n header:\n expression: orderId\n aggregationStrategy: groupedBodies\n completionSize: 10\n completionTimeout: 5000\n steps:\n - to:\n uri: direct:order\n- beans:\n - name: groupedBodies\n type: org.apache.camel.processor.aggregate.GroupedBodyAggregationStrategy\n"
+ },
+ {
+ "source": "aggregate-eip.adoc",
+ "yaml": "- route:\n from:\n uri: direct:start\n steps:\n - aggregate:\n id: myAggregator\n aggregateController: myController\n aggregationStrategy: myAppender\n completionSize: 10\n correlationExpression:\n header:\n expression: id\n steps:\n - to:\n uri: mock:result\n"
},
{
"source": "aggregate-eip.adoc",
@@ -18,6 +26,14 @@
"source": "aggregate-eip.adoc",
"yaml": "- route:\n from:\n uri: direct:start\n steps:\n - aggregate:\n aggregationStrategy: \"#class:com.foo.MyBodyAppender\"\n aggregationStrategyMethodName: append\n completionSize: 3\n correlationExpression:\n constant:\n expression: \"true\"\n steps:\n - to:\n uri: mock:result\n"
},
+ {
+ "source": "aggregate-eip.adoc",
+ "yaml": "- route:\n from:\n uri: direct:start\n steps:\n - aggregate:\n aggregationStrategy: \"#class:com.foo.MyBodyAppender\"\n completionSize: 3\n correlationExpression:\n constant:\n expression: \"true\"\n steps:\n - to:\n uri: mock:result\n"
+ },
+ {
+ "source": "aggregate-eip.adoc",
+ "yaml": "- route:\n from:\n uri: direct:start\n steps:\n - pollEnrich:\n expression:\n constant:\n expression: seda:foo\n timeout: 1000\n aggregationStrategy: myAppender\n aggregationStrategyMethodName: append\n aggregationStrategyMethodAllowNull: true\n - to:\n uri: mock:result\n"
+ },
{
"source": "aggregate-eip.adoc",
"yaml": "- route:\n from:\n uri: direct:start\n steps:\n - aggregate:\n aggregationStrategy: myAppender\n aggregationStrategyMethodName: append\n aggregationStrategyMethodAllowNull: true\n completionSize: 3\n correlationExpression:\n constant:\n expression: \"true\"\n steps:\n - to:\n uri: mock:result\n"
@@ -90,7 +106,23 @@
"cache": [
{
"source": "cache-eip.adoc",
- "yaml": "- from:\n uri: \"direct:start\"\n steps:\n - cache:\n simple: \"${header.productId}\"\n keyValueRepository: \"myCache\"\n ttl: \"10m\"\n steps:\n - to: \"http:\/\/product-service\/api\/product\"\n - to: \"direct:continue\"\n"
+ "yaml": "- route:\n from:\n uri: direct:start\n steps:\n - cache:\n expression:\n simple:\n expression: \"${header.productId}\"\n steps:\n - to:\n uri: http:\/\/product-service\/api\/product\n - unmarshal:\n json: {}\n - to:\n uri: direct:continue\n"
+ },
+ {
+ "source": "cache-eip.adoc",
+ "yaml": "- route:\n from:\n uri: direct:start\n steps:\n - cache:\n expression:\n simple:\n expression: \"${header.productId}\"\n keyValueRepository: myCache\n ttl: 10m\n steps:\n - to:\n uri: http:\/\/product-service\/api\/product\n - to:\n uri: direct:continue\n"
+ },
+ {
+ "source": "cache-eip.adoc",
+ "yaml": "- route:\n from:\n uri: direct:start\n steps:\n - cache:\n expression:\n simple:\n expression: \"${header.key}\"\n keyValueRepository: myCache\n steps:\n - to:\n uri: http:\/\/service\n"
+ },
+ {
+ "source": "cache-eip.adoc",
+ "yaml": "- route:\n from:\n uri: direct:getProduct\n steps:\n - cache:\n expression:\n simple:\n expression: \"${header.productId}\"\n keyValueRepository: productCache\n ttl: 30m\n steps:\n - to:\n uri: http:\/\/product-service\/api\/product\n\n# Invalidation route\n- route:\n from:\n uri: jms:topic:product.changes\n steps:\n - bean:\n ref: productCache\n method: \"delete(${header.productId})\"\n - log:\n message: \"Invalidated cache for product ${header.productId}\"\n"
+ },
+ {
+ "source": "cache-eip.adoc",
+ "yaml": "- route:\n from:\n uri: direct:clearAll\n steps:\n - bean:\n ref: productCache\n method: clear\n"
}
],
"channelAdapter": [
@@ -112,18 +144,14 @@
"circuitBreaker": [
{
"source": "circuitBreaker-eip.adoc",
- "yaml": "- route:\n from:\n uri: timer:stock?period=1000\n steps:\n - circuitBreaker:\n steps:\n - to:\n uri: direct:supplier\n onFallback:\n steps:\n - choice:\n when:\n - expression:\n simple:\n expression: \"${exchangeProperty.CamelCircuitBreakerResponseRejected} == true\"\n steps:\n - setBody:\n expression:\n constant:\n expression: \"supplier is down, using the last known stock\"\n otherwise:\n steps:\n - setBody:\n expression:\n constant:\n expression: \"stock check failed, will try again\"\n"
+ "yaml": "- route:\n from:\n uri: direct:start\n steps:\n - circuitBreaker:\n steps:\n - to:\n uri: http:\/\/fooservice.com\/slow\n onFallback:\n steps:\n - transform:\n expression:\n constant:\n expression: Fallback message\n - to:\n uri: mock:result\n"
},
{
"source": "circuitBreaker-eip.adoc",
- "yaml": "- route:\n from:\n uri: direct:start\n steps:\n - circuitBreaker:\n steps:\n - to:\n uri: http:\/\/fooservice.com\/slow\n onFallback:\n steps:\n - transform:\n expression:\n constant:\n expression: Fallback message\n - to:\n uri: mock:result\n"
+ "yaml": "- route:\n from:\n uri: timer:stock?period=1000\n steps:\n - circuitBreaker:\n steps:\n - to:\n uri: direct:supplier\n onFallback:\n steps:\n - choice:\n when:\n - expression:\n simple:\n expression: \"${exchangeProperty.CamelCircuitBreakerResponseRejected} == true\"\n steps:\n - setBody:\n expression:\n constant:\n expression: \"supplier is down, using the last known stock\"\n otherwise:\n steps:\n - setBody:\n expression:\n constant:\n expression: \"stock check failed, will try again\"\n"
}
],
"claimCheck": [
- {
- "source": "claimCheck-eip.adoc",
- "yaml": "- route:\n from:\n uri: direct\n parameters:\n name: start\n steps:\n - to:\n uri: mock\n parameters:\n name: a\n - claimCheck:\n operation: Set\n key: \"${header.myKey}\"\n - transform:\n expression:\n constant:\n expression: Bye World\n - to:\n uri: mock\n parameters:\n name: b\n - claimCheck:\n operation: Get\n key: \"${header.myKey}\"\n - to:\n uri: mock\n parameters:\n name: c\n - transform:\n expression:\n constant:\n expression: Hi World\n - to:\n uri: mock\n parameters:\n name: d\n - claimCheck:\n operation: Get\n key: \"${header.myKey}\"\n - to:\n uri: mock\n parameters:\n name: e\n"
- },
{
"source": "claimCheck-eip.adoc",
"yaml": "- route:\n from:\n uri: direct:start\n steps:\n - to: mock:a\n - claimCheck:\n operation: Push\n - transform:\n expression:\n constant: Bye World\n - to:\n uri: mock:b\n - claimCheck:\n operation: Pop\n - to:\n uri: mock:c\n"
@@ -135,6 +163,10 @@
{
"source": "claimCheck-eip.adoc",
"yaml": "- route:\n from:\n uri: direct:start\n steps:\n - to:\n uri: mock:a\n - claimCheck:\n operation: Push\n - transform:\n expression:\n constant: ByeWorld\n - setHeader:\n name: foo\n expression:\n constant: 456\n - removeHeader:\n name: bar\n - to:\n uri: mock:b\n - claimCheck:\n operation: Pop\n filter: \"header:(foo|bar)\"\n - to:\n uri: mock:c\n"
+ },
+ {
+ "source": "claimCheck-eip.adoc",
+ "yaml": "- route:\n from:\n uri: direct\n parameters:\n name: start\n steps:\n - to:\n uri: mock\n parameters:\n name: a\n - claimCheck:\n operation: Set\n key: \"${header.myKey}\"\n - transform:\n expression:\n constant:\n expression: Bye World\n - to:\n uri: mock\n parameters:\n name: b\n - claimCheck:\n operation: Get\n key: \"${header.myKey}\"\n - to:\n uri: mock\n parameters:\n name: c\n - transform:\n expression:\n constant:\n expression: Hi World\n - to:\n uri: mock\n parameters:\n name: d\n - claimCheck:\n operation: Get\n key: \"${header.myKey}\"\n - to:\n uri: mock\n parameters:\n name: e\n"
}
],
"competingConsumers": [
@@ -164,29 +196,29 @@
},
{
"source": "content-enricher.adoc",
- "yaml": "- route:\n from:\n uri: direct\n parameters:\n name: start\n steps:\n - setBody:\n expression:\n simple:\n expression: \"${body} World!\"\n - to:\n uri: mock\n parameters:\n name: result\n"
+ "yaml": "- route:\n from:\n uri: direct:start\n steps:\n - setBody:\n expression:\n simple:\n expression: \"${body} World!\"\n - to:\n uri: mock:result\n"
},
{
"source": "content-enricher.adoc",
- "yaml": "- route:\n from:\n uri: direct\n parameters:\n name: start\n steps:\n - process:\n ref: myProcessor\n - to:\n uri: mock\n parameters:\n name: result\n"
+ "yaml": "- route:\n from:\n uri: direct:start\n steps:\n - process:\n ref: myProcessor\n - to:\n uri: mock:result\n"
},
{
"source": "content-enricher.adoc",
- "yaml": "- route:\n from:\n uri: activemq:Input\n steps:\n - bean:\n ref: myBeanName\n method: doTransform\n - to:\n uri: activemq:Output\n"
+ "yaml": "- route:\n from:\n uri: activemq:My.Queue\n steps:\n - bean:\n ref: myBeanName\n method: doTransform\n - to:\n uri: activemq:Another.Queue\n"
}
],
"contentFilter": [
{
"source": "content-filter-eip.adoc",
- "yaml": "- route:\n from:\n uri: direct\n parameters:\n name: start\n steps:\n - process:\n ref: myProcessor\n - to:\n uri: mock\n parameters:\n name: result\n"
+ "yaml": "- route:\n from:\n uri: activemq:Input\n steps:\n - setBody:\n expression:\n xpath:\n expression: \/\/foo\/bar\n - to:\n uri: activemq:Output\n"
},
{
"source": "content-filter-eip.adoc",
- "yaml": "- route:\n from:\n uri: activemq:Input\n steps:\n - bean:\n ref: myBeanName\n method: doFilter\n - to:\n uri: activemq:Output\n"
+ "yaml": "- route:\n from:\n uri: direct:start\n steps:\n - process:\n ref: myProcessor\n - to:\n uri: mock:result\n"
},
{
"source": "content-filter-eip.adoc",
- "yaml": "- route:\n from:\n uri: activemq:Input\n steps:\n - setBody:\n expression:\n xpath: \/\/foo:bar\n - to:\n uri: activemq:Output\n"
+ "yaml": "- route:\n from:\n uri: activemq:Input\n steps:\n - bean:\n ref: myBeanName\n method: doFilter\n - to:\n uri: activemq:Output\n"
}
],
"convertBodyTo": [
@@ -246,6 +278,10 @@
"source": "dead-letter-channel.adoc",
"yaml": "- errorHandler:\n deadLetterChannel:\n deadLetterUri: \"log:dead?level=ERROR\"\n"
},
+ {
+ "source": "dead-letter-channel.adoc",
+ "yaml": "- errorHandler:\n deadLetterChannel:\n deadLetterUri: \"log:dead?level=ERROR\"\n- route:\n from:\n uri: direct:newOrder\n steps:\n - bean:\n ref: orderService\n method: validate\n - bean:\n ref: orderService\n method: store\n"
+ },
{
"source": "dead-letter-channel.adoc",
"yaml": "- errorHandler:\n deadLetterChannel:\n deadLetterUri: jms:queue:dead\n redeliveryPolicy:\n maximumRedeliveries: 3\n redeliveryDelay: \"5000\"\n"
@@ -441,6 +477,16 @@
"yaml": "- route:\n from:\n uri: direct:start\n steps:\n - to:\n uri: jms:queue:foo\n"
}
],
+ "idempotentConsumer": [
+ {
+ "source": "idempotentConsumer-eip.adoc",
+ "yaml": "- beans:\n - name: myRepo\n type: org.apache.camel.support.processor.idempotent.MemoryIdempotentRepository\n- route:\n from:\n uri: direct:start\n steps:\n - idempotentConsumer:\n expression:\n header:\n expression: messageId\n idempotentRepository: myRepo\n steps:\n - to:\n uri: mock:result\n"
+ },
+ {
+ "source": "idempotentConsumer-eip.adoc",
+ "yaml": "- beans:\n - name: myRepo\n type: org.apache.camel.support.processor.idempotent.MemoryIdempotentRepository\n- route:\n from:\n uri: direct:start\n steps:\n - idempotentConsumer:\n expression:\n header:\n expression: messageId\n idempotentRepository: myRepo\n skipDuplicate: false\n steps:\n - choice:\n when:\n - expression:\n simple:\n expression: \"${exchangeProperty.CamelDuplicateMessage} == true\"\n steps:\n - log:\n message: \"Message ${header.messageId} is a duplicate\"\n otherwise:\n steps:\n - to:\n uri: mock:result\n"
+ }
+ ],
"intercept": [
{
"source": "intercept.adoc",
@@ -500,10 +546,6 @@
}
],
"kamelet": [
- {
- "source": "kamelet-eip.adoc",
- "yaml": "- route:\n from:\n uri: direct:start\n steps:\n - to:\n uri: kamelet:my-aggregate\n parameters:\n count: 5\n - to:\n uri: log:info\n - to:\n uri: mock:result\n"
- },
{
"source": "kamelet-eip.adoc",
"yaml": "- route:\n from:\n uri: direct:start\n steps:\n - kamelet:\n name: my-aggregate?count=5\n steps:\n - to:\n uri: log:info\n - to:\n uri: mock:result\n"
@@ -590,7 +632,7 @@
},
{
"source": "log-eip.adoc",
- "yaml": "- route:\n id: moo\n from:\n uri: direct:moo\n steps:\n - log:\n logger: myLogger\n message: \"Me Got ${body}\"\n - to:\n uri: mock:baz\n"
+ "yaml": "- route:\n from:\n uri: direct:moo\n steps:\n - log:\n logger: myLogger\n message: \"Me Got ${body}\"\n - to:\n uri: bean:foo\n- beans:\n - name: myLogger\n type: org.slf4j.LoggerFactory\n factoryMethod: getLogger\n constructors:\n 0: com.mycompany.mylogger\n"
},
{
"source": "log-eip.adoc",
@@ -624,6 +666,10 @@
}
],
"marshal": [
+ {
+ "source": "marshal-eip.adoc",
+ "yaml": "- route:\n from:\n uri: direct:order\n steps:\n - marshal:\n json:\n library: Jackson\n - to:\n uri: jms:queue:order\n"
+ },
{
"source": "marshal-eip.adoc",
"yaml": "- route:\n from:\n uri: file:inbox\/xml\n steps:\n - unmarshal:\n jaxb:\n contextPath: com.acme.order\n - to:\n uri: bean:validateOrder\n - marshal:\n jaxb:\n contextPath: com.acme.order\n - to:\n uri: jms:queue:order\n"
@@ -769,6 +815,16 @@
"yaml": "- onException:\n exception:\n - org.apache.camel.ValidationException\n steps:\n - to:\n uri: activemq:validationFailed\n- onException:\n exception:\n - com.foo.ShipOrderException\n steps:\n - to:\n uri: activemq:shipFailed\n- route:\n from:\n uri: seda:order\n steps:\n - to:\n uri: bean:processOrder\n"
}
],
+ "pausable": [
+ {
+ "source": "pausable-eip.adoc",
+ "yaml": "- route:\n from:\n uri: kafka:topic\n steps:\n - pausable:\n consumerListener: kafkaConsumerListener\n untilCheck: canContinue\n - log:\n message: \"Received: ${body}\"\n - to:\n uri: direct:destination\n- beans:\n - name: kafkaConsumerListener\n type: org.apache.camel.component.kafka.consumer.errorhandler.KafkaConsumerListener\n - name: canContinue\n type: com.foo.CanContinuePredicate\n"
+ },
+ {
+ "source": "pausable-eip.adoc",
+ "yaml": "- route:\n from:\n uri: kafka:topic\n steps:\n - pausable:\n consumerListener: kafkaConsumerListener\n untilCheck: canContinue\n - circuitBreaker:\n resilience4jConfiguration:\n circuitBreaker: pausableCircuit\n steps:\n - to:\n uri: direct:downstream\n"
+ }
+ ],
"pipeline": [
{
"source": "pipeline-eip.adoc",
@@ -806,18 +862,26 @@
"pollEnrich": [
{
"source": "pollEnrich-eip.adoc",
- "yaml": "- route:\n from:\n uri: direct:start\n steps:\n - pollEnrich:\n aggregationStrategy: myStrategy\n timeout: 10000\n expression:\n constant:\n expression: file:inbox?fileName=data.txt\n - to:\n uri: mock:result\n"
+ "yaml": "- route:\n from:\n uri: direct:start\n steps:\n - pollEnrich:\n timeout: 10000\n expression:\n constant:\n expression: file:inbox?fileName=data.txt\n - to:\n uri: mock:result\n"
},
{
"source": "pollEnrich-eip.adoc",
- "yaml": "- rest:\n path: \/report\n get:\n - path: \/{id}\/report\n to:\n uri: direct:report\n\n- route:\n from:\n uri: direct:report\n steps:\n - pollEnrich:\n expression:\n constant:\n expression: \"aws-s3:xavier-dev?amazonS3Client=#s3client&deleteAfterRead=false&fileName=report-file.pdf\"\n"
+ "yaml": "- route:\n from:\n uri: direct:start\n steps:\n - pollEnrich:\n expression:\n simple:\n expression: \"seda:${header.queueName}\"\n - to:\n uri: direct:result\n"
},
{
"source": "pollEnrich-eip.adoc",
- "yaml": "- route:\n from:\n uri: direct:start\n steps:\n - pollEnrich:\n expression:\n simple:\n expression: \"seda:${header.queueName}\"\n - to:\n uri: direct:result\n"
+ "yaml": "- route:\n from:\n uri: direct:start\n steps:\n - pollEnrich:\n aggregationStrategy: myStrategy\n timeout: 10000\n expression:\n constant:\n expression: file:inbox?fileName=data.txt\n - to:\n uri: mock:result\n"
+ },
+ {
+ "source": "pollEnrich-eip.adoc",
+ "yaml": "- rest:\n path: \/report\n get:\n - path: \/{id}\/report\n to:\n uri: direct:report\n\n- route:\n from:\n uri: direct:report\n steps:\n - pollEnrich:\n expression:\n constant:\n expression: \"aws-s3:xavier-dev?amazonS3Client=#s3client&deleteAfterRead=false&fileName=report-file.pdf\"\n"
}
],
"process": [
+ {
+ "source": "process-eip.adoc",
+ "yaml": "- route:\n from:\n uri: activemq:myQueue\n steps:\n - process:\n ref: myProcessor\n- beans:\n - name: myProcessor\n type: com.foo.MyProcessor\n"
+ },
{
"source": "process-eip.adoc",
"yaml": "- route:\n from:\n uri: activemq:myQueue\n steps:\n - process:\n ref: \"#class:com.foo.MyProcessor\"\n"
@@ -860,6 +924,10 @@
"source": "recipientList-eip.adoc",
"yaml": "- route:\n from:\n uri: direct:a\n steps:\n - recipientList:\n ignoreInvalidEndpoints: \"true\"\n expression:\n header:\n expression: myHeader\n"
},
+ {
+ "source": "recipientList-eip.adoc",
+ "yaml": "- route:\n from:\n uri: direct:start\n steps:\n - multicast:\n aggregationStrategy: myStrategy\n parallelProcessing: true\n timeout: 250\n steps:\n - to:\n uri: direct:a\n - to:\n uri: direct:b\n - to:\n uri: direct:c\n - to:\n uri: mock:result\n- route:\n from:\n uri: direct:a\n steps:\n - delay:\n expression:\n constant:\n expression: \"1000\"\n - to:\n uri: mock:A\n - setBody:\n expression:\n constant:\n expression: A\n- route:\n from:\n uri: direct:b\n steps:\n - to:\n uri: mock:B\n - setBody:\n expression:\n constant:\n expression: B\n- route:\n from:\n uri: direct:c\n steps:\n - to:\n uri: mock:C\n - setBody:\n expression:\n constant:\n expression: C\n- beans:\n - name: myStrategy\n type: com.foo.MyAggregationStrategy\n"
+ },
{
"source": "recipientList-eip.adoc",
"yaml": "- route:\n from:\n uri: file:inbox\n steps:\n - recipientList:\n expression:\n constant:\n expression: activemq:queue:inbox?exchangePattern=InOut\n - to:\n uri: file:outbox\n"
@@ -1049,6 +1117,46 @@
"yaml": "- restConfiguration:\n inlineRoutes: true\n- rest:\n path: \"\/customers\/\"\n get:\n - path: \"\/{id}\"\n to: \"direct:customerDetails\"\n - path: \"\/{id}\/orders\"\n to: \"direct:customerOrders\"\n post:\n - path: \"\/neworder\"\n to: \"direct:customerNewOrder\"\n- route:\n from:\n uri: direct:customerNewOrder\n steps:\n - to:\n uri: direct:commonCustomerNewOrder\n- route:\n from:\n uri: direct:commonCustomerNewOrder\n steps:\n - log:\n message: Created new order\n- route:\n from:\n uri: kafka:new-order\n steps:\n - to:\n uri: direct:commonCustomerNewOrder\n"
}
],
+ "resumable": [
+ {
+ "source": "resumable-eip.adoc",
+ "yaml": "- route:\n from:\n uri: some:component\n steps:\n - resumable:\n resumeStrategy: kafkaResumeStrategy\n - process:\n ref: myProcessor\n"
+ },
+ {
+ "source": "resumable-eip.adoc",
+ "yaml": "- route:\n from:\n uri: some:component\n steps:\n - resumable:\n resumeStrategy: testResumeStrategy\n intermittent: true\n - process:\n ref: myProcessor\n- beans:\n - name: testResumeStrategy\n type: com.foo.MyTestResumeStrategy\n"
+ }
+ ],
+ "resumeStrategies": [
+ {
+ "source": "resume-strategies.adoc",
+ "yaml": "- beans:\n - name: testResumeStrategy\n type: com.foo.MyTestResumeStrategy\n - name: resumeCache\n type: com.foo.MyChoiceOfResumeCache\n constructors:\n 0: 100\n- route:\n from:\n uri: some:component\n steps:\n - resumable:\n resumeStrategy: testResumeStrategy\n - process:\n ref: myProcessor\n"
+ },
+ {
+ "source": "resume-strategies.adoc",
+ "yaml": "- beans:\n - name: resumeCache\n type: com.foo.MyChoiceOfResumeCache\n constructors:\n 0: 100\n- route:\n from:\n uri: some:component\n steps:\n - resumable:\n resumeStrategy: \"#class:com.foo.MyTestResumeStrategy\"\n - process:\n ref: myProcessor\n"
+ },
+ {
+ "source": "resume-strategies.adoc",
+ "yaml": "- route:\n from:\n uri: some:component\n steps:\n - resumable:\n resumeStrategy: testResumeStrategy\n intermittent: true\n - process:\n ref: myProcessor\n- beans:\n - name: testResumeStrategy\n type: com.foo.MyTestResumeStrategy\n"
+ },
+ {
+ "source": "resume-strategies.adoc",
+ "yaml": "- route:\n from:\n uri: \"file:{{input.dir}}\"\n parameters:\n noop: true\n fileName: \"{{input.file}}\"\n steps:\n - resumable:\n resumeStrategy: testResumeStrategy\n - convertBodyTo:\n type: java.io.Reader\n - process:\n ref: myProcessor\n"
+ },
+ {
+ "source": "resume-strategies.adoc",
+ "yaml": "- route:\n from:\n uri: \"file:{{input.dir}}\"\n parameters:\n noop: true\n fileName: \"{{input.file}}\"\n steps:\n - resumable:\n resumeStrategy: testResumeStrategy\n - convertBodyTo:\n type: java.io.InputStream\n - process:\n ref: myProcessor\n"
+ },
+ {
+ "source": "resume-strategies.adoc",
+ "yaml": "- route:\n from:\n uri: kafka:topic\n steps:\n - pausable:\n consumerListener: kafkaConsumerListener\n untilCheck: canContinue\n - log:\n message: \"Received an exchange: ${body}\"\n - to:\n uri: direct:destination\n- beans:\n - name: kafkaConsumerListener\n type: org.apache.camel.component.kafka.consumer.errorhandler.KafkaConsumerListener\n - name: canContinue\n type: com.foo.CanContinuePredicate\n"
+ },
+ {
+ "source": "resume-strategies.adoc",
+ "yaml": "- route:\n id: pausable-it\n from:\n uri: kafka:topic\n steps:\n - pausable:\n consumerListener: kafkaConsumerListener\n untilCheck: canContinue\n - log:\n message: \"Got record from Kafka: ${body}\"\n - circuitBreaker:\n resilience4jConfiguration:\n circuitBreaker: pausableCircuit\n steps:\n - to:\n uri: direct:downstream\n"
+ }
+ ],
"returnAddress": [
{
"source": "return-address.adoc",
@@ -1356,15 +1464,15 @@
},
{
"source": "split-eip.adoc",
- "yaml": "- route:\n from:\n uri: direct:a\n steps:\n - split:\n delimiter: \"\\n\"\n expression:\n simple:\n expression: \"${body}\"\n steps:\n - to:\n uri: direct:b\n"
+ "yaml": "- route:\n from:\n uri: direct:splitUsingBody\n steps:\n - split:\n expression:\n simple:\n expression: \"${body}\"\n steps:\n - to:\n uri: mock:result\n"
},
{
"source": "split-eip.adoc",
- "yaml": "- route:\n from:\n uri: activemq:my.queue\n steps:\n - split:\n expression:\n xpath:\n expression: \/\/foo\/bar\n steps:\n - to:\n uri: file:some\/directory\n"
+ "yaml": "- route:\n from:\n uri: direct:a\n steps:\n - split:\n delimiter: \"\\n\"\n expression:\n simple:\n expression: \"${body}\"\n steps:\n - to:\n uri: direct:b\n"
},
{
"source": "split-eip.adoc",
- "yaml": "- route:\n from:\n uri: direct:splitUsingBody\n steps:\n - split:\n expression:\n simple:\n expression: \"${body}\"\n steps:\n - to:\n uri: mock:result\n"
+ "yaml": "- route:\n from:\n uri: activemq:my.queue\n steps:\n - split:\n expression:\n xpath:\n expression: \/\/foo\/bar\n steps:\n - to:\n uri: file:some\/directory\n"
},
{
"source": "split-eip.adoc",
@@ -1432,7 +1540,7 @@
},
{
"source": "split-eip.adoc",
- "yaml": "- route:\n from:\n uri: direct:start\n steps:\n - to:\n uri: mock:a\n - split:\n shareUnitOfWork: \"true\"\n expression:\n tokenize:\n token: \",\"\n steps:\n - to:\n uri: mock:b\n - to:\n uri: direct:line\n - to:\n uri: mock:result\n\n- route:\n from:\n uri: direct:line\n steps:\n - to:\n uri: log:line\n - process:\n ref: myProcessor\n - to:\n uri: mock:line\n"
+ "yaml": "- errorHandler:\n deadLetterChannel:\n deadLetterUri: mock:dead\n useOriginalMessage: true\n redeliveryPolicy:\n maximumRedeliveries: 3\n redeliveryDelay: \"0\"\n- route:\n from:\n uri: direct:start\n steps:\n - to:\n uri: mock:a\n - split:\n shareUnitOfWork: \"true\"\n expression:\n tokenize:\n token: \",\"\n steps:\n - to:\n uri: mock:b\n - to:\n uri: direct:line\n - to:\n uri: mock:result\n\n- route:\n from:\n uri: direct:line\n steps:\n - to:\n uri: log:line\n - process:\n ref: myProcessor\n - to:\n uri: mock:line\n"
}
],
"sslContextParameters": [
@@ -1586,7 +1694,17 @@
},
{
"source": "toD-eip.adoc",
- "yaml": "- from:\n uri: direct:start\n steps:\n - toD:\n uri: \"${header.target}\"\n allowedSchemes: \"http,https\"\n"
+ "yaml": "- route:\n from:\n uri: direct:start\n steps:\n - toD:\n uri: \"${header.target}\"\n allowedSchemes: \"http,https\"\n"
+ }
+ ],
+ "tokenizer": [
+ {
+ "source": "tokenizer-eip.adoc",
+ "yaml": "- route:\n from:\n uri: direct:start\n steps:\n - tokenizer:\n langChain4jParagraphTokenizer:\n maxTokens: 1024\n maxOverlap: 10\n tokenizerType: OPEN_AI\n - split:\n expression:\n simple:\n expression: \"${body}\"\n steps:\n - to:\n uri: mock:result\n"
+ },
+ {
+ "source": "tokenizer-eip.adoc",
+ "yaml": "- route:\n from:\n uri: direct:start\n steps:\n - tokenizer:\n langChain4jParagraphTokenizer:\n maxTokens: 1024\n modelName: gpt-4o-mini\n maxOverlap: 10\n tokenizerType: OPEN_AI\n - split:\n expression:\n simple:\n expression: \"${body}\"\n steps:\n - to:\n uri: mock:result\n"
}
],
"topicLoadBalancer": [
@@ -1652,6 +1770,10 @@
}
],
"unmarshal": [
+ {
+ "source": "unmarshal-eip.adoc",
+ "yaml": "- route:\n from:\n uri: file:inbox\/json\n steps:\n - unmarshal:\n json:\n library: Jackson\n - log:\n message: \"Order ${body[orderId]} for customer ${body[customer]}\"\n - to:\n uri: bean:validateOrder\n"
+ },
{
"source": "unmarshal-eip.adoc",
"yaml": "- route:\n from:\n uri: file:inbox\/xml\n steps:\n - unmarshal:\n jaxb:\n contextPath: com.acme.order\n - to:\n uri: bean:validateOrder\n - marshal:\n jaxb:\n contextPath: com.acme.order\n - to:\n uri: jms:queue:order\n"
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogSamples.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogSamples.java
index 4a6390156edae..0bb0209f06e3f 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogSamples.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogSamples.java
@@ -249,7 +249,36 @@ static Found fromCatalog(CamelCatalog catalog, String kind, String key) {
static final Map SAMPLES_ON_PAGE_OF
= Map.of("interceptFrom", "intercept", "interceptSendToEndpoint", "intercept");
+ /**
+ * The EIPs whose own page has no example: their samples are the examples on the pages of what they belong to that
+ * write them, the first of each page first (CAMEL-25774). loadBalance is on the pages of its policies, the circuit
+ * breaker settings on the pages of Resilience4j and Fault Tolerance, and the resequencer settings on its page.
+ */
+ static final Map> SAMPLES_IN_PAGES_OF = Map.of(
+ "loadBalance", List.of("failoverLoadBalancer-eip", "roundRobinLoadBalancer-eip", "weightedLoadBalancer-eip",
+ "randomLoadBalancer-eip", "stickyLoadBalancer-eip", "topicLoadBalancer-eip", "customLoadBalancer-eip"),
+ "resilience4jConfiguration", List.of("resilience4j-eip"),
+ "faultToleranceConfiguration", List.of("fault-tolerance-eip"),
+ "batchConfig", List.of("resequence-eip"),
+ "streamConfig", List.of("resequence-eip"));
+
+ /** The settings written inside an EIP as one of its options, not as a step (CAMEL-25774). */
+ static final Map SETTINGS_OF = Map.of(
+ "resilience4jConfiguration", "circuitBreaker", "faultToleranceConfiguration", "circuitBreaker",
+ "batchConfig", "resequence", "streamConfig", "resequence");
+
private static Found eipSamples(CamelCatalog catalog, String key) {
+ List pages = SAMPLES_IN_PAGES_OF.get(key);
+ if (pages != null) {
+ Pattern writes = Pattern.compile("(?m)^\\s*(- )?" + Pattern.quote(key) + ":");
+ List>> perPage = new ArrayList<>();
+ for (String page : pages) {
+ perPage.add(pageSamples(catalog, List.of(page), null).samples().stream()
+ .filter(s -> writes.matcher(s.get("yaml")).find()).toList());
+ }
+ List