davsclaus opened a new pull request, #28241:
URL: https://github.com/apache/camel/pull/28241

   The catalog has every option and every header of a component, and nothing 
about the message body. So a person, an IDE, the Camel MCP tools and the YAML 
validator cannot answer the question asked most when writing the next step: 
what is the body after this step? This adds it for a first slice, as the design 
note on CAMEL-24844 (2026-10-05) proposed.
   
   **The shape.** A `@BodyTypes` annotation on the endpoint, data format or 
language class has one entry per side:
   
   | Side | The body |
   |---|---|
   | `consumerOut` | the body of the message the consumer creates |
   | `consumerReply` | the bodies a consumer that answers the caller can reply 
with |
   | `producerIn` | the bodies the producer can send or write |
   | `producerOut` | the body after the producer |
   | `marshalIn`/`Out`, `unmarshalIn`/`Out` | the same for a data format |
   | `expressionIn`/`Out` | the same for a language |
   
   Each side has carriers:
   - the kinds of Java object the body is in: `null`, `text`, `bytes`, 
`stream`, `file`, `value`, `map`, `list` and `pojo`;
   - `unchanged`, `any` or `unknown`.
   
   A side can also have:
   - the Java type, when the body is always of that type;
   - the format (json, xml, csv, yaml, text, binary);
   - the options the body depends on;
   - the body for other values of an option;
   - a description.
   
   The package plugin writes a `bodyTypes` section into the catalog JSON next 
to `headers`, one line per side:
   
   ```json
   "bodyTypes": {
     "producerIn": { "carriers": [ "text", "bytes", "stream", "file", "pojo" ], 
"format": "xml", "dependsOn": [ "source" ], ... },
     "producerOut": { "carriers": [ "text" ], "javaType": "java.lang.String", 
"dependsOn": [ "resultHandlerFactory" ],
       "byOption": [ { "option": "output", "value": "bytes", "carriers": [ 
"bytes" ], "javaType": "byte[]" }, ... ],
       "description": "The result of the transformation" }
   }
   ```
   
   **No guesses.**
   - A side that is not declared is not known. `unknown` is declared on 
purpose, for a body that depends on the other system or on a class the user 
configures.
   - The build fails when:
     - a side does not fit the kind, such as a producer side on a consumer-only 
component;
     - `unchanged` is on what a step needs, or `any` on what it leaves;
     - an option a side names does not exist.
   - A subclass declares its own body types. For example, `protobufJackson` 
extends the jackson data format, but its payload is not JSON.
   
   **The first slice.** The top 10 schemes of 1712 real route files, plus the 
json data format, which has 92% of the data format uses. Each value is read 
from the code that sets the body (main @ 5c9d5bc1c133):
   
   | Component | Side | Body | Where |
   |---|---|---|---|
   | file | consumerOut | file (a GenericFile; a java.io.File with preMove) | 
GenericFileEndpoint.java:1671 |
   | | producerIn | text, bytes, stream, file (with allowNullBody also null) | 
FileOperations.java:306, :330, :364, :399 |
   | | producerOut | unchanged | GenericFileProducer has no setBody |
   | direct | consumerOut / producerIn / producerOut | unchanged / any / 
unknown (the body the called route ends with) | DirectProducer.java:63 |
   | seda | consumerOut / producerIn | unchanged / any | SedaProducer.java:209 |
   | | producerOut | unknown: unchanged for InOnly, the seda route's body for 
InOut or Always (unchanged with waitForTaskToComplete=Never) | 
SedaProducer.java:67-68, :93 |
   | timer, cron | consumerOut | null | TimerConsumer.java:277; 
CamelQuartzCronService.java:45, QuartzMessage.java:37 |
   | kamelet | consumerOut / producerIn / producerOut | unknown / any / unknown 
| KameletProducer.java:103, :109 |
   | http, https | producerIn | null, text, bytes, stream, file; a Map or a 
