[ 
https://issues.apache.org/jira/browse/CAMEL-25928?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18131599#comment-18131599
 ] 

Claus Ibsen commented on CAMEL-25928:
-------------------------------------

h2. Plan (2026-10-24)

The components, data formats and languages all have body types now 
(CAMEL-24961), so the EIPs are the part of CAMEL-24844 that is left. Besides 
the EIPs, the open items of the epic are CAMEL-25334 (content-type aware 
payloads, its own track) and the CAMEL-26117..26270 bug JIRAs found while 
adding the body types.

h3. Where the EIPs live

All 86 EIP models are in camel-core-model (82 with the {{eip}} label, plus 
doCatch, doFinally, policy and transacted), also those implemented outside 
core. Those plug in their implementation:
* through a {{ProcessorFactory}} in 
{{META-INF/services/org/apache/camel/model/}}: {{circuitBreaker}} and 
{{onFallback}} (camel-resilience4j and camel-microprofile-fault-tolerance), 
{{a2aSubTask}} (camel-a2a), {{kamelet}} (camel-kamelet);
* through a service the core processor looks up: {{tokenizer}} 
(camel-langchain4j-tokenizer), {{saga}} (camel-lra), {{transformDataType}} (the 
transformers of the components), {{pausable}} and {{resumable}} (camel-kafka, 
camel-aws2-kinesis, camel-mongodb, camel-wal), {{policy}} and {{transacted}} 
(camel-spring, camel-jta).

