This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new bb3130a6a09f CAMEL-25182: Check the Java DSL examples of the
documentation with the Java DSL parser (#27130)
bb3130a6a09f is described below
commit bb3130a6a09fe0caa7b30dc27b9f8bba884a2f35
Author: Claus Ibsen <[email protected]>
AuthorDate: Wed Sep 30 20:50:58 2026 +0200
CAMEL-25182: Check the Java DSL examples of the documentation with the Java
DSL parser (#27130)
CatalogDocJavaExamplesTest reads the Java route examples of the catalog docs
and user manual with LwJavaParser and fails on DSL calls that do not exist
or
take other arguments, covering 2,000+ examples. Documentation examples that
called the Java DSL wrongly are fixed, and the obsolete
exceptionPolicyStrategy
section is removed.
DataFormatClause gains univocityCsv(), univocityFixed(), univocityTsv() and
yaml(), as the data format pages always showed. The parser handles char
literals, JDK collection factories, JDK constants, enum name()/toString(),
stand-ins for objects it cannot create, and no longer denies model methods
named configure/configuration. The endpoint DSL resolver only treats calls
with a known component scheme and path arguments as endpoints.
Closes #27130
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Signed-off-by: Claus Ibsen <[email protected]>
---
.../camel/catalog/docs/aws2-ses-component.adoc | 2 +-
.../camel/catalog/docs/aws2-sqs-component.adoc | 2 +-
.../catalog/docs/aws2-translate-component.adoc | 2 +-
.../camel/catalog/docs/dead-letter-channel.adoc | 2 +-
.../camel/catalog/docs/disruptor-component.adoc | 2 +-
.../catalog/docs/google-bigquery-component.adoc | 2 +-
.../camel/catalog/docs/hwcloud-dms-component.adoc | 8 +-
.../camel/catalog/docs/hwcloud-iam-component.adoc | 4 +-
.../camel/catalog/docs/hwcloud-obs-component.adoc | 4 +-
.../org/apache/camel/catalog/docs/intercept.adoc | 2 +-
.../apache/camel/catalog/docs/jdbc-component.adoc | 2 +-
.../org/apache/camel/catalog/docs/jq-language.adoc | 6 +-
.../camel/catalog/docs/keycloak-security.adoc | 3 +-
.../camel/catalog/docs/olingo2-component.adoc | 2 +-
.../camel/catalog/docs/olingo4-component.adoc | 2 +-
.../apache/camel/catalog/docs/resumable-eip.adoc | 2 +-
.../camel/catalog/docs/resume-strategies.adoc | 2 +-
.../apache/camel/catalog/docs/seda-component.adoc | 4 +-
.../apache/camel/catalog/docs/validate-eip.adoc | 2 +-
.../camel/catalog/docs/xmlSecurity-dataformat.adoc | 8 +-
.../src/main/docs/aws2-ses-component.adoc | 2 +-
.../src/main/docs/aws2-sqs-component.adoc | 2 +-
.../src/main/docs/aws2-translate-component.adoc | 2 +-
.../src/main/docs/disruptor-component.adoc | 2 +-
.../src/main/docs/google-bigquery-component.adoc | 2 +-
.../src/main/docs/hwcloud-dms-component.adoc | 8 +-
.../src/main/docs/hwcloud-iam-component.adoc | 4 +-
.../src/main/docs/hwcloud-obs-component.adoc | 4 +-
.../camel-jdbc/src/main/docs/jdbc-component.adoc | 2 +-
components/camel-jq/src/main/docs/jq-language.adoc | 6 +-
.../src/main/docs/keycloak-security.adoc | 3 +-
.../src/main/docs/olingo2-component.adoc | 2 +-
.../src/main/docs/olingo4-component.adoc | 2 +-
.../camel-seda/src/main/docs/seda-component.adoc | 4 +-
.../snakeyaml/SnakeYAMLDataFormatClauseTest.java | 49 ++++++++
.../univocity/UniVocityDataFormatClauseTest.java | 63 ++++++++++
.../src/main/docs/xmlSecurity-dataformat.adoc | 8 +-
.../modules/eips/pages/dead-letter-channel.adoc | 2 +-
.../main/docs/modules/eips/pages/intercept.adoc | 2 +-
.../docs/modules/eips/pages/resumable-eip.adoc | 2 +-
.../docs/modules/eips/pages/resume-strategies.adoc | 2 +-
.../main/docs/modules/eips/pages/validate-eip.adoc | 2 +-
.../org/apache/camel/builder/DataFormatClause.java | 31 +++++
.../org/apache/camel/java/in/ChainReplayer.java | 127 ++++++++++++++++++++-
.../org/apache/camel/java/in/JavaChainParser.java | 10 +-
.../org/apache/camel/java/in/LwJavaParserTest.java | 66 +++++++++++
.../modules/ROOT/pages/exception-advanced.adoc | 33 ------
.../ROOT/pages/exception-handling-patterns.adoc | 2 +-
.../modules/ROOT/pages/exception-redelivery.adoc | 6 +-
.../modules/ROOT/pages/rest-dsl-openapi.adoc | 6 +-
.../modules/ROOT/pages/route-template.adoc | 2 +-
.../commands/ai/CatalogEndpointDslResolver.java | 7 +-
.../commands/ai/CatalogDocJavaExamplesTest.java | 124 ++++++++++++++++++++
53 files changed, 542 insertions(+), 110 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/aws2-ses-component.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/aws2-ses-component.adoc
index 7ab6c2dbe639..4368831ee4cb 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/aws2-ses-component.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/aws2-ses-component.adoc
@@ -121,7 +121,7 @@ Java::
----
from("direct:start")
.setHeader(SesConstants.SUBJECT, constant("This is my subject"))
- .setHeader(SesConstants.TO,
constant(Collections.singletonList("[email protected]"))
+ .setHeader(SesConstants.TO,
constant(Collections.singletonList("[email protected]")))
.setBody(constant("This is my message text."))
.to("aws2-ses://[email protected]?accessKey=xxx&secretKey=yyy");
----
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/aws2-sqs-component.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/aws2-sqs-component.adoc
index 89fa5105095b..0b355120b900 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/aws2-sqs-component.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/aws2-sqs-component.adoc
@@ -138,7 +138,7 @@ Java::
[source,java]
----
from("aws2-sqs://MyQueue?amazonSQSClient=#client&defaultVisibilityTimeout=5000&deleteIfFiltered=false&deleteAfterRead=false")
-.filter("${header.login} == true")
+.filter(simple("${header.login} == true"))
.setProperty("CamelAwsSqsDeleteFiltered", constant(true))
.to("mock:filter");
----
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/aws2-translate-component.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/aws2-translate-component.adoc
index 8c976e072bc7..bb6cf7ae4179 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/aws2-translate-component.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/aws2-translate-component.adoc
@@ -84,7 +84,7 @@ Camel-AWS Translate component provides the following
operation on the producer s
from("direct:start")
.setHeader(TranslateConstants.SOURCE_LANGUAGE, TranslateLanguageEnum.ITALIAN)
.setHeader(TranslateConstants.TARGET_LANGUAGE, TranslateLanguageEnum.GERMAN)
- .setBody("Ciao")
+ .setBody(constant("Ciao"))
.to("aws2-translate://test?translateClient=#amazonTranslateClient&operation=translateText");
----
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 93e755f663a7..2df230324a93 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
@@ -324,7 +324,7 @@ Java::
----
errorHandler(deadLetterChannel("jms:queue:dead")
.maximumRedeliveries(3)
- .onRedeliver(new MyOnRedeliveryProcessor());
+ .onRedelivery(new MyOnRedeliveryProcessor()));
----
Spring XML::
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/disruptor-component.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/disruptor-component.adoc
index a864a33aff77..4fb1d1e7c904 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/disruptor-component.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/disruptor-component.adoc
@@ -196,7 +196,7 @@ something like:
._Java-only: incomplete route (thread pool example)_
[source,java]
----
-from("disruptor:stageName").thread(5).process(...)
+from("disruptor:stageName").threads(5).process(...)
----
Can wind up with adding a normal
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/google-bigquery-component.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/google-bigquery-component.adoc
index 6685537b880a..965789ea0996 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/google-bigquery-component.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/google-bigquery-component.adoc
@@ -109,7 +109,7 @@ I.e. the following route will create tables and insert
records sharded on a per-
[source,java]
----
from("direct:start")
- .header(GoogleBigQueryConstants.TABLE_SUFFIX, "_${date:now:yyyyMMdd}")
+ .setHeader(GoogleBigQueryConstants.TABLE_SUFFIX,
simple("_${date:now:yyyyMMdd}"))
.to("google-bigquery:sampleDataset:sampleTable")
----
Note it is recommended to use partitioning for this use case.
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/hwcloud-dms-component.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/hwcloud-dms-component.adoc
index f747c4dabadf..e1264510d92a 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/hwcloud-dms-component.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/hwcloud-dms-component.adoc
@@ -119,7 +119,7 @@ To create an instance, you can pass the parameters through
the endpoint, the exc
[source,java]
----
from("direct:triggerRoute")
- .setBody(new
CreateInstanceRequestBody().withName("new-instance").withDescription("description").with*)
// add remaining options
+ .setBody(constant(new
CreateInstanceRequestBody().withName("new-instance").withDescription("description").with*))
// add remaining options
.to("hwcloud-dms:createInstance?region=cn-north-4&accessKey=********&secretKey=********&projectId=*******")
----
@@ -130,7 +130,7 @@ Java::
[source,java]
----
from("direct:triggerRoute")
- .setBody("{\"name\":\"new-instance\",\"description\":\"description\"}") //
add remaining options
+
.setBody(constant("{\"name\":\"new-instance\",\"description\":\"description\"}"))
// add remaining options
.to("hwcloud-dms:createInstance?region=cn-north-4&accessKey=********&secretKey=********&projectId=*******")
----
@@ -175,7 +175,7 @@ To update an instance, you must pass the parameters through
the exchange body as
[source,java]
----
from("direct:triggerRoute")
- .setBody(new
UpdateInstanceRequestBody().withName("new-instance").withDescription("description").with*)
// add remaining options
+ .setBody(constant(new
UpdateInstanceRequestBody().withName("new-instance").withDescription("description").with*))
// add remaining options
.to("hwcloud-dms:updateInstance?instanceId=******®ion=cn-north-4&accessKey=********&secretKey=********&projectId=*******")
----
@@ -186,7 +186,7 @@ Java::
[source,java]
----
from("direct:triggerRoute")
- .setBody("{\"name\":\"new-instance\",\"description\":\"description\"}") //
add remaining options
+
.setBody(constant("{\"name\":\"new-instance\",\"description\":\"description\"}"))
// add remaining options
.to("hwcloud-dms:updateInstance?instanceId=******®ion=cn-north-4&accessKey=********&secretKey=********&projectId=*******")
----
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/hwcloud-iam-component.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/hwcloud-iam-component.adoc
index b6b0dc845cdd..6a160eea888d 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/hwcloud-iam-component.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/hwcloud-iam-component.adoc
@@ -89,7 +89,7 @@ Java::
[source,java]
----
from("direct:triggerRoute")
-
.setBody("{\"name\":\"user\",\"description\":\"employee\",\"email\":\"[email protected]\"}")
+
.setBody(constant("{\"name\":\"user\",\"description\":\"employee\",\"email\":\"[email protected]\"}"))
.to("hwcloud-iam:updateUser?userId=********®ion=cn-north-4&accessKey=********&secretKey=********")
----
@@ -144,7 +144,7 @@ Java::
[source,java]
----
from("direct:triggerRoute")
-
.setBody("{\"name\":\"group\",\"description\":\"employees\",\"domain_id\":\"1234\"}")
+
.setBody(constant("{\"name\":\"group\",\"description\":\"employees\",\"domain_id\":\"1234\"}"))
.to("hwcloud-iam:updateUser?groupId=********®ion=cn-north-4&accessKey=********&secretKey=********")
----
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/hwcloud-obs-component.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/hwcloud-obs-component.adoc
index 14b0c984a363..49f3fa4b0750 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/hwcloud-obs-component.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/hwcloud-obs-component.adoc
@@ -104,7 +104,7 @@ Java::
[source,java]
----
from("direct:triggerRoute")
- .setBody("{\"bucketName\":\"Bucket name\",\"location\":\"Bucket location\"}")
+ .setBody(constant("{\"bucketName\":\"Bucket name\",\"location\":\"Bucket
location\"}"))
.to("hwcloud-obs:createBucket?region=cn-north-4&accessKey=********&secretKey=********")
----
@@ -158,7 +158,7 @@ Java::
[source,java]
----
from("direct:triggerRoute")
- .setBody("{\"bucketName\":\"Bucket name\",\"maxKeys\":1000}")
+ .setBody(constant("{\"bucketName\":\"Bucket name\",\"maxKeys\":1000}"))
.to("hwcloud-obs:listObjects?region=cn-north-4&accessKey=********&secretKey=********")
----
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/intercept.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/intercept.adoc
index 8211ded75f3c..e023a41d1ad3 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/intercept.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/intercept.adoc
@@ -640,7 +640,7 @@ Java::
[source,java]
----
interceptSendToEndpoint("kafka*").skipSendToOriginalEndpoint()
- .onWhen(simple("${header.biztype} == 'TEST'")
+ .onWhen(simple("${header.biztype} == 'TEST'"))
.log("TEST message detected - is NOT send to kafka");
from("jms:queue:order")
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/jdbc-component.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/jdbc-component.adoc
index 8ada1f09e9d0..d9c4efcd0e19 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/jdbc-component.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/jdbc-component.adoc
@@ -93,7 +93,7 @@ Java::
from("direct:projects")
.setHeader("lic", constant("ASF"))
.setHeader("min", constant(123))
- .setBody("select * from projects where license = :?lic and id > :?min
order by id")
+ .setBody(constant("select * from projects where license = :?lic and id >
:?min order by id"))
.to("jdbc:myDataSource?useHeadersAsParameters=true");
----
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/jq-language.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/jq-language.adoc
index d0199d69c8ec..b98597ad069a 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/jq-language.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/jq-language.adoc
@@ -27,7 +27,7 @@ To enable camel-jackson POJO type conversion, refer to the
Camel Jackson documen
=== Using header as input
-By default, JQ uses the message body as the input source. However, you can
also use a header as input by specifying the `headerName` option.
+By default, JQ uses the message body as the input source. However, you can
also use a header, property or variable as input by specifying the `source`
option, such as `header:books`.
For example, to count the number of books from a JSON document that was stored
in a header named `books` you can do:
@@ -35,8 +35,8 @@ For example, to count the number of books from a JSON
document that was stored i
[source,java]
----
from("direct:start")
- .setHeader("numberOfBooks")
- .jq(".store.books | length", int.class, "books")
+ .setHeader("numberOfBooks",
+ expression().jq(".store.books |
length").resultType(int.class).source("header:books").end())
.to("mock:result");
----
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/keycloak-security.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/keycloak-security.adoc
index d56eabee6e85..e4b7da310e94 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/keycloak-security.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/keycloak-security.adoc
@@ -990,7 +990,8 @@ onException(CamelAuthorizationException.class)
from("rest:post:/secure-data")
.doTry()
.policy(keycloakPolicy)
- .to("bean:dataProcessor")
+ .to("bean:dataProcessor")
+ .endDoTry() // the policy wraps the steps after it, until the end of
the doTry
.doCatch(CamelAuthorizationException.class)
.setHeader(Exchange.HTTP_RESPONSE_CODE, constant(403))
.transform().constant("Access denied")
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/olingo2-component.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/olingo2-component.adoc
index a02cf1fb73e4..5fcdfdf8f378 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/olingo2-component.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/olingo2-component.adoc
@@ -128,7 +128,7 @@ ordered by ascending Name property.
[source,java]
----
from("direct:...")
- .setHeader("CamelOlingo2.$top", "5");
+ .setHeader("CamelOlingo2.$top", constant("5"))
.to("olingo2://read/Manufacturers?orderBy=Name%20asc");
----
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/olingo4-component.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/olingo4-component.adoc
index 2792b2f1ba8a..5e125800872a 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/olingo4-component.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/olingo4-component.adoc
@@ -118,7 +118,7 @@ ordered by ascending FirstName property.
[source,java]
----
from("direct:...")
- .setHeader("CamelOlingo4.$top", "5");
+ .setHeader("CamelOlingo4.$top", constant("5"))
.to("olingo4://read/People?orderBy=FirstName%20asc");
----
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 702e92e4970a..cd2c2c679afb 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
@@ -46,7 +46,7 @@ Enable intermittent mode to avoid updating the offset for
every exchange:
[source,java]
----
from("some:component")
- .resumable(new MyTestResumeStrategy()).intermittent(true)
+ .resumable().resumeStrategy(new MyTestResumeStrategy()).intermittent(true)
.process(this::process);
----
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 8bfdcdb7434d..569e0b61619e 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
@@ -98,7 +98,7 @@ You can enable the intermittent mode to modify the route
behavior so that missin
[source,java]
----
from("some:component")
-.resumable(new MyTestResumeStrategy()).intermittent(true)
+.resumable().resumeStrategy(new MyTestResumeStrategy()).intermittent(true)
.process(this::process)
----
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/seda-component.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/seda-component.adoc
index b9b9befc43ec..53b298cfe7fc 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/seda-component.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/seda-component.adoc
@@ -199,7 +199,7 @@ like:
._Java-only: thread pool added to SEDA endpoint_
[source,java]
----
-from("seda:stageName").thread(5).process(...)
+from("seda:stageName").threads(5).process(...)
----
Can wind up with two `BlockQueues`: one from the SEDA endpoint, and one
@@ -211,7 +211,7 @@ asynchronously. For example:
._Java-only: thread pool on a Direct endpoint_
[source,java]
----
-from("direct:stageName").thread(5).process(...)
+from("direct:stageName").threads(5).process(...)
----
You can also directly configure number of threads that process messages
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/validate-eip.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/validate-eip.adoc
index 06fd3bfa3c87..e694bf9c56de 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/validate-eip.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/validate-eip.adoc
@@ -38,7 +38,7 @@ Java::
[source,java]
----
from("file:inbox")
- .validate(body(String.class).regex("^\\w{10}\\,\\d{2}\\,\\w{24}$"))
+ .validate(bodyAs(String.class).regex("^\\w{10}\\,\\d{2}\\,\\w{24}$"))
.to("bean:myServiceBean.processLine");
----
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/xmlSecurity-dataformat.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/xmlSecurity-dataformat.adoc
index ced411c85754..e7b53ee5d873 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/xmlSecurity-dataformat.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/xmlSecurity-dataformat.adoc
@@ -89,7 +89,7 @@ Key key = keyGenerator.generateKey();
from("direct:start")
.marshal().xmlSecurity(key.getEncoded())
- .unmarshal().xmlSecurity(key.getEncoded()
+ .unmarshal().xmlSecurity(key.getEncoded())
.to("direct:end");
----
@@ -160,7 +160,7 @@ Java::
[source,java]
----
from("direct:start")
- .marshal().xmlSecurity("//cheese:cheesesites/italy", true)
+ .marshal().xmlSecurity("//cheese:cheesesites/italy", true, "Just another
32 Byte key for AES")
.to("...");
----
@@ -175,7 +175,8 @@ XML::
<from uri="direct://start"/>
<marshal>
<xmlSecurity secureTag="//cheese:cheesesites/italy"
- secureTagContents="true"/>
+ secureTagContents="true"
+ passPhrase="Just another 32 Byte key for AES"/>
</marshal>
...
----
@@ -192,6 +193,7 @@ YAML::
xmlSecurity:
secureTag: "//cheese:cheesesites/italy"
secureTagContents: true
+ passPhrase: "Just another 32 Byte key for AES"
----
====
diff --git
a/components/camel-aws/camel-aws2-ses/src/main/docs/aws2-ses-component.adoc
b/components/camel-aws/camel-aws2-ses/src/main/docs/aws2-ses-component.adoc
index 7ab6c2dbe639..4368831ee4cb 100644
--- a/components/camel-aws/camel-aws2-ses/src/main/docs/aws2-ses-component.adoc
+++ b/components/camel-aws/camel-aws2-ses/src/main/docs/aws2-ses-component.adoc
@@ -121,7 +121,7 @@ Java::
----
from("direct:start")
.setHeader(SesConstants.SUBJECT, constant("This is my subject"))
- .setHeader(SesConstants.TO,
constant(Collections.singletonList("[email protected]"))
+ .setHeader(SesConstants.TO,
constant(Collections.singletonList("[email protected]")))
.setBody(constant("This is my message text."))
.to("aws2-ses://[email protected]?accessKey=xxx&secretKey=yyy");
----
diff --git
a/components/camel-aws/camel-aws2-sqs/src/main/docs/aws2-sqs-component.adoc
b/components/camel-aws/camel-aws2-sqs/src/main/docs/aws2-sqs-component.adoc
index 89fa5105095b..0b355120b900 100644
--- a/components/camel-aws/camel-aws2-sqs/src/main/docs/aws2-sqs-component.adoc
+++ b/components/camel-aws/camel-aws2-sqs/src/main/docs/aws2-sqs-component.adoc
@@ -138,7 +138,7 @@ Java::
[source,java]
----
from("aws2-sqs://MyQueue?amazonSQSClient=#client&defaultVisibilityTimeout=5000&deleteIfFiltered=false&deleteAfterRead=false")
-.filter("${header.login} == true")
+.filter(simple("${header.login} == true"))
.setProperty("CamelAwsSqsDeleteFiltered", constant(true))
.to("mock:filter");
----
diff --git
a/components/camel-aws/camel-aws2-translate/src/main/docs/aws2-translate-component.adoc
b/components/camel-aws/camel-aws2-translate/src/main/docs/aws2-translate-component.adoc
index 8c976e072bc7..bb6cf7ae4179 100644
---
a/components/camel-aws/camel-aws2-translate/src/main/docs/aws2-translate-component.adoc
+++
b/components/camel-aws/camel-aws2-translate/src/main/docs/aws2-translate-component.adoc
@@ -84,7 +84,7 @@ Camel-AWS Translate component provides the following
operation on the producer s
from("direct:start")
.setHeader(TranslateConstants.SOURCE_LANGUAGE, TranslateLanguageEnum.ITALIAN)
.setHeader(TranslateConstants.TARGET_LANGUAGE, TranslateLanguageEnum.GERMAN)
- .setBody("Ciao")
+ .setBody(constant("Ciao"))
.to("aws2-translate://test?translateClient=#amazonTranslateClient&operation=translateText");
----
diff --git a/components/camel-disruptor/src/main/docs/disruptor-component.adoc
b/components/camel-disruptor/src/main/docs/disruptor-component.adoc
index a864a33aff77..4fb1d1e7c904 100644
--- a/components/camel-disruptor/src/main/docs/disruptor-component.adoc
+++ b/components/camel-disruptor/src/main/docs/disruptor-component.adoc
@@ -196,7 +196,7 @@ something like:
._Java-only: incomplete route (thread pool example)_
[source,java]
----
-from("disruptor:stageName").thread(5).process(...)
+from("disruptor:stageName").threads(5).process(...)
----
Can wind up with adding a normal
diff --git
a/components/camel-google/camel-google-bigquery/src/main/docs/google-bigquery-component.adoc
b/components/camel-google/camel-google-bigquery/src/main/docs/google-bigquery-component.adoc
index 6685537b880a..965789ea0996 100644
---
a/components/camel-google/camel-google-bigquery/src/main/docs/google-bigquery-component.adoc
+++
b/components/camel-google/camel-google-bigquery/src/main/docs/google-bigquery-component.adoc
@@ -109,7 +109,7 @@ I.e. the following route will create tables and insert
records sharded on a per-
[source,java]
----
from("direct:start")
- .header(GoogleBigQueryConstants.TABLE_SUFFIX, "_${date:now:yyyyMMdd}")
+ .setHeader(GoogleBigQueryConstants.TABLE_SUFFIX,
simple("_${date:now:yyyyMMdd}"))
.to("google-bigquery:sampleDataset:sampleTable")
----
Note it is recommended to use partitioning for this use case.
diff --git
a/components/camel-huawei/camel-huaweicloud-dms/src/main/docs/hwcloud-dms-component.adoc
b/components/camel-huawei/camel-huaweicloud-dms/src/main/docs/hwcloud-dms-component.adoc
index f747c4dabadf..e1264510d92a 100644
---
a/components/camel-huawei/camel-huaweicloud-dms/src/main/docs/hwcloud-dms-component.adoc
+++
b/components/camel-huawei/camel-huaweicloud-dms/src/main/docs/hwcloud-dms-component.adoc
@@ -119,7 +119,7 @@ To create an instance, you can pass the parameters through
the endpoint, the exc
[source,java]
----
from("direct:triggerRoute")
- .setBody(new
CreateInstanceRequestBody().withName("new-instance").withDescription("description").with*)
// add remaining options
+ .setBody(constant(new
CreateInstanceRequestBody().withName("new-instance").withDescription("description").with*))
// add remaining options
.to("hwcloud-dms:createInstance?region=cn-north-4&accessKey=********&secretKey=********&projectId=*******")
----
@@ -130,7 +130,7 @@ Java::
[source,java]
----
from("direct:triggerRoute")
- .setBody("{\"name\":\"new-instance\",\"description\":\"description\"}") //
add remaining options
+
.setBody(constant("{\"name\":\"new-instance\",\"description\":\"description\"}"))
// add remaining options
.to("hwcloud-dms:createInstance?region=cn-north-4&accessKey=********&secretKey=********&projectId=*******")
----
@@ -175,7 +175,7 @@ To update an instance, you must pass the parameters through
the exchange body as
[source,java]
----
from("direct:triggerRoute")
- .setBody(new
UpdateInstanceRequestBody().withName("new-instance").withDescription("description").with*)
// add remaining options
+ .setBody(constant(new
UpdateInstanceRequestBody().withName("new-instance").withDescription("description").with*))
// add remaining options
.to("hwcloud-dms:updateInstance?instanceId=******®ion=cn-north-4&accessKey=********&secretKey=********&projectId=*******")
----
@@ -186,7 +186,7 @@ Java::
[source,java]
----
from("direct:triggerRoute")
- .setBody("{\"name\":\"new-instance\",\"description\":\"description\"}") //
add remaining options
+
.setBody(constant("{\"name\":\"new-instance\",\"description\":\"description\"}"))
// add remaining options
.to("hwcloud-dms:updateInstance?instanceId=******®ion=cn-north-4&accessKey=********&secretKey=********&projectId=*******")
----
diff --git
a/components/camel-huawei/camel-huaweicloud-iam/src/main/docs/hwcloud-iam-component.adoc
b/components/camel-huawei/camel-huaweicloud-iam/src/main/docs/hwcloud-iam-component.adoc
index b6b0dc845cdd..6a160eea888d 100644
---
a/components/camel-huawei/camel-huaweicloud-iam/src/main/docs/hwcloud-iam-component.adoc
+++
b/components/camel-huawei/camel-huaweicloud-iam/src/main/docs/hwcloud-iam-component.adoc
@@ -89,7 +89,7 @@ Java::
[source,java]
----
from("direct:triggerRoute")
-
.setBody("{\"name\":\"user\",\"description\":\"employee\",\"email\":\"[email protected]\"}")
+
.setBody(constant("{\"name\":\"user\",\"description\":\"employee\",\"email\":\"[email protected]\"}"))
.to("hwcloud-iam:updateUser?userId=********®ion=cn-north-4&accessKey=********&secretKey=********")
----
@@ -144,7 +144,7 @@ Java::
[source,java]
----
from("direct:triggerRoute")
-
.setBody("{\"name\":\"group\",\"description\":\"employees\",\"domain_id\":\"1234\"}")
+
.setBody(constant("{\"name\":\"group\",\"description\":\"employees\",\"domain_id\":\"1234\"}"))
.to("hwcloud-iam:updateUser?groupId=********®ion=cn-north-4&accessKey=********&secretKey=********")
----
diff --git
a/components/camel-huawei/camel-huaweicloud-obs/src/main/docs/hwcloud-obs-component.adoc
b/components/camel-huawei/camel-huaweicloud-obs/src/main/docs/hwcloud-obs-component.adoc
index 14b0c984a363..49f3fa4b0750 100644
---
a/components/camel-huawei/camel-huaweicloud-obs/src/main/docs/hwcloud-obs-component.adoc
+++
b/components/camel-huawei/camel-huaweicloud-obs/src/main/docs/hwcloud-obs-component.adoc
@@ -104,7 +104,7 @@ Java::
[source,java]
----
from("direct:triggerRoute")
- .setBody("{\"bucketName\":\"Bucket name\",\"location\":\"Bucket location\"}")
+ .setBody(constant("{\"bucketName\":\"Bucket name\",\"location\":\"Bucket
location\"}"))
.to("hwcloud-obs:createBucket?region=cn-north-4&accessKey=********&secretKey=********")
----
@@ -158,7 +158,7 @@ Java::
[source,java]
----
from("direct:triggerRoute")
- .setBody("{\"bucketName\":\"Bucket name\",\"maxKeys\":1000}")
+ .setBody(constant("{\"bucketName\":\"Bucket name\",\"maxKeys\":1000}"))
.to("hwcloud-obs:listObjects?region=cn-north-4&accessKey=********&secretKey=********")
----
diff --git a/components/camel-jdbc/src/main/docs/jdbc-component.adoc
b/components/camel-jdbc/src/main/docs/jdbc-component.adoc
index 8ada1f09e9d0..d9c4efcd0e19 100644
--- a/components/camel-jdbc/src/main/docs/jdbc-component.adoc
+++ b/components/camel-jdbc/src/main/docs/jdbc-component.adoc
@@ -93,7 +93,7 @@ Java::
from("direct:projects")
.setHeader("lic", constant("ASF"))
.setHeader("min", constant(123))
- .setBody("select * from projects where license = :?lic and id > :?min
order by id")
+ .setBody(constant("select * from projects where license = :?lic and id >
:?min order by id"))
.to("jdbc:myDataSource?useHeadersAsParameters=true");
----
diff --git a/components/camel-jq/src/main/docs/jq-language.adoc
b/components/camel-jq/src/main/docs/jq-language.adoc
index d0199d69c8ec..b98597ad069a 100644
--- a/components/camel-jq/src/main/docs/jq-language.adoc
+++ b/components/camel-jq/src/main/docs/jq-language.adoc
@@ -27,7 +27,7 @@ To enable camel-jackson POJO type conversion, refer to the
Camel Jackson documen
=== Using header as input
-By default, JQ uses the message body as the input source. However, you can
also use a header as input by specifying the `headerName` option.
+By default, JQ uses the message body as the input source. However, you can
also use a header, property or variable as input by specifying the `source`
option, such as `header:books`.
For example, to count the number of books from a JSON document that was stored
in a header named `books` you can do:
@@ -35,8 +35,8 @@ For example, to count the number of books from a JSON
document that was stored i
[source,java]
----
from("direct:start")
- .setHeader("numberOfBooks")
- .jq(".store.books | length", int.class, "books")
+ .setHeader("numberOfBooks",
+ expression().jq(".store.books |
length").resultType(int.class).source("header:books").end())
.to("mock:result");
----
diff --git a/components/camel-keycloak/src/main/docs/keycloak-security.adoc
b/components/camel-keycloak/src/main/docs/keycloak-security.adoc
index d56eabee6e85..e4b7da310e94 100644
--- a/components/camel-keycloak/src/main/docs/keycloak-security.adoc
+++ b/components/camel-keycloak/src/main/docs/keycloak-security.adoc
@@ -990,7 +990,8 @@ onException(CamelAuthorizationException.class)
from("rest:post:/secure-data")
.doTry()
.policy(keycloakPolicy)
- .to("bean:dataProcessor")
+ .to("bean:dataProcessor")
+ .endDoTry() // the policy wraps the steps after it, until the end of
the doTry
.doCatch(CamelAuthorizationException.class)
.setHeader(Exchange.HTTP_RESPONSE_CODE, constant(403))
.transform().constant("Access denied")
diff --git
a/components/camel-olingo2/camel-olingo2-component/src/main/docs/olingo2-component.adoc
b/components/camel-olingo2/camel-olingo2-component/src/main/docs/olingo2-component.adoc
index a02cf1fb73e4..5fcdfdf8f378 100644
---
a/components/camel-olingo2/camel-olingo2-component/src/main/docs/olingo2-component.adoc
+++
b/components/camel-olingo2/camel-olingo2-component/src/main/docs/olingo2-component.adoc
@@ -128,7 +128,7 @@ ordered by ascending Name property.
[source,java]
----
from("direct:...")
- .setHeader("CamelOlingo2.$top", "5");
+ .setHeader("CamelOlingo2.$top", constant("5"))
.to("olingo2://read/Manufacturers?orderBy=Name%20asc");
----
diff --git
a/components/camel-olingo4/camel-olingo4-component/src/main/docs/olingo4-component.adoc
b/components/camel-olingo4/camel-olingo4-component/src/main/docs/olingo4-component.adoc
index 2792b2f1ba8a..5e125800872a 100644
---
a/components/camel-olingo4/camel-olingo4-component/src/main/docs/olingo4-component.adoc
+++
b/components/camel-olingo4/camel-olingo4-component/src/main/docs/olingo4-component.adoc
@@ -118,7 +118,7 @@ ordered by ascending FirstName property.
[source,java]
----
from("direct:...")
- .setHeader("CamelOlingo4.$top", "5");
+ .setHeader("CamelOlingo4.$top", constant("5"))
.to("olingo4://read/People?orderBy=FirstName%20asc");
----
diff --git a/components/camel-seda/src/main/docs/seda-component.adoc
b/components/camel-seda/src/main/docs/seda-component.adoc
index b9b9befc43ec..53b298cfe7fc 100644
--- a/components/camel-seda/src/main/docs/seda-component.adoc
+++ b/components/camel-seda/src/main/docs/seda-component.adoc
@@ -199,7 +199,7 @@ like:
._Java-only: thread pool added to SEDA endpoint_
[source,java]
----
-from("seda:stageName").thread(5).process(...)
+from("seda:stageName").threads(5).process(...)
----
Can wind up with two `BlockQueues`: one from the SEDA endpoint, and one
@@ -211,7 +211,7 @@ asynchronously. For example:
._Java-only: thread pool on a Direct endpoint_
[source,java]
----
-from("direct:stageName").thread(5).process(...)
+from("direct:stageName").threads(5).process(...)
----
You can also directly configure number of threads that process messages
diff --git
a/components/camel-snakeyaml/src/test/java/org/apache/camel/component/snakeyaml/SnakeYAMLDataFormatClauseTest.java
b/components/camel-snakeyaml/src/test/java/org/apache/camel/component/snakeyaml/SnakeYAMLDataFormatClauseTest.java
new file mode 100644
index 000000000000..e841a6c8f1c5
--- /dev/null
+++
b/components/camel-snakeyaml/src/test/java/org/apache/camel/component/snakeyaml/SnakeYAMLDataFormatClauseTest.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.snakeyaml;
+
+import java.util.Map;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * The YAML data format from the data format clause: {@code
.marshal().yaml()}, as the documentation shows.
+ */
+public class SnakeYAMLDataFormatClauseTest extends CamelTestSupport {
+
+ @Test
+ public void marshalAndUnmarshal() {
+ String yaml = template.requestBody("direct:marshal", Map.of("name",
"Camel"), String.class);
+ assertEquals("{name: Camel}", yaml.trim());
+ assertEquals(Map.of("name", "Camel"),
template.requestBody("direct:unmarshal", yaml, Map.class));
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:marshal").marshal().yaml();
+ from("direct:unmarshal").unmarshal().yaml();
+ }
+ };
+ }
+}
diff --git
a/components/camel-univocity-parsers/src/test/java/org/apache/camel/dataformat/univocity/UniVocityDataFormatClauseTest.java
b/components/camel-univocity-parsers/src/test/java/org/apache/camel/dataformat/univocity/UniVocityDataFormatClauseTest.java
new file mode 100644
index 000000000000..c0a665efadcf
--- /dev/null
+++
b/components/camel-univocity-parsers/src/test/java/org/apache/camel/dataformat/univocity/UniVocityDataFormatClauseTest.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dataformat.univocity;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.MarshalDefinition;
+import org.apache.camel.model.dataformat.UniVocityFixedDataFormat;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.camel.dataformat.univocity.UniVocityTestHelper.asMap;
+import static org.apache.camel.dataformat.univocity.UniVocityTestHelper.join;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+
+/**
+ * The uniVocity data formats from the data format clause: {@code
.marshal().univocityCsv()}, as the documentation
+ * shows.
+ */
+public class UniVocityDataFormatClauseTest extends CamelTestSupport {
+
+ @Test
+ public void marshalCsv() {
+ assertEquals(join("1,2,3"), template.requestBody("direct:csv",
asMap("A", "1", "B", "2", "C", "3"), String.class));
+ }
+
+ @Test
+ public void marshalTsv() {
+ assertEquals(join("1\t2\t3"), template.requestBody("direct:tsv",
asMap("A", "1", "B", "2", "C", "3"), String.class));
+ }
+
+ @Test
+ public void fixedInTheModel() {
+ MarshalDefinition marshal = (MarshalDefinition)
context.getRouteDefinition("fixed").getOutputs().get(0);
+ assertInstanceOf(UniVocityFixedDataFormat.class,
marshal.getDataFormatType());
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:csv").marshal().univocityCsv();
+ from("direct:tsv").marshal().univocityTsv();
+
from("direct:fixed").routeId("fixed").marshal().univocityFixed();
+ }
+ };
+ }
+}
diff --git
a/components/camel-xmlsecurity/src/main/docs/xmlSecurity-dataformat.adoc
b/components/camel-xmlsecurity/src/main/docs/xmlSecurity-dataformat.adoc
index ced411c85754..e7b53ee5d873 100644
--- a/components/camel-xmlsecurity/src/main/docs/xmlSecurity-dataformat.adoc
+++ b/components/camel-xmlsecurity/src/main/docs/xmlSecurity-dataformat.adoc
@@ -89,7 +89,7 @@ Key key = keyGenerator.generateKey();
from("direct:start")
.marshal().xmlSecurity(key.getEncoded())
- .unmarshal().xmlSecurity(key.getEncoded()
+ .unmarshal().xmlSecurity(key.getEncoded())
.to("direct:end");
----
@@ -160,7 +160,7 @@ Java::
[source,java]
----
from("direct:start")
- .marshal().xmlSecurity("//cheese:cheesesites/italy", true)
+ .marshal().xmlSecurity("//cheese:cheesesites/italy", true, "Just another
32 Byte key for AES")
.to("...");
----
@@ -175,7 +175,8 @@ XML::
<from uri="direct://start"/>
<marshal>
<xmlSecurity secureTag="//cheese:cheesesites/italy"
- secureTagContents="true"/>
+ secureTagContents="true"
+ passPhrase="Just another 32 Byte key for AES"/>
</marshal>
...
----
@@ -192,6 +193,7 @@ YAML::
xmlSecurity:
secureTag: "//cheese:cheesesites/italy"
secureTagContents: true
+ passPhrase: "Just another 32 Byte key for AES"
----
====
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 93e755f663a7..2df230324a93 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
@@ -324,7 +324,7 @@ Java::
----
errorHandler(deadLetterChannel("jms:queue:dead")
.maximumRedeliveries(3)
- .onRedeliver(new MyOnRedeliveryProcessor());
+ .onRedelivery(new MyOnRedeliveryProcessor()));
----
Spring XML::
diff --git
a/core/camel-core-engine/src/main/docs/modules/eips/pages/intercept.adoc
b/core/camel-core-engine/src/main/docs/modules/eips/pages/intercept.adoc
index 8211ded75f3c..e023a41d1ad3 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/intercept.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/intercept.adoc
@@ -640,7 +640,7 @@ Java::
[source,java]
----
interceptSendToEndpoint("kafka*").skipSendToOriginalEndpoint()
- .onWhen(simple("${header.biztype} == 'TEST'")
+ .onWhen(simple("${header.biztype} == 'TEST'"))
.log("TEST message detected - is NOT send to kafka");
from("jms:queue:order")
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 702e92e4970a..cd2c2c679afb 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
@@ -46,7 +46,7 @@ Enable intermittent mode to avoid updating the offset for
every exchange:
[source,java]
----
from("some:component")
- .resumable(new MyTestResumeStrategy()).intermittent(true)
+ .resumable().resumeStrategy(new MyTestResumeStrategy()).intermittent(true)
.process(this::process);
----
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 8bfdcdb7434d..569e0b61619e 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
@@ -98,7 +98,7 @@ You can enable the intermittent mode to modify the route
behavior so that missin
[source,java]
----
from("some:component")
-.resumable(new MyTestResumeStrategy()).intermittent(true)
+.resumable().resumeStrategy(new MyTestResumeStrategy()).intermittent(true)
.process(this::process)
----
diff --git
a/core/camel-core-engine/src/main/docs/modules/eips/pages/validate-eip.adoc
b/core/camel-core-engine/src/main/docs/modules/eips/pages/validate-eip.adoc
index 06fd3bfa3c87..e694bf9c56de 100644
--- a/core/camel-core-engine/src/main/docs/modules/eips/pages/validate-eip.adoc
+++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/validate-eip.adoc
@@ -38,7 +38,7 @@ Java::
[source,java]
----
from("file:inbox")
- .validate(body(String.class).regex("^\\w{10}\\,\\d{2}\\,\\w{24}$"))
+ .validate(bodyAs(String.class).regex("^\\w{10}\\,\\d{2}\\,\\w{24}$"))
.to("bean:myServiceBean.processLine");
----
diff --git
a/core/camel-core-model/src/main/java/org/apache/camel/builder/DataFormatClause.java
b/core/camel-core-model/src/main/java/org/apache/camel/builder/DataFormatClause.java
index a1549752cd10..8fc664634ad7 100644
---
a/core/camel-core-model/src/main/java/org/apache/camel/builder/DataFormatClause.java
+++
b/core/camel-core-model/src/main/java/org/apache/camel/builder/DataFormatClause.java
@@ -66,6 +66,9 @@ import org.apache.camel.model.dataformat.TarFileDataFormat;
import org.apache.camel.model.dataformat.ThriftDataFormat;
import org.apache.camel.model.dataformat.ToonDataFormat;
import org.apache.camel.model.dataformat.UblDataFormat;
+import org.apache.camel.model.dataformat.UniVocityCsvDataFormat;
+import org.apache.camel.model.dataformat.UniVocityFixedDataFormat;
+import org.apache.camel.model.dataformat.UniVocityTsvDataFormat;
import org.apache.camel.model.dataformat.XMLSecurityDataFormat;
import org.apache.camel.model.dataformat.YAMLDataFormat;
import org.apache.camel.model.dataformat.YAMLLibrary;
@@ -1121,6 +1124,34 @@ public class DataFormatClause<T extends
ProcessorDefinition<?>> {
return dataFormat(ublDataFormat);
}
+ /**
+ * Uses the uniVocity CSV data format
+ */
+ public T univocityCsv() {
+ return dataFormat(new UniVocityCsvDataFormat());
+ }
+
+ /**
+ * Uses the uniVocity fixed-length data format
+ */
+ public T univocityFixed() {
+ return dataFormat(new UniVocityFixedDataFormat());
+ }
+
+ /**
+ * Uses the uniVocity TSV data format
+ */
+ public T univocityTsv() {
+ return dataFormat(new UniVocityTsvDataFormat());
+ }
+
+ /**
+ * Uses the YAML data format with the SnakeYAML library
+ */
+ public T yaml() {
+ return dataFormat(new YAMLDataFormat());
+ }
+
/**
* Uses the YAML data format
*
diff --git
a/core/camel-java-io/src/main/java/org/apache/camel/java/in/ChainReplayer.java
b/core/camel-java-io/src/main/java/org/apache/camel/java/in/ChainReplayer.java
index 576088a50ca3..256d7d7428ed 100644
---
a/core/camel-java-io/src/main/java/org/apache/camel/java/in/ChainReplayer.java
+++
b/core/camel-java-io/src/main/java/org/apache/camel/java/in/ChainReplayer.java
@@ -32,11 +32,13 @@ import java.util.ArrayList;
import java.util.HashSet;
import java.util.IllegalFormatException;
import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
+import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -51,6 +53,7 @@ import org.apache.camel.java.in.JavaChainParser.BinOp;
import org.apache.camel.java.in.JavaChainParser.Bool;
import org.apache.camel.java.in.JavaChainParser.Call;
import org.apache.camel.java.in.JavaChainParser.Chain;
+import org.apache.camel.java.in.JavaChainParser.Chr;
import org.apache.camel.java.in.JavaChainParser.ClassLit;
import org.apache.camel.java.in.JavaChainParser.ClassName;
import org.apache.camel.java.in.JavaChainParser.Concat;
@@ -118,7 +121,7 @@ final class ChainReplayer {
private static final Set<String> READABLE_JDK = Set.of(
"java.lang.Integer", "java.lang.Long", "java.lang.Short",
"java.lang.Byte", "java.lang.Double",
"java.lang.Float", "java.lang.Boolean", "java.lang.Character",
"java.util.concurrent.TimeUnit",
- "java.nio.charset.StandardCharsets");
+ "java.nio.charset.StandardCharsets", "java.util.zip.Deflater");
/** Classes whose static DSL methods a route may call, qualified or
statically imported: language(...), and(...). */
private static final List<Class<?>> STATIC_DSL
@@ -135,12 +138,13 @@ final class ChainReplayer {
private static final Set<String> DENIED_METHODS = Set.of(
"getContext", "getCamelContext", "setContext", "setCamelContext",
"addRoutesToCamelContext",
- "addRouteConfigurationsToCamelContext",
"addTemplatedRoutesToCamelContext", "configure", "configuration",
+ "addRouteConfigurationsToCamelContext",
"addTemplatedRoutesToCamelContext",
"includeRoutes", "bindToRegistry", "propertyInject", "endpoint",
"getClass", "wait", "notify", "notifyAll");
/** The life cycle of a route builder, which a parse never runs: only the
DSL it offers is called. */
private static final List<String> BUILDER_LIFE_CYCLE = List.of(
- "populate", "configure", "prepare", "update", "initialize", "set",
"check", "add", "remove", "customize");
+ "populate", "configure", "configuration", "prepare", "update",
"initialize", "set", "check", "add", "remove",
+ "customize");
/** The builder of the configure() being replayed. */
private ReplayBuilder builder;
@@ -247,6 +251,8 @@ final class ChainReplayer {
private Object evaluate(Node node) {
if (node instanceof Str s) {
return s.value();
+ } else if (node instanceof Chr c) {
+ return c.value();
} else if (node instanceof Num n) {
return number(n.text());
} else if (node instanceof Bool b) {
@@ -546,6 +552,12 @@ final class ChainReplayer {
for (String pkg : CAMEL_PACKAGES) {
candidates.add(pkg + name);
}
+ // a snippet without its imports: TimeUnit.SECONDS,
Deflater.BEST_COMPRESSION
+ for (String jdk : READABLE_JDK) {
+ if (jdk.endsWith("." + first)) {
+ candidates.add(jdk + name.substring(first.length()));
+ }
+ }
}
for (String candidate : candidates) {
String binary = candidate;
@@ -610,6 +622,11 @@ final class ChainReplayer {
String text = formatText(c.calls().get(0).args());
return text != null ? text : new Unknown(c, "a format the parser
cannot work out");
}
+ if (c.qualifier() != null && c.calls().size() == 1
+ && COLLECTIONS.contains(c.qualifier() + "." +
c.calls().get(0).name())) {
+ // setHeaders(Map.of("foo", constant("ABC"))): a collection of
values the parser works out
+ return collection(c);
+ }
if (c.qualifier() != null && c.qualifier().equals(builderParameter)) {
// rb.simple(...) in a builder lambda: the builder
target = builder;
@@ -668,6 +685,36 @@ final class ChainReplayer {
return target;
}
+ /** Factories of JDK collections a route passes values in: only data,
nothing of the project runs. */
+ private static final Set<String> COLLECTIONS = Set.of(
+ "Map.of", "java.util.Map.of", "List.of", "java.util.List.of",
"Set.of", "java.util.Set.of",
+ "Arrays.asList", "java.util.Arrays.asList",
"Collections.singletonList", "java.util.Collections.singletonList");
+
+ private Object collection(Chain c) {
+ Call call = c.calls().get(0);
+ List<Object> values = new ArrayList<>();
+ for (Node arg : call.args()) {
+ Object v = evaluate(arg);
+ if (v instanceof Unknown || v == null) {
+ return new Unknown(c, "a collection of values the parser
cannot work out");
+ }
+ values.add(v);
+ }
+ String qualifier =
c.qualifier().substring(c.qualifier().lastIndexOf('.') + 1);
+ if (qualifier.equals("Map")) {
+ if (values.size() % 2 != 0) {
+ return new Unknown(c, "a collection of values the parser
cannot work out");
+ }
+ // in the order of the source, which Map.of does not keep but the
model and a dump should
+ Map<Object, Object> map = new LinkedHashMap<>();
+ for (int i = 0; i < values.size(); i += 2) {
+ map.put(values.get(i), values.get(i + 1));
+ }
+ return map;
+ }
+ return qualifier.equals("Set") ? new LinkedHashSet<>(values) : values;
+ }
+
/** Why a statement that sets up the CamelContext (components, beans,
properties) is not read: it is not a route. */
static final String CONFIGURES_THE_CONTEXT = "configures the CamelContext,
not a route";
@@ -794,9 +841,21 @@ final class ChainReplayer {
/** The URI of an endpoint DSL chain, or null when the resolver does not
know its factory. */
private String endpoint(Chain c) {
Call factory = c.calls().get(0);
- List<String> args = new ArrayList<>();
+ if (factory.args().size() > 2) {
+ return null;
+ }
+ List<Object> paths = new ArrayList<>();
for (Node arg : factory.args()) {
- args.add(uriValue(arg));
+ Object v = evaluate(arg);
+ if (!(v instanceof String) && !(v instanceof Unknown)) {
+ // a factory of the endpoint DSL takes the path as text:
split(stax(Record.class)) is StAXBuilder
+ return null;
+ }
+ paths.add(v);
+ }
+ List<String> args = new ArrayList<>();
+ for (int i = 0; i < paths.size(); i++) {
+ args.add(uriValue(factory.args().get(i), paths.get(i)));
}
List<EndpointDslResolver.Option> options = new ArrayList<>();
for (int i = 1; i < c.calls().size(); i++) {
@@ -819,7 +878,10 @@ final class ChainReplayer {
/** A value in an endpoint URI as the endpoint DSL writes it; a marked
placeholder when unknown (reported). */
private String uriValue(Node node) {
- Object v = evaluate(node);
+ return uriValue(node, evaluate(node));
+ }
+
+ private String uriValue(Node node, Object v) {
if (v instanceof Unknown u) {
report(node, u.reason());
return LwJavaParser.UNRESOLVED_PREFIX + text(node) + "}";
@@ -851,6 +913,10 @@ final class ChainReplayer {
}
private Object invoke(Object target, Call call) {
+ Object value = valueCall(target, call);
+ if (value != null) {
+ return value;
+ }
List<Method> candidates = candidates(target.getClass(), call.name(),
call.args().size());
if (candidates.isEmpty()) {
String reason = target == builder
@@ -861,6 +927,31 @@ final class ChainReplayer {
return call(target, candidates, call);
}
+ /**
+ * A call on a JDK value that only computes a value: {@code
TimeUnit.MILLISECONDS.toString()},
+ * {@code TimeUnit.SECONDS.toMillis(5)}; null for anything else.
+ */
+ private Object valueCall(Object target, Call call) {
+ if (target instanceof Enum<?> e && call.args().isEmpty()
+ && (call.name().equals("name") ||
call.name().equals("toString"))) {
+ return e.name();
+ }
+ if (target instanceof TimeUnit unit && call.args().size() == 1 &&
call.name().startsWith("to")
+ && evaluate(call.args().get(0)) instanceof Number n) {
+ return switch (call.name()) {
+ case "toNanos" -> unit.toNanos(n.longValue());
+ case "toMicros" -> unit.toMicros(n.longValue());
+ case "toMillis" -> unit.toMillis(n.longValue());
+ case "toSeconds" -> unit.toSeconds(n.longValue());
+ case "toMinutes" -> unit.toMinutes(n.longValue());
+ case "toHours" -> unit.toHours(n.longValue());
+ case "toDays" -> unit.toDays(n.longValue());
+ default -> null;
+ };
+ }
+ return null;
+ }
+
/** Calls the overload the arguments fit best; a static one when {@code
target} is null. */
private Object call(Object target, List<Method> candidates, Call call) {
Object[] values = new Object[call.args().size()];
@@ -923,6 +1014,11 @@ final class ChainReplayer {
} catch (InvocationTargetException e) {
Throwable cause = e.getCause() != null ? e.getCause() : e;
String message = String.valueOf(cause.getMessage());
+ if (standsIn(values)) {
+ // the DSL checks or uses right away what stands in for a
value: configuration(builder) calls
+ // builder.build(), setHeaders(headerMap) needs the map; the
source is not wrong
+ return new Unknown(call, "needs a value the parser cannot
see");
+ }
if (cause instanceof NullPointerException &&
message.contains("CamelContext")) {
// the replay builder never has a context: whatever needs one
is not done in a parse
return new Unknown(call, "needs a running CamelContext, which
a parse does not have");
@@ -976,6 +1072,16 @@ final class ChainReplayer {
}
}
+ /** Whether something stands in for a value the parser does not know, in
the arguments of a call. */
+ private static boolean standsIn(Object[] values) {
+ for (Object v : values) {
+ if (v instanceof Unknown) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/** How well a method's parameter types match the values: exactly the
value's type counts most, Object least. */
private static int specificity(Method m, Object[] values) {
Class<?>[] types = m.getParameterTypes();
@@ -1204,6 +1310,13 @@ final class ChainReplayer {
default -> defaultValue(method.getReturnType());
});
}
+ if (!type.isPrimitive() && !type.isEnum() && type != Class.class) {
+ // an object of a class, such as xpath("/c:n", ns) with Namespaces
ns built in the method: null stands
+ // in, as the parser creates no objects of a route
+ cost[0] += 30;
+ reports.add(new JavaParseResult.Unresolved(node.line(), text,
u.reason()));
+ return null;
+ }
return NO;
}
@@ -1260,6 +1373,8 @@ final class ChainReplayer {
static String text(Node node) {
if (node instanceof Str s) {
return "\"" + s.value() + "\"";
+ } else if (node instanceof Chr c) {
+ return "'" + c.value() + "'";
} else if (node instanceof Num n) {
return n.text();
} else if (node instanceof Bool b) {
diff --git
a/core/camel-java-io/src/main/java/org/apache/camel/java/in/JavaChainParser.java
b/core/camel-java-io/src/main/java/org/apache/camel/java/in/JavaChainParser.java
index 8b37fc25cdb9..fa4381fcb792 100644
---
a/core/camel-java-io/src/main/java/org/apache/camel/java/in/JavaChainParser.java
+++
b/core/camel-java-io/src/main/java/org/apache/camel/java/in/JavaChainParser.java
@@ -45,6 +45,10 @@ final class JavaChainParser {
record Num(String text, int line) implements Node {
}
+ /** A char literal: {@code 'i'}. */
+ record Chr(char value, int line) implements Node {
+ }
+
record Bool(boolean value, int line) implements Node {
}
@@ -428,10 +432,14 @@ final class JavaChainParser {
Token t = peek();
int line = t.line();
switch (t.kind()) {
- case STRING, CHAR -> {
+ case STRING -> {
pos++;
return new Str(t.text(), line);
}
+ case CHAR -> {
+ pos++;
+ return t.text().length() == 1 ? new Chr(t.text().charAt(0),
line) : new Str(t.text(), line);
+ }
case NUMBER -> {
pos++;
return new Num(t.text(), line);
diff --git
a/core/camel-java-io/src/test/java/org/apache/camel/java/in/LwJavaParserTest.java
b/core/camel-java-io/src/test/java/org/apache/camel/java/in/LwJavaParserTest.java
index 0a399f581fff..d582aeb265f6 100644
---
a/core/camel-java-io/src/test/java/org/apache/camel/java/in/LwJavaParserTest.java
+++
b/core/camel-java-io/src/test/java/org/apache/camel/java/in/LwJavaParserTest.java
@@ -19,16 +19,22 @@ package org.apache.camel.java.in;
import java.util.List;
import org.apache.camel.model.ChoiceDefinition;
+import org.apache.camel.model.CircuitBreakerDefinition;
+import org.apache.camel.model.DelayDefinition;
+import org.apache.camel.model.FilterDefinition;
+import org.apache.camel.model.MarshalDefinition;
import org.apache.camel.model.OnExceptionDefinition;
import org.apache.camel.model.ProcessDefinition;
import org.apache.camel.model.ProcessorDefinition;
import org.apache.camel.model.RouteDefinition;
import org.apache.camel.model.SetHeaderDefinition;
+import org.apache.camel.model.SetHeadersDefinition;
import org.apache.camel.model.SplitDefinition;
import org.apache.camel.model.SwitchCaseDefinition;
import org.apache.camel.model.SwitchDefinition;
import org.apache.camel.model.ToDefinition;
import org.apache.camel.model.TryDefinition;
+import org.apache.camel.model.dataformat.ZipDeflaterDataFormat;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
@@ -198,6 +204,66 @@ class LwJavaParserTest {
assertThat(sw.getOtherwiseDefinition().getLineNumber()).isEqualTo(5);
}
+ @Test
+ void charLiteralsCollectionsAndJdkConstants() {
+ // CAMEL-25182: shapes of the Java examples of the documentation
+ JavaParseResult result = new LwJavaParser().parse("""
+ from("direct:a")
+ .split(body().tokenize(","))
+ .setHeaders(Map.of("foo", constant("ABC"), "bar", "XYZ"))
+ .marshal().zipDeflater(Deflater.BEST_COMPRESSION)
+
.setHeader("unit").constant(TimeUnit.MILLISECONDS.toString())
+ .delay(TimeUnit.SECONDS.toMillis(5))
+ .to("mock:a");
+ """);
+ assertThat(result.isComplete()).as("%s", result.unresolved()).isTrue();
+ RouteDefinition route = result.routes().getRoutes().get(0);
+ SplitDefinition split = (SplitDefinition) route.getOutputs().get(0);
+ SetHeadersDefinition headers = (SetHeadersDefinition)
split.getOutputs().get(0);
+
assertThat(headers.getHeaders()).extracting(SetHeaderDefinition::getName).containsExactly("foo",
"bar");
+ MarshalDefinition marshal = (MarshalDefinition)
split.getOutputs().get(1);
+ assertThat(((ZipDeflaterDataFormat)
marshal.getDataFormatType()).getCompressionLevel()).isEqualTo("9");
+ SetHeaderDefinition unit = (SetHeaderDefinition)
split.getOutputs().get(2);
+
assertThat(unit.getExpression().getExpression()).isEqualTo("MILLISECONDS");
+ DelayDefinition delay = (DelayDefinition) split.getOutputs().get(3);
+ assertThat(delay.getExpression().getExpression()).isEqualTo("5000");
+ }
+
+ @Test
+ void anObjectOfAClassBuiltInTheMethod() {
+ // Namespaces ns = new Namespaces(...): null stands in, and the rest
of the route is read
+ JavaParseResult result = new LwJavaParser().parse("""
+ from("direct:a")
+ .filter(xpath("/c:number = 55", ns))
+ .to("mock:result");
+ """);
+
assertThat(result.unresolved()).extracting(JavaParseResult.Unresolved::text).containsExactly("ns");
+ FilterDefinition filter = (FilterDefinition)
result.routes().getRoutes().get(0).getOutputs().get(0);
+
assertThat(filter.getExpression().getExpression()).isEqualTo("/c:number = 55");
+ assertThat(filter.getOutputs()).hasSize(1);
+
+ // xtokenize(path, 'i', ns) is the DSL method with a char, which uses
ns right away
+ result = new LwJavaParser().parse("""
+ from("direct:a").split().xtokenize("//order", 'i',
ns).to("mock:order");
+ """);
+
assertThat(result.unresolved()).extracting(JavaParseResult.Unresolved::reason)
+ .contains("needs a value the parser cannot see");
+ }
+
+ @Test
+ void modelMethodsNamedLikeTheBuilderLifeCycle() {
+ // configuration(...) and configure(...) are only the builder's life
cycle on the route builder
+ JavaParseResult result = new LwJavaParser().parse("""
+ from("direct:a")
+ .circuitBreaker().configuration("myConfig")
+ .to("mock:a")
+ .end();
+ """);
+ assertThat(result.isComplete()).as("%s", result.unresolved()).isTrue();
+ CircuitBreakerDefinition cb = (CircuitBreakerDefinition)
result.routes().getRoutes().get(0).getOutputs().get(0);
+ assertThat(cb.getConfiguration()).isEqualTo("myConfig");
+ }
+
@Test
void globalErrorHandlingAndRest() {
JavaParseResult result = new LwJavaParser().parse("""
diff --git a/docs/user-manual/modules/ROOT/pages/exception-advanced.adoc
b/docs/user-manual/modules/ROOT/pages/exception-advanced.adoc
index 2356967142d3..3de7dddb9746 100644
--- a/docs/user-manual/modules/ROOT/pages/exception-advanced.adoc
+++ b/docs/user-manual/modules/ROOT/pages/exception-advanced.adoc
@@ -425,36 +425,3 @@ public class MyRetryBean {
}
}
----
-
-=== Using Custom ExceptionPolicyStrategy
-
-The default `org.apache.camel.processor.errorhandler.ExceptionPolicyStrategy`
in Camel should be sufficient in nearly all use-cases.
-However, if you need to use your own (use only for rare and advanced
use-cases) this can be configured as the
-sample below illustrates:
-
-._Java-only: configuring a custom ExceptionPolicyStrategy_
-[source,java]
-----
-// configure the error handler to use my policy instead of the default from
Camel
-errorHandler(deadLetterChannel("mock:error").exceptionPolicyStrategy(new
MyPolicy()));
-----
-
-Using our own strategy *`MyPolicy`* we can change the default behavior of
-Camel with our own code to resolve which exception type
-from above should be handling the given thrown exception.
-
-._Java-only: custom ExceptionPolicyStrategy implementation_
-[source,java]
-----
-public static class MyPolicy implements ExceptionPolicyStrategy {
-
- @Override
- public ExceptionPolicyKey getExceptionPolicy(Set<ExceptionPolicyKey>
exceptionPolicies, Exchange exchange, Throwable exception) {
- // This is just an example that always forces the exception type
configured
- // with MyPolicyException to win.
- return new ExceptionPolicyKey(null, MyPolicyException.class, null);
- }
-}
-----
-
-
diff --git
a/docs/user-manual/modules/ROOT/pages/exception-handling-patterns.adoc
b/docs/user-manual/modules/ROOT/pages/exception-handling-patterns.adoc
index 8f4d84d210e0..e588166c4e80 100644
--- a/docs/user-manual/modules/ROOT/pages/exception-handling-patterns.adoc
+++ b/docs/user-manual/modules/ROOT/pages/exception-handling-patterns.adoc
@@ -96,7 +96,7 @@ Java::
+
[source,java]
----
-onException(ValidationException)
+onException(ValidationException.class)
.handled(true);
----
diff --git a/docs/user-manual/modules/ROOT/pages/exception-redelivery.adoc
b/docs/user-manual/modules/ROOT/pages/exception-redelivery.adoc
index cdd9ffff1a31..37c244c1a78d 100644
--- a/docs/user-manual/modules/ROOT/pages/exception-redelivery.adoc
+++ b/docs/user-manual/modules/ROOT/pages/exception-redelivery.adoc
@@ -115,10 +115,12 @@ All redelivery attempts start at the point of the
failure. So the route:
[source,java]
----
onException(ConnectException.class)
- .from("direct:start")
+ .maximumRedeliveries(3);
+
+from("direct:start")
.process("processor1")
.process("processor2") // <--- throws a ConnectException
- .to("mock:theEnd")
+ .to("mock:theEnd");
----
Will retry from *`processor2`* - not the complete route.
diff --git a/docs/user-manual/modules/ROOT/pages/rest-dsl-openapi.adoc
b/docs/user-manual/modules/ROOT/pages/rest-dsl-openapi.adoc
index 97c4f1fc8a4d..39538fd49071 100644
--- a/docs/user-manual/modules/ROOT/pages/rest-dsl-openapi.adoc
+++ b/docs/user-manual/modules/ROOT/pages/rest-dsl-openapi.adoc
@@ -255,7 +255,7 @@ Java::
+
[source,java]
----
- rest().openApi("petstore-v3.json").missingOperation("ignore");
+
rest().openApi().specification("petstore-v3.json").missingOperation("ignore");
----
XML::
@@ -291,7 +291,7 @@ Java::
+
[source,java]
----
- rest().openApi("petstore-v3.json").missingOperation("mock");
+
rest().openApi().specification("petstore-v3.json").missingOperation("mock");
----
XML::
@@ -346,7 +346,7 @@ Java::
----
restConfiguration().clientRequestValidation(true);
-rest().openApi("petstore-v3.json").missingOperation("mock");
+rest().openApi().specification("petstore-v3.json").missingOperation("mock");
----
XML::
diff --git a/docs/user-manual/modules/ROOT/pages/route-template.adoc
b/docs/user-manual/modules/ROOT/pages/route-template.adoc
index 30ac699d67ba..a89dcad18011 100644
--- a/docs/user-manual/modules/ROOT/pages/route-template.adoc
+++ b/docs/user-manual/modules/ROOT/pages/route-template.adoc
@@ -501,7 +501,7 @@ public class MyRouteTemplates extends RouteBuilder {
.templateParameter("color")
.from("direct:{{name}}")
.choice()
- .when(simple("'{{color}}' == 'red'") // <1>
+ .when(simple("'{{color}}' == 'red'")) // <1>
.to("direct:red")
.otherwise()
.to("color:other")
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogEndpointDslResolver.java
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogEndpointDslResolver.java
index 1265d81d240b..502883bce5ab 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogEndpointDslResolver.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogEndpointDslResolver.java
@@ -45,11 +45,12 @@ final class CatalogEndpointDslResolver implements
EndpointDslResolver {
return null;
}
String scheme = named.uri().substring(0, named.uri().indexOf("://"));
- // kafka("myKafka", "orders"): a component under another name, the
catalog cannot say what it is
- ComponentModel model = args.size() == 2 ? null :
catalog.componentModel(scheme);
- if (args.size() != 2 && model == null) {
+ if (catalog.componentModel(scheme) == null) {
+ // and(user, admin) is no endpoint
return null;
}
+ // kafka("myKafka", "orders"): a component under another name, the
catalog cannot say what its options are
+ ComponentModel model = args.size() == 2 ? null :
catalog.componentModel(scheme);
List<Option> resolved = new ArrayList<>();
List<String> problems = new ArrayList<>();
for (Option o : options) {
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocJavaExamplesTest.java
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocJavaExamplesTest.java
new file mode 100644
index 000000000000..becc40b7badd
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocJavaExamplesTest.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands.ai;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Stream;
+
+import org.apache.camel.catalog.CamelCatalog;
+import org.apache.camel.catalog.DefaultCamelCatalog;
+import org.apache.camel.java.in.JavaParseResult;
+import org.apache.camel.java.in.LwJavaParser;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * CAMEL-25182: every Java route example of the documentation calls the Java
DSL as it is. The examples are read with
+ * the Java DSL parser of camel-java-io, which replays each chain of calls
against Camel's DSL without compiling the
+ * snippet. What the parser cannot see (lambdas, variables, helper methods) is
fine; a DSL method that does not exist,
+ * or does not take the arguments of the example, is a broken example.
+ */
+class CatalogDocJavaExamplesTest {
+
+ private static final Pattern BLOCK
+ =
Pattern.compile("(?:^\\.(?<title>[^\\n]*)\\n)?\\[source,java\\]\\n-{4,}\\n(?<code>.*?)\\n-{4,}",
+ Pattern.DOTALL | Pattern.MULTILINE);
+
+ /** A snippet with routes: blocks of plain Java (beans, configuration) are
not read. */
+ private static final Pattern ROUTE_DSL = Pattern.compile(
+
"(?<![\\w.])(from|fromF|rest|routeTemplate|onException|errorHandler|interceptFrom|intercept"
+ +
"|interceptSendToEndpoint|onCompletion)\\s*\\(");
+
+ /** Titles of examples that show code that is wrong on purpose, or is not
Camel's Java DSL. */
+ private static final List<String> TITLES_SKIPPED
+ = List.of("pseudo", "incorrect", "will not compile", "does not
compile", "before migration", "old ");
+
+ /** The reasons of the parser that mean the example calls the DSL wrongly.
*/
+ private static final List<String> BROKEN = List.of(
+ "no such DSL method", "arguments the DSL method does not take",
"the DSL refused it");
+
+ /** Where the user manual is in the source tree; its pages are not in the
catalog. */
+ private static final Path USER_MANUAL =
Path.of("../../../docs/user-manual/modules/ROOT/pages");
+
+ /** User manual pages that show old syntax on purpose. */
+ private static final List<String> USER_MANUAL_SKIPPED =
List.of("upgrade-guide", "migration-guide");
+
+ private static CamelCatalog catalog;
+
+ @BeforeAll
+ static void setup() {
+ catalog = new DefaultCamelCatalog();
+ }
+
+ @Test
+ void everyJavaExampleOfTheDocumentationCallsTheJavaDsl() throws
IOException {
+ List<String> failures = new ArrayList<>();
+ int examples = 0;
+ for (String name : catalog.findDocNames()) {
+ examples += check(name, catalog.asciiDoc(name), failures);
+ }
+ if (Files.isDirectory(USER_MANUAL)) {
+ try (Stream<Path> files = Files.list(USER_MANUAL)) {
+ for (Path f : files.filter(f ->
f.toString().endsWith(".adoc")).sorted().toList()) {
+ String name = f.getFileName().toString().replace(".adoc",
"");
+ if
(USER_MANUAL_SKIPPED.stream().noneMatch(name::contains)) {
+ examples += check(name, Files.readString(f), failures);
+ }
+ }
+ }
+ }
+ assertThat(examples).as("Java route examples found in the
documentation").isGreaterThan(2000);
+ assertThat(failures).as("Documentation examples that call the Java DSL
wrongly").isEmpty();
+ }
+
+ private static int check(String page, String doc, List<String> failures) {
+ if (doc == null) {
+ return 0;
+ }
+ int examples = 0;
+ Matcher m = BLOCK.matcher(doc);
+ while (m.find()) {
+ String title = m.group("title") != null ?
m.group("title").toLowerCase(Locale.ROOT) : "";
+ String code = m.group("code");
+ if (!ROUTE_DSL.matcher(code).find() ||
TITLES_SKIPPED.stream().anyMatch(title::contains)) {
+ continue;
+ }
+ examples++;
+ int line = 1 + (int) doc.substring(0,
m.start("code")).chars().filter(c -> c == '\n').count();
+ JavaParseResult result = new LwJavaParser()
+ .setEndpointDslResolver(new
CatalogEndpointDslResolver(catalog))
+ .setConstantResolver(new ProjectConstantResolver(Map.of(),
catalog))
+ .parse(code);
+ for (JavaParseResult.Unresolved u : result.unresolved()) {
+ if (BROKEN.stream().anyMatch(u.reason()::startsWith)) {
+ failures.add(page + ":" + (line + u.line() - 1) + " " +
u.text() + " - " + u.reason());
+ }
+ }
+ }
+ return examples;
+ }
+}