List fails | HttpProducer.java:755, :793-802, :842 |
   | | producerOut | bytes (Content-Length up to 8192), stream, null | 
HttpProducer.java:395, :612-635 |
   | rest-openapi | producerIn / producerOut | as http; depends on 
componentName | RestOpenApiEndpoint.java:583, :631 |
   | xslt | producerIn | text, bytes, stream, file, pojo (a DOM or a Source); 
xml | XmlSourceHandlerFactoryImpl.java:132-165 |
   | | producerOut | text (String); output=bytes, DOM or file | 
StringResultHandler.java:41, StreamResultHandler.java:41, 
DomResultHandler.java:38, FileResultHandler.java:48 |
   | sql | consumerOut | map (a row); list with useIterator=false or 
StreamList; map or value with SelectOne | SqlConsumer.java:246 |
   | | producerIn | any (the parameters) | SqlHelper.java:72 |
   | | producerOut | list for a select, unchanged otherwise (the count goes in 
a header) | SqlProducer.java:147, :157, :204, :219 |
   | json (jackson) | marshalIn / marshalOut | any / stream (the stream cache) 
| MarshalProcessor.java:92-97 |
   | | unmarshalIn / unmarshalOut | text, bytes, stream, file / map or list; a 
pojo with unmarshalType | AbstractJacksonDataFormat.java:100, :204 |
   
   **Checked at runtime.** The message history records the body type at each 
step (CAMEL-25750), and the route coverage dump carries it. I ran two surveys 
with message history forced on by a context service plugin 
(`ContextServicePlugin`) on the test classpath, with no test changed:
   - The tests of sql, http, platform-http, jackson, csv, jacksonxml, 
snakeyaml, jsonpath, kamelet, rest-openapi, jms, aws2-sqs, kafka, groovy and 
jq: 971 coverage dumps.
   - Reference routes for the core components. camel-core's own tests use 
`ContextTestSupport`, which does not dump the coverage.
   
   Every recorded type fits the declared carriers:
   - **file:** read as a GenericFile, or a java.io.File with preMove; written 
from a String, the stream cache or a GenericFile; the body after the write is 
unchanged.
   - **timer, cron:** null.
   - **xslt:** a String, byte[], or a DOM Document (DocumentImpl), by the 
output option.
   - **http:** the response is the stream cache, or the raw stream 
(EofSensorInputStream) with disableStreamCache.
   - **sql:**
     - the consumer gives a LinkedCaseInsensitiveMap per row, or a 
ResultSetIterator with StreamList;
     - a select gives an ArrayList;
     - an insert leaves the body unchanged;
     - a SelectOne without a row gives null.
   - **json:** unmarshal gives a LinkedHashMap or an ArrayList; marshal gives 
the InputStreamCache.
   
   **For AI agents:** `camel_catalog_doc` returns the body types, with a hint 
that says what each side means, so an agent can read them before it writes the 
next step.
   
   **Next:**
   - The rest of the pilot set is already read and checked, and comes in the 
next PR: platform-http, rest, ftp/sftp, jms and its family, kafka, aws2-sqs, 
paho-mqtt5, mongodb, log, mock, csv, jacksonXml, yaml and the languages.
   - Then the validator reads the catalog instead of its hand tables.
   - Then the component pages show the body types.
   
   **Tests**
   - `JsonMapperTest`: the round trip for a component, a data format and a 
language; nothing written when there are no body types.
   - `EndpointSchemaGeneratorMojoTest`: the sides in order, no inheritance, and 
the seven shapes the build rejects.
   - `CatalogDocsTest`: `camel_catalog_doc` for xslt, json and kafka (kafka has 
none).
   - Also passed: camel-catalog (1103 tests), the catalog doc tests of 
camel-jbang-core, camel-jbang-mcp and the TUI.
   
   Docs: "Body types in the Catalog" on the Camel Catalog page, and 
`@BodyTypes` on the endpoint annotations page.
   
   _Claude Code on behalf of davsclaus_
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to