So the {{@BodyTypes}} of every EIP goes on its model class in camel-core-model, 
and the build writes it into {{models/*.json}}. For an EIP implemented outside 
core, the annotation is the contract every implementation keeps, and the 
runtime check of it lives in the implementing module (both circuit breakers 
must agree; where they cannot, the description says how they differ).

h3. Shape

* Three new sides on {{@BodyTypes}}:
** {{eipIn}}: the bodies the EIP needs (tokenizer needs text);
** {{eipOut}}: the body after it;
** {{eipSteps}}: the body its own steps start with.
* {{eipIn}} can be {{any}}. {{eipOut}} and {{eipSteps}} can be {{unchanged}} or 
{{unknown}}.
* A new member {{from}} on {{@BodyType}}, for the EIPs whose body is another 
model's (group 2 of the description). Its value is the name of the other side:
** {{expressionOut}}: the language of the EIP's expression (setBody, transform);
** {{marshalIn}} / {{marshalOut}} and {{unmarshalIn}} / {{unmarshalOut}}: the 
data format (marshal, unmarshal);
** {{producerIn}} / {{producerOut}}: the endpoint (to, toD, enrich);
** {{consumerOut}}: the endpoint read by a consumer (poll, pollEnrich);
** {{steps}}: what its own steps end with (choice, filter, doTry, step, 
circuitBreaker and the other blocks).
** A side with {{from}} needs no carriers. The side name says which model to 
follow, so {{enrich}} ({{producerOut}}) and {{pollEnrich}} ({{consumerOut}}) 
need no other hint. This answers open decision 1: a reference, not a copy.
* {{dependsOn}} as for the components:
** {{aggregationStrategy}} for split, multicast, enrich, pollEnrich and 
claimCheck;
** {{variableReceive}} for to, toD, enrich, pollEnrich, poll, marshal and 
unmarshal.
** With another value, the body is not known. This is conservative: with 
{{variableReceive}} the body stays as it was, and stating that needs a 
{{byOption}} form for "any value". That form is a follow-up (open decision 2).
* convertBodyTo: a {{byOption}} for each common value of {{type}}: String, 
byte[], InputStream, Map and List, both short and fully qualified. Otherwise 
the body is {{unknown}} (open decision 3).
* {{BodyTypesHelper}} checks the EIP sides as it does the others: the sides of 
the model kind, carriers that fit the side, {{from}} only with a side name that 
fits it, and option values that exist.

h3. The body types, by EIP

* *eipOut unchanged*: setHeader, setHeaders, setProperty, setVariable, 
setVariables, removeHeader, removeHeaders, removeProperty, removeProperties, 
removeVariable, convertHeaderTo, convertVariableTo, log, validate, delay, 
throttle, sample, wireTap, stop, rollback, throwException, threads, pausable, 
resumable.
* *from another model*:
** setBody and transform: eipOut from {{expressionOut}};
** marshal and unmarshal: eipIn and eipOut from their data format side;
** to and toD: eipIn from {{producerIn}}, eipOut from {{producerOut}};
** enrich: eipOut from {{producerOut}};
** poll and pollEnrich: eipOut from {{consumerOut}}; pollEnrich gives null 
after a timeout.
* *blocks*: eipSteps unchanged and eipOut from {{steps}}: choice, when, 
otherwise, filter, doTry, doCatch, doFinally, step, pipeline, circuitBreaker, 
onFallback, saga, a2aSubTask, idempotentConsumer, loadBalance, cache, policy, 
transacted, resequence, loop and multicast.
** loop: with copy=false, each iteration starts with the body of the last one.
** multicast: each output gets a copy of the body; after it, the last reply 
unless aggregationStrategy is set.
* *own rule*:
** split: eipSteps {{unknown}} (each part of what its expression gives); eipOut 
unchanged, dependsOn aggregationStrategy;
** aggregate: eipSteps {{unknown}} (what the aggregationStrategy makes); eipOut 
unchanged (the incoming message goes on);
** sort: eipOut a {{list}} ({{java.util.List}});
** tokenizer: eipIn {{text}}, eipOut a {{list}} ({{java.lang.String[]}});
** claimCheck: unchanged for Set and Push, {{unknown}} (the stored body) for 
Get, GetAndRemove and Pop;
** convertBodyTo: as above.
* *unknown* (user code, or decided at runtime): bean, process, script (the EIP 
does not use the result, but the script can set the body), kamelet (until the 
Kamelet data types are read), transformDataType (until the transformers have 
body types), recipientList, routingSlip, dynamicRouter, switch.
* *none*: the configuration models (batchConfig, streamConfig, the load 
balancer kinds, resilience4jConfiguration, faultToleranceConfiguration, the 
langChain4j tokenizer settings) and from, whose body is the endpoint's 
consumerOut.

h3. Pull requests

# *Tooling and the core EIPs* (camel-core-model, camel-package-maven-plugin, 
camel-tooling-model, docs, camel-jbang):
#* the sides, {{from}} and the checks;
#* {{JsonMapper}} reads and writes {{bodyTypes}} for {{EipModel}}, so the 
{{models/*.json}} files carry them, and the MCP {{camel_catalog_doc}} returns 
them;
#* {{camel catalog doc}} prints them for an EIP;
#* the EIP pages get the _Message Body_ section through the shared 
{{body-types.adoc}} partial;
#* the annotations on all the EIP models above;
#* a runtime check in camel-core: a reference route for each core EIP of the 
groups _unchanged_, _blocks_ and _own rule_. It compares the body after the 
EIP, and at the first step inside it, with what the annotation of its model 
says.
# *The EIPs implemented outside core*: the same runtime check in 
camel-resilience4j, camel-microprofile-fault-tolerance, camel-a2a, 
camel-kamelet, camel-langchain4j-tokenizer and camel-lra, with fixes to the 
annotations where an implementation differs.
# *The validator reads them*: {{TextBodyFlow}} and {{BodyTypeFlow}} in 
camel-yaml-dsl-validator drop {{KEEPING_STEPS}}, {{SAME_BODY_BLOCKS}} and 
{{SETS_THE_BODY}} for the catalog. They then walk the blocks they skip today: 
split, multicast, loop, circuitBreaker, aggregate and the rest. 
{{EipDocExamplesTest}} (more than 250 EIP doc examples) guards against new 
false reports.
# *Follow-ups*:
#* a {{byOption}} form for "any value" (variableReceive);
#* the body types of the data type transformers (the 47 {{transformers/}} of 
the catalog), which transformDataType then follows;
#* the Kamelet data types for the kamelet EIP.

_Claude Code on behalf of davsclaus_


> camel-catalog - the body types of the EIPs: the body after each EIP and the 
> body its own steps start with
> ---------------------------------------------------------------------------------------------------------
>
>                 Key: CAMEL-25928
>                 URL: https://issues.apache.org/jira/browse/CAMEL-25928
>             Project: Camel
>          Issue Type: Improvement
>          Components: camel-catalog, camel-core
>            Reporter: Claus Ibsen
>            Priority: Major
>             Fix For: 4.24.0
>
>
> The EIP part of CAMEL-24961 (phase C of CAMEL-24844). CAMEL-24961 adds 
> {{@BodyTypes}} and a {{bodyTypes}} section in the catalog JSON for the 
> components, data formats and languages 
> (https://github.com/apache/camel/pull/28241 and 
> https://github.com/apache/camel/pull/28242). The EIPs have none yet. So the 
> YAML validator keeps its own EIP tables, and {{camel_catalog_doc}} cannot say 
> what the body is after a {{split}} or a {{convertBodyTo}}.
> h3. What the EIPs do with the body
> The steps of the YAML routes in camel-jbang-examples, camel-examples and 
> camel-kamelets use 38 EIPs: 4278 uses in 1107 files. The 20 EIPs in this 
> table have 98.5% of them:
> ||EIP||uses||the body after it||
> |to|1244|the endpoint's producerOut (CAMEL-24961)|
> |setHeader|1084|unchanged|
> |choice|408|its branches start with the same body; after it, what the branch 
> that ran ends with|
> |log|240|unchanged|
> |marshal|203|the data format's marshalOut|
> |setBody|193|the language's expressionOut|
> |setProperty|193|unchanged|
> |removeHeader|150|unchanged|
> |unmarshal|141|the data format's unmarshalOut|
> |convertBodyTo|56|the type that its type option names; null with 
> mandatory=false when there is no conversion 
> (ConvertBodyProcessor.java:149-152)|
> |bean|44|the method's return value; unchanged for a void method 
> (MethodInfo.java:337)|
> |process|42|unknown: the user's code|
> |setVariable|41|unchanged|
> |toD|31|the producerOut of the endpoint's scheme|
> |filter|29|its steps start with the same body; unchanged when it does not 
> match|
> |removeHeaders|28|unchanged|
> |split|26|its steps get each part; after it, the body before the split, 
> unless aggregationStrategy is set (UseOriginalAggregationStrategy, 
> Splitter.java:193-194)|
> |script|24|unchanged: the EIP does not use the result 
> (ScriptProcessor.java:46), but the script can set the body|
> |stop|22|unchanged: the route ends|
> |transform|15|the language's expressionOut|
> The EIPs fall into three groups:
> # *They keep the body*: setHeader, setHeaders, setProperty, setVariable, 
> setVariables, removeHeader(s), removeProperty(ies), removeVariable, 
> convertHeaderTo, convertVariableTo, log, validate, delay, throttle, sample, 
> wireTap, stop. Each is a fact the catalog can state: {{unchanged}}.
> # *Their body comes from another model of the catalog*: the language 
> (setBody, transform), the data format (marshal, unmarshal), the endpoint (to, 
> toD, enrich; the consumer side for pollEnrich and poll), or their own steps 
> (choice, filter, doTry, step, circuitBreaker). Their body types are the ones 
> CAMEL-24961 adds, so they need a reference, not a copy.
> # *They have their own rule*:
> #* split, as above;
> #* multicast and recipientList: the last reply, unless aggregationStrategy is 
> set (UseLatestAggregationStrategy: MulticastReifier.java:125-127, 
> RecipientListReifier.java:151-153);
> #* aggregate: what its aggregationStrategy makes;
> #* loop: the body of the last iteration (LoopProcessor.java:168);
> #* sort: a List (SortProcessor.java:50-53);
> #* claimCheck: unchanged for Set and Push; the stored body for Get, 
> GetAndRemove and Pop (ClaimCheckAggregationStrategy.java:67-69);
> #* enrich and pollEnrich: the resource's reply with the default strategy 
> (Enricher.java:314, PollEnricher.java:618-619). pollEnrich gives null after a 
> timeout (PollEnricher.java:719);
> #* convertBodyTo, as above.
> The table and the groups are read from the code on main and not yet checked 
> at runtime.
> h3. Proposed shape
> * {{@BodyTypes}} goes on the EIP model classes of camel-core-model, such as 
> {{SplitDefinition}}. {{SchemaGeneratorMojo}} reads it: it loads the model 
> class, as {{EndpointSchemaGeneratorMojo}} does the endpoint class. 
> {{BodyTypesHelper}} checks it, and it goes into the {{bodyTypes}} section of 
> the {{models/*.json}} files.
> * {{JsonMapper}} reads and writes it for {{EipModel}}. {{camel_catalog_doc}} 
> then returns it with no change of its own, since {{addCommonModelFields}} is 
> shared with the EIPs.
> * The sides for the eip kind:
> ** {{eipIn}}: the bodies the EIP needs (sort needs a List);
> ** {{eipOut}}: the body after it;
> ** {{eipSteps}}: the body that its own steps start with. That is each part 
> for split, each iteration for loop, the aggregated body for aggregate, and 
> the same body for choice and filter.
> * {{dependsOn = "aggregationStrategy"}} for split, multicast and 
> recipientList: the default strategy's body holds until a strategy is set.
> h3. Open decisions
> # *Group 2*: point to the other model in the metadata, for example {{from = 
> "expression"}}, {{"dataFormat"}}, {{"endpoint"}} or {{"steps"}}. Or leave 
> group 2 to the validator's walk, which already follows the language, the data 
> format, the endpoint and the nested steps, and declare groups 1 and 3 only.
> # *variableReceive*: with it, the result goes into the variable and the body 
> stays as it was (MarshalProcessor.java:94-95, PollEnricher.java:436-440). to, 
> toD, enrich, pollEnrich, poll, marshal and unmarshal have the option. The 
> {{byOption}} of {{@BodyType}} needs an exact value today, so it needs a form 
> for "set to any value".
> # *convertBodyTo*: its body is the Java type that an option value names. 
> Either a {{byOption}} for each common type (String, byte[], InputStream, 
> Map), or a new member that names the option that holds the type.
> h3. How it is checked
> As for the components:
> * The message history records the body type at each node (CAMEL-25750). A 
> reference route for each EIP, run with message history on, gives the body 
> after the EIP (at the next step) and in its steps (at the first nested step).
> * camel-core's tests use {{ContextTestSupport}}, which does not dump the 
> route coverage. So the reference routes run in a camel-main harness, as for 
> timer and xslt in CAMEL-24961.
> * The YAML examples of the EIP pages, which {{EipDocExamplesTest}} validates 
> in the build, are a starting set of reference routes.
> h3. What it unlocks
> * The EIP tables of {{TextBodyFlow}} in camel-yaml-dsl-validator move into 
> the catalog:
> ** {{KEEPING_STEPS}}: group 1;
> ** {{SAME_BODY_BLOCKS}}: choice, when, otherwise, filter, doTry, doCatch, 
> doFinally.
> * The EIP pages and {{camel_catalog_doc}} say what the body is after each 
> EIP, as the component pages will.
> h3. First slice
> The EIPs of the table except {{to}}, whose body types are the components'. 
> Then the other 62 EIPs of the catalog.
> _Claude Code on behalf of davsclaus_



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to