[
https://issues.apache.org/jira/browse/CAMEL-24844?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18131881#comment-18131881
]
Claus Ibsen commented on CAMEL-24844:
-------------------------------------
Status 2026-10-25.
*Catalog metadata: done for 4.24.0.*
* CAMEL-24961 is resolved. Every component (415), data format (56) and language
(29) has body types in the catalog: the Java carrier and the format of the body
each side needs and leaves, and how options change them. The last one was
azure-eventgrid ([#28446|https://github.com/apache/camel/pull/28446]). Their
pages show them in a _Message Body_ section.
* CAMEL-25928 is resolved. The EIPs have body types: the body after each EIP,
the body its own steps start with, and the side of another model the body comes
from (a data format, an endpoint, a language). With them come:
** {{variableReceive}} and {{variableSend}} for any variable name;
** the 47 data type transformers ({{transformerIn}} and {{transformerOut}}),
which {{transformDataType}} takes its body from;
** the body of a Kamelet from its data types.
Every model with body types is checked by a runtime test, in core or in the
module that implements the EIP.
* CAMEL-11132 (type metadata for components and data formats, from 2017) is
resolved by this work.
*The tools read them.*
* The YAML validator walks a route with the body types of the catalog, the EIPs
included, instead of its own tables
([#28447|https://github.com/apache/camel/pull/28447]). It knows what a step
with {{variableReceive}} keeps, what {{transformDataType}} makes, and what a
Kamelet gives or needs.
* {{camel_catalog_doc}} and {{camel_catalog_kamelet_doc}} of the Camel MCP
server, and {{camel doc}}, give them to AI assistants.
* The user manual has a _Message Body Types_ page ({{body-types.adoc}}), with
the JSON reference and the rule to read a side for a step ({{BodyTypeResolver}}
of camel-tooling-model).
*Bugs found on the way.*
* The first batch from the runtime checks of the components (CAMEL-25924 to
CAMEL-26105) is mostly resolved.
* The second batch (CAMEL-26117 to CAMEL-26270, 154 JIRAs) is open and left for
later: the metadata came first.
* Also open:
** CAMEL-26299 (azure-eventgrid producer);
** CAMEL-26300 (data type transformers);
** CAMEL-26301 (the tokenizer EIP hangs when the tokenizer throws);
** apache/camel-kamelets#3117 (only 19 of 259 Kamelets declare data types).
*Still open in this epic:*
* CAMEL-25334: content-type aware payloads for low-code (the body stays text
with its content type, and Groovy and simple read JSON fields from it).
* The open bug JIRAs above.
_Claude Code on behalf of davsclaus_
> Body type flow: the validator carries the body's Java type from step to step
> and warns where a step assumes text; the message history's recorded body
> types feed the same check at runtime
> ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
>
> Key: CAMEL-24844
> URL: https://issues.apache.org/jira/browse/CAMEL-24844
> Project: Camel
> Issue Type: Improvement
> Components: camel-core, camel-jbang, camel-yaml-dsl
> Reporter: Claus Ibsen
> Assignee: Claus Ibsen
> Priority: Major
> Fix For: 4.24.0
>
>
> Camel carries the payload as the natural Java object: a GenericFile from the
> file consumer, a Map or List after {{unmarshal json}}, a byte[] or a
> StreamCache after {{marshal}}, an InputStream from http. Integration people
> think in text and bytes, and write routes as if the body were a String. Every
> failure in the round-2 local-model benchmark that was not a YAML shape error
> was that collision, and a person writing the same route gets the same runtime
> exception:
> * {{${body.orderId}}} after {{unmarshal: json}} (the body is a Map):
> MethodNotFoundException, in five examples. The runtime message now says "the
> value is a Map: a key is read with [orderId]".
> * {{body.email}} in a Groovy expression on a GenericFile, before any
> conversion: MissingPropertyException.
> * {{jsonpath}} on a body that is already a Map after unmarshal, or on a null
> body on a timer route (CAMEL-24838).
> * {{${body}}} after {{unmarshal: json}} treated as JSON text: it is the Map's
> toString ({{{id=ORD-1001, ...}}}).
> * {{new ByteArrayInputStream(body)}} in Groovy after {{marshal}}: with stream
> caching on (the Camel CLI default) the body is a StreamCache, not a byte[]:
> "could not find matching constructor". Same route, different Java type
> depending on a runtime setting.
> Measured with the CLI on 4.23.0-SNAPSHOT: logging {{${body}}} prints readable
> text at every stage (GenericFile, Map, marshal output, InputStream with
> stream caching on, twice); only with
> {{camel.main.streamCachingEnabled=false}} does a log step consume an
> InputStream and leave every later step an empty body.
> The object model must stay: it is why a Map flows into a bean and a stream
> into a file without copies. What can change is that Camel knows the type at
> every step and says nothing until the step that assumes text explodes.
> Proposal, in two halves that share one rule set (which step produces which
> type, which expressions and steps need which type):
> # *Static, in {{camel validate yaml}} and the camel-jbang-mcp validation
> tool.* Walk the route and carry the body type forward from the catalog:
> {{from: file}} gives GenericFile, {{unmarshal: json}} gives Map/List (or the
> unmarshalType), {{marshal}} gives byte[]/StreamCache, {{unmarshal:
> jacksonXml}} gives Map, {{split}} on a List gives an element, {{setBody:
> constant}} gives String, {{to: http}} gives InputStream, {{convertBodyTo}}
> gives its type, a bean gives its return type when it can be resolved. At each
> step check the expression against the type and say it in the words of the
> text world, with the line: "after unmarshal json the body is a Map:
> ${body.orderId} fails, write ${body[orderId]}"; "jsonpath needs the JSON
> text, move it before the unmarshal or use simple on the Map"; "the body is
> the file (GenericFile): convert it with convertBodyTo String or unmarshal it
> before the Groovy expression reads body.email"; "after marshal the body is
> bytes (a cached stream when stream caching is on): a Groovy script gets it
> with exchange.message.getBody(byte[])". Unknown types (a bean with no source)
> stop the flow silently, no false positives.
> # *At runtime, from the message history.* The message history already
> records, per step, what went through, including the class of the body at each
> previous step. Use it in two places: (a) the exception message of a failing
> expression or bean invocation can say "the body reaching this step was a
> java.util.LinkedHashMap (set by unmarshal at line 12)", the fact a person
> otherwise gets from a debugger; (b) {{camel trace}} and the camel-jbang-mcp
> tools that show a message's history can show the body type per step, so the
> flow is visible on a running app, and the static half can be checked against
> it (a recorded history of the reference run is the ground truth for the
> catalog's type rules).
> The static half is where most of the value is (it reaches the file before it
> runs); the runtime half makes the rule set honest and gives the "what is the
> body here" answer on a live app.
> Context: the round-2 local-model benchmark on the camel-jbang-examples ladder
> (2026-09-19); the pattern was in aggregator, order-lines, csv-to-json,
> groovy, openapi-client, filter-and-multicast, json-transform.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)