From b7a779cefec41075bc8d4b4fe5541258d90a4c17 Mon Sep 17 00:00:00 2001 From: Claus Ibsen Date: Wed, 21 Oct 2026 13:43:19 +0200 Subject: [PATCH 1/9] CAMEL-24961: camel-catalog - the body a component or data format needs, and the body it leaves after a step A @BodyTypes annotation on the endpoint, data format or language class says the body per side (consumerOut, consumerReply, producerIn, producerOut, marshal, unmarshal and expression in and out): its carriers (null, text, bytes, stream, file, value, map, list, pojo, unchanged, any, unknown), the Java type when it is always the same, the format, the options it depends on, the body for other option values, and a description. The package plugin writes it as a bodyTypes section of the catalog JSON and checks that each side fits the kind and that the options it names exist. A subclass declares its own. The first slice: file, direct, seda, timer, cron, kamelet, http, https, rest-openapi, xslt, sql and the json data format (jackson), read from the code that sets the body and checked against the body types the message history records when routes run. camel_catalog_doc returns them. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claus Ibsen --- .../apache/camel/catalog/components/cron.json | 3 + .../camel/catalog/components/direct.json | 5 + .../apache/camel/catalog/components/file.json | 5 + .../apache/camel/catalog/components/http.json | 4 + .../camel/catalog/components/https.json | 4 + .../camel/catalog/components/kamelet.json | 5 + .../catalog/components/rest-openapi.json | 4 + .../apache/camel/catalog/components/seda.json | 5 + .../apache/camel/catalog/components/sql.json | 5 + .../camel/catalog/components/timer.json | 3 + .../apache/camel/catalog/components/xslt.json | 4 + .../camel/catalog/dataformats/jackson.json | 6 + .../org/apache/camel/component/cron/cron.json | 3 + .../camel/component/cron/CronEndpoint.java | 4 + .../apache/camel/component/direct/direct.json | 5 + .../component/direct/DirectEndpoint.java | 6 + .../org/apache/camel/component/file/file.json | 5 + .../camel/component/file/FileEndpoint.java | 14 ++ .../org/apache/camel/component/http/http.json | 4 + .../apache/camel/component/http/https.json | 4 + .../camel/component/http/HttpEndpoint.java | 12 ++ .../camel/component/jackson/jackson.json | 6 + .../component/jackson/JacksonDataFormat.java | 14 ++ .../camel/component/kamelet/kamelet.json | 5 + .../component/kamelet/KameletEndpoint.java | 9 + .../component/rest/openapi/rest-openapi.json | 4 + .../rest/openapi/RestOpenApiEndpoint.java | 10 + .../org/apache/camel/component/seda/seda.json | 5 + .../camel/component/seda/SedaEndpoint.java | 11 + .../org/apache/camel/component/sql/sql.json | 5 + .../camel/component/sql/SqlEndpoint.java | 25 +++ .../apache/camel/component/timer/timer.json | 3 + .../camel/component/timer/TimerEndpoint.java | 4 + .../org/apache/camel/component/xslt/xslt.json | 4 + .../camel/component/xslt/XsltEndpoint.java | 20 ++ .../java/org/apache/camel/spi/BodyType.java | 148 ++++++++++++++ .../java/org/apache/camel/spi/BodyTypes.java | 105 ++++++++++ .../modules/ROOT/pages/camel-catalog.adoc | 69 +++++++ .../ROOT/pages/endpoint-annotations.adoc | 4 + .../jbang/core/commands/ai/CatalogDocs.java | 12 ++ .../core/commands/ai/CatalogDocsTest.java | 19 ++ .../apache/camel/tooling/model/BaseModel.java | 21 ++ .../camel/tooling/model/BodyTypeModel.java | 193 ++++++++++++++++++ .../camel/tooling/model/JsonMapper.java | 86 ++++++++ .../camel/tooling/model/JsonMapperTest.java | 78 +++++++ .../maven/packaging/BodyTypesHelper.java | 185 +++++++++++++++++ .../EndpointSchemaGeneratorMojo.java | 14 ++ .../packaging/PackageDataFormatMojo.java | 2 + .../maven/packaging/PackageLanguageMojo.java | 2 + .../EndpointSchemaGeneratorMojoTest.java | 69 +++++++ .../SomeEndpointExtendingBodyTypes.java | 23 +++ .../SomeEndpointWithBadBodyTypes.java | 66 ++++++ .../endpoint/SomeEndpointWithBodyTypes.java | 33 +++ .../java/org/apache/camel/spi/BodyType.java | 148 ++++++++++++++ .../java/org/apache/camel/spi/BodyTypes.java | 105 ++++++++++ 55 files changed, 1617 insertions(+) create mode 100644 core/camel-api/src/generated/java/org/apache/camel/spi/BodyType.java create mode 100644 core/camel-api/src/generated/java/org/apache/camel/spi/BodyTypes.java create mode 100644 tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/BodyTypeModel.java create mode 100644 tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/BodyTypesHelper.java create mode 100644 tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointExtendingBodyTypes.java create mode 100644 tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointWithBadBodyTypes.java create mode 100644 tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointWithBodyTypes.java create mode 100644 tooling/spi-annotations/src/main/java/org/apache/camel/spi/BodyType.java create mode 100644 tooling/spi-annotations/src/main/java/org/apache/camel/spi/BodyTypes.java diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/cron.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/cron.json index f0a2ca0b9f63d..75c3a691f33d4 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/cron.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/cron.json @@ -28,6 +28,9 @@ "autowiredEnabled": { "index": 1, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which then gets configured on the component. This can be used for automatic configuring JDBC data sources, JMS connection factories, AWS Clients, etc." }, "cronService": { "index": 2, "kind": "property", "displayName": "Cron Service", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The id of the CamelCronService to use when multiple implementations are provided" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "null" ], "description": "No body: the message only starts the route" } + }, "properties": { "name": { "index": 0, "kind": "path", "displayName": "Name", "group": "consumer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.cron.api.CamelCronConfiguration", "configurationField": "configuration", "description": "The name of the cron trigger" }, "schedule": { "index": 1, "kind": "parameter", "displayName": "Schedule", "group": "consumer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.cron.api.CamelCronConfiguration", "configurationField": "configuration", "description": "A cron expression that will be used to generate events" }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/direct.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/direct.json index 25bd03ca6b52c..b79f963ec03b3 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/direct.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/direct.json @@ -31,6 +31,11 @@ "timeout": { "index": 3, "kind": "property", "displayName": "Timeout", "group": "producer", "label": "producer", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 30000, "description": "The timeout value to use if block is enabled." }, "autowiredEnabled": { "index": 4, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which then gets configured on the component. This can be used for automatic configuring JDBC data sources, JMS connection factories, AWS Clients, etc." } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "unchanged" ], "description": "The body the caller sent" }, + "producerIn": { "carriers": [ "any" ] }, + "producerOut": { "carriers": [ "unknown" ], "description": "The body the route it calls ends with" } + }, "properties": { "name": { "index": 0, "kind": "path", "displayName": "Name", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Name of direct endpoint" }, "bridgeErrorHandler": { "index": 1, "kind": "parameter", "displayName": "Bridge Error Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions (if possible) occurred while the Camel consumer is trying to pickup incoming messages, or the likes, will now be processed as a message and handled by the routing Error Handler. Important: This is only possible if the 3rd party component allows Camel to be alerted if an exception was thrown. Some components handle this internally only, and therefore bridgeErrorHandler is not possible. In other situations we may improve the Camel component to hook into the 3rd party component and make this possible for future releases. By default the consumer will use the org.apache.camel.spi.ExceptionHandler to deal with exceptions, that will be logged at WARN or ERROR level and ignored." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/file.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/file.json index 2d400e50bff03..5458c5f580788 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/file.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/file.json @@ -49,6 +49,11 @@ "CamelFileInitialOffset": { "index": 15, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "A long value containing the initial offset.", "constantName": "org.apache.camel.component.file.FileConstants#INITIAL_OFFSET" }, "CamelFileChecksum": { "index": 16, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The checksum of the file written, when checksumAlgorithm is in use", "constantName": "org.apache.camel.component.file.FileConstants#FILE_CHECKSUM" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "file" ], "description": "The file as read: a GenericFile (a java.io.File with preMove), which converts to text, bytes or a stream" }, + "producerIn": { "carriers": [ "text", "bytes", "stream", "file" ], "byOption": [ { "option": "allowNullBody", "value": "true", "carriers": [ "null", "text", "bytes", "stream", "file" ] } ], "description": "What converts to an InputStream: text, bytes, a stream or a file. A Map or a List fails: marshal it first" }, + "producerOut": { "carriers": [ "unchanged" ] } + }, "properties": { "directoryName": { "index": 0, "kind": "path", "displayName": "Directory Name", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.io.File", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The starting directory" }, "charset": { "index": 1, "kind": "parameter", "displayName": "Charset", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "This option is used to specify the encoding of the file. You can use this on the consumer, to specify the encodings of the files, which allow Camel to know the charset it should load the file content in case the file content is being accessed. Likewise when writing a file, you can use this option to specify which charset to write the file as well. Do mind that when writing the file Camel may have to read the message content into memory to be able to convert the data into the configured charset, so do not use this if you have big messages." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/http.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/http.json index f6b9d06e87f85..7c35d5b29b3e3 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/http.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/http.json @@ -95,6 +95,10 @@ "CamelHttpCharacterEncoding": { "index": 12, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The character encoding.", "constantName": "org.apache.camel.component.http.HttpConstants#HTTP_CHARACTER_ENCODING" }, "Content-Type": { "index": 13, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "important": true, "description": "The HTTP content type. Is set on both the IN and OUT message to provide a content type, such as text\/html.", "constantName": "org.apache.camel.component.http.HttpConstants#CONTENT_TYPE" } }, + "bodyTypes": { + "producerIn": { "carriers": [ "null", "text", "bytes", "stream", "file" ], "description": "The request body: text, bytes, a stream or a file. A Map or a List fails: marshal it first. Without httpMethod, no body sends a GET and a body a POST" }, + "producerOut": { "carriers": [ "bytes", "stream", "null" ], "description": "The response body: a byte array when its Content-Length is at most responsePayloadStreamingThreshold (8192), otherwise the stream cache (the stream itself with disableStreamCache); null for a response without a body" } + }, "properties": { "httpUri": { "index": 0, "kind": "path", "displayName": "Http Uri", "group": "common", "label": "common", "required": true, "type": "string", "javaType": "java.net.URI", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The url of the HTTP endpoint to call." }, "disableStreamCache": { "index": 1, "kind": "parameter", "displayName": "Disable Stream Cache", "group": "common", "label": "common", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Determines whether or not the raw input stream is cached or not. The Camel consumer (camel-servlet, camel-jetty etc.) will by default cache the input stream to support reading it multiple times to ensure it Camel can retrieve all data from the stream. However you can set this option to true when you for example need to access the raw stream, such as streaming it directly to a file or other persistent store. DefaultHttpBinding will copy the request input stream into a stream cache and put it into message body if this option is false to support reading the stream multiple times. If you use Servlet to bridge\/proxy an endpoint then consider enabling this option to improve performance, in case you do not need to read the message payload multiple times. The producer (camel-http) will by default cache the response body stream. If setting this option to true, then the producers will not cache the response body stream but use the response stream as-is (the stream can only be read once) as the message body." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/https.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/https.json index d17bb0c1e3239..d3f802d744179 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/https.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/https.json @@ -95,6 +95,10 @@ "CamelHttpCharacterEncoding": { "index": 12, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The character encoding.", "constantName": "org.apache.camel.component.http.HttpConstants#HTTP_CHARACTER_ENCODING" }, "Content-Type": { "index": 13, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "important": true, "description": "The HTTP content type. Is set on both the IN and OUT message to provide a content type, such as text\/html.", "constantName": "org.apache.camel.component.http.HttpConstants#CONTENT_TYPE" } }, + "bodyTypes": { + "producerIn": { "carriers": [ "null", "text", "bytes", "stream", "file" ], "description": "The request body: text, bytes, a stream or a file. A Map or a List fails: marshal it first. Without httpMethod, no body sends a GET and a body a POST" }, + "producerOut": { "carriers": [ "bytes", "stream", "null" ], "description": "The response body: a byte array when its Content-Length is at most responsePayloadStreamingThreshold (8192), otherwise the stream cache (the stream itself with disableStreamCache); null for a response without a body" } + }, "properties": { "httpUri": { "index": 0, "kind": "path", "displayName": "Http Uri", "group": "common", "label": "common", "required": true, "type": "string", "javaType": "java.net.URI", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The url of the HTTP endpoint to call." }, "disableStreamCache": { "index": 1, "kind": "parameter", "displayName": "Disable Stream Cache", "group": "common", "label": "common", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Determines whether or not the raw input stream is cached or not. The Camel consumer (camel-servlet, camel-jetty etc.) will by default cache the input stream to support reading it multiple times to ensure it Camel can retrieve all data from the stream. However you can set this option to true when you for example need to access the raw stream, such as streaming it directly to a file or other persistent store. DefaultHttpBinding will copy the request input stream into a stream cache and put it into message body if this option is false to support reading the stream multiple times. If you use Servlet to bridge\/proxy an endpoint then consider enabling this option to improve performance, in case you do not need to read the message payload multiple times. The producer (camel-http) will by default cache the response body stream. If setting this option to true, then the producers will not cache the response body stream but use the response stream as-is (the stream can only be read once) as the message body." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/kamelet.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/kamelet.json index 282770899b57b..d8ac7c103040e 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/kamelet.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/kamelet.json @@ -36,6 +36,11 @@ "noErrorHandler": { "index": 9, "kind": "property", "displayName": "No Error Handler", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether kamelets should use error handling or not. By default, the Kamelet uses the same error handler as from the calling route. This means that if the calling route has error handling that performs retries, or routing to a dead letter channel, then the kamelet route will use this also. This can be turned off by setting this option to true. If off then the kamelet route is not using error handling, and any exception thrown will for source kamelets be logged by the consumer, and the sink\/action kamelets will fail processing." }, "routeTemplateLoaderListener": { "index": 10, "kind": "property", "displayName": "Route Template Loader Listener", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.RouteTemplateLoaderListener", "deprecated": false, "autowired": true, "secret": false, "description": "To plugin a custom listener for when the Kamelet component is loading Kamelets from external resources." } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "unknown" ], "description": "The body the Kamelet sends, as its data types or its template say. In the template of a Kamelet, kamelet:source gives the body the caller sent" }, + "producerIn": { "carriers": [ "any" ] }, + "producerOut": { "carriers": [ "unknown" ], "description": "The body the template of the Kamelet ends with" } + }, "properties": { "templateId": { "index": 0, "kind": "path", "displayName": "Template Id", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The Route Template ID" }, "routeId": { "index": 1, "kind": "path", "displayName": "Route Id", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The Route ID. Default value notice: The ID will be auto-generated if not provided" }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/rest-openapi.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/rest-openapi.json index d649162cff1c1..22417ab779dfc 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/rest-openapi.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/rest-openapi.json @@ -46,6 +46,10 @@ "sslContextParameters": { "index": 19, "kind": "property", "displayName": "Ssl Context Parameters", "group": "security", "label": "security", "required": false, "type": "object", "javaType": "org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, "autowired": false, "secret": false, "description": "Customize TLS parameters used by the component. If not set defaults to the TLS parameters set in the Camel context" }, "useGlobalSslContextParameters": { "index": 20, "kind": "property", "displayName": "Use Global Ssl Context Parameters", "group": "security", "label": "security", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Enable usage of global SSL context parameters." } }, + "bodyTypes": { + "producerIn": { "carriers": [ "null", "text", "bytes", "stream", "file" ], "dependsOn": [ "componentName" ], "description": "The request body of the operation, which the http component sends: text, bytes, a stream or a file. A Map or a List fails: marshal it first" }, + "producerOut": { "carriers": [ "bytes", "stream", "null" ], "dependsOn": [ "componentName" ], "description": "The response body, as the http component gives it: a byte array or the stream cache; null for a response without a body" } + }, "properties": { "specificationUri": { "index": 0, "kind": "path", "displayName": "Specification Uri", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "openapi.json", "description": "Path to the OpenApi specification file. The scheme, host base path are taken from this specification, but these can be overridden with properties on the component or endpoint level. If not given the component tries to load openapi.json resource from the classpath. Note that the host defined on the component and endpoint of this Component should contain the scheme, hostname and optionally the port in the URI syntax (i.e. http:\/\/api.example.com:8080). Overrides component configuration. The OpenApi specification can be loaded from different sources by prefixing with file: classpath: http: https:. Support for https is limited to using the JDK installed UrlHandler, and as such it can be cumbersome to setup TLS\/SSL certificates for https (such as setting a number of javax.net.ssl JVM system properties). How to do that consult the JDK documentation for UrlHandler. Default value notice: By default loads openapi.json file" }, "operationId": { "index": 1, "kind": "path", "displayName": "Operation Id", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "ID of the operation from the OpenApi specification. This is required when using producer" }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/seda.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/seda.json index d3e1df34277b5..268b123f87582 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/seda.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/seda.json @@ -36,6 +36,11 @@ "defaultQueueFactory": { "index": 8, "kind": "property", "displayName": "Default Queue Factory", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.component.seda.BlockingQueueFactory", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the default queue factory." }, "queueSize": { "index": 9, "kind": "property", "displayName": "Queue Size", "group": "advanced", "label": "advanced", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 1000, "description": "Sets the default maximum capacity of the SEDA queue (i.e., the number of messages it can hold)." } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "unchanged" ], "description": "The body the producer sent" }, + "producerIn": { "carriers": [ "any" ] }, + "producerOut": { "carriers": [ "unknown" ], "byOption": [ { "option": "waitForTaskToComplete", "value": "Never", "carriers": [ "unchanged" ] } ], "description": "Unchanged for a message that expects no reply (InOnly). A message that expects one (InOut), or waitForTaskToComplete=Always, waits for the body the seda route ends with" } + }, "properties": { "name": { "index": 0, "kind": "path", "displayName": "Name", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Name of queue" }, "size": { "index": 1, "kind": "parameter", "displayName": "Size", "group": "common", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 1000, "description": "The maximum capacity of the SEDA queue (i.e., the number of messages it can hold). Will by default use the queueSize set on the SEDA component." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/sql.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/sql.json index c537163715c87..27ec6331908f9 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/sql.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/sql.json @@ -48,6 +48,11 @@ "CamelSqlParameters": { "index": 7, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "Iterator", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The SQL parameters when using the option useMessageBodyForSql", "constantName": "org.apache.camel.component.sql.SqlConstants#SQL_PARAMETERS" }, "CamelSqlDataSource": { "index": 8, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "javax.sql.DataSource", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "To use a specific DataSource to operate on another database than the pre configured DataSource on the component", "constantName": "org.apache.camel.component.sql.SqlConstants#SQL_DATA_SOURCE" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "map" ], "javaType": "java.util.Map", "dependsOn": [ "outputClass", "outputHeader" ], "byOption": [ { "option": "useIterator", "value": "false", "carriers": [ "list" ] }, { "option": "outputType", "value": "SelectOne", "carriers": [ "map", "value" ] }, { "option": "outputType", "value": "StreamList", "carriers": [ "list" ], "javaType": "java.util.Iterator" } ], "description": "A row of the query: a Map of the column names and their values" }, + "producerIn": { "carriers": [ "any" ], "description": "The parameters of the query: a Map for the named ones, a List or an array for the # ones; the query itself with useMessageBodyForSql" }, + "producerOut": { "carriers": [ "list", "unchanged" ], "dependsOn": [ "outputClass", "outputHeader", "noop" ], "byOption": [ { "option": "outputType", "value": "SelectOne", "carriers": [ "map", "value", "null" ] }, { "option": "outputType", "value": "StreamList", "carriers": [ "list" ], "javaType": "java.util.Iterator" } ], "description": "The rows of a select: a List of Maps of the column names and their values. A query that is not a select leaves the body unchanged, with the count in the CamelSqlUpdateCount header" } + }, "properties": { "query": { "index": 0, "kind": "path", "displayName": "Query", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "supportFileReference": true, "largeInput": true, "inputLanguage": "sql", "description": "Sets the SQL query to perform. You can externalize the query by using file: or classpath: as prefix and specify the location of the file." }, "allowNamedParameters": { "index": 1, "kind": "parameter", "displayName": "Allow Named Parameters", "group": "common", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to allow using named parameters in the queries." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/timer.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/timer.json index fef8e3d02c437..338cb6f43820c 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/timer.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/timer.json @@ -33,6 +33,9 @@ "CamelTimerFiredTime": { "index": 0, "kind": "header", "displayName": "", "group": "consumer", "label": "", "required": false, "javaType": "Date", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The fired time", "constantName": "org.apache.camel.component.timer.TimerConstants#HEADER_FIRED_TIME" }, "CamelMessageTimestamp": { "index": 1, "kind": "header", "displayName": "", "group": "consumer", "label": "", "required": false, "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The timestamp of the message", "constantName": "org.apache.camel.component.timer.TimerConstants#HEADER_MESSAGE_TIMESTAMP" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "null" ], "description": "No body: the message only starts the route" } + }, "properties": { "timerName": { "index": 0, "kind": "path", "displayName": "Timer Name", "group": "consumer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The name of the timer" }, "delay": { "index": 1, "kind": "parameter", "displayName": "Delay", "group": "consumer", "label": "", "required": false, "type": "duration", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "1000", "description": "The number of milliseconds to wait before the first event is generated. Should not be used in conjunction with the time option. The default value is 1000." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/xslt.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/xslt.json index 0b2ffa8645429..7c6a1d43795b0 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/xslt.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/xslt.json @@ -39,6 +39,10 @@ "CamelXsltStylesheet": { "index": 1, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The template to use instead of the endpoint configured.", "constantName": "org.apache.camel.component.xslt.XsltConstants#XSLT_STYLESHEET" }, "CamelXsltFileName": { "index": 2, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The XSLT file name", "constantName": "org.apache.camel.component.xslt.XsltConstants#XSLT_FILE_NAME" } }, + "bodyTypes": { + "producerIn": { "carriers": [ "text", "bytes", "stream", "file", "pojo" ], "format": "xml", "dependsOn": [ "source" ], "byOption": [ { "option": "failOnNullBody", "value": "false", "carriers": [ "null", "text", "bytes", "stream", "file", "pojo" ] } ], "description": "The XML to transform: text, bytes, a stream, a file, a DOM or another javax.xml.transform.Source. A Map, a List or no body fails" }, + "producerOut": { "carriers": [ "text" ], "javaType": "java.lang.String", "dependsOn": [ "resultHandlerFactory" ], "byOption": [ { "option": "output", "value": "bytes", "carriers": [ "bytes" ], "javaType": "byte[]" }, { "option": "output", "value": "DOM", "carriers": [ "pojo" ], "javaType": "org.w3c.dom.Document" }, { "option": "output", "value": "file", "carriers": [ "file" ], "javaType": "java.io.File" } ], "description": "The result of the transformation" } + }, "properties": { "resourceUri": { "index": 0, "kind": "path", "displayName": "Resource Uri", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Path to the template. The following is supported by the default URIResolver. You can prefix with: classpath, file, http, ref, or bean. classpath, file and http loads the resource using these protocols (classpath is default). ref will lookup the resource in the registry. bean will call a method on a bean to be used as the resource. For bean you can specify the method name after dot, eg bean:myBean.myMethod" }, "allowTemplateFromHeader": { "index": 1, "kind": "parameter", "displayName": "Allow Template From Header", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to allow to use resource template from header or not (default false). Enabling this allows to specify dynamic templates via message header. However this can be seen as a potential security vulnerability if the header is coming from a malicious user, so use this with care." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/jackson.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/jackson.json index 60f4d41e2401d..36de62e80cf39 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/jackson.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/jackson.json @@ -15,6 +15,12 @@ "modelName": "json", "modelJavaType": "org.apache.camel.model.dataformat.JsonDataFormat" }, + "bodyTypes": { + "marshalIn": { "carriers": [ "any" ], "description": "A Map, a List or a POJO to write as JSON. Text, bytes or a stream that is JSON already is written as it is" }, + "marshalOut": { "carriers": [ "stream" ], "format": "json", "description": "The JSON in the stream cache, or a byte array when stream caching is off" }, + "unmarshalIn": { "carriers": [ "text", "bytes", "stream", "file" ], "format": "json" }, + "unmarshalOut": { "carriers": [ "map", "list" ], "dependsOn": [ "unmarshalType", "collectionType", "allowUnmarshallType" ], "byOption": [ { "option": "useList", "value": "true", "carriers": [ "list" ] } ], "description": "A Map (LinkedHashMap) for a JSON object, a List (ArrayList) for an array; an object of the class unmarshalType names" } + }, "properties": { "id": { "index": 0, "kind": "attribute", "displayName": "Id", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The id of this node" }, "objectMapper": { "index": 1, "kind": "attribute", "displayName": "Object Mapper", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Lookup and use the existing ObjectMapper with the given id when using Jackson." }, diff --git a/components/camel-cron/src/generated/resources/META-INF/org/apache/camel/component/cron/cron.json b/components/camel-cron/src/generated/resources/META-INF/org/apache/camel/component/cron/cron.json index f0a2ca0b9f63d..75c3a691f33d4 100644 --- a/components/camel-cron/src/generated/resources/META-INF/org/apache/camel/component/cron/cron.json +++ b/components/camel-cron/src/generated/resources/META-INF/org/apache/camel/component/cron/cron.json @@ -28,6 +28,9 @@ "autowiredEnabled": { "index": 1, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which then gets configured on the component. This can be used for automatic configuring JDBC data sources, JMS connection factories, AWS Clients, etc." }, "cronService": { "index": 2, "kind": "property", "displayName": "Cron Service", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The id of the CamelCronService to use when multiple implementations are provided" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "null" ], "description": "No body: the message only starts the route" } + }, "properties": { "name": { "index": 0, "kind": "path", "displayName": "Name", "group": "consumer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.cron.api.CamelCronConfiguration", "configurationField": "configuration", "description": "The name of the cron trigger" }, "schedule": { "index": 1, "kind": "parameter", "displayName": "Schedule", "group": "consumer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.cron.api.CamelCronConfiguration", "configurationField": "configuration", "description": "A cron expression that will be used to generate events" }, diff --git a/components/camel-cron/src/main/java/org/apache/camel/component/cron/CronEndpoint.java b/components/camel-cron/src/main/java/org/apache/camel/component/cron/CronEndpoint.java index b67ea6348f813..716fab485903a 100644 --- a/components/camel-cron/src/main/java/org/apache/camel/component/cron/CronEndpoint.java +++ b/components/camel-cron/src/main/java/org/apache/camel/component/cron/CronEndpoint.java @@ -23,6 +23,9 @@ import org.apache.camel.Processor; import org.apache.camel.Producer; import org.apache.camel.component.cron.api.CamelCronConfiguration; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.UriEndpoint; import org.apache.camel.spi.UriParam; import org.apache.camel.support.DefaultEndpoint; @@ -34,6 +37,7 @@ */ @UriEndpoint(firstVersion = "3.1.0", scheme = "cron", title = "Cron", syntax = "cron:name", consumerOnly = true, remote = false, category = { Category.SCHEDULING }) +@BodyTypes(consumerOut = @BodyType(value = Carrier.NULL, description = "No body: the message only starts the route")) public class CronEndpoint extends DefaultEndpoint implements DelegateEndpoint { private Endpoint delegate; diff --git a/components/camel-direct/src/generated/resources/META-INF/org/apache/camel/component/direct/direct.json b/components/camel-direct/src/generated/resources/META-INF/org/apache/camel/component/direct/direct.json index 25bd03ca6b52c..b79f963ec03b3 100644 --- a/components/camel-direct/src/generated/resources/META-INF/org/apache/camel/component/direct/direct.json +++ b/components/camel-direct/src/generated/resources/META-INF/org/apache/camel/component/direct/direct.json @@ -31,6 +31,11 @@ "timeout": { "index": 3, "kind": "property", "displayName": "Timeout", "group": "producer", "label": "producer", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 30000, "description": "The timeout value to use if block is enabled." }, "autowiredEnabled": { "index": 4, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which then gets configured on the component. This can be used for automatic configuring JDBC data sources, JMS connection factories, AWS Clients, etc." } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "unchanged" ], "description": "The body the caller sent" }, + "producerIn": { "carriers": [ "any" ] }, + "producerOut": { "carriers": [ "unknown" ], "description": "The body the route it calls ends with" } + }, "properties": { "name": { "index": 0, "kind": "path", "displayName": "Name", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Name of direct endpoint" }, "bridgeErrorHandler": { "index": 1, "kind": "parameter", "displayName": "Bridge Error Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions (if possible) occurred while the Camel consumer is trying to pickup incoming messages, or the likes, will now be processed as a message and handled by the routing Error Handler. Important: This is only possible if the 3rd party component allows Camel to be alerted if an exception was thrown. Some components handle this internally only, and therefore bridgeErrorHandler is not possible. In other situations we may improve the Camel component to hook into the 3rd party component and make this possible for future releases. By default the consumer will use the org.apache.camel.spi.ExceptionHandler to deal with exceptions, that will be logged at WARN or ERROR level and ignored." }, diff --git a/components/camel-direct/src/main/java/org/apache/camel/component/direct/DirectEndpoint.java b/components/camel-direct/src/main/java/org/apache/camel/component/direct/DirectEndpoint.java index 1452087ad592a..d40853506ee56 100644 --- a/components/camel-direct/src/main/java/org/apache/camel/component/direct/DirectEndpoint.java +++ b/components/camel-direct/src/main/java/org/apache/camel/component/direct/DirectEndpoint.java @@ -20,6 +20,9 @@ import org.apache.camel.Consumer; import org.apache.camel.Processor; import org.apache.camel.Producer; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.Metadata; import org.apache.camel.spi.UriEndpoint; import org.apache.camel.spi.UriParam; @@ -34,6 +37,9 @@ */ @UriEndpoint(firstVersion = "1.0.0", scheme = "direct", title = "Direct", syntax = "direct:name", remote = false, internal = true, category = { Category.CORE, Category.MESSAGING }) +@BodyTypes(consumerOut = @BodyType(value = Carrier.UNCHANGED, description = "The body the caller sent"), + producerIn = @BodyType(Carrier.ANY), + producerOut = @BodyType(value = Carrier.UNKNOWN, description = "The body the route it calls ends with")) public class DirectEndpoint extends DefaultEndpoint { private final DirectComponent component; diff --git a/components/camel-file/src/generated/resources/META-INF/org/apache/camel/component/file/file.json b/components/camel-file/src/generated/resources/META-INF/org/apache/camel/component/file/file.json index 2d400e50bff03..5458c5f580788 100644 --- a/components/camel-file/src/generated/resources/META-INF/org/apache/camel/component/file/file.json +++ b/components/camel-file/src/generated/resources/META-INF/org/apache/camel/component/file/file.json @@ -49,6 +49,11 @@ "CamelFileInitialOffset": { "index": 15, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "A long value containing the initial offset.", "constantName": "org.apache.camel.component.file.FileConstants#INITIAL_OFFSET" }, "CamelFileChecksum": { "index": 16, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The checksum of the file written, when checksumAlgorithm is in use", "constantName": "org.apache.camel.component.file.FileConstants#FILE_CHECKSUM" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "file" ], "description": "The file as read: a GenericFile (a java.io.File with preMove), which converts to text, bytes or a stream" }, + "producerIn": { "carriers": [ "text", "bytes", "stream", "file" ], "byOption": [ { "option": "allowNullBody", "value": "true", "carriers": [ "null", "text", "bytes", "stream", "file" ] } ], "description": "What converts to an InputStream: text, bytes, a stream or a file. A Map or a List fails: marshal it first" }, + "producerOut": { "carriers": [ "unchanged" ] } + }, "properties": { "directoryName": { "index": 0, "kind": "path", "displayName": "Directory Name", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.io.File", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The starting directory" }, "charset": { "index": 1, "kind": "parameter", "displayName": "Charset", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "This option is used to specify the encoding of the file. You can use this on the consumer, to specify the encodings of the files, which allow Camel to know the charset it should load the file content in case the file content is being accessed. Likewise when writing a file, you can use this option to specify which charset to write the file as well. Do mind that when writing the file Camel may have to read the message content into memory to be able to convert the data into the configured charset, so do not use this if you have big messages." }, diff --git a/components/camel-file/src/main/java/org/apache/camel/component/file/FileEndpoint.java b/components/camel-file/src/main/java/org/apache/camel/component/file/FileEndpoint.java index 621e144f76cd6..283e0b32e9e80 100644 --- a/components/camel-file/src/main/java/org/apache/camel/component/file/FileEndpoint.java +++ b/components/camel-file/src/main/java/org/apache/camel/component/file/FileEndpoint.java @@ -31,6 +31,9 @@ import org.apache.camel.Processor; import org.apache.camel.component.file.strategy.FileMoveExistingStrategy; import org.apache.camel.component.file.strategy.FileProcessStrategyFactory; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.Metadata; import org.apache.camel.spi.UriEndpoint; import org.apache.camel.spi.UriParam; @@ -46,6 +49,17 @@ */ @UriEndpoint(firstVersion = "1.0.0", scheme = "file", title = "File", syntax = "file:directoryName", category = { Category.FILE, Category.CORE }, headersClass = FileConstants.class) +@BodyTypes(consumerOut = @BodyType(value = Carrier.FILE, + description = "The file as read: a GenericFile (a java.io.File with preMove), which" + + " converts to text, bytes or a stream"), + producerIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }, + byOption = @BodyType.When(option = "allowNullBody", value = "true", + carrier = { + Carrier.NULL, Carrier.TEXT, Carrier.BYTES, + Carrier.STREAM, Carrier.FILE }), + description = "What converts to an InputStream: text, bytes, a stream or a file. A Map or" + + " a List fails: marshal it first"), + producerOut = @BodyType(Carrier.UNCHANGED)) public class FileEndpoint extends GenericFileEndpoint { private static final Logger LOG = LoggerFactory.getLogger(FileEndpoint.class); diff --git a/components/camel-http/src/generated/resources/META-INF/org/apache/camel/component/http/http.json b/components/camel-http/src/generated/resources/META-INF/org/apache/camel/component/http/http.json index f6b9d06e87f85..7c35d5b29b3e3 100644 --- a/components/camel-http/src/generated/resources/META-INF/org/apache/camel/component/http/http.json +++ b/components/camel-http/src/generated/resources/META-INF/org/apache/camel/component/http/http.json @@ -95,6 +95,10 @@ "CamelHttpCharacterEncoding": { "index": 12, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The character encoding.", "constantName": "org.apache.camel.component.http.HttpConstants#HTTP_CHARACTER_ENCODING" }, "Content-Type": { "index": 13, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "important": true, "description": "The HTTP content type. Is set on both the IN and OUT message to provide a content type, such as text\/html.", "constantName": "org.apache.camel.component.http.HttpConstants#CONTENT_TYPE" } }, + "bodyTypes": { + "producerIn": { "carriers": [ "null", "text", "bytes", "stream", "file" ], "description": "The request body: text, bytes, a stream or a file. A Map or a List fails: marshal it first. Without httpMethod, no body sends a GET and a body a POST" }, + "producerOut": { "carriers": [ "bytes", "stream", "null" ], "description": "The response body: a byte array when its Content-Length is at most responsePayloadStreamingThreshold (8192), otherwise the stream cache (the stream itself with disableStreamCache); null for a response without a body" } + }, "properties": { "httpUri": { "index": 0, "kind": "path", "displayName": "Http Uri", "group": "common", "label": "common", "required": true, "type": "string", "javaType": "java.net.URI", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The url of the HTTP endpoint to call." }, "disableStreamCache": { "index": 1, "kind": "parameter", "displayName": "Disable Stream Cache", "group": "common", "label": "common", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Determines whether or not the raw input stream is cached or not. The Camel consumer (camel-servlet, camel-jetty etc.) will by default cache the input stream to support reading it multiple times to ensure it Camel can retrieve all data from the stream. However you can set this option to true when you for example need to access the raw stream, such as streaming it directly to a file or other persistent store. DefaultHttpBinding will copy the request input stream into a stream cache and put it into message body if this option is false to support reading the stream multiple times. If you use Servlet to bridge\/proxy an endpoint then consider enabling this option to improve performance, in case you do not need to read the message payload multiple times. The producer (camel-http) will by default cache the response body stream. If setting this option to true, then the producers will not cache the response body stream but use the response stream as-is (the stream can only be read once) as the message body." }, diff --git a/components/camel-http/src/generated/resources/META-INF/org/apache/camel/component/http/https.json b/components/camel-http/src/generated/resources/META-INF/org/apache/camel/component/http/https.json index d17bb0c1e3239..d3f802d744179 100644 --- a/components/camel-http/src/generated/resources/META-INF/org/apache/camel/component/http/https.json +++ b/components/camel-http/src/generated/resources/META-INF/org/apache/camel/component/http/https.json @@ -95,6 +95,10 @@ "CamelHttpCharacterEncoding": { "index": 12, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The character encoding.", "constantName": "org.apache.camel.component.http.HttpConstants#HTTP_CHARACTER_ENCODING" }, "Content-Type": { "index": 13, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "important": true, "description": "The HTTP content type. Is set on both the IN and OUT message to provide a content type, such as text\/html.", "constantName": "org.apache.camel.component.http.HttpConstants#CONTENT_TYPE" } }, + "bodyTypes": { + "producerIn": { "carriers": [ "null", "text", "bytes", "stream", "file" ], "description": "The request body: text, bytes, a stream or a file. A Map or a List fails: marshal it first. Without httpMethod, no body sends a GET and a body a POST" }, + "producerOut": { "carriers": [ "bytes", "stream", "null" ], "description": "The response body: a byte array when its Content-Length is at most responsePayloadStreamingThreshold (8192), otherwise the stream cache (the stream itself with disableStreamCache); null for a response without a body" } + }, "properties": { "httpUri": { "index": 0, "kind": "path", "displayName": "Http Uri", "group": "common", "label": "common", "required": true, "type": "string", "javaType": "java.net.URI", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The url of the HTTP endpoint to call." }, "disableStreamCache": { "index": 1, "kind": "parameter", "displayName": "Disable Stream Cache", "group": "common", "label": "common", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Determines whether or not the raw input stream is cached or not. The Camel consumer (camel-servlet, camel-jetty etc.) will by default cache the input stream to support reading it multiple times to ensure it Camel can retrieve all data from the stream. However you can set this option to true when you for example need to access the raw stream, such as streaming it directly to a file or other persistent store. DefaultHttpBinding will copy the request input stream into a stream cache and put it into message body if this option is false to support reading the stream multiple times. If you use Servlet to bridge\/proxy an endpoint then consider enabling this option to improve performance, in case you do not need to read the message payload multiple times. The producer (camel-http) will by default cache the response body stream. If setting this option to true, then the producers will not cache the response body stream but use the response stream as-is (the stream can only be read once) as the message body." }, diff --git a/components/camel-http/src/main/java/org/apache/camel/component/http/HttpEndpoint.java b/components/camel-http/src/main/java/org/apache/camel/component/http/HttpEndpoint.java index f7f3c664db22d..334bf81faaa1a 100644 --- a/components/camel-http/src/main/java/org/apache/camel/component/http/HttpEndpoint.java +++ b/components/camel-http/src/main/java/org/apache/camel/component/http/HttpEndpoint.java @@ -35,6 +35,9 @@ import org.apache.camel.api.management.ManagedResource; import org.apache.camel.http.base.cookie.CookieHandler; import org.apache.camel.http.common.HttpCommonEndpoint; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.Metadata; import org.apache.camel.spi.UriEndpoint; import org.apache.camel.spi.UriParam; @@ -74,6 +77,15 @@ "protocol=http" }) @ManagedResource(description = "Managed HttpEndpoint") +@BodyTypes(producerIn = @BodyType(value = { Carrier.NULL, Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }, + description = "The request body: text, bytes, a stream or a file. A Map or a List fails:" + + " marshal it first. Without httpMethod, no body sends a GET and a body a" + + " POST"), + producerOut = @BodyType(value = { Carrier.BYTES, Carrier.STREAM, Carrier.NULL }, + description = "The response body: a byte array when its Content-Length is at most" + + " responsePayloadStreamingThreshold (8192), otherwise the stream cache" + + " (the stream itself with disableStreamCache); null for a response" + + " without a body")) public class HttpEndpoint extends HttpCommonEndpoint implements LineNumberAware { private static final Logger LOG = LoggerFactory.getLogger(HttpEndpoint.class); diff --git a/components/camel-jackson/src/generated/resources/META-INF/org/apache/camel/component/jackson/jackson.json b/components/camel-jackson/src/generated/resources/META-INF/org/apache/camel/component/jackson/jackson.json index 60f4d41e2401d..36de62e80cf39 100644 --- a/components/camel-jackson/src/generated/resources/META-INF/org/apache/camel/component/jackson/jackson.json +++ b/components/camel-jackson/src/generated/resources/META-INF/org/apache/camel/component/jackson/jackson.json @@ -15,6 +15,12 @@ "modelName": "json", "modelJavaType": "org.apache.camel.model.dataformat.JsonDataFormat" }, + "bodyTypes": { + "marshalIn": { "carriers": [ "any" ], "description": "A Map, a List or a POJO to write as JSON. Text, bytes or a stream that is JSON already is written as it is" }, + "marshalOut": { "carriers": [ "stream" ], "format": "json", "description": "The JSON in the stream cache, or a byte array when stream caching is off" }, + "unmarshalIn": { "carriers": [ "text", "bytes", "stream", "file" ], "format": "json" }, + "unmarshalOut": { "carriers": [ "map", "list" ], "dependsOn": [ "unmarshalType", "collectionType", "allowUnmarshallType" ], "byOption": [ { "option": "useList", "value": "true", "carriers": [ "list" ] } ], "description": "A Map (LinkedHashMap) for a JSON object, a List (ArrayList) for an array; an object of the class unmarshalType names" } + }, "properties": { "id": { "index": 0, "kind": "attribute", "displayName": "Id", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The id of this node" }, "objectMapper": { "index": 1, "kind": "attribute", "displayName": "Object Mapper", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Lookup and use the existing ObjectMapper with the given id when using Jackson." }, diff --git a/components/camel-jackson/src/main/java/org/apache/camel/component/jackson/JacksonDataFormat.java b/components/camel-jackson/src/main/java/org/apache/camel/component/jackson/JacksonDataFormat.java index e01f3254ec18b..6519c2d6a2ae0 100644 --- a/components/camel-jackson/src/main/java/org/apache/camel/component/jackson/JacksonDataFormat.java +++ b/components/camel-jackson/src/main/java/org/apache/camel/component/jackson/JacksonDataFormat.java @@ -23,6 +23,9 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.json.JsonMapper; import org.apache.camel.Exchange; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.Metadata; import org.apache.camel.spi.annotations.Dataformat; import org.apache.camel.support.JsonPayloadHelper; @@ -34,6 +37,17 @@ */ @Dataformat("jackson") @Metadata(excludeProperties = "library,permissions,dateFormatPattern") +@BodyTypes(marshalIn = @BodyType(value = Carrier.ANY, + description = "A Map, a List or a POJO to write as JSON. Text, bytes or a stream that is" + + " JSON already is written as it is"), + marshalOut = @BodyType(value = Carrier.STREAM, format = "json", + description = "The JSON in the stream cache, or a byte array when stream caching is off"), + unmarshalIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }, format = "json"), + unmarshalOut = @BodyType(value = { Carrier.MAP, Carrier.LIST }, + dependsOn = { "unmarshalType", "collectionType", "allowUnmarshallType" }, + byOption = @BodyType.When(option = "useList", value = "true", carrier = Carrier.LIST), + description = "A Map (LinkedHashMap) for a JSON object, a List (ArrayList) for an array;" + + " an object of the class unmarshalType names")) public class JacksonDataFormat extends AbstractJacksonDataFormat { private static final Logger LOG = LoggerFactory.getLogger(JacksonDataFormat.class); diff --git a/components/camel-kamelet/src/generated/resources/META-INF/org/apache/camel/component/kamelet/kamelet.json b/components/camel-kamelet/src/generated/resources/META-INF/org/apache/camel/component/kamelet/kamelet.json index 282770899b57b..d8ac7c103040e 100644 --- a/components/camel-kamelet/src/generated/resources/META-INF/org/apache/camel/component/kamelet/kamelet.json +++ b/components/camel-kamelet/src/generated/resources/META-INF/org/apache/camel/component/kamelet/kamelet.json @@ -36,6 +36,11 @@ "noErrorHandler": { "index": 9, "kind": "property", "displayName": "No Error Handler", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether kamelets should use error handling or not. By default, the Kamelet uses the same error handler as from the calling route. This means that if the calling route has error handling that performs retries, or routing to a dead letter channel, then the kamelet route will use this also. This can be turned off by setting this option to true. If off then the kamelet route is not using error handling, and any exception thrown will for source kamelets be logged by the consumer, and the sink\/action kamelets will fail processing." }, "routeTemplateLoaderListener": { "index": 10, "kind": "property", "displayName": "Route Template Loader Listener", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.RouteTemplateLoaderListener", "deprecated": false, "autowired": true, "secret": false, "description": "To plugin a custom listener for when the Kamelet component is loading Kamelets from external resources." } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "unknown" ], "description": "The body the Kamelet sends, as its data types or its template say. In the template of a Kamelet, kamelet:source gives the body the caller sent" }, + "producerIn": { "carriers": [ "any" ] }, + "producerOut": { "carriers": [ "unknown" ], "description": "The body the template of the Kamelet ends with" } + }, "properties": { "templateId": { "index": 0, "kind": "path", "displayName": "Template Id", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The Route Template ID" }, "routeId": { "index": 1, "kind": "path", "displayName": "Route Id", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The Route ID. Default value notice: The ID will be auto-generated if not provided" }, diff --git a/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletEndpoint.java b/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletEndpoint.java index 943f6fe5c129c..879ad17ef9caa 100644 --- a/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletEndpoint.java +++ b/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletEndpoint.java @@ -26,6 +26,9 @@ import org.apache.camel.Consumer; import org.apache.camel.Processor; import org.apache.camel.Producer; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.Metadata; import org.apache.camel.spi.PropertyConfigurer; import org.apache.camel.spi.UriEndpoint; @@ -37,6 +40,12 @@ @UriEndpoint(firstVersion = "3.8.0", scheme = "kamelet", syntax = "kamelet:templateId/routeId", title = "Kamelet", lenientProperties = true, category = Category.CORE) +@BodyTypes(consumerOut = @BodyType(value = Carrier.UNKNOWN, + description = "The body the Kamelet sends, as its data types or its template say. In" + + " the template of a Kamelet, kamelet:source gives the body the caller" + + " sent"), + producerIn = @BodyType(Carrier.ANY), + producerOut = @BodyType(value = Carrier.UNKNOWN, description = "The body the template of the Kamelet ends with")) public class KameletEndpoint extends DefaultEndpoint implements AfterPropertiesConfigured { private final String key; private final Map kameletProperties; diff --git a/components/camel-rest-openapi/src/generated/resources/META-INF/org/apache/camel/component/rest/openapi/rest-openapi.json b/components/camel-rest-openapi/src/generated/resources/META-INF/org/apache/camel/component/rest/openapi/rest-openapi.json index d649162cff1c1..22417ab779dfc 100644 --- a/components/camel-rest-openapi/src/generated/resources/META-INF/org/apache/camel/component/rest/openapi/rest-openapi.json +++ b/components/camel-rest-openapi/src/generated/resources/META-INF/org/apache/camel/component/rest/openapi/rest-openapi.json @@ -46,6 +46,10 @@ "sslContextParameters": { "index": 19, "kind": "property", "displayName": "Ssl Context Parameters", "group": "security", "label": "security", "required": false, "type": "object", "javaType": "org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, "autowired": false, "secret": false, "description": "Customize TLS parameters used by the component. If not set defaults to the TLS parameters set in the Camel context" }, "useGlobalSslContextParameters": { "index": 20, "kind": "property", "displayName": "Use Global Ssl Context Parameters", "group": "security", "label": "security", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Enable usage of global SSL context parameters." } }, + "bodyTypes": { + "producerIn": { "carriers": [ "null", "text", "bytes", "stream", "file" ], "dependsOn": [ "componentName" ], "description": "The request body of the operation, which the http component sends: text, bytes, a stream or a file. A Map or a List fails: marshal it first" }, + "producerOut": { "carriers": [ "bytes", "stream", "null" ], "dependsOn": [ "componentName" ], "description": "The response body, as the http component gives it: a byte array or the stream cache; null for a response without a body" } + }, "properties": { "specificationUri": { "index": 0, "kind": "path", "displayName": "Specification Uri", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "openapi.json", "description": "Path to the OpenApi specification file. The scheme, host base path are taken from this specification, but these can be overridden with properties on the component or endpoint level. If not given the component tries to load openapi.json resource from the classpath. Note that the host defined on the component and endpoint of this Component should contain the scheme, hostname and optionally the port in the URI syntax (i.e. http:\/\/api.example.com:8080). Overrides component configuration. The OpenApi specification can be loaded from different sources by prefixing with file: classpath: http: https:. Support for https is limited to using the JDK installed UrlHandler, and as such it can be cumbersome to setup TLS\/SSL certificates for https (such as setting a number of javax.net.ssl JVM system properties). How to do that consult the JDK documentation for UrlHandler. Default value notice: By default loads openapi.json file" }, "operationId": { "index": 1, "kind": "path", "displayName": "Operation Id", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "ID of the operation from the OpenApi specification. This is required when using producer" }, diff --git a/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpoint.java b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpoint.java index d4c39cf24d1c4..4f1c00d080713 100644 --- a/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpoint.java +++ b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpoint.java @@ -62,6 +62,9 @@ import org.apache.camel.component.rest.openapi.validator.DefaultRequestValidator; import org.apache.camel.component.rest.openapi.validator.RequestValidator; import org.apache.camel.component.rest.openapi.validator.RestOpenApiOperation; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.CamelInternalProcessorAdvice; import org.apache.camel.spi.InternalProcessor; import org.apache.camel.spi.Metadata; @@ -92,6 +95,13 @@ */ @UriEndpoint(firstVersion = "3.1.0", scheme = "rest-openapi", title = "REST OpenApi", syntax = "rest-openapi:specificationUri#operationId", category = { Category.REST, Category.API }) +@BodyTypes(producerIn = @BodyType(value = { Carrier.NULL, Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }, + dependsOn = "componentName", + description = "The request body of the operation, which the http component sends: text," + + " bytes, a stream or a file. A Map or a List fails: marshal it first"), + producerOut = @BodyType(value = { Carrier.BYTES, Carrier.STREAM, Carrier.NULL }, dependsOn = "componentName", + description = "The response body, as the http component gives it: a byte array or the" + + " stream cache; null for a response without a body")) public final class RestOpenApiEndpoint extends DefaultEndpoint { private static final Logger LOG = LoggerFactory.getLogger(RestOpenApiEndpoint.class); diff --git a/components/camel-seda/src/generated/resources/META-INF/org/apache/camel/component/seda/seda.json b/components/camel-seda/src/generated/resources/META-INF/org/apache/camel/component/seda/seda.json index d3e1df34277b5..268b123f87582 100644 --- a/components/camel-seda/src/generated/resources/META-INF/org/apache/camel/component/seda/seda.json +++ b/components/camel-seda/src/generated/resources/META-INF/org/apache/camel/component/seda/seda.json @@ -36,6 +36,11 @@ "defaultQueueFactory": { "index": 8, "kind": "property", "displayName": "Default Queue Factory", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.component.seda.BlockingQueueFactory", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the default queue factory." }, "queueSize": { "index": 9, "kind": "property", "displayName": "Queue Size", "group": "advanced", "label": "advanced", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 1000, "description": "Sets the default maximum capacity of the SEDA queue (i.e., the number of messages it can hold)." } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "unchanged" ], "description": "The body the producer sent" }, + "producerIn": { "carriers": [ "any" ] }, + "producerOut": { "carriers": [ "unknown" ], "byOption": [ { "option": "waitForTaskToComplete", "value": "Never", "carriers": [ "unchanged" ] } ], "description": "Unchanged for a message that expects no reply (InOnly). A message that expects one (InOut), or waitForTaskToComplete=Always, waits for the body the seda route ends with" } + }, "properties": { "name": { "index": 0, "kind": "path", "displayName": "Name", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Name of queue" }, "size": { "index": 1, "kind": "parameter", "displayName": "Size", "group": "common", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 1000, "description": "The maximum capacity of the SEDA queue (i.e., the number of messages it can hold). Will by default use the queueSize set on the SEDA component." }, diff --git a/components/camel-seda/src/main/java/org/apache/camel/component/seda/SedaEndpoint.java b/components/camel-seda/src/main/java/org/apache/camel/component/seda/SedaEndpoint.java index 0da80bde3d089..7cedba92ad122 100644 --- a/components/camel-seda/src/main/java/org/apache/camel/component/seda/SedaEndpoint.java +++ b/components/camel-seda/src/main/java/org/apache/camel/component/seda/SedaEndpoint.java @@ -40,6 +40,9 @@ import org.apache.camel.api.management.ManagedAttribute; import org.apache.camel.api.management.ManagedOperation; import org.apache.camel.api.management.ManagedResource; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.BrowsableEndpoint; import org.apache.camel.spi.Metadata; import org.apache.camel.spi.UriEndpoint; @@ -59,6 +62,14 @@ @ManagedResource(description = "Managed SedaEndpoint") @UriEndpoint(firstVersion = "1.1.0", scheme = "seda", title = "SEDA", syntax = "seda:name", remote = false, internal = true, category = { Category.CORE, Category.MESSAGING }) +@BodyTypes(consumerOut = @BodyType(value = Carrier.UNCHANGED, description = "The body the producer sent"), + producerIn = @BodyType(Carrier.ANY), + producerOut = @BodyType(value = Carrier.UNKNOWN, + byOption = @BodyType.When(option = "waitForTaskToComplete", value = "Never", + carrier = Carrier.UNCHANGED), + description = "Unchanged for a message that expects no reply (InOnly). A message that" + + " expects one (InOut), or waitForTaskToComplete=Always, waits for the" + + " body the seda route ends with")) public class SedaEndpoint extends DefaultEndpoint implements AsyncEndpoint, BrowsableEndpoint, MultipleConsumersSupport { private static final Logger LOG = LoggerFactory.getLogger(SedaEndpoint.class); diff --git a/components/camel-sql/src/generated/resources/META-INF/org/apache/camel/component/sql/sql.json b/components/camel-sql/src/generated/resources/META-INF/org/apache/camel/component/sql/sql.json index c537163715c87..27ec6331908f9 100644 --- a/components/camel-sql/src/generated/resources/META-INF/org/apache/camel/component/sql/sql.json +++ b/components/camel-sql/src/generated/resources/META-INF/org/apache/camel/component/sql/sql.json @@ -48,6 +48,11 @@ "CamelSqlParameters": { "index": 7, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "Iterator", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The SQL parameters when using the option useMessageBodyForSql", "constantName": "org.apache.camel.component.sql.SqlConstants#SQL_PARAMETERS" }, "CamelSqlDataSource": { "index": 8, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "javax.sql.DataSource", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "To use a specific DataSource to operate on another database than the pre configured DataSource on the component", "constantName": "org.apache.camel.component.sql.SqlConstants#SQL_DATA_SOURCE" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "map" ], "javaType": "java.util.Map", "dependsOn": [ "outputClass", "outputHeader" ], "byOption": [ { "option": "useIterator", "value": "false", "carriers": [ "list" ] }, { "option": "outputType", "value": "SelectOne", "carriers": [ "map", "value" ] }, { "option": "outputType", "value": "StreamList", "carriers": [ "list" ], "javaType": "java.util.Iterator" } ], "description": "A row of the query: a Map of the column names and their values" }, + "producerIn": { "carriers": [ "any" ], "description": "The parameters of the query: a Map for the named ones, a List or an array for the # ones; the query itself with useMessageBodyForSql" }, + "producerOut": { "carriers": [ "list", "unchanged" ], "dependsOn": [ "outputClass", "outputHeader", "noop" ], "byOption": [ { "option": "outputType", "value": "SelectOne", "carriers": [ "map", "value", "null" ] }, { "option": "outputType", "value": "StreamList", "carriers": [ "list" ], "javaType": "java.util.Iterator" } ], "description": "The rows of a select: a List of Maps of the column names and their values. A query that is not a select leaves the body unchanged, with the count in the CamelSqlUpdateCount header" } + }, "properties": { "query": { "index": 0, "kind": "path", "displayName": "Query", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "supportFileReference": true, "largeInput": true, "inputLanguage": "sql", "description": "Sets the SQL query to perform. You can externalize the query by using file: or classpath: as prefix and specify the location of the file." }, "allowNamedParameters": { "index": 1, "kind": "parameter", "displayName": "Allow Named Parameters", "group": "common", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to allow using named parameters in the queries." }, diff --git a/components/camel-sql/src/main/java/org/apache/camel/component/sql/SqlEndpoint.java b/components/camel-sql/src/main/java/org/apache/camel/component/sql/SqlEndpoint.java index fd6affe1e4d1a..ece6dd8ce371c 100644 --- a/components/camel-sql/src/main/java/org/apache/camel/component/sql/SqlEndpoint.java +++ b/components/camel-sql/src/main/java/org/apache/camel/component/sql/SqlEndpoint.java @@ -21,6 +21,9 @@ import org.apache.camel.Consumer; import org.apache.camel.Processor; import org.apache.camel.Producer; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.Metadata; import org.apache.camel.spi.UriEndpoint; import org.apache.camel.spi.UriParam; @@ -32,6 +35,28 @@ */ @UriEndpoint(firstVersion = "1.4.0", scheme = "sql", title = "SQL", syntax = "sql:query", category = { Category.DATABASE }, headersClass = SqlConstants.class) +@BodyTypes(consumerOut = @BodyType(value = Carrier.MAP, javaType = "java.util.Map", + dependsOn = { "outputClass", "outputHeader" }, + byOption = { + @BodyType.When(option = "useIterator", value = "false", carrier = Carrier.LIST), + @BodyType.When(option = "outputType", value = "SelectOne", + carrier = { Carrier.MAP, Carrier.VALUE }), + @BodyType.When(option = "outputType", value = "StreamList", carrier = Carrier.LIST, + javaType = "java.util.Iterator") }, + description = "A row of the query: a Map of the column names and their values"), + producerIn = @BodyType(value = Carrier.ANY, + description = "The parameters of the query: a Map for the named ones, a List or an array" + + " for the # ones; the query itself with useMessageBodyForSql"), + producerOut = @BodyType(value = { Carrier.LIST, Carrier.UNCHANGED }, + dependsOn = { "outputClass", "outputHeader", "noop" }, + byOption = { + @BodyType.When(option = "outputType", value = "SelectOne", + carrier = { Carrier.MAP, Carrier.VALUE, Carrier.NULL }), + @BodyType.When(option = "outputType", value = "StreamList", carrier = Carrier.LIST, + javaType = "java.util.Iterator") }, + description = "The rows of a select: a List of Maps of the column names and their values." + + " A query that is not a select leaves the body unchanged, with the count" + + " in the CamelSqlUpdateCount header")) public class SqlEndpoint extends DefaultSqlEndpoint { @UriPath(description = "Sets the SQL query to perform. You can externalize the query by using file: or classpath: as prefix and specify the location of the file.") diff --git a/components/camel-timer/src/generated/resources/META-INF/org/apache/camel/component/timer/timer.json b/components/camel-timer/src/generated/resources/META-INF/org/apache/camel/component/timer/timer.json index fef8e3d02c437..338cb6f43820c 100644 --- a/components/camel-timer/src/generated/resources/META-INF/org/apache/camel/component/timer/timer.json +++ b/components/camel-timer/src/generated/resources/META-INF/org/apache/camel/component/timer/timer.json @@ -33,6 +33,9 @@ "CamelTimerFiredTime": { "index": 0, "kind": "header", "displayName": "", "group": "consumer", "label": "", "required": false, "javaType": "Date", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The fired time", "constantName": "org.apache.camel.component.timer.TimerConstants#HEADER_FIRED_TIME" }, "CamelMessageTimestamp": { "index": 1, "kind": "header", "displayName": "", "group": "consumer", "label": "", "required": false, "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The timestamp of the message", "constantName": "org.apache.camel.component.timer.TimerConstants#HEADER_MESSAGE_TIMESTAMP" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "null" ], "description": "No body: the message only starts the route" } + }, "properties": { "timerName": { "index": 0, "kind": "path", "displayName": "Timer Name", "group": "consumer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The name of the timer" }, "delay": { "index": 1, "kind": "parameter", "displayName": "Delay", "group": "consumer", "label": "", "required": false, "type": "duration", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "1000", "description": "The number of milliseconds to wait before the first event is generated. Should not be used in conjunction with the time option. The default value is 1000." }, diff --git a/components/camel-timer/src/main/java/org/apache/camel/component/timer/TimerEndpoint.java b/components/camel-timer/src/main/java/org/apache/camel/component/timer/TimerEndpoint.java index 943e98c3783b8..8fcb40f7acad9 100644 --- a/components/camel-timer/src/main/java/org/apache/camel/component/timer/TimerEndpoint.java +++ b/components/camel-timer/src/main/java/org/apache/camel/component/timer/TimerEndpoint.java @@ -29,6 +29,9 @@ import org.apache.camel.RuntimeCamelException; import org.apache.camel.api.management.ManagedAttribute; import org.apache.camel.api.management.ManagedResource; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.Metadata; import org.apache.camel.spi.UriEndpoint; import org.apache.camel.spi.UriParam; @@ -44,6 +47,7 @@ @UriEndpoint(firstVersion = "1.0.0", scheme = "timer", title = "Timer", syntax = "timer:timerName", consumerOnly = true, remote = false, internal = true, category = { Category.CORE, Category.SCHEDULING }, headersClass = TimerConstants.class) +@BodyTypes(consumerOut = @BodyType(value = Carrier.NULL, description = "No body: the message only starts the route")) public class TimerEndpoint extends DefaultEndpoint implements MultipleConsumersSupport { @UriPath @Metadata(required = true) diff --git a/components/camel-xslt/src/generated/resources/META-INF/org/apache/camel/component/xslt/xslt.json b/components/camel-xslt/src/generated/resources/META-INF/org/apache/camel/component/xslt/xslt.json index 0b2ffa8645429..7c6a1d43795b0 100644 --- a/components/camel-xslt/src/generated/resources/META-INF/org/apache/camel/component/xslt/xslt.json +++ b/components/camel-xslt/src/generated/resources/META-INF/org/apache/camel/component/xslt/xslt.json @@ -39,6 +39,10 @@ "CamelXsltStylesheet": { "index": 1, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The template to use instead of the endpoint configured.", "constantName": "org.apache.camel.component.xslt.XsltConstants#XSLT_STYLESHEET" }, "CamelXsltFileName": { "index": 2, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The XSLT file name", "constantName": "org.apache.camel.component.xslt.XsltConstants#XSLT_FILE_NAME" } }, + "bodyTypes": { + "producerIn": { "carriers": [ "text", "bytes", "stream", "file", "pojo" ], "format": "xml", "dependsOn": [ "source" ], "byOption": [ { "option": "failOnNullBody", "value": "false", "carriers": [ "null", "text", "bytes", "stream", "file", "pojo" ] } ], "description": "The XML to transform: text, bytes, a stream, a file, a DOM or another javax.xml.transform.Source. A Map, a List or no body fails" }, + "producerOut": { "carriers": [ "text" ], "javaType": "java.lang.String", "dependsOn": [ "resultHandlerFactory" ], "byOption": [ { "option": "output", "value": "bytes", "carriers": [ "bytes" ], "javaType": "byte[]" }, { "option": "output", "value": "DOM", "carriers": [ "pojo" ], "javaType": "org.w3c.dom.Document" }, { "option": "output", "value": "file", "carriers": [ "file" ], "javaType": "java.io.File" } ], "description": "The result of the transformation" } + }, "properties": { "resourceUri": { "index": 0, "kind": "path", "displayName": "Resource Uri", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Path to the template. The following is supported by the default URIResolver. You can prefix with: classpath, file, http, ref, or bean. classpath, file and http loads the resource using these protocols (classpath is default). ref will lookup the resource in the registry. bean will call a method on a bean to be used as the resource. For bean you can specify the method name after dot, eg bean:myBean.myMethod" }, "allowTemplateFromHeader": { "index": 1, "kind": "parameter", "displayName": "Allow Template From Header", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to allow to use resource template from header or not (default false). Enabling this allows to specify dynamic templates via message header. However this can be seen as a potential security vulnerability if the header is coming from a malicious user, so use this with care." }, diff --git a/components/camel-xslt/src/main/java/org/apache/camel/component/xslt/XsltEndpoint.java b/components/camel-xslt/src/main/java/org/apache/camel/component/xslt/XsltEndpoint.java index d7dd5b0ec1b8d..17c699c6e21e0 100644 --- a/components/camel-xslt/src/main/java/org/apache/camel/component/xslt/XsltEndpoint.java +++ b/components/camel-xslt/src/main/java/org/apache/camel/component/xslt/XsltEndpoint.java @@ -39,6 +39,9 @@ import org.apache.camel.api.management.ManagedAttribute; import org.apache.camel.api.management.ManagedOperation; import org.apache.camel.api.management.ManagedResource; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.ClassResolver; import org.apache.camel.spi.Injector; import org.apache.camel.spi.Metadata; @@ -60,6 +63,23 @@ @ManagedResource(description = "Managed XsltEndpoint") @UriEndpoint(firstVersion = "1.3.0", scheme = "xslt", title = "XSLT", syntax = "xslt:resourceUri", producerOnly = true, remote = false, category = { Category.CORE, Category.TRANSFORMATION }, headersClass = XsltConstants.class) +@BodyTypes(producerIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, Carrier.POJO }, + format = "xml", dependsOn = "source", + byOption = @BodyType.When(option = "failOnNullBody", value = "false", + carrier = { + Carrier.NULL, Carrier.TEXT, Carrier.BYTES, + Carrier.STREAM, Carrier.FILE, Carrier.POJO }), + description = "The XML to transform: text, bytes, a stream, a file, a DOM or another" + + " javax.xml.transform.Source. A Map, a List or no body fails"), + producerOut = @BodyType(value = Carrier.TEXT, javaType = "java.lang.String", dependsOn = "resultHandlerFactory", + byOption = { + @BodyType.When(option = "output", value = "bytes", carrier = Carrier.BYTES, + javaType = "byte[]"), + @BodyType.When(option = "output", value = "DOM", carrier = Carrier.POJO, + javaType = "org.w3c.dom.Document"), + @BodyType.When(option = "output", value = "file", carrier = Carrier.FILE, + javaType = "java.io.File") }, + description = "The result of the transformation")) public class XsltEndpoint extends ProcessorEndpoint { private static final Logger LOG = LoggerFactory.getLogger(XsltEndpoint.class); diff --git a/core/camel-api/src/generated/java/org/apache/camel/spi/BodyType.java b/core/camel-api/src/generated/java/org/apache/camel/spi/BodyType.java new file mode 100644 index 0000000000000..366a81f7e7116 --- /dev/null +++ b/core/camel-api/src/generated/java/org/apache/camel/spi/BodyType.java @@ -0,0 +1,148 @@ +/* + * 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.spi; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * One side of {@link BodyTypes}: the body a step needs or leaves. + *

+ * The carriers are coarse on purpose: a carrier that is right is better than a precise Java type that is wrong. Give + * the {@link #javaType()} only when the body is always of that type. + * + * @since 4.24 + */ +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Target({}) +public @interface BodyType { + + /** + * The carriers of the body: the kinds of Java object it is in. Several for a body that can be any of them. None + * when the side is not known. + */ + Carrier[] value() default {}; + + /** + * The Java type of the body, when it is always of this type, such as + * {@code org.apache.camel.component.file.GenericFile}. + */ + String javaType() default ""; + + /** + * The format of the payload, when it is always the same: json, xml, csv, yaml, text or binary. These are the names + * of the data types of Kamelets. + */ + String format() default ""; + + /** + * The options the body depends on. The carriers hold while these options keep their default values; with another + * value the body is not known, unless {@link #byOption()} says it. + */ + String[] dependsOn() default {}; + + /** + * The body for other values of an option, such as {@code output=bytes}. + */ + When[] byOption() default {}; + + /** + * What to know about the body, in a sentence for the documentation and the tools. + */ + String description() default ""; + + /** + * The body when an option has a value. + */ + @Retention(RetentionPolicy.RUNTIME) + @Documented + @Target({}) + @interface When { + + /** + * The name of the option. + */ + String option(); + + /** + * The value of the option. + */ + String value(); + + /** + * The carriers of the body with this value. + */ + Carrier[] carrier(); + + /** + * The Java type of the body with this value, when it is always of this type. + */ + String javaType() default ""; + } + + /** + * The kinds of Java object a body is in. The runtime sorts the bodies it records into kinds as well (null, payload, + * data and collection): text, bytes, a stream, a file and a value are a payload, a map and a pojo are data, a list + * is a collection. + */ + enum Carrier { + + /** No body. */ + NULL("null"), + /** Text: a String. */ + TEXT("text"), + /** A byte array. */ + BYTES("bytes"), + /** A stream: an InputStream, a Reader, or the stream cache of Camel. */ + STREAM("stream"), + /** A file: the GenericFile of the file consumers, a java.io.File or a Path. */ + FILE("file"), + /** A single value: a number, a boolean or an enum. */ + VALUE("value"), + /** A java.util.Map, such as a JSON object parsed or a row of a query. */ + MAP("map"), + /** A List, another Collection, an array or an Iterator, such as a JSON array parsed or the rows of a query. */ + LIST("list"), + /** Another Java object, such as a DOM Document or a class a payload is unmarshalled into. */ + POJO("pojo"), + /** The body as it was before the step: for the body after a step only. */ + UNCHANGED("unchanged"), + /** Any body: for the bodies a step needs only. */ + ANY("any"), + /** + * The body depends on what the other system sends, or on a class the user configures: tooling must not guess + * it. + */ + UNKNOWN("unknown"); + + private final String value; + + Carrier(String value) { + this.value = value; + } + + /** + * The name of the carrier, as the catalog has it. + */ + public String getValue() { + return value; + } + } +} diff --git a/core/camel-api/src/generated/java/org/apache/camel/spi/BodyTypes.java b/core/camel-api/src/generated/java/org/apache/camel/spi/BodyTypes.java new file mode 100644 index 0000000000000..485ac6c17c7d8 --- /dev/null +++ b/core/camel-api/src/generated/java/org/apache/camel/spi/BodyTypes.java @@ -0,0 +1,105 @@ +/* + * 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.spi; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The message body a component, data format or language works with: the body it needs, and the body it leaves after it. + * The build carries it into the catalog, so tooling can say what the body is after a step without running the route: + * the validators, the catalog tools and the documentation. + *

+ * Put it on the endpoint class (the class with {@link UriEndpoint}), the data format class or the language class. A + * subclass does not have the body types of its superclass, as it may change the body: it declares its own. + *

+ * Each side that is given says what the body is with the default options. A side that is not given is not known, and + * tooling must not guess it. A side whose body depends on what the other system sends, or on a class the user + * configures, says {@link BodyType.Carrier#UNKNOWN}. + *

+ * For example the file component: the consumer gives the file it read, the producer writes text, bytes, a stream or a + * file, and leaves the body as it was: + * + *

+ * @BodyTypes(consumerOut = @BodyType(value = Carrier.FILE, description = "The file as read"),
+ *            producerIn = @BodyType({ Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }),
+ *            producerOut = @BodyType(Carrier.UNCHANGED))
+ * 
+ * + * @since 4.24 + */ +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Target({ ElementType.TYPE }) +public @interface BodyTypes { + + /** + * The body of the message the consumer creates. + */ + BodyType consumerOut() default @BodyType; + + /** + * The bodies a consumer that answers the caller can reply with: the body the route ends with, such as the response + * of an HTTP server. + */ + BodyType consumerReply() default @BodyType; + + /** + * The bodies the producer can send or write. + */ + BodyType producerIn() default @BodyType; + + /** + * The body after the producer: {@link BodyType.Carrier#UNCHANGED} when it leaves the body as it was, otherwise what + * it sets, such as the response of a call. + */ + BodyType producerOut() default @BodyType; + + /** + * The bodies a data format can marshal. + */ + BodyType marshalIn() default @BodyType; + + /** + * The body after marshal. + */ + BodyType marshalOut() default @BodyType; + + /** + * The bodies a data format can unmarshal. + */ + BodyType unmarshalIn() default @BodyType; + + /** + * The body after unmarshal. + */ + BodyType unmarshalOut() default @BodyType; + + /** + * The bodies the expressions of a language can read. + */ + BodyType expressionIn() default @BodyType; + + /** + * The result of an expression of a language, which becomes the body in setBody and transform. + */ + BodyType expressionOut() default @BodyType; + +} diff --git a/docs/user-manual/modules/ROOT/pages/camel-catalog.adoc b/docs/user-manual/modules/ROOT/pages/camel-catalog.adoc index 7f63d0149d286..78348be8b710e 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-catalog.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-catalog.adoc @@ -122,6 +122,75 @@ public class MailEndpoint extends ScheduledPollEndpoint { Aliases are descriptive names for discovery only; they cannot be used as the scheme in an endpoint URI. +=== Body types in the Catalog + +The options of a component say how to configure it, but not what the message body is after a step. That is the +question people, IDEs and AI agents ask most when they write the next step of a route: the file consumer gives the +file it read, the `http` producer leaves the response as a byte array or a stream, and after `unmarshal: json` the body +is a `Map` or a `List`. + +A component, data format or language can declare its body types, and the catalog then has a `bodyTypes` section with +one entry per side: + +[width="100%",cols="25%,75%",options="header",] +|=== +|Side |The body +|`consumerOut` |The body of the message the consumer creates. +|`consumerReply` |The bodies a consumer that answers the caller can reply with: the body the route ends with. +|`producerIn` |The bodies the producer can send or write. +|`producerOut` |The body after the producer. +|`marshalIn`, `marshalOut` |The bodies a data format can marshal, and the body after marshal. +|`unmarshalIn`, `unmarshalOut` |The bodies a data format can unmarshal, and the body after unmarshal. +|`expressionIn`, `expressionOut` |The bodies the expressions of a language can read, and their result. +|=== + +Each side has the _carriers_ of the body, the kinds of Java object it is in: `null`, `text`, `bytes`, `stream`, +`file`, `value`, `map`, `list` and `pojo`; `unchanged` for a step that leaves the body as it was, `any` for a step that +takes any body, and `unknown` for a body that depends on what the other system sends or on a class the user +configures. A side can also have the Java type when the body is always of that type, the format of the payload (json, +xml, csv, yaml, text or binary), the options the body depends on, the body for other values of an option, and a +description: + +[source,json] +---- +"bodyTypes": { + "producerIn": { "carriers": [ "text", "bytes", "stream", "file", "pojo" ], "format": "xml", "dependsOn": [ "source" ], + "description": "The XML to transform: text, bytes, a stream, a file, a DOM or another javax.xml.transform.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" } +} +---- + +The sides say what the body is with the default options. A side that is not in the catalog is not known, and tooling +should not guess it. The Java API gives them on the model: + +[source,java] +---- +CamelCatalog catalog = new DefaultCamelCatalog(); + +BodyTypeModel out = catalog.componentModel("xslt").getBodyType(BodyTypeModel.PRODUCER_OUT); +out.getCarriers(); // [text] +out.getJavaType(); // java.lang.String +---- + +Component developers declare them with the `@BodyTypes` annotation on the endpoint, data format or language class. A +subclass does not have the body types of its superclass, as it may change the body: + +[source,java] +---- +@BodyTypes(consumerOut = @BodyType(value = Carrier.FILE, description = "The file as read ..."), + producerIn = @BodyType({ Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }), + producerOut = @BodyType(Carrier.UNCHANGED)) +@UriEndpoint(firstVersion = "1.0.0", scheme = "file", ...) +public class FileEndpoint extends GenericFileEndpoint { +---- + +The build checks that each side belongs to the kind (a consumer-only component has no producer sides), that the +options a side names exist, that `unchanged` is only on the body after a step and `any` only on what a step needs. +The values are read from the code that sets the body, and checked against the body types the message history records +when the routes run. + === Documentation in the Catalog The catalog includes the AsciiDoc documentation pages from all modules. This makes the full diff --git a/docs/user-manual/modules/ROOT/pages/endpoint-annotations.adoc b/docs/user-manual/modules/ROOT/pages/endpoint-annotations.adoc index c46b4fedafcd4..aa9b755a18b8a 100644 --- a/docs/user-manual/modules/ROOT/pages/endpoint-annotations.adoc +++ b/docs/user-manual/modules/ROOT/pages/endpoint-annotations.adoc @@ -31,6 +31,10 @@ configuration parameters; then the class of this field should be annotated with one or more `@UriParam` or `@UriParams` annotations |`@Metadata` | Used for special situations to provide additional information. + +|`@BodyTypes` | The message body the endpoint works with: the body the consumer creates, the bodies the +producer can send, and the body after the producer. See xref:camel-catalog.adoc#_body_types_in_the_catalog[Body types +in the Catalog]. |======================================================================= For example see the `TimerEndpoint` from the `camel-timer` component and diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocs.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocs.java index f780f04260069..d69c883fbab17 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocs.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocs.java @@ -66,6 +66,13 @@ public final class CatalogDocs { /** The sub-pages of a language's documentation (simple has functions, operators, ognl and advanced). */ private static final List LANGUAGE_DOC_PAGES = List.of("functions", "operators", "ognl", "advanced"); + static final String BODY_TYPES_HINT = "the message body with the default options. consumerOut: the body a from:" + + " gives the route; consumerReply: the bodies it can reply with; producerIn:" + + " the bodies a to: can send; producerOut: the body after the to:; marshal," + + " unmarshal and expression In/Out: the same for a data format and a" + + " language. Carriers: text, bytes, stream, file, value, map, list, pojo," + + " null, unchanged, any, unknown. A side not listed is not known"; + /** * The rules a small model gets wrong most: functions live inside the placeholder, operators between placeholders. * Sent with the simple language result so an answer's examples follow the same shape as the catalog's. @@ -1626,6 +1633,11 @@ private static void addCommonModelFields(JsonObject result, BaseModel model) result.put("deprecationNote", model.getDeprecationNote()); } } + if (!model.getBodyTypes().isEmpty()) { + // what the body is after the step and what it needs, which the options do not say (CAMEL-24961) + result.put("bodyTypes", JsonMapper.asJsonObjectBodyTypes(model.getBodyTypes())); + result.put("bodyTypesHint", BODY_TYPES_HINT); + } } /** diff --git a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java index ae686c0d8080d..87de31b39b658 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java +++ b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java @@ -133,6 +133,25 @@ void componentOptionsSayWhetherTheyArePathOrQueryAndTheUriRulesComeAlong() throw assertTrue(direct.getString("uriSyntax").contains("(name) go in the path")); } + @Test + void theBodyTypesSayWhatTheBodyIsAfterAStep() throws Exception { + // CAMEL-24961: what the options of a component do not say + JsonObject xslt = catalogDoc(Map.of("name", "xslt", "kind", "component", "includeOptions", false)); + JsonObject out = ((JsonObject) xslt.get("bodyTypes")).getMap("producerOut"); + assertEquals(List.of("text"), out.getCollection("carriers")); + assertEquals("java.lang.String", out.getString("javaType")); + assertTrue(xslt.getString("bodyTypesHint").contains("A side not listed is not known")); + + JsonObject json = catalogDoc(Map.of("name", "json", "kind", "dataformat", "includeOptions", false)); + JsonObject unmarshalOut = ((JsonObject) json.get("bodyTypes")).getMap("unmarshalOut"); + assertEquals(List.of("map", "list"), unmarshalOut.getCollection("carriers")); + + // a component that does not declare them has none, rather than a guess + JsonObject kafka = catalogDoc(Map.of("name", "kafka", "kind", "component", "includeOptions", false)); + assertNull(kafka.get("bodyTypes")); + assertNull(kafka.get("bodyTypesHint")); + } + @Test void endpointArgumentValidatesAUriAgainstTheComponent() throws Exception { JsonObject bad = catalogDoc(Map.of("endpoint", "timer:tick?period=5s&fixedRte=true&repeatCount=abc")); diff --git a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/BaseModel.java b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/BaseModel.java index 7e08abe942402..381aeb9412a6d 100644 --- a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/BaseModel.java +++ b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/BaseModel.java @@ -38,6 +38,7 @@ public abstract class BaseModel { protected boolean nativeSupported; protected List aliases = new ArrayList<>(); protected Map metadata = new LinkedHashMap<>(); + protected Map bodyTypes = new LinkedHashMap<>(); public static Comparator> compareTitle() { return (m1, m2) -> m1.getTitle().compareToIgnoreCase(m2.getTitle()); @@ -175,6 +176,26 @@ public void setMetadata(Map metadata) { this.metadata = metadata; } + /** + * The body types of a component, data format or language, by side ({@link BodyTypeModel#CONSUMER_OUT}, + * {@link BodyTypeModel#PRODUCER_IN}...): the body a step needs and the body it leaves. A side that is not here is + * not known. + */ + public Map getBodyTypes() { + return bodyTypes; + } + + public void setBodyTypes(Map bodyTypes) { + this.bodyTypes = bodyTypes; + } + + /** + * The body type of a side, such as {@link BodyTypeModel#PRODUCER_OUT}, or null when it is not known. + */ + public BodyTypeModel getBodyType(String side) { + return bodyTypes.get(side); + } + @Override public String toString() { return name; diff --git a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/BodyTypeModel.java b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/BodyTypeModel.java new file mode 100644 index 0000000000000..bde3c29c0090a --- /dev/null +++ b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/BodyTypeModel.java @@ -0,0 +1,193 @@ +/* + * 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.tooling.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * One side of the body types of a component, data format or language: the body a step needs, or the body it leaves + * after it, with the default options (CAMEL-24961). + *

+ * The carriers are the kinds of Java object the body is in: null, text, bytes, stream, file, value, map, list and pojo; + * unchanged for a step that leaves the body as it was, any for a step that takes any body, and unknown for a body that + * depends on what the other system sends or on a class the user configures. + */ +public class BodyTypeModel { + + /** The body of the message the consumer creates. */ + public static final String CONSUMER_OUT = "consumerOut"; + /** The bodies a consumer that answers the caller can reply with: the body the route ends with. */ + public static final String CONSUMER_REPLY = "consumerReply"; + /** The bodies the producer can send or write. */ + public static final String PRODUCER_IN = "producerIn"; + /** The body after the producer. */ + public static final String PRODUCER_OUT = "producerOut"; + /** The bodies a data format can marshal. */ + public static final String MARSHAL_IN = "marshalIn"; + /** The body after marshal. */ + public static final String MARSHAL_OUT = "marshalOut"; + /** The bodies a data format can unmarshal. */ + public static final String UNMARSHAL_IN = "unmarshalIn"; + /** The body after unmarshal. */ + public static final String UNMARSHAL_OUT = "unmarshalOut"; + /** The bodies the expressions of a language can read. */ + public static final String EXPRESSION_IN = "expressionIn"; + /** The result of an expression of a language. */ + public static final String EXPRESSION_OUT = "expressionOut"; + + private List carriers = new ArrayList<>(); + private String javaType; + private String format; + private List dependsOn = new ArrayList<>(); + private List byOption = new ArrayList<>(); + private String description; + + /** + * The carriers of the body: one, or several for a body that can be any of them. + */ + public List getCarriers() { + return carriers; + } + + public void setCarriers(List carriers) { + this.carriers = carriers; + } + + /** + * The Java type of the body, when it is always of this type. + */ + public String getJavaType() { + return javaType; + } + + public void setJavaType(String javaType) { + this.javaType = javaType; + } + + /** + * The format of the payload, when it is always the same: json, xml, csv, yaml, text or binary. + */ + public String getFormat() { + return format; + } + + public void setFormat(String format) { + this.format = format; + } + + /** + * The options the body depends on: with another value than the default the body is not known, unless + * {@link #getByOption()} says it. + */ + public List getDependsOn() { + return dependsOn; + } + + public void setDependsOn(List dependsOn) { + this.dependsOn = dependsOn; + } + + /** + * The body for other values of an option. + */ + public List getByOption() { + return byOption; + } + + public void setByOption(List byOption) { + this.byOption = byOption; + } + + /** + * What to know about the body, in a sentence. + */ + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + /** + * Whether the body is left as it was (for the body after a step). + */ + public boolean isUnchanged() { + return carriers.contains("unchanged"); + } + + /** + * Whether the body is not known without the configuration or the data of the other system. + */ + public boolean isUnknown() { + return carriers.isEmpty() || carriers.contains("unknown"); + } + + @Override + public String toString() { + return String.join(", ", carriers) + (javaType != null ? " (" + javaType + ")" : ""); + } + + /** + * The body when an option has a value. + */ + public static class OptionBodyTypeModel { + + private String option; + private String value; + private List carriers = new ArrayList<>(); + private String javaType; + + public String getOption() { + return option; + } + + public void setOption(String option) { + this.option = option; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public List getCarriers() { + return carriers; + } + + public void setCarriers(List carriers) { + this.carriers = carriers; + } + + public String getJavaType() { + return javaType; + } + + public void setJavaType(String javaType) { + this.javaType = javaType; + } + + @Override + public String toString() { + return option + "=" + value + ": " + String.join(", ", carriers); + } + } +} diff --git a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/JsonMapper.java b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/JsonMapper.java index ac23618bc572f..28e0ab4a28345 100644 --- a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/JsonMapper.java +++ b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/JsonMapper.java @@ -19,7 +19,9 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Collection; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; @@ -97,6 +99,7 @@ public static ComponentModel generateComponentModel(JsonObject obj) { JsonObject mobj = (JsonObject) obj.get("component"); ComponentModel model = new ComponentModel(); parseComponentModel(mobj, model); + model.setBodyTypes(parseBodyTypes(obj.getMap("bodyTypes"))); JsonObject mcprp = (JsonObject) obj.get("componentProperties"); if (mcprp != null) { for (Map.Entry entry : mcprp.entrySet()) { @@ -252,6 +255,7 @@ public static JsonObject asJsonObject(ComponentModel model) { if (!headers.isEmpty()) { wrapper.put("headers", asJsonObject(headers)); } + bodyTypesToJson(model, wrapper); wrapper.put("properties", asJsonObject(model.getEndpointOptions())); if (!model.getApiOptions().isEmpty()) { wrapper.put("apis", apiModelAsJsonObject(model.getApiOptions(), false)); @@ -269,6 +273,7 @@ public static DataFormatModel generateDataFormatModel(JsonObject obj) { JsonObject mobj = (JsonObject) obj.get("dataformat"); DataFormatModel model = new DataFormatModel(); parseModel(mobj, model); + model.setBodyTypes(parseBodyTypes(obj.getMap("bodyTypes"))); parseArtifact(mobj, model); model.setModelName(mobj.getString("modelName")); model.setModelJavaType(mobj.getString("modelJavaType")); @@ -297,6 +302,7 @@ public static JsonObject asJsonObject(DataFormatModel model) { obj.entrySet().removeIf(e -> e.getValue() == null); JsonObject wrapper = new JsonObject(); wrapper.put("dataformat", obj); + bodyTypesToJson(model, wrapper); wrapper.put("properties", asJsonObject(model.getOptions())); return wrapper; } @@ -478,6 +484,7 @@ public static LanguageModel generateLanguageModel(JsonObject obj) { JsonObject mobj = (JsonObject) obj.get("language"); LanguageModel model = new LanguageModel(); parseModel(mobj, model); + model.setBodyTypes(parseBodyTypes(obj.getMap("bodyTypes"))); model.setModelName(mobj.getString("modelName")); model.setModelJavaType(mobj.getString("modelJavaType")); parseArtifact(mobj, model); @@ -523,6 +530,7 @@ public static JsonObject asJsonObject(LanguageModel model) { obj.entrySet().removeIf(e -> e.getValue() == null); JsonObject wrapper = new JsonObject(); wrapper.put("language", obj); + bodyTypesToJson(model, wrapper); wrapper.put("properties", asJsonObject(model.getOptions())); final List functions = model.getFunctions(); if (!functions.isEmpty()) { @@ -693,6 +701,84 @@ private static void parseModel(JsonObject mobj, BaseModel model) { model.setMetadata(mobj.getMapOrDefault("metadata", new JsonObject())); } + /** The body types of a component, data format or language: one object per side (CAMEL-24961). */ + private static Map parseBodyTypes(JsonObject json) { + Map answer = new LinkedHashMap<>(); + if (json == null) { + return answer; + } + for (Map.Entry entry : json.entrySet()) { + JsonObject side = (JsonObject) entry.getValue(); + BodyTypeModel bodyType = new BodyTypeModel(); + bodyType.setCarriers(stringList(side.getCollection("carriers"))); + bodyType.setJavaType(side.getString("javaType")); + bodyType.setFormat(side.getString("format")); + bodyType.setDependsOn(stringList(side.getCollection("dependsOn"))); + Collection byOption = side.getCollection("byOption"); + if (byOption != null) { + for (JsonObject when : byOption) { + BodyTypeModel.OptionBodyTypeModel option = new BodyTypeModel.OptionBodyTypeModel(); + option.setOption(when.getString("option")); + option.setValue(when.getString("value")); + option.setCarriers(stringList(when.getCollection("carriers"))); + option.setJavaType(when.getString("javaType")); + bodyType.getByOption().add(option); + } + } + bodyType.setDescription(side.getString("description")); + answer.put(entry.getKey(), bodyType); + } + return answer; + } + + private static List stringList(Collection values) { + return values != null ? new ArrayList<>(values) : new ArrayList<>(); + } + + /** The body types as a section of their own, next to the options, so each side is a line of the catalog. */ + private static void bodyTypesToJson(BaseModel model, JsonObject wrapper) { + if (model.getBodyTypes().isEmpty()) { + return; + } + wrapper.put("bodyTypes", asJsonObjectBodyTypes(model.getBodyTypes())); + } + + /** + * The body types of a component, data format or language as the catalog has them: an object per side, with the + * carriers, the Java type, the format, the options it depends on, the body for other option values and the + * description (CAMEL-24961). + */ + public static JsonObject asJsonObjectBodyTypes(Map bodyTypes) { + JsonObject json = new JsonObject(); + for (Map.Entry entry : bodyTypes.entrySet()) { + BodyTypeModel bodyType = entry.getValue(); + JsonObject side = new JsonObject(); + side.put("carriers", bodyType.getCarriers()); + side.put("javaType", bodyType.getJavaType()); + side.put("format", bodyType.getFormat()); + if (!bodyType.getDependsOn().isEmpty()) { + side.put("dependsOn", bodyType.getDependsOn()); + } + if (!bodyType.getByOption().isEmpty()) { + JsonArray byOption = new JsonArray(); + for (BodyTypeModel.OptionBodyTypeModel option : bodyType.getByOption()) { + JsonObject when = new JsonObject(); + when.put("option", option.getOption()); + when.put("value", option.getValue()); + when.put("carriers", option.getCarriers()); + when.put("javaType", option.getJavaType()); + when.entrySet().removeIf(e -> e.getValue() == null); + byOption.add(when); + } + side.put("byOption", byOption); + } + side.put("description", bodyType.getDescription()); + side.entrySet().removeIf(e -> e.getValue() == null); + json.put(entry.getKey(), side); + } + return json; + } + private static void parseOption(JsonObject mp, BaseOptionModel option, String name) { option.setName(name); Integer idx = mp.getInteger("index"); diff --git a/tooling/camel-tooling-model/src/test/java/org/apache/camel/tooling/model/JsonMapperTest.java b/tooling/camel-tooling-model/src/test/java/org/apache/camel/tooling/model/JsonMapperTest.java index c97f45b0b7d22..0b5fdc875bc14 100644 --- a/tooling/camel-tooling-model/src/test/java/org/apache/camel/tooling/model/JsonMapperTest.java +++ b/tooling/camel-tooling-model/src/test/java/org/apache/camel/tooling/model/JsonMapperTest.java @@ -23,6 +23,7 @@ import static org.apache.camel.tooling.model.ComponentModel.EndpointHeaderModel; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** @@ -161,4 +162,81 @@ void testShouldSerializeAndDeserializeApiReference() { assertEquals(List.of("Object getBody()", " T getBody(Class type)"), m.getSignatures()); assertEquals(List.of("message.getBody(String.class)"), m.getExamples()); } + + @Test + void testShouldWriteBodyTypesOnlyWhenSet() { + // CAMEL-24961: the body a step needs and the body it leaves + ComponentModel model = new ComponentModel(); + String json = JsonMapper.createParameterJsonSchema(model); + assertFalse(json.contains("\"bodyTypes\"")); + assertTrue(JsonMapper.generateComponentModel(json).getBodyTypes().isEmpty()); + + BodyTypeModel in = new BodyTypeModel(); + in.setCarriers(List.of("text", "bytes", "stream", "file")); + in.setFormat("xml"); + BodyTypeModel out = new BodyTypeModel(); + out.setCarriers(List.of("text")); + out.setJavaType("java.lang.String"); + out.setDependsOn(List.of("resultHandlerFactory")); + BodyTypeModel.OptionBodyTypeModel bytes = new BodyTypeModel.OptionBodyTypeModel(); + bytes.setOption("output"); + bytes.setValue("bytes"); + bytes.setCarriers(List.of("bytes")); + bytes.setJavaType("byte[]"); + out.getByOption().add(bytes); + out.setDescription("The result of the transformation"); + model.getBodyTypes().put(BodyTypeModel.PRODUCER_IN, in); + model.getBodyTypes().put(BodyTypeModel.PRODUCER_OUT, out); + + json = JsonMapper.createParameterJsonSchema(model); + assertTrue(json.contains("\"bodyTypes\""), json); + ComponentModel model2 = JsonMapper.generateComponentModel(json); + assertEquals(List.of(BodyTypeModel.PRODUCER_IN, BodyTypeModel.PRODUCER_OUT), + List.copyOf(model2.getBodyTypes().keySet())); + assertNull(model2.getBodyType(BodyTypeModel.CONSUMER_OUT)); + + BodyTypeModel in2 = model2.getBodyType(BodyTypeModel.PRODUCER_IN); + assertEquals(List.of("text", "bytes", "stream", "file"), in2.getCarriers()); + assertEquals("xml", in2.getFormat()); + assertNull(in2.getJavaType()); + assertTrue(in2.getDependsOn().isEmpty()); + assertTrue(in2.getByOption().isEmpty()); + + BodyTypeModel out2 = model2.getBodyType(BodyTypeModel.PRODUCER_OUT); + assertEquals(List.of("text"), out2.getCarriers()); + assertEquals("java.lang.String", out2.getJavaType()); + assertEquals(List.of("resultHandlerFactory"), out2.getDependsOn()); + assertEquals("The result of the transformation", out2.getDescription()); + assertEquals(1, out2.getByOption().size()); + BodyTypeModel.OptionBodyTypeModel bytes2 = out2.getByOption().get(0); + assertEquals("output", bytes2.getOption()); + assertEquals("bytes", bytes2.getValue()); + assertEquals(List.of("bytes"), bytes2.getCarriers()); + assertEquals("byte[]", bytes2.getJavaType()); + assertFalse(out2.isUnchanged()); + assertFalse(out2.isUnknown()); + } + + @Test + void testShouldWriteBodyTypesOfDataFormatsAndLanguages() { + DataFormatModel dataFormat = new DataFormatModel(); + dataFormat.setName("jackson"); + BodyTypeModel unmarshalOut = new BodyTypeModel(); + unmarshalOut.setCarriers(List.of("map", "list")); + unmarshalOut.setDependsOn(List.of("unmarshalType")); + dataFormat.getBodyTypes().put(BodyTypeModel.UNMARSHAL_OUT, unmarshalOut); + DataFormatModel dataFormat2 + = JsonMapper.generateDataFormatModel(JsonMapper.createParameterJsonSchema(dataFormat)); + assertEquals(List.of("map", "list"), dataFormat2.getBodyType(BodyTypeModel.UNMARSHAL_OUT).getCarriers()); + assertEquals(List.of("unmarshalType"), dataFormat2.getBodyType(BodyTypeModel.UNMARSHAL_OUT).getDependsOn()); + + LanguageModel language = new LanguageModel(); + language.setName("constant"); + BodyTypeModel expressionOut = new BodyTypeModel(); + expressionOut.setCarriers(List.of("text")); + language.getBodyTypes().put(BodyTypeModel.EXPRESSION_OUT, expressionOut); + LanguageModel language2 = JsonMapper.generateLanguageModel(JsonMapper.createParameterJsonSchema(language)); + assertEquals(List.of("text"), language2.getBodyType(BodyTypeModel.EXPRESSION_OUT).getCarriers()); + assertNull(language2.getBodyType(BodyTypeModel.EXPRESSION_IN)); + } } diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/BodyTypesHelper.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/BodyTypesHelper.java new file mode 100644 index 0000000000000..02e23fbd3b8bc --- /dev/null +++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/BodyTypesHelper.java @@ -0,0 +1,185 @@ +/* + * 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.maven.packaging; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; +import org.apache.camel.tooling.model.BaseModel; +import org.apache.camel.tooling.model.BodyTypeModel; +import org.apache.camel.tooling.model.ComponentModel; + +/** + * Carries the {@link BodyTypes} of a component, data format or language into its model, which the catalog JSON is + * written from (CAMEL-24961). + */ +final class BodyTypesHelper { + + private static final Set IN = Set.of(BodyTypeModel.CONSUMER_REPLY, BodyTypeModel.PRODUCER_IN, + BodyTypeModel.MARSHAL_IN, BodyTypeModel.UNMARSHAL_IN, BodyTypeModel.EXPRESSION_IN); + + private BodyTypesHelper() { + } + + /** + * Adds the sides the annotation gives to the model, in the order of the annotation. + * + * @param model the model of the component, data format or language + * @param bodyTypes the annotation, null when the class has none + * @param owner the class with the annotation, for the error message + * @throws IllegalArgumentException when a side does not fit the model: a data format side on a component, a + * producer side on a consumer-only component, unchanged on what a step needs or + * any on what it leaves + */ + static void addBodyTypes(BaseModel model, BodyTypes bodyTypes, Class owner) { + if (bodyTypes == null) { + return; + } + Map sides = new LinkedHashMap<>(); + sides.put(BodyTypeModel.CONSUMER_OUT, bodyTypes.consumerOut()); + sides.put(BodyTypeModel.CONSUMER_REPLY, bodyTypes.consumerReply()); + sides.put(BodyTypeModel.PRODUCER_IN, bodyTypes.producerIn()); + sides.put(BodyTypeModel.PRODUCER_OUT, bodyTypes.producerOut()); + sides.put(BodyTypeModel.MARSHAL_IN, bodyTypes.marshalIn()); + sides.put(BodyTypeModel.MARSHAL_OUT, bodyTypes.marshalOut()); + sides.put(BodyTypeModel.UNMARSHAL_IN, bodyTypes.unmarshalIn()); + sides.put(BodyTypeModel.UNMARSHAL_OUT, bodyTypes.unmarshalOut()); + sides.put(BodyTypeModel.EXPRESSION_IN, bodyTypes.expressionIn()); + sides.put(BodyTypeModel.EXPRESSION_OUT, bodyTypes.expressionOut()); + + Set allowed = allowedSides(model); + Set options = optionNames(model); + for (Map.Entry entry : sides.entrySet()) { + String side = entry.getKey(); + BodyType bodyType = entry.getValue(); + if (bodyType.value().length == 0) { + if (!bodyType.javaType().isEmpty() || !bodyType.format().isEmpty() || bodyType.dependsOn().length > 0 + || bodyType.byOption().length > 0 || !bodyType.description().isEmpty()) { + throw new IllegalArgumentException( + owner.getName() + ": the @BodyTypes " + side + " has no carriers, but other attributes"); + } + continue; + } + if (!allowed.contains(side)) { + throw new IllegalArgumentException( + owner.getName() + ": the @BodyTypes " + side + " is not a side of this " + model.getKind() + + ", which has " + allowed); + } + check(owner, side, bodyType.value()); + for (String option : bodyType.dependsOn()) { + checkOption(owner, side, option, options); + } + for (BodyType.When when : bodyType.byOption()) { + checkOption(owner, side, when.option(), options); + check(owner, side + " " + when.option() + "=" + when.value(), when.carrier()); + } + model.getBodyTypes().put(side, asModel(bodyType)); + } + } + + private static Set allowedSides(BaseModel model) { + return switch (model.getKind()) { + case component -> { + ComponentModel component = (ComponentModel) model; + if (component.isConsumerOnly()) { + yield Set.of(BodyTypeModel.CONSUMER_OUT, BodyTypeModel.CONSUMER_REPLY); + } else if (component.isProducerOnly()) { + yield Set.of(BodyTypeModel.PRODUCER_IN, BodyTypeModel.PRODUCER_OUT); + } + yield Set.of(BodyTypeModel.CONSUMER_OUT, BodyTypeModel.CONSUMER_REPLY, BodyTypeModel.PRODUCER_IN, + BodyTypeModel.PRODUCER_OUT); + } + case dataformat -> Set.of(BodyTypeModel.MARSHAL_IN, BodyTypeModel.MARSHAL_OUT, BodyTypeModel.UNMARSHAL_IN, + BodyTypeModel.UNMARSHAL_OUT); + case language -> Set.of(BodyTypeModel.EXPRESSION_IN, BodyTypeModel.EXPRESSION_OUT); + default -> Set.of(); + }; + } + + /** The options of the model, which the body types can depend on. */ + private static Set optionNames(BaseModel model) { + Set names = new HashSet<>(); + model.getOptions().forEach(o -> names.add(o.getName())); + if (model instanceof ComponentModel component) { + component.getComponentOptions().forEach(o -> names.add(o.getName())); + component.getEndpointOptions().forEach(o -> names.add(o.getName())); + } + return names; + } + + private static void checkOption(Class owner, String side, String option, Set options) { + if (!options.contains(option)) { + throw new IllegalArgumentException( + owner.getName() + ": the @BodyTypes " + side + " depends on " + option + ", which is not an option"); + } + } + + /** + * UNCHANGED is for the body after a step, such as a sql query that leaves it unchanged unless it is a select. ANY + * is for the bodies a step needs. ANY and UNKNOWN stand alone. + */ + private static void check(Class owner, String side, Carrier[] carriers) { + List list = Arrays.asList(carriers); + boolean in = IN.contains(side.split(" ")[0]); + if (in && list.contains(Carrier.UNCHANGED)) { + throw new IllegalArgumentException( + owner.getName() + ": the @BodyTypes " + side + " is what a step needs, which cannot be unchanged"); + } + if (!in && list.contains(Carrier.ANY)) { + throw new IllegalArgumentException( + owner.getName() + ": the @BodyTypes " + side + " is the body after a step, which cannot be any"); + } + if (list.size() > 1 && (list.contains(Carrier.ANY) || list.contains(Carrier.UNKNOWN))) { + throw new IllegalArgumentException( + owner.getName() + ": the @BodyTypes " + side + " has any or unknown with other carriers"); + } + } + + private static BodyTypeModel asModel(BodyType bodyType) { + BodyTypeModel model = new BodyTypeModel(); + model.setCarriers(names(bodyType.value())); + model.setJavaType(emptyAsNull(bodyType.javaType())); + model.setFormat(emptyAsNull(bodyType.format())); + model.setDependsOn(new ArrayList<>(Arrays.asList(bodyType.dependsOn()))); + for (BodyType.When when : bodyType.byOption()) { + BodyTypeModel.OptionBodyTypeModel option = new BodyTypeModel.OptionBodyTypeModel(); + option.setOption(when.option()); + option.setValue(when.value()); + option.setCarriers(names(when.carrier())); + option.setJavaType(emptyAsNull(when.javaType())); + model.getByOption().add(option); + } + model.setDescription(emptyAsNull(bodyType.description())); + return model; + } + + private static List names(Carrier[] carriers) { + return new ArrayList<>(Arrays.stream(carriers).map(Carrier::getValue).toList()); + } + + private static String emptyAsNull(String value) { + return value.isEmpty() ? null : value; + } +} diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojo.java index 061b650e06b54..6621f1022b577 100644 --- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojo.java +++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojo.java @@ -52,6 +52,7 @@ import org.apache.camel.spi.ApiMethod; import org.apache.camel.spi.ApiParam; import org.apache.camel.spi.ApiParams; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.Metadata; import org.apache.camel.spi.UriEndpoint; import org.apache.camel.spi.UriParam; @@ -306,6 +307,7 @@ protected ComponentModel writeJSonSchemeAndPropertyConfigurer( SchemaHelper.addModelMetadata(componentModel, project); SchemaHelper.addModelMetadata(componentModel, classElement.getAnnotation(Metadata.class)); + addBodyTypes(componentModel, classElement); String json = JsonMapper.createParameterJsonSchema(componentModel); @@ -322,6 +324,18 @@ protected ComponentModel writeJSonSchemeAndPropertyConfigurer( return componentModel; } + /** + * Adds the body types of the endpoint, which the endpoint class or a superclass declares with {@link BodyTypes} + * (CAMEL-24961). + */ + void addBodyTypes(ComponentModel componentModel, Class classElement) throws MojoExecutionException { + try { + BodyTypesHelper.addBodyTypes(componentModel, classElement.getAnnotation(BodyTypes.class), classElement); + } catch (IllegalArgumentException e) { + throw new MojoExecutionException(e.getMessage(), e); + } + } + /** * Retrieve the metadata added to all the {@code String} constants defined in the class corresponding to the element * {@code headersClass} of the annotation {@code UriEndpoint} along with all its super classes and implemented diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageDataFormatMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageDataFormatMojo.java index edc311d02b7ad..66377f9c3cf08 100644 --- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageDataFormatMojo.java +++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageDataFormatMojo.java @@ -35,6 +35,7 @@ import javax.inject.Inject; import org.apache.camel.maven.packaging.generics.ClassUtil; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.Metadata; import org.apache.camel.tooling.model.BaseOptionModel; import org.apache.camel.tooling.model.DataFormatModel; @@ -202,6 +203,7 @@ public int prepareDataFormat() throws MojoExecutionException { SchemaHelper.addModelMetadata(dataFormatModel, project); SchemaHelper.addModelMetadata(dataFormatModel, metadata); + BodyTypesHelper.addBodyTypes(dataFormatModel, clazz.getAnnotation(BodyTypes.class), clazz); if (!modelName.equals(name)) { /* Prefer description from the clazz */ diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageLanguageMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageLanguageMojo.java index 94f1f3974bc89..862a093d70655 100644 --- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageLanguageMojo.java +++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageLanguageMojo.java @@ -29,6 +29,7 @@ import javax.inject.Inject; import org.apache.camel.maven.packaging.generics.ClassUtil; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.Metadata; import org.apache.camel.spi.annotations.Language; import org.apache.camel.tooling.model.EipModel; @@ -166,6 +167,7 @@ public int prepareLanguage() throws MojoExecutionException { SchemaHelper.addModelMetadata(languageModel, project); SchemaHelper.addModelMetadata(languageModel, javaType.getAnnotation(Metadata.class)); + BodyTypesHelper.addBodyTypes(languageModel, javaType.getAnnotation(BodyTypes.class), javaType); // build json schema for the language String schema = JsonMapper.createParameterJsonSchema(languageModel); diff --git a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojoTest.java b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojoTest.java index 562fce01273ec..5fa0723fa8f5c 100644 --- a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojoTest.java +++ b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojoTest.java @@ -20,11 +20,14 @@ import java.util.List; import org.apache.camel.maven.packaging.endpoint.SomeEndpoint; +import org.apache.camel.maven.packaging.endpoint.SomeEndpointExtendingBodyTypes; import org.apache.camel.maven.packaging.endpoint.SomeEndpointUsingEnumConstants; import org.apache.camel.maven.packaging.endpoint.SomeEndpointUsingEnumConstantsByField; import org.apache.camel.maven.packaging.endpoint.SomeEndpointUsingEnumConstantsByMethod; import org.apache.camel.maven.packaging.endpoint.SomeEndpointUsingInterfaceConstants; +import org.apache.camel.maven.packaging.endpoint.SomeEndpointWithBadBodyTypes; import org.apache.camel.maven.packaging.endpoint.SomeEndpointWithBadHeaders; +import org.apache.camel.maven.packaging.endpoint.SomeEndpointWithBodyTypes; import org.apache.camel.maven.packaging.endpoint.SomeEndpointWithFilter; import org.apache.camel.maven.packaging.endpoint.SomeEndpointWithHeaderClassHierarchy; import org.apache.camel.maven.packaging.endpoint.SomeEndpointWithHeaderInterfaceHierarchy; @@ -33,6 +36,7 @@ import org.apache.camel.maven.packaging.endpoint.SomeExtendingEndpoint; import org.apache.camel.maven.packaging.endpoint.SomeInternalRemoteEndpoint; import org.apache.camel.spi.UriEndpoint; +import org.apache.camel.tooling.model.BodyTypeModel; import org.apache.camel.tooling.model.ComponentModel; import org.apache.camel.tooling.model.ComponentModel.EndpointHeaderModel; import org.apache.maven.plugin.MojoExecutionException; @@ -225,4 +229,69 @@ void testEndpointWithHeadersOnSelfAndSuperclass() { assertEquals("KEY_EMPTY_WITH_JAVA_DOC", headerFromParent.getName()); } + + @Test + void testEndpointWithBodyTypes() throws Exception { + // CAMEL-24961: the body a step needs and the body it leaves, in the order of the annotation + addEndpointOptions("resultHandlerFactory", "output"); + mojo.addBodyTypes(model, SomeEndpointWithBodyTypes.class); + assertEquals(List.of(BodyTypeModel.CONSUMER_OUT, BodyTypeModel.PRODUCER_IN, BodyTypeModel.PRODUCER_OUT), + List.copyOf(model.getBodyTypes().keySet())); + + BodyTypeModel consumerOut = model.getBodyType(BodyTypeModel.CONSUMER_OUT); + assertEquals(List.of("file"), consumerOut.getCarriers()); + assertEquals("org.apache.camel.component.file.GenericFile", consumerOut.getJavaType()); + assertEquals("The file as read", consumerOut.getDescription()); + assertNull(consumerOut.getFormat()); + assertTrue(consumerOut.getDependsOn().isEmpty()); + + assertEquals(List.of("text", "bytes", "stream", "file"), + model.getBodyType(BodyTypeModel.PRODUCER_IN).getCarriers()); + + BodyTypeModel producerOut = model.getBodyType(BodyTypeModel.PRODUCER_OUT); + assertEquals(List.of("text"), producerOut.getCarriers()); + assertEquals("xml", producerOut.getFormat()); + assertEquals(List.of("resultHandlerFactory"), producerOut.getDependsOn()); + assertEquals(1, producerOut.getByOption().size()); + BodyTypeModel.OptionBodyTypeModel bytes = producerOut.getByOption().get(0); + assertEquals("output", bytes.getOption()); + assertEquals("bytes", bytes.getValue()); + assertEquals(List.of("bytes"), bytes.getCarriers()); + assertEquals("byte[]", bytes.getJavaType()); + } + + @Test + void testEndpointDoesNotInheritBodyTypes() throws Exception { + // a subclass may change the body, so it declares its own + mojo.addBodyTypes(model, SomeEndpointExtendingBodyTypes.class); + assertTrue(model.getBodyTypes().isEmpty()); + } + + @Test + void testEndpointWithoutBodyTypes() throws Exception { + mojo.addBodyTypes(model, SomeEndpointWithoutHeaders.class); + assertTrue(model.getBodyTypes().isEmpty()); + } + + @ParameterizedTest + @ValueSource(classes = { + SomeEndpointWithBadBodyTypes.DataFormatSide.class, SomeEndpointWithBadBodyTypes.ProducerSideOfConsumer.class, + SomeEndpointWithBadBodyTypes.UnchangedInput.class, SomeEndpointWithBadBodyTypes.AnyOutput.class, + SomeEndpointWithBadBodyTypes.UnknownWithOthers.class, SomeEndpointWithBadBodyTypes.NoCarriers.class, + SomeEndpointWithBadBodyTypes.UnknownOption.class }) + void testEndpointWithBadBodyTypes(Class clazz) { + addEndpointOptions("output"); + model.setConsumerOnly(clazz.getAnnotation(UriEndpoint.class).consumerOnly()); + MojoExecutionException e = assertThrows(MojoExecutionException.class, () -> mojo.addBodyTypes(model, clazz)); + assertTrue(e.getMessage().startsWith(clazz.getName() + ": the @BodyTypes "), e.getMessage()); + assertTrue(model.getBodyTypes().isEmpty()); + } + + private void addEndpointOptions(String... names) { + for (String name : names) { + ComponentModel.EndpointOptionModel option = new ComponentModel.EndpointOptionModel(); + option.setName(name); + model.addEndpointOption(option); + } + } } diff --git a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointExtendingBodyTypes.java b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointExtendingBodyTypes.java new file mode 100644 index 0000000000000..31f9aa9563d84 --- /dev/null +++ b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointExtendingBodyTypes.java @@ -0,0 +1,23 @@ +/* + * 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.maven.packaging.endpoint; + +import org.apache.camel.spi.UriEndpoint; + +@UriEndpoint(scheme = "someextbody", syntax = "someextbody", title = "someextbody") +public class SomeEndpointExtendingBodyTypes extends SomeEndpointWithBodyTypes { +} diff --git a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointWithBadBodyTypes.java b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointWithBadBodyTypes.java new file mode 100644 index 0000000000000..cd66ff0c96706 --- /dev/null +++ b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointWithBadBodyTypes.java @@ -0,0 +1,66 @@ +/* + * 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.maven.packaging.endpoint; + +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; +import org.apache.camel.spi.UriEndpoint; + +/** + * Body types that do not fit an endpoint, one per nested class. + */ +public final class SomeEndpointWithBadBodyTypes { + + private SomeEndpointWithBadBodyTypes() { + } + + @UriEndpoint(scheme = "some", syntax = "some", title = "some") + @BodyTypes(marshalOut = @BodyType(Carrier.STREAM)) + public static class DataFormatSide { + } + + @UriEndpoint(scheme = "some", syntax = "some", title = "some", consumerOnly = true) + @BodyTypes(producerOut = @BodyType(Carrier.UNCHANGED)) + public static class ProducerSideOfConsumer { + } + + @UriEndpoint(scheme = "some", syntax = "some", title = "some") + @BodyTypes(producerIn = @BodyType(Carrier.UNCHANGED)) + public static class UnchangedInput { + } + + @UriEndpoint(scheme = "some", syntax = "some", title = "some") + @BodyTypes(producerOut = @BodyType(Carrier.ANY)) + public static class AnyOutput { + } + + @UriEndpoint(scheme = "some", syntax = "some", title = "some") + @BodyTypes(producerOut = @BodyType({ Carrier.UNKNOWN, Carrier.TEXT })) + public static class UnknownWithOthers { + } + + @UriEndpoint(scheme = "some", syntax = "some", title = "some") + @BodyTypes(producerOut = @BodyType(javaType = "java.lang.String")) + public static class NoCarriers { + } + + @UriEndpoint(scheme = "some", syntax = "some", title = "some") + @BodyTypes(producerOut = @BodyType(value = Carrier.TEXT, dependsOn = "nope")) + public static class UnknownOption { + } +} diff --git a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointWithBodyTypes.java b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointWithBodyTypes.java new file mode 100644 index 0000000000000..18c10606a6461 --- /dev/null +++ b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointWithBodyTypes.java @@ -0,0 +1,33 @@ +/* + * 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.maven.packaging.endpoint; + +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; +import org.apache.camel.spi.UriEndpoint; + +@UriEndpoint(scheme = "some", syntax = "some", title = "some") +@BodyTypes(consumerOut = @BodyType(value = Carrier.FILE, javaType = "org.apache.camel.component.file.GenericFile", + description = "The file as read"), + producerIn = @BodyType({ Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }), + producerOut = @BodyType(value = Carrier.TEXT, javaType = "java.lang.String", format = "xml", + dependsOn = "resultHandlerFactory", + byOption = @BodyType.When(option = "output", value = "bytes", + carrier = Carrier.BYTES, javaType = "byte[]"))) +public class SomeEndpointWithBodyTypes { +} diff --git a/tooling/spi-annotations/src/main/java/org/apache/camel/spi/BodyType.java b/tooling/spi-annotations/src/main/java/org/apache/camel/spi/BodyType.java new file mode 100644 index 0000000000000..366a81f7e7116 --- /dev/null +++ b/tooling/spi-annotations/src/main/java/org/apache/camel/spi/BodyType.java @@ -0,0 +1,148 @@ +/* + * 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.spi; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * One side of {@link BodyTypes}: the body a step needs or leaves. + *

+ * The carriers are coarse on purpose: a carrier that is right is better than a precise Java type that is wrong. Give + * the {@link #javaType()} only when the body is always of that type. + * + * @since 4.24 + */ +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Target({}) +public @interface BodyType { + + /** + * The carriers of the body: the kinds of Java object it is in. Several for a body that can be any of them. None + * when the side is not known. + */ + Carrier[] value() default {}; + + /** + * The Java type of the body, when it is always of this type, such as + * {@code org.apache.camel.component.file.GenericFile}. + */ + String javaType() default ""; + + /** + * The format of the payload, when it is always the same: json, xml, csv, yaml, text or binary. These are the names + * of the data types of Kamelets. + */ + String format() default ""; + + /** + * The options the body depends on. The carriers hold while these options keep their default values; with another + * value the body is not known, unless {@link #byOption()} says it. + */ + String[] dependsOn() default {}; + + /** + * The body for other values of an option, such as {@code output=bytes}. + */ + When[] byOption() default {}; + + /** + * What to know about the body, in a sentence for the documentation and the tools. + */ + String description() default ""; + + /** + * The body when an option has a value. + */ + @Retention(RetentionPolicy.RUNTIME) + @Documented + @Target({}) + @interface When { + + /** + * The name of the option. + */ + String option(); + + /** + * The value of the option. + */ + String value(); + + /** + * The carriers of the body with this value. + */ + Carrier[] carrier(); + + /** + * The Java type of the body with this value, when it is always of this type. + */ + String javaType() default ""; + } + + /** + * The kinds of Java object a body is in. The runtime sorts the bodies it records into kinds as well (null, payload, + * data and collection): text, bytes, a stream, a file and a value are a payload, a map and a pojo are data, a list + * is a collection. + */ + enum Carrier { + + /** No body. */ + NULL("null"), + /** Text: a String. */ + TEXT("text"), + /** A byte array. */ + BYTES("bytes"), + /** A stream: an InputStream, a Reader, or the stream cache of Camel. */ + STREAM("stream"), + /** A file: the GenericFile of the file consumers, a java.io.File or a Path. */ + FILE("file"), + /** A single value: a number, a boolean or an enum. */ + VALUE("value"), + /** A java.util.Map, such as a JSON object parsed or a row of a query. */ + MAP("map"), + /** A List, another Collection, an array or an Iterator, such as a JSON array parsed or the rows of a query. */ + LIST("list"), + /** Another Java object, such as a DOM Document or a class a payload is unmarshalled into. */ + POJO("pojo"), + /** The body as it was before the step: for the body after a step only. */ + UNCHANGED("unchanged"), + /** Any body: for the bodies a step needs only. */ + ANY("any"), + /** + * The body depends on what the other system sends, or on a class the user configures: tooling must not guess + * it. + */ + UNKNOWN("unknown"); + + private final String value; + + Carrier(String value) { + this.value = value; + } + + /** + * The name of the carrier, as the catalog has it. + */ + public String getValue() { + return value; + } + } +} diff --git a/tooling/spi-annotations/src/main/java/org/apache/camel/spi/BodyTypes.java b/tooling/spi-annotations/src/main/java/org/apache/camel/spi/BodyTypes.java new file mode 100644 index 0000000000000..485ac6c17c7d8 --- /dev/null +++ b/tooling/spi-annotations/src/main/java/org/apache/camel/spi/BodyTypes.java @@ -0,0 +1,105 @@ +/* + * 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.spi; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The message body a component, data format or language works with: the body it needs, and the body it leaves after it. + * The build carries it into the catalog, so tooling can say what the body is after a step without running the route: + * the validators, the catalog tools and the documentation. + *

+ * Put it on the endpoint class (the class with {@link UriEndpoint}), the data format class or the language class. A + * subclass does not have the body types of its superclass, as it may change the body: it declares its own. + *

+ * Each side that is given says what the body is with the default options. A side that is not given is not known, and + * tooling must not guess it. A side whose body depends on what the other system sends, or on a class the user + * configures, says {@link BodyType.Carrier#UNKNOWN}. + *

+ * For example the file component: the consumer gives the file it read, the producer writes text, bytes, a stream or a + * file, and leaves the body as it was: + * + *

+ * @BodyTypes(consumerOut = @BodyType(value = Carrier.FILE, description = "The file as read"),
+ *            producerIn = @BodyType({ Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }),
+ *            producerOut = @BodyType(Carrier.UNCHANGED))
+ * 
+ * + * @since 4.24 + */ +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Target({ ElementType.TYPE }) +public @interface BodyTypes { + + /** + * The body of the message the consumer creates. + */ + BodyType consumerOut() default @BodyType; + + /** + * The bodies a consumer that answers the caller can reply with: the body the route ends with, such as the response + * of an HTTP server. + */ + BodyType consumerReply() default @BodyType; + + /** + * The bodies the producer can send or write. + */ + BodyType producerIn() default @BodyType; + + /** + * The body after the producer: {@link BodyType.Carrier#UNCHANGED} when it leaves the body as it was, otherwise what + * it sets, such as the response of a call. + */ + BodyType producerOut() default @BodyType; + + /** + * The bodies a data format can marshal. + */ + BodyType marshalIn() default @BodyType; + + /** + * The body after marshal. + */ + BodyType marshalOut() default @BodyType; + + /** + * The bodies a data format can unmarshal. + */ + BodyType unmarshalIn() default @BodyType; + + /** + * The body after unmarshal. + */ + BodyType unmarshalOut() default @BodyType; + + /** + * The bodies the expressions of a language can read. + */ + BodyType expressionIn() default @BodyType; + + /** + * The result of an expression of a language, which becomes the body in setBody and transform. + */ + BodyType expressionOut() default @BodyType; + +} From 09db3bec9a3b4ff8c43b67a0306b898cf9eff11d Mon Sep 17 00:00:00 2001 From: Claus Ibsen Date: Wed, 21 Oct 2026 14:14:33 +0200 Subject: [PATCH 2/9] CAMEL-24961: camel-catalog - the body types of platform-http, rest, ftp, jms, kafka, aws2-sqs, the csv, jacksonXml and yaml data formats, and the languages The second slice of the catalog body types: - components: platform-http, rest (and the rest-openapi consumer), ftp, ftps, sftp, jms, activemq, activemq6, amqp, kafka, aws2-sqs, log, mock, scheduler, quartz; - data formats: csv, jacksonXml, snakeYaml; - languages: simple, constant, jsonpath, xpath, groovy, jq. Each value is read from the code that sets the body, and checked against the body types the message history recorded when the tests of the modules and reference routes ran (the ftp integration tests included). camel_catalog_doc gives the body types a line per side, and its answer is cut at 18000 characters instead of 16000, so the four sides of the jms components leave the options a limit asks for room. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claus Ibsen --- .../camel/catalog/components/activemq.json | 6 ++ .../camel/catalog/components/activemq6.json | 6 ++ .../apache/camel/catalog/components/amqp.json | 6 ++ .../camel/catalog/components/aws2-sqs.json | 5 ++ .../apache/camel/catalog/components/ftp.json | 5 ++ .../apache/camel/catalog/components/ftps.json | 5 ++ .../apache/camel/catalog/components/jms.json | 6 ++ .../camel/catalog/components/kafka.json | 5 ++ .../apache/camel/catalog/components/log.json | 4 ++ .../apache/camel/catalog/components/mock.json | 4 ++ .../catalog/components/platform-http.json | 4 ++ .../camel/catalog/components/quartz.json | 3 + .../catalog/components/rest-openapi.json | 2 + .../apache/camel/catalog/components/rest.json | 6 ++ .../camel/catalog/components/scheduler.json | 3 + .../apache/camel/catalog/components/sftp.json | 5 ++ .../apache/camel/catalog/dataformats/csv.json | 6 ++ .../camel/catalog/dataformats/jacksonXml.json | 6 ++ .../camel/catalog/dataformats/snakeYaml.json | 6 ++ .../camel/catalog/languages/constant.json | 3 + .../camel/catalog/languages/groovy.json | 4 ++ .../apache/camel/catalog/languages/jq.json | 4 ++ .../camel/catalog/languages/jsonpath.json | 4 ++ .../camel/catalog/languages/simple.json | 4 ++ .../apache/camel/catalog/languages/xpath.json | 4 ++ .../camel/component/activemq/activemq.json | 6 ++ .../component/activemq/ActiveMQEndpoint.java | 39 +++++++++++++ .../camel/component/activemq6/activemq6.json | 6 ++ .../component/activemq6/ActiveMQEndpoint.java | 39 +++++++++++++ .../org/apache/camel/component/amqp/amqp.json | 6 ++ .../camel/component/amqp/AMQPEndpoint.java | 39 +++++++++++++ .../camel/component/aws2/sqs/aws2-sqs.json | 5 ++ .../component/aws2/sqs/Sqs2Endpoint.java | 27 +++++++++ .../org/apache/camel/dataformat/csv/csv.json | 6 ++ .../camel/dataformat/csv/CsvDataFormat.java | 13 +++++ .../camel/component/file/remote/ftp.json | 5 ++ .../camel/component/file/remote/ftps.json | 5 ++ .../camel/component/file/remote/sftp.json | 5 ++ .../component/file/remote/FtpEndpoint.java | 17 ++++++ .../component/file/remote/FtpsEndpoint.java | 17 ++++++ .../component/file/remote/SftpEndpoint.java | 17 ++++++ .../apache/camel/language/groovy/groovy.json | 4 ++ .../camel/language/groovy/GroovyLanguage.java | 5 ++ .../component/jacksonxml/jacksonXml.json | 6 ++ .../jacksonxml/JacksonXMLDataFormat.java | 14 +++++ .../org/apache/camel/component/jms/jms.json | 6 ++ .../camel/component/jms/JmsEndpoint.java | 39 +++++++++++++ .../org/apache/camel/language/jq/jq.json | 4 ++ .../apache/camel/language/jq/JqLanguage.java | 8 +++ .../org/apache/camel/jsonpath/jsonpath.json | 4 ++ .../camel/jsonpath/JsonPathLanguage.java | 12 ++++ .../apache/camel/component/kafka/kafka.json | 5 ++ .../camel/component/kafka/KafkaEndpoint.java | 18 ++++++ .../org/apache/camel/component/log/log.json | 4 ++ .../camel/component/log/LogEndpoint.java | 4 ++ .../org/apache/camel/component/mock/mock.json | 4 ++ .../camel/component/mock/MockEndpoint.java | 4 ++ .../platform/http/platform-http.json | 4 ++ .../platform/http/PlatformHttpEndpoint.java | 13 +++++ .../apache/camel/component/quartz/quartz.json | 3 + .../component/quartz/QuartzEndpoint.java | 4 ++ .../component/rest/openapi/rest-openapi.json | 2 + .../rest/openapi/RestOpenApiEndpoint.java | 12 +++- .../org/apache/camel/component/rest/rest.json | 6 ++ .../camel/component/rest/RestEndpoint.java | 24 ++++++++ .../camel/component/scheduler/scheduler.json | 3 + .../scheduler/SchedulerEndpoint.java | 4 ++ .../camel/component/snakeyaml/snakeYaml.json | 6 ++ .../snakeyaml/SnakeYAMLDataFormat.java | 10 ++++ .../apache/camel/language/xpath/xpath.json | 4 ++ .../camel/language/xpath/XPathLanguage.java | 16 ++++++ .../camel/language/constant/constant.json | 3 + .../apache/camel/language/simple/simple.json | 4 ++ .../language/constant/ConstantLanguage.java | 6 ++ .../camel/language/simple/SimpleLanguage.java | 7 +++ .../pages/camel-4x-upgrade-guide-4_24.adoc | 2 +- .../modules/ROOT/pages/camel-jbang-mcp.adoc | 7 ++- .../jbang/core/commands/ai/CatalogDocs.java | 56 ++++++++++++++++--- .../core/commands/ai/CatalogDocsTest.java | 28 ++++++---- 79 files changed, 710 insertions(+), 23 deletions(-) diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/activemq.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/activemq.json index e0b80d458e33e..486cc84e783d9 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/activemq.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/activemq.json @@ -160,6 +160,12 @@ "CamelJmsMessageType": { "index": 16, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "org.apache.camel.component.jms.JmsMessageType", "enum": [ "Bytes", "Map", "Object", "Stream", "Text", "Blob" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The message type.", "constantName": "org.apache.camel.component.jms.JmsConstants#JMS_MESSAGE_TYPE" }, "CamelJmsRequestTimeout": { "index": 17, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "20000", "description": "The timeout for waiting for a reply when using the InOut Exchange Pattern (in milliseconds).", "constantName": "org.apache.camel.component.jms.JmsConstants#JMS_REQUEST_TIMEOUT" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "text", "bytes", "map", "stream", "null" ], "dependsOn": [ "messageConverter", "transferExchange" ], "byOption": [ { "option": "mapJmsMessage", "value": "false", "carriers": [ "pojo" ], "javaType": "jakarta.jms.Message" }, { "option": "objectMessageEnabled", "value": "true", "carriers": [ "text", "bytes", "map", "stream", "pojo", "null" ] } ], "description": "By the JMS message received: a String for a TextMessage, a byte array for a BytesMessage, a Map for a MapMessage, a stream for a StreamMessage; an ObjectMessage only with objectMessageEnabled" }, + "consumerReply": { "carriers": [ "text", "bytes", "stream", "file", "map" ], "dependsOn": [ "jmsMessageType", "messageConverter", "transferExchange" ], "byOption": [ { "option": "objectMessageEnabled", "value": "true", "carriers": [ "text", "bytes", "stream", "file", "map", "pojo" ] } ], "description": "The reply, sent as the producer sends a message" }, + "producerIn": { "carriers": [ "text", "bytes", "stream", "file", "map" ], "dependsOn": [ "jmsMessageType", "messageConverter", "transferExchange" ], "byOption": [ { "option": "objectMessageEnabled", "value": "true", "carriers": [ "text", "bytes", "stream", "file", "map", "pojo" ] } ], "description": "Text is sent as a TextMessage; bytes, a stream or a file as a BytesMessage; a Map as a MapMessage. Another Serializable object fails unless objectMessageEnabled" }, + "producerOut": { "carriers": [ "unknown" ], "byOption": [ { "option": "exchangePattern", "value": "InOnly", "carriers": [ "unchanged" ] }, { "option": "disableReplyTo", "value": "true", "carriers": [ "unchanged" ] } ], "description": "Unchanged for a message that expects no reply (InOnly). A message that expects one (InOut) gets the body of the reply, as the consumer gives it" } + }, "properties": { "destinationType": { "index": 0, "kind": "path", "displayName": "Destination Type", "group": "common", "label": "", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "queue", "topic", "temp-queue", "temp-topic" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "queue", "description": "The kind of destination to use" }, "destinationName": { "index": 1, "kind": "path", "displayName": "Destination Name", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Name of the queue or topic to use as destination" }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/activemq6.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/activemq6.json index 99e6755d4daf3..a24c009644694 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/activemq6.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/activemq6.json @@ -160,6 +160,12 @@ "CamelJmsMessageType": { "index": 16, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "org.apache.camel.component.jms.JmsMessageType", "enum": [ "Bytes", "Map", "Object", "Stream", "Text", "Blob" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The message type.", "constantName": "org.apache.camel.component.jms.JmsConstants#JMS_MESSAGE_TYPE" }, "CamelJmsRequestTimeout": { "index": 17, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "20000", "description": "The timeout for waiting for a reply when using the InOut Exchange Pattern (in milliseconds).", "constantName": "org.apache.camel.component.jms.JmsConstants#JMS_REQUEST_TIMEOUT" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "text", "bytes", "map", "stream", "null" ], "dependsOn": [ "messageConverter", "transferExchange" ], "byOption": [ { "option": "mapJmsMessage", "value": "false", "carriers": [ "pojo" ], "javaType": "jakarta.jms.Message" }, { "option": "objectMessageEnabled", "value": "true", "carriers": [ "text", "bytes", "map", "stream", "pojo", "null" ] } ], "description": "By the JMS message received: a String for a TextMessage, a byte array for a BytesMessage, a Map for a MapMessage, a stream for a StreamMessage; an ObjectMessage only with objectMessageEnabled" }, + "consumerReply": { "carriers": [ "text", "bytes", "stream", "file", "map" ], "dependsOn": [ "jmsMessageType", "messageConverter", "transferExchange" ], "byOption": [ { "option": "objectMessageEnabled", "value": "true", "carriers": [ "text", "bytes", "stream", "file", "map", "pojo" ] } ], "description": "The reply, sent as the producer sends a message" }, + "producerIn": { "carriers": [ "text", "bytes", "stream", "file", "map" ], "dependsOn": [ "jmsMessageType", "messageConverter", "transferExchange" ], "byOption": [ { "option": "objectMessageEnabled", "value": "true", "carriers": [ "text", "bytes", "stream", "file", "map", "pojo" ] } ], "description": "Text is sent as a TextMessage; bytes, a stream or a file as a BytesMessage; a Map as a MapMessage. Another Serializable object fails unless objectMessageEnabled" }, + "producerOut": { "carriers": [ "unknown" ], "byOption": [ { "option": "exchangePattern", "value": "InOnly", "carriers": [ "unchanged" ] }, { "option": "disableReplyTo", "value": "true", "carriers": [ "unchanged" ] } ], "description": "Unchanged for a message that expects no reply (InOnly). A message that expects one (InOut) gets the body of the reply, as the consumer gives it" } + }, "properties": { "destinationType": { "index": 0, "kind": "path", "displayName": "Destination Type", "group": "common", "label": "", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "queue", "topic", "temp-queue", "temp-topic" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "queue", "description": "The kind of destination to use" }, "destinationName": { "index": 1, "kind": "path", "displayName": "Destination Name", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Name of the queue or topic to use as destination" }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/amqp.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/amqp.json index aa458bcd193d6..8557ccf657541 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/amqp.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/amqp.json @@ -166,6 +166,12 @@ "CamelJmsMessageType": { "index": 16, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "org.apache.camel.component.jms.JmsMessageType", "enum": [ "Bytes", "Map", "Object", "Stream", "Text", "Blob" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The message type.", "constantName": "org.apache.camel.component.jms.JmsConstants#JMS_MESSAGE_TYPE" }, "CamelJmsRequestTimeout": { "index": 17, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "20000", "description": "The timeout for waiting for a reply when using the InOut Exchange Pattern (in milliseconds).", "constantName": "org.apache.camel.component.jms.JmsConstants#JMS_REQUEST_TIMEOUT" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "text", "bytes", "map", "stream", "null" ], "dependsOn": [ "messageConverter", "transferExchange" ], "byOption": [ { "option": "mapJmsMessage", "value": "false", "carriers": [ "pojo" ], "javaType": "jakarta.jms.Message" }, { "option": "objectMessageEnabled", "value": "true", "carriers": [ "text", "bytes", "map", "stream", "pojo", "null" ] } ], "description": "By the JMS message received: a String for a TextMessage, a byte array for a BytesMessage, a Map for a MapMessage, a stream for a StreamMessage; an ObjectMessage only with objectMessageEnabled" }, + "consumerReply": { "carriers": [ "text", "bytes", "stream", "file", "map" ], "dependsOn": [ "jmsMessageType", "messageConverter", "transferExchange" ], "byOption": [ { "option": "objectMessageEnabled", "value": "true", "carriers": [ "text", "bytes", "stream", "file", "map", "pojo" ] } ], "description": "The reply, sent as the producer sends a message" }, + "producerIn": { "carriers": [ "text", "bytes", "stream", "file", "map" ], "dependsOn": [ "jmsMessageType", "messageConverter", "transferExchange" ], "byOption": [ { "option": "objectMessageEnabled", "value": "true", "carriers": [ "text", "bytes", "stream", "file", "map", "pojo" ] } ], "description": "Text is sent as a TextMessage; bytes, a stream or a file as a BytesMessage; a Map as a MapMessage. Another Serializable object fails unless objectMessageEnabled" }, + "producerOut": { "carriers": [ "unknown" ], "byOption": [ { "option": "exchangePattern", "value": "InOnly", "carriers": [ "unchanged" ] }, { "option": "disableReplyTo", "value": "true", "carriers": [ "unchanged" ] } ], "description": "Unchanged for a message that expects no reply (InOnly). A message that expects one (InOut) gets the body of the reply, as the consumer gives it" } + }, "properties": { "destinationType": { "index": 0, "kind": "path", "displayName": "Destination Type", "group": "common", "label": "", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "queue", "topic", "temp-queue", "temp-topic" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "queue", "description": "The kind of destination to use" }, "destinationName": { "index": 1, "kind": "path", "displayName": "Destination Name", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Name of the queue or topic to use as destination" }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/aws2-sqs.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/aws2-sqs.json index 71cc592888ed6..8ff172f1e1f3b 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/aws2-sqs.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/aws2-sqs.json @@ -93,6 +93,11 @@ "CamelAwsSqsFailedMessageCount": { "index": 12, "kind": "header", "displayName": "", "group": "sendBatchMessage", "label": "sendBatchMessage", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The number of failed messages in a batch send operation.", "constantName": "org.apache.camel.component.aws2.sqs.Sqs2Constants#FAILED_MESSAGE_COUNT" }, "CamelAwsSqsSuccessfulMessageCount": { "index": 13, "kind": "header", "displayName": "", "group": "sendBatchMessage", "label": "sendBatchMessage", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The number of successful messages in a batch send operation.", "constantName": "org.apache.camel.component.aws2.sqs.Sqs2Constants#SUCCESSFUL_MESSAGE_COUNT" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "text" ], "javaType": "java.lang.String", "description": "The body of the SQS message" }, + "producerIn": { "carriers": [ "text" ], "byOption": [ { "option": "operation", "value": "sendBatchMessage", "carriers": [ "list", "text" ] } ], "description": "The body of the message to send, as text, so a Map or a POJO is sent as its toString(): marshal it first. sendBatchMessage takes a List of text, or text that batchSeparator splits" }, + "producerOut": { "carriers": [ "unchanged" ], "byOption": [ { "option": "operation", "value": "sendBatchMessage", "carriers": [ "pojo" ], "javaType": "software.amazon.awssdk.services.sqs.model.SendMessageBatchResponse" }, { "option": "operation", "value": "deleteMessage", "carriers": [ "pojo" ], "javaType": "software.amazon.awssdk.services.sqs.model.DeleteMessageResponse" }, { "option": "operation", "value": "listQueues", "carriers": [ "pojo" ], "javaType": "software.amazon.awssdk.services.sqs.model.ListQueuesResponse" }, { "option": "operation", "value": "purgeQueue", "carriers": [ "pojo" ], "javaType": "software.amazon.awssdk.services.sqs.model.PurgeQueueResponse" }, { "option": "operation", "value": "deleteQueue", "carriers": [ "pojo" ], "javaType": "software.amazon.awssdk.services.sqs.model.DeleteQueueResponse" } ], "description": "Unchanged for a send, with the id of the message in a header; the response of the AWS SDK for the other operations" } + }, "properties": { "queueNameOrArn": { "index": 0, "kind": "path", "displayName": "Queue Name Or Arn", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Queue name or ARN" }, "amazonAWSHost": { "index": 1, "kind": "parameter", "displayName": "Amazon AWSHost", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "amazonaws.com", "configurationClass": "org.apache.camel.component.aws2.sqs.Sqs2Configuration", "configurationField": "configuration", "description": "The hostname of the Amazon AWS cloud." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ftp.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ftp.json index e56b6813c06bd..8d0a3be6738aa 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ftp.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ftp.json @@ -50,6 +50,11 @@ "CamelFtpReplyCode": { "index": 15, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "important": true, "description": "The FTP client reply code", "constantName": "org.apache.camel.component.file.remote.FtpConstants#FTP_REPLY_CODE" }, "CamelFtpReplyString": { "index": 16, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "important": true, "description": "The FTP client reply string", "constantName": "org.apache.camel.component.file.remote.FtpConstants#FTP_REPLY_STRING" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "file" ], "dependsOn": [ "preMove" ], "byOption": [ { "option": "streamDownload", "value": "true", "carriers": [ "stream" ] }, { "option": "download", "value": "false", "carriers": [ "null" ] } ], "description": "The file as read: a RemoteFile, which converts to text, bytes or a stream; a stream with streamDownload; with preMove its content, a byte array" }, + "producerIn": { "carriers": [ "text", "bytes", "stream", "file" ], "byOption": [ { "option": "allowNullBody", "value": "true", "carriers": [ "null", "text", "bytes", "stream", "file" ] } ], "description": "What converts to an InputStream: text, bytes, a stream or a file. A Map or a List fails: marshal it first" }, + "producerOut": { "carriers": [ "unchanged" ] } + }, "properties": { "host": { "index": 0, "kind": "path", "displayName": "Host", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.file.remote.FtpConfiguration", "configurationField": "configuration", "description": "Hostname of the FTP server" }, "port": { "index": 1, "kind": "path", "displayName": "Port", "group": "common", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.file.remote.FtpConfiguration", "configurationField": "configuration", "description": "Port of the FTP server" }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ftps.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ftps.json index 075f91d010d24..ba1fade434f6b 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ftps.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ftps.json @@ -51,6 +51,11 @@ "CamelFtpReplyCode": { "index": 15, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "important": true, "description": "The FTP client reply code", "constantName": "org.apache.camel.component.file.remote.FtpConstants#FTP_REPLY_CODE" }, "CamelFtpReplyString": { "index": 16, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "important": true, "description": "The FTP client reply string", "constantName": "org.apache.camel.component.file.remote.FtpConstants#FTP_REPLY_STRING" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "file" ], "dependsOn": [ "preMove" ], "byOption": [ { "option": "streamDownload", "value": "true", "carriers": [ "stream" ] }, { "option": "download", "value": "false", "carriers": [ "null" ] } ], "description": "The file as read: a RemoteFile, which converts to text, bytes or a stream; a stream with streamDownload; with preMove its content, a byte array" }, + "producerIn": { "carriers": [ "text", "bytes", "stream", "file" ], "byOption": [ { "option": "allowNullBody", "value": "true", "carriers": [ "null", "text", "bytes", "stream", "file" ] } ], "description": "What converts to an InputStream: text, bytes, a stream or a file. A Map or a List fails: marshal it first" }, + "producerOut": { "carriers": [ "unchanged" ] } + }, "properties": { "host": { "index": 0, "kind": "path", "displayName": "Host", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.file.remote.FtpsConfiguration", "configurationField": "configuration", "description": "Hostname of the FTP server" }, "port": { "index": 1, "kind": "path", "displayName": "Port", "group": "common", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.file.remote.FtpsConfiguration", "configurationField": "configuration", "description": "Port of the FTP server" }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/jms.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/jms.json index 7307e4f146f23..43327061d9863 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/jms.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/jms.json @@ -154,6 +154,12 @@ "CamelJmsMessageType": { "index": 16, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "org.apache.camel.component.jms.JmsMessageType", "enum": [ "Bytes", "Map", "Object", "Stream", "Text", "Blob" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The message type.", "constantName": "org.apache.camel.component.jms.JmsConstants#JMS_MESSAGE_TYPE" }, "CamelJmsRequestTimeout": { "index": 17, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "20000", "description": "The timeout for waiting for a reply when using the InOut Exchange Pattern (in milliseconds).", "constantName": "org.apache.camel.component.jms.JmsConstants#JMS_REQUEST_TIMEOUT" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "text", "bytes", "map", "stream", "null" ], "dependsOn": [ "messageConverter", "transferExchange" ], "byOption": [ { "option": "mapJmsMessage", "value": "false", "carriers": [ "pojo" ], "javaType": "jakarta.jms.Message" }, { "option": "objectMessageEnabled", "value": "true", "carriers": [ "text", "bytes", "map", "stream", "pojo", "null" ] } ], "description": "By the JMS message received: a String for a TextMessage, a byte array for a BytesMessage, a Map for a MapMessage, a stream for a StreamMessage; an ObjectMessage only with objectMessageEnabled" }, + "consumerReply": { "carriers": [ "text", "bytes", "stream", "file", "map" ], "dependsOn": [ "jmsMessageType", "messageConverter", "transferExchange" ], "byOption": [ { "option": "objectMessageEnabled", "value": "true", "carriers": [ "text", "bytes", "stream", "file", "map", "pojo" ] } ], "description": "The reply, sent as the producer sends a message" }, + "producerIn": { "carriers": [ "text", "bytes", "stream", "file", "map" ], "dependsOn": [ "jmsMessageType", "messageConverter", "transferExchange" ], "byOption": [ { "option": "objectMessageEnabled", "value": "true", "carriers": [ "text", "bytes", "stream", "file", "map", "pojo" ] } ], "description": "Text is sent as a TextMessage; bytes, a stream or a file as a BytesMessage; a Map as a MapMessage. Another Serializable object fails unless objectMessageEnabled" }, + "producerOut": { "carriers": [ "unknown" ], "byOption": [ { "option": "exchangePattern", "value": "InOnly", "carriers": [ "unchanged" ] }, { "option": "disableReplyTo", "value": "true", "carriers": [ "unchanged" ] } ], "description": "Unchanged for a message that expects no reply (InOnly). A message that expects one (InOut) gets the body of the reply, as the consumer gives it" } + }, "properties": { "destinationType": { "index": 0, "kind": "path", "displayName": "Destination Type", "group": "common", "label": "", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "queue", "topic", "temp-queue", "temp-topic" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "queue", "description": "The kind of destination to use" }, "destinationName": { "index": 1, "kind": "path", "displayName": "Destination Name", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Name of the queue or topic to use as destination" }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/kafka.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/kafka.json index f40e398b911ba..d1dafe51f2f12 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/kafka.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/kafka.json @@ -169,6 +169,11 @@ "CamelKafkaRecordMeta": { "index": 11, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "List", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The metadata (only configured if recordMetadata endpoint parameter is true)", "constantName": "org.apache.camel.component.kafka.KafkaConstants#KAFKA_RECORD_META" }, "CamelKafkaManualCommit": { "index": 12, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "org.apache.camel.component.kafka.consumer.KafkaManualCommit", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Can be used for forcing manual offset commit when using Kafka consumer.", "constantName": "org.apache.camel.component.kafka.KafkaConstants#MANUAL_COMMIT" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "text", "null" ], "dependsOn": [ "valueDeserializer" ], "byOption": [ { "option": "valueDeserializer", "value": "org.apache.kafka.common.serialization.ByteArrayDeserializer", "carriers": [ "bytes", "null" ], "javaType": "byte[]" }, { "option": "batching", "value": "true", "carriers": [ "list" ] } ], "description": "The value of the record: a String with the default valueDeserializer (StringDeserializer), null for a tombstone. With batching, a List of the exchanges of the records" }, + "producerIn": { "carriers": [ "any" ], "dependsOn": [ "valueSerializer" ], "description": "The value of the record, converted for the valueSerializer: text with the default StringSerializer, so a Map or a POJO is sent as its toString(): marshal it first. With useIterator, an Iterable or an Iterator is sent as one record per element" }, + "producerOut": { "carriers": [ "unchanged" ] } + }, "properties": { "topic": { "index": 0, "kind": "path", "displayName": "Topic", "group": "common", "label": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "Name of the topic to use. On the consumer you can use comma to separate multiple topics. A producer can only send a message to a single topic." }, "additionalProperties": { "index": 1, "kind": "parameter", "displayName": "Additional Properties", "group": "common", "label": "common", "required": false, "type": "object", "javaType": "java.util.Map", "prefix": "additionalProperties.", "multiValue": true, "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "Sets additional properties for either kafka consumer or kafka producer in case they can't be set directly on the camel configurations (e.g.: new Kafka properties that are not reflected yet in Camel configurations), the properties have to be prefixed with additionalProperties., e.g.: additionalProperties.transactional.id=12345&additionalProperties.schema.registry.url=http:\/\/localhost:8811\/avro. If the properties are set in the application.properties file, they must be prefixed with camel.component.kafka.additional-properties (camel.component.kafka-share.additional-properties for the Kafka Share component) followed by the property name enclosed in square brackets, for example the delivery.timeout.ms property in square brackets. This is a multi-value option with prefix: additionalProperties." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/log.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/log.json index 1780d6316af2e..bf19483c839cb 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/log.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/log.json @@ -30,6 +30,10 @@ "autowiredEnabled": { "index": 2, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which then gets configured on the component. This can be used for automatic configuring JDBC data sources, JMS connection factories, AWS Clients, etc." }, "exchangeFormatter": { "index": 3, "kind": "property", "displayName": "Exchange Formatter", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.ExchangeFormatter", "deprecated": false, "autowired": true, "secret": false, "description": "Sets a custom ExchangeFormatter to convert the Exchange to a String suitable for logging. If not specified, we default to DefaultExchangeFormatter." } }, + "bodyTypes": { + "producerIn": { "carriers": [ "any" ] }, + "producerOut": { "carriers": [ "unchanged" ] } + }, "properties": { "loggerName": { "index": 0, "kind": "path", "displayName": "Logger Name", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Name of the logging category to use" }, "groupActiveOnly": { "index": 1, "kind": "parameter", "displayName": "Group Active Only", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "If true, will hide stats when no new messages have been received for a time interval, if false, show stats regardless of message traffic." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/mock.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/mock.json index eadcc424ddeb2..4982ad8453e7f 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/mock.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/mock.json @@ -30,6 +30,10 @@ "autowiredEnabled": { "index": 2, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which then gets configured on the component. This can be used for automatic configuring JDBC data sources, JMS connection factories, AWS Clients, etc." }, "exchangeFormatter": { "index": 3, "kind": "property", "displayName": "Exchange Formatter", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.ExchangeFormatter", "deprecated": false, "autowired": true, "secret": false, "description": "To use a custom ExchangeFormatter to format the Exchange into a String suitable for logging." } }, + "bodyTypes": { + "producerIn": { "carriers": [ "any" ] }, + "producerOut": { "carriers": [ "unchanged" ] } + }, "properties": { "name": { "index": 0, "kind": "path", "displayName": "Name", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Name of mock endpoint" }, "assertPeriod": { "index": 1, "kind": "parameter", "displayName": "Assert Period", "group": "producer", "label": "producer", "required": false, "type": "duration", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "description": "Sets a grace period after which the mock will re-assert to ensure the preliminary assertion is still valid. This is used, for example, to assert that exactly a number of messages arrive. For example, if the expected count was set to 5, then the assertion is satisfied when five or more messages arrive. To ensure that exactly 5 messages arrive, then you would need to wait a little period to ensure no further message arrives. This is what you can use this method for. By default, this period is disabled." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/platform-http.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/platform-http.json index e60f0367259fb..3c00abc0f66aa 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/platform-http.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/platform-http.json @@ -33,6 +33,10 @@ "engine": { "index": 5, "kind": "property", "displayName": "Engine", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.component.platform.http.spi.PlatformHttpEngine", "deprecated": false, "autowired": false, "secret": false, "description": "An HTTP Server engine implementation to serve the requests" }, "headerFilterStrategy": { "index": 6, "kind": "property", "displayName": "Header Filter Strategy", "group": "filter", "label": "filter", "required": false, "type": "object", "javaType": "org.apache.camel.spi.HeaderFilterStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom org.apache.camel.spi.HeaderFilterStrategy to filter header to and from Camel message." } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "stream", "bytes", "null", "map", "file" ], "byOption": [ { "option": "useBodyHandler", "value": "false", "carriers": [ "pojo" ] } ], "description": "The request body: the stream cache with the Vert.x engine, a byte array with Spring Boot; null for a request without a body, such as a GET; a Map of the fields of a form (populateBodyWithForm); a File for an upload" }, + "consumerReply": { "carriers": [ "null", "text", "bytes", "stream" ], "description": "The response body: text, bytes, a stream or no body. A Map or a List fails: marshal it first" } + }, "properties": { "path": { "index": 0, "kind": "path", "displayName": "Path", "group": "consumer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The path under which this endpoint serves the HTTP requests, for proxy use 'proxy'" }, "consumes": { "index": 1, "kind": "parameter", "displayName": "Consumes", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The content type this endpoint accepts as an input, such as application\/xml or application\/json. null or *\/* mean no restriction." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/quartz.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/quartz.json index d56eb70d9f853..8b8cc7acc2acb 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/quartz.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/quartz.json @@ -37,6 +37,9 @@ "autoStartScheduler": { "index": 10, "kind": "property", "displayName": "Auto Start Scheduler", "group": "scheduler", "label": "scheduler", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether the scheduler should be auto started. This option is default true" }, "interruptJobsOnShutdown": { "index": 11, "kind": "property", "displayName": "Interrupt Jobs On Shutdown", "group": "scheduler", "label": "scheduler", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to interrupt jobs on shutdown, which forces the scheduler to shut down quicker and attempt to interrupt any running jobs. If this is enabled, then any running jobs can fail due to being interrupted. When a job is interrupted then Camel will mark the exchange to stop to continue routing and set java.util.concurrent.RejectedExecutionException as caused exception. Therefore, use this with care, as its often better to allow Camel jobs to complete and shutdown gracefully." } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "null" ], "description": "No body: the message only starts the route" } + }, "properties": { "groupName": { "index": 0, "kind": "path", "displayName": "Group Name", "group": "consumer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "Camel", "description": "The quartz group name to use. The combination of group name and trigger name should be unique." }, "triggerName": { "index": 1, "kind": "path", "displayName": "Trigger Name", "group": "consumer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The quartz trigger name to use. The combination of group name and trigger name should be unique." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/rest-openapi.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/rest-openapi.json index 22417ab779dfc..f175ff957c19b 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/rest-openapi.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/rest-openapi.json @@ -47,6 +47,8 @@ "useGlobalSslContextParameters": { "index": 20, "kind": "property", "displayName": "Use Global Ssl Context Parameters", "group": "security", "label": "security", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Enable usage of global SSL context parameters." } }, "bodyTypes": { + "consumerOut": { "carriers": [ "stream", "bytes", "null", "map", "file" ], "dependsOn": [ "consumerComponentName" ], "description": "The request body as the HTTP consumer gives it (platform-http by default). With the json binding mode of the rest configuration, the object of the schema (bindingPackageScan), or a Map or a List" }, + "consumerReply": { "carriers": [ "null", "text", "bytes", "stream" ], "dependsOn": [ "consumerComponentName" ], "description": "The response body as the HTTP consumer writes it. With the json binding mode of the rest configuration, any object, which the binding writes as JSON" }, "producerIn": { "carriers": [ "null", "text", "bytes", "stream", "file" ], "dependsOn": [ "componentName" ], "description": "The request body of the operation, which the http component sends: text, bytes, a stream or a file. A Map or a List fails: marshal it first" }, "producerOut": { "carriers": [ "bytes", "stream", "null" ], "dependsOn": [ "componentName" ], "description": "The response body, as the http component gives it: a byte array or the stream cache; null for a response without a body" } }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/rest.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/rest.json index 041ff7467c2e6..8ad7a7dbc0771 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/rest.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/rest.json @@ -43,6 +43,12 @@ "Accept": { "index": 4, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The media type such as: 'text\/xml', or 'application\/json' this REST service accepts.", "constantName": "org.apache.camel.component.rest.RestConstants#ACCEPT" }, "CamelHttpResponseCode": { "index": 5, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "important": true, "description": "The http response code", "constantName": "org.apache.camel.component.rest.RestConstants#HTTP_RESPONSE_CODE" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "stream", "bytes", "null", "map", "file" ], "dependsOn": [ "consumerComponentName" ], "byOption": [ { "option": "bindingMode", "value": "json", "carriers": [ "map", "list", "pojo" ] } ], "description": "The request body as the HTTP consumer gives it (platform-http by default). With bindingMode json, the object of inType, or a Map or a List without inType" }, + "consumerReply": { "carriers": [ "null", "text", "bytes", "stream" ], "dependsOn": [ "consumerComponentName" ], "byOption": [ { "option": "bindingMode", "value": "json", "carriers": [ "any" ] } ], "description": "The response body as the HTTP consumer writes it. With bindingMode json, any object, which the binding writes as JSON" }, + "producerIn": { "carriers": [ "null", "text", "bytes", "stream", "file" ], "dependsOn": [ "producerComponentName" ], "byOption": [ { "option": "bindingMode", "value": "json", "carriers": [ "any" ] } ], "description": "The request body the HTTP producer sends (the http component by default). With bindingMode json, any object, which the binding writes as JSON" }, + "producerOut": { "carriers": [ "bytes", "stream", "null" ], "dependsOn": [ "producerComponentName", "outType" ], "description": "The response body as the HTTP producer gives it; with bindingMode json and outType, an object of outType" } + }, "properties": { "method": { "index": 0, "kind": "path", "displayName": "Method", "group": "common", "label": "common", "required": true, "type": "enum", "javaType": "java.lang.String", "enum": [ "get", "post", "put", "delete", "patch", "head", "trace", "connect", "options" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "HTTP method to use." }, "path": { "index": 1, "kind": "path", "displayName": "Path", "group": "common", "label": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The base path, can use * as path suffix to support wildcard HTTP route matching." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/scheduler.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/scheduler.json index 491fc0b24d373..83c5065eeca03 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/scheduler.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/scheduler.json @@ -34,6 +34,9 @@ "headers": { "CamelMessageTimestamp": { "index": 0, "kind": "header", "displayName": "", "group": "consumer", "label": "", "required": false, "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The timestamp of the message", "constantName": "org.apache.camel.component.scheduler.SchedulerConstants#MESSAGE_TIMESTAMP" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "null" ], "description": "No body: the message only starts the route" } + }, "properties": { "name": { "index": 0, "kind": "path", "displayName": "Name", "group": "consumer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The name of the scheduler" }, "includeMetadata": { "index": 1, "kind": "parameter", "displayName": "Include Metadata", "group": "consumer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to include metadata in the exchange such as fired time, timer name, timer count etc." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/sftp.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/sftp.json index 11096138d4192..a0471a76cb2f0 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/sftp.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/sftp.json @@ -55,6 +55,11 @@ "CamelFtpReplyCode": { "index": 15, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "important": true, "description": "The FTP client reply code", "constantName": "org.apache.camel.component.file.remote.FtpConstants#FTP_REPLY_CODE" }, "CamelFtpReplyString": { "index": 16, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "important": true, "description": "The FTP client reply string", "constantName": "org.apache.camel.component.file.remote.FtpConstants#FTP_REPLY_STRING" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "file" ], "dependsOn": [ "preMove" ], "byOption": [ { "option": "streamDownload", "value": "true", "carriers": [ "stream" ] }, { "option": "download", "value": "false", "carriers": [ "null" ] } ], "description": "The file as read: a RemoteFile, which converts to text, bytes or a stream; a stream with streamDownload; with preMove its content, a byte array" }, + "producerIn": { "carriers": [ "text", "bytes", "stream", "file" ], "byOption": [ { "option": "allowNullBody", "value": "true", "carriers": [ "null", "text", "bytes", "stream", "file" ] } ], "description": "What converts to an InputStream: text, bytes, a stream or a file. A Map or a List fails: marshal it first" }, + "producerOut": { "carriers": [ "unchanged" ] } + }, "properties": { "host": { "index": 0, "kind": "path", "displayName": "Host", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.file.remote.SftpConfiguration", "configurationField": "configuration", "description": "Hostname of the FTP server" }, "port": { "index": 1, "kind": "path", "displayName": "Port", "group": "common", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.file.remote.SftpConfiguration", "configurationField": "configuration", "description": "Port of the FTP server" }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/csv.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/csv.json index c1044cee0dd0c..6e696b52703e8 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/csv.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/csv.json @@ -15,6 +15,12 @@ "modelName": "csv", "modelJavaType": "org.apache.camel.model.dataformat.CsvDataFormat" }, + "bodyTypes": { + "marshalIn": { "carriers": [ "map", "list" ], "description": "One row as a Map, or the rows as a List (or an Iterator) of Maps or Lists" }, + "marshalOut": { "carriers": [ "stream" ], "format": "csv", "description": "The CSV in the stream cache, or a byte array when stream caching is off" }, + "unmarshalIn": { "carriers": [ "text", "bytes", "stream", "file" ], "format": "csv" }, + "unmarshalOut": { "carriers": [ "list" ], "dependsOn": [ "recordConverterRef" ], "byOption": [ { "option": "lazyLoad", "value": "true", "carriers": [ "list" ], "javaType": "java.util.Iterator" } ], "description": "A List of the rows, each a List of its values; with useMaps or useOrderedMaps, a Map of the header names and the values" } + }, "properties": { "id": { "index": 0, "kind": "attribute", "displayName": "Id", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The id of this node" }, "format": { "index": 1, "kind": "attribute", "displayName": "Format", "group": "common", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "DEFAULT", "EXCEL", "INFORMIX_UNLOAD", "INFORMIX_UNLOAD_CSV", "MONGODB_CSV", "MONGODB_TSV", "MYSQL", "ORACLE", "POSTGRESQL_CSV", "POSTGRESQL_TEXT", "RFC4180" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "DEFAULT", "description": "The format to use." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/jacksonXml.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/jacksonXml.json index 1eca4610e6271..e01c38e3002ee 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/jacksonXml.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/jacksonXml.json @@ -15,6 +15,12 @@ "modelName": "jacksonXml", "modelJavaType": "org.apache.camel.model.dataformat.JacksonXMLDataFormat" }, + "bodyTypes": { + "marshalIn": { "carriers": [ "any" ], "description": "A Map, a List or a POJO to write as XML. Text is written as an XML value, not as it is" }, + "marshalOut": { "carriers": [ "stream" ], "format": "xml", "description": "The XML in the stream cache, or a byte array when stream caching is off" }, + "unmarshalIn": { "carriers": [ "text", "bytes", "stream", "file" ], "format": "xml" }, + "unmarshalOut": { "carriers": [ "map" ], "dependsOn": [ "unmarshalType", "collectionType", "allowUnmarshallType" ], "byOption": [ { "option": "useList", "value": "true", "carriers": [ "list" ] } ], "description": "A Map (LinkedHashMap) of the XML; an object of the class unmarshalType names" } + }, "properties": { "id": { "index": 0, "kind": "attribute", "displayName": "Id", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The id of this node" }, "xmlMapper": { "index": 1, "kind": "attribute", "displayName": "Xml Mapper", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Lookup and use the existing XmlMapper with the given id." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/snakeYaml.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/snakeYaml.json index cc69504fdca03..a24fa2186d37c 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/snakeYaml.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/snakeYaml.json @@ -15,6 +15,12 @@ "modelName": "yaml", "modelJavaType": "org.apache.camel.model.dataformat.YAMLDataFormat" }, + "bodyTypes": { + "marshalIn": { "carriers": [ "any" ], "description": "A Map, a List or a POJO to write as YAML" }, + "marshalOut": { "carriers": [ "stream" ], "format": "yaml", "description": "The YAML in the stream cache, or a byte array when stream caching is off" }, + "unmarshalIn": { "carriers": [ "text", "bytes", "stream", "file" ], "format": "yaml" }, + "unmarshalOut": { "carriers": [ "map", "list" ], "dependsOn": [ "unmarshalType" ], "description": "A Map (LinkedHashMap) for a mapping, a List (ArrayList) for a sequence; a document that is a single value gives that value" } + }, "properties": { "id": { "index": 0, "kind": "attribute", "displayName": "Id", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The id of this node" }, "library": { "index": 1, "kind": "attribute", "displayName": "Library", "group": "common", "required": false, "type": "enum", "javaType": "org.apache.camel.model.dataformat.YAMLLibrary", "enum": [ "SnakeYAML" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "SnakeYAML", "description": "Which yaml library to use. By default it is SnakeYAML." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/constant.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/constant.json index e1c1c40cb2ea8..9889e6d5b957e 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/constant.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/constant.json @@ -15,6 +15,9 @@ "modelName": "constant", "modelJavaType": "org.apache.camel.model.language.ConstantExpression" }, + "bodyTypes": { + "expressionOut": { "carriers": [ "text" ], "javaType": "java.lang.String", "dependsOn": [ "resultType" ], "description": "The text as it is written; resource:classpath:name loads the text of the resource" } + }, "properties": { "id": { "index": 0, "kind": "attribute", "displayName": "Id", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The id of this node." }, "expression": { "index": 1, "kind": "value", "displayName": "Expression", "group": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The expression value in your chosen language syntax." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/groovy.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/groovy.json index db276ac056830..fd58e1de9825c 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/groovy.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/groovy.json @@ -15,6 +15,10 @@ "modelName": "groovy", "modelJavaType": "org.apache.camel.model.language.GroovyExpression" }, + "bodyTypes": { + "expressionIn": { "carriers": [ "any" ], "description": "The body as it is, in the variable body" }, + "expressionOut": { "carriers": [ "unknown" ], "description": "What the script returns" } + }, "properties": { "id": { "index": 0, "kind": "attribute", "displayName": "Id", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The id of this node." }, "expression": { "index": 1, "kind": "value", "displayName": "Expression", "group": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The expression value in your chosen language syntax." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/jq.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/jq.json index 8d658803476a5..781f11d2f1794 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/jq.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/jq.json @@ -15,6 +15,10 @@ "modelName": "jq", "modelJavaType": "org.apache.camel.model.language.JqExpression" }, + "bodyTypes": { + "expressionIn": { "carriers": [ "text", "bytes", "stream", "file", "map" ], "format": "json", "description": "JSON as text, bytes, a stream or a file, or a Map" }, + "expressionOut": { "carriers": [ "pojo", "list", "null" ], "dependsOn": [ "resultType" ], "description": "A JsonNode; a List of JsonNode when the filter gives several, null when it gives none" } + }, "properties": { "id": { "index": 0, "kind": "attribute", "displayName": "Id", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The id of this node." }, "expression": { "index": 1, "kind": "value", "displayName": "Expression", "group": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The expression value in your chosen language syntax." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/jsonpath.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/jsonpath.json index af55d48104eaa..e3aeffd210b31 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/jsonpath.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/jsonpath.json @@ -15,6 +15,10 @@ "modelName": "jsonpath", "modelJavaType": "org.apache.camel.model.language.JsonPathExpression" }, + "bodyTypes": { + "expressionIn": { "carriers": [ "any" ], "description": "JSON as text, bytes, a stream or a file, or parsed: a Map or a List. A POJO is read through Jackson" }, + "expressionOut": { "carriers": [ "unknown" ], "byOption": [ { "option": "writeAsString", "value": "true", "carriers": [ "text", "list" ] } ], "description": "The value the path selects: a Map for an object, a List for an array or for a path with a wildcard or a filter, otherwise text, a number or a boolean" } + }, "properties": { "id": { "index": 0, "kind": "attribute", "displayName": "Id", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The id of this node." }, "expression": { "index": 1, "kind": "value", "displayName": "Expression", "group": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The expression value in your chosen language syntax." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/simple.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/simple.json index 6de66be485c43..5648229337930 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/simple.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/simple.json @@ -15,6 +15,10 @@ "modelName": "simple", "modelJavaType": "org.apache.camel.model.language.SimpleExpression" }, + "bodyTypes": { + "expressionIn": { "carriers": [ "any" ] }, + "expressionOut": { "carriers": [ "unknown" ], "description": "A text with functions gives text. A single function, such as ${body} or ${header.foo}, gives its value as it is" } + }, "properties": { "id": { "index": 0, "kind": "attribute", "displayName": "Id", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The id of this node." }, "expression": { "index": 1, "kind": "value", "displayName": "Expression", "group": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The expression value in your chosen language syntax." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/xpath.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/xpath.json index 0a6a706898992..e7e50b9357a1e 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/xpath.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/xpath.json @@ -15,6 +15,10 @@ "modelName": "xpath", "modelJavaType": "org.apache.camel.model.language.XPathExpression" }, + "bodyTypes": { + "expressionIn": { "carriers": [ "text", "bytes", "stream", "file", "pojo" ], "format": "xml", "description": "XML as text, bytes, a stream or a file, or a DOM" }, + "expressionOut": { "carriers": [ "pojo" ], "javaType": "org.w3c.dom.NodeList", "dependsOn": [ "resultType" ], "byOption": [ { "option": "resultQName", "value": "STRING", "carriers": [ "text" ], "javaType": "java.lang.String" }, { "option": "resultQName", "value": "NUMBER", "carriers": [ "value" ], "javaType": "java.lang.Double" }, { "option": "resultQName", "value": "BOOLEAN", "carriers": [ "value" ], "javaType": "java.lang.Boolean" }, { "option": "resultQName", "value": "NODE", "carriers": [ "pojo" ], "javaType": "org.w3c.dom.Node" } ], "description": "The nodes the expression selects" } + }, "properties": { "id": { "index": 0, "kind": "attribute", "displayName": "Id", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The id of this node." }, "expression": { "index": 1, "kind": "value", "displayName": "Expression", "group": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The expression value in your chosen language syntax." }, diff --git a/components/camel-activemq/src/generated/resources/META-INF/org/apache/camel/component/activemq/activemq.json b/components/camel-activemq/src/generated/resources/META-INF/org/apache/camel/component/activemq/activemq.json index e0b80d458e33e..486cc84e783d9 100644 --- a/components/camel-activemq/src/generated/resources/META-INF/org/apache/camel/component/activemq/activemq.json +++ b/components/camel-activemq/src/generated/resources/META-INF/org/apache/camel/component/activemq/activemq.json @@ -160,6 +160,12 @@ "CamelJmsMessageType": { "index": 16, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "org.apache.camel.component.jms.JmsMessageType", "enum": [ "Bytes", "Map", "Object", "Stream", "Text", "Blob" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The message type.", "constantName": "org.apache.camel.component.jms.JmsConstants#JMS_MESSAGE_TYPE" }, "CamelJmsRequestTimeout": { "index": 17, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "20000", "description": "The timeout for waiting for a reply when using the InOut Exchange Pattern (in milliseconds).", "constantName": "org.apache.camel.component.jms.JmsConstants#JMS_REQUEST_TIMEOUT" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "text", "bytes", "map", "stream", "null" ], "dependsOn": [ "messageConverter", "transferExchange" ], "byOption": [ { "option": "mapJmsMessage", "value": "false", "carriers": [ "pojo" ], "javaType": "jakarta.jms.Message" }, { "option": "objectMessageEnabled", "value": "true", "carriers": [ "text", "bytes", "map", "stream", "pojo", "null" ] } ], "description": "By the JMS message received: a String for a TextMessage, a byte array for a BytesMessage, a Map for a MapMessage, a stream for a StreamMessage; an ObjectMessage only with objectMessageEnabled" }, + "consumerReply": { "carriers": [ "text", "bytes", "stream", "file", "map" ], "dependsOn": [ "jmsMessageType", "messageConverter", "transferExchange" ], "byOption": [ { "option": "objectMessageEnabled", "value": "true", "carriers": [ "text", "bytes", "stream", "file", "map", "pojo" ] } ], "description": "The reply, sent as the producer sends a message" }, + "producerIn": { "carriers": [ "text", "bytes", "stream", "file", "map" ], "dependsOn": [ "jmsMessageType", "messageConverter", "transferExchange" ], "byOption": [ { "option": "objectMessageEnabled", "value": "true", "carriers": [ "text", "bytes", "stream", "file", "map", "pojo" ] } ], "description": "Text is sent as a TextMessage; bytes, a stream or a file as a BytesMessage; a Map as a MapMessage. Another Serializable object fails unless objectMessageEnabled" }, + "producerOut": { "carriers": [ "unknown" ], "byOption": [ { "option": "exchangePattern", "value": "InOnly", "carriers": [ "unchanged" ] }, { "option": "disableReplyTo", "value": "true", "carriers": [ "unchanged" ] } ], "description": "Unchanged for a message that expects no reply (InOnly). A message that expects one (InOut) gets the body of the reply, as the consumer gives it" } + }, "properties": { "destinationType": { "index": 0, "kind": "path", "displayName": "Destination Type", "group": "common", "label": "", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "queue", "topic", "temp-queue", "temp-topic" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "queue", "description": "The kind of destination to use" }, "destinationName": { "index": 1, "kind": "path", "displayName": "Destination Name", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Name of the queue or topic to use as destination" }, diff --git a/components/camel-activemq/src/main/java/org/apache/camel/component/activemq/ActiveMQEndpoint.java b/components/camel-activemq/src/main/java/org/apache/camel/component/activemq/ActiveMQEndpoint.java index c2c8432f5ff0f..1e0ca6e7876f6 100644 --- a/components/camel-activemq/src/main/java/org/apache/camel/component/activemq/ActiveMQEndpoint.java +++ b/components/camel-activemq/src/main/java/org/apache/camel/component/activemq/ActiveMQEndpoint.java @@ -24,6 +24,9 @@ import org.apache.camel.component.jms.JmsComponent; import org.apache.camel.component.jms.JmsConfiguration; import org.apache.camel.component.jms.JmsEndpoint; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.EndpointServiceLocation; import org.apache.camel.spi.Metadata; import org.apache.camel.spi.UriEndpoint; @@ -36,6 +39,42 @@ @UriEndpoint(firstVersion = "1.0.0", extendsScheme = "jms", scheme = "activemq", title = "ActiveMQ 5.x", syntax = "activemq:destinationType:destinationName", category = { Category.MESSAGING }) +@BodyTypes(consumerOut = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.MAP, Carrier.STREAM, Carrier.NULL }, + dependsOn = { "messageConverter", "transferExchange" }, + byOption = { + @BodyType.When(option = "mapJmsMessage", value = "false", carrier = Carrier.POJO, + javaType = "jakarta.jms.Message"), + @BodyType.When(option = "objectMessageEnabled", value = "true", + carrier = { + Carrier.TEXT, Carrier.BYTES, Carrier.MAP, + Carrier.STREAM, Carrier.POJO, Carrier.NULL }) }, + description = "By the JMS message received: a String for a TextMessage, a byte array for a" + + " BytesMessage, a Map for a MapMessage, a stream for a StreamMessage;" + + " an ObjectMessage only with objectMessageEnabled"), + consumerReply = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, Carrier.MAP }, + dependsOn = { "jmsMessageType", "messageConverter", "transferExchange" }, + byOption = @BodyType.When(option = "objectMessageEnabled", value = "true", + carrier = { + Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, + Carrier.FILE, Carrier.MAP, Carrier.POJO }), + description = "The reply, sent as the producer sends a message"), + producerIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, Carrier.MAP }, + dependsOn = { "jmsMessageType", "messageConverter", "transferExchange" }, + byOption = @BodyType.When(option = "objectMessageEnabled", value = "true", + carrier = { + Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, + Carrier.FILE, Carrier.MAP, Carrier.POJO }), + description = "Text is sent as a TextMessage; bytes, a stream or a file as a BytesMessage;" + + " a Map as a MapMessage. Another Serializable object fails unless" + + " objectMessageEnabled"), + producerOut = @BodyType(value = Carrier.UNKNOWN, + byOption = { + @BodyType.When(option = "exchangePattern", value = "InOnly", + carrier = Carrier.UNCHANGED), + @BodyType.When(option = "disableReplyTo", value = "true", + carrier = Carrier.UNCHANGED) }, + description = "Unchanged for a message that expects no reply (InOnly). A message that" + + " expects one (InOut) gets the body of the reply, as the consumer gives it")) public class ActiveMQEndpoint extends JmsEndpoint implements EndpointServiceLocation { @UriParam(multiValue = true, prefix = "destination.", label = "consumer,advanced") diff --git a/components/camel-activemq6/src/generated/resources/META-INF/org/apache/camel/component/activemq6/activemq6.json b/components/camel-activemq6/src/generated/resources/META-INF/org/apache/camel/component/activemq6/activemq6.json index 99e6755d4daf3..a24c009644694 100644 --- a/components/camel-activemq6/src/generated/resources/META-INF/org/apache/camel/component/activemq6/activemq6.json +++ b/components/camel-activemq6/src/generated/resources/META-INF/org/apache/camel/component/activemq6/activemq6.json @@ -160,6 +160,12 @@ "CamelJmsMessageType": { "index": 16, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "org.apache.camel.component.jms.JmsMessageType", "enum": [ "Bytes", "Map", "Object", "Stream", "Text", "Blob" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The message type.", "constantName": "org.apache.camel.component.jms.JmsConstants#JMS_MESSAGE_TYPE" }, "CamelJmsRequestTimeout": { "index": 17, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "20000", "description": "The timeout for waiting for a reply when using the InOut Exchange Pattern (in milliseconds).", "constantName": "org.apache.camel.component.jms.JmsConstants#JMS_REQUEST_TIMEOUT" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "text", "bytes", "map", "stream", "null" ], "dependsOn": [ "messageConverter", "transferExchange" ], "byOption": [ { "option": "mapJmsMessage", "value": "false", "carriers": [ "pojo" ], "javaType": "jakarta.jms.Message" }, { "option": "objectMessageEnabled", "value": "true", "carriers": [ "text", "bytes", "map", "stream", "pojo", "null" ] } ], "description": "By the JMS message received: a String for a TextMessage, a byte array for a BytesMessage, a Map for a MapMessage, a stream for a StreamMessage; an ObjectMessage only with objectMessageEnabled" }, + "consumerReply": { "carriers": [ "text", "bytes", "stream", "file", "map" ], "dependsOn": [ "jmsMessageType", "messageConverter", "transferExchange" ], "byOption": [ { "option": "objectMessageEnabled", "value": "true", "carriers": [ "text", "bytes", "stream", "file", "map", "pojo" ] } ], "description": "The reply, sent as the producer sends a message" }, + "producerIn": { "carriers": [ "text", "bytes", "stream", "file", "map" ], "dependsOn": [ "jmsMessageType", "messageConverter", "transferExchange" ], "byOption": [ { "option": "objectMessageEnabled", "value": "true", "carriers": [ "text", "bytes", "stream", "file", "map", "pojo" ] } ], "description": "Text is sent as a TextMessage; bytes, a stream or a file as a BytesMessage; a Map as a MapMessage. Another Serializable object fails unless objectMessageEnabled" }, + "producerOut": { "carriers": [ "unknown" ], "byOption": [ { "option": "exchangePattern", "value": "InOnly", "carriers": [ "unchanged" ] }, { "option": "disableReplyTo", "value": "true", "carriers": [ "unchanged" ] } ], "description": "Unchanged for a message that expects no reply (InOnly). A message that expects one (InOut) gets the body of the reply, as the consumer gives it" } + }, "properties": { "destinationType": { "index": 0, "kind": "path", "displayName": "Destination Type", "group": "common", "label": "", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "queue", "topic", "temp-queue", "temp-topic" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "queue", "description": "The kind of destination to use" }, "destinationName": { "index": 1, "kind": "path", "displayName": "Destination Name", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Name of the queue or topic to use as destination" }, diff --git a/components/camel-activemq6/src/main/java/org/apache/camel/component/activemq6/ActiveMQEndpoint.java b/components/camel-activemq6/src/main/java/org/apache/camel/component/activemq6/ActiveMQEndpoint.java index cd3bed8b2bbaf..2ebf48a77856e 100644 --- a/components/camel-activemq6/src/main/java/org/apache/camel/component/activemq6/ActiveMQEndpoint.java +++ b/components/camel-activemq6/src/main/java/org/apache/camel/component/activemq6/ActiveMQEndpoint.java @@ -24,6 +24,9 @@ import org.apache.camel.component.jms.JmsComponent; import org.apache.camel.component.jms.JmsConfiguration; import org.apache.camel.component.jms.JmsEndpoint; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.EndpointServiceLocation; import org.apache.camel.spi.Metadata; import org.apache.camel.spi.UriEndpoint; @@ -36,6 +39,42 @@ @UriEndpoint(firstVersion = "4.7.0", extendsScheme = "jms", scheme = "activemq6", title = "ActiveMQ 6.x", syntax = "activemq6:destinationType:destinationName", category = { Category.MESSAGING }) +@BodyTypes(consumerOut = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.MAP, Carrier.STREAM, Carrier.NULL }, + dependsOn = { "messageConverter", "transferExchange" }, + byOption = { + @BodyType.When(option = "mapJmsMessage", value = "false", carrier = Carrier.POJO, + javaType = "jakarta.jms.Message"), + @BodyType.When(option = "objectMessageEnabled", value = "true", + carrier = { + Carrier.TEXT, Carrier.BYTES, Carrier.MAP, + Carrier.STREAM, Carrier.POJO, Carrier.NULL }) }, + description = "By the JMS message received: a String for a TextMessage, a byte array for a" + + " BytesMessage, a Map for a MapMessage, a stream for a StreamMessage;" + + " an ObjectMessage only with objectMessageEnabled"), + consumerReply = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, Carrier.MAP }, + dependsOn = { "jmsMessageType", "messageConverter", "transferExchange" }, + byOption = @BodyType.When(option = "objectMessageEnabled", value = "true", + carrier = { + Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, + Carrier.FILE, Carrier.MAP, Carrier.POJO }), + description = "The reply, sent as the producer sends a message"), + producerIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, Carrier.MAP }, + dependsOn = { "jmsMessageType", "messageConverter", "transferExchange" }, + byOption = @BodyType.When(option = "objectMessageEnabled", value = "true", + carrier = { + Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, + Carrier.FILE, Carrier.MAP, Carrier.POJO }), + description = "Text is sent as a TextMessage; bytes, a stream or a file as a BytesMessage;" + + " a Map as a MapMessage. Another Serializable object fails unless" + + " objectMessageEnabled"), + producerOut = @BodyType(value = Carrier.UNKNOWN, + byOption = { + @BodyType.When(option = "exchangePattern", value = "InOnly", + carrier = Carrier.UNCHANGED), + @BodyType.When(option = "disableReplyTo", value = "true", + carrier = Carrier.UNCHANGED) }, + description = "Unchanged for a message that expects no reply (InOnly). A message that" + + " expects one (InOut) gets the body of the reply, as the consumer gives it")) public class ActiveMQEndpoint extends JmsEndpoint implements EndpointServiceLocation { @UriParam(multiValue = true, prefix = "destination.", label = "consumer,advanced") diff --git a/components/camel-amqp/src/generated/resources/META-INF/org/apache/camel/component/amqp/amqp.json b/components/camel-amqp/src/generated/resources/META-INF/org/apache/camel/component/amqp/amqp.json index aa458bcd193d6..8557ccf657541 100644 --- a/components/camel-amqp/src/generated/resources/META-INF/org/apache/camel/component/amqp/amqp.json +++ b/components/camel-amqp/src/generated/resources/META-INF/org/apache/camel/component/amqp/amqp.json @@ -166,6 +166,12 @@ "CamelJmsMessageType": { "index": 16, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "org.apache.camel.component.jms.JmsMessageType", "enum": [ "Bytes", "Map", "Object", "Stream", "Text", "Blob" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The message type.", "constantName": "org.apache.camel.component.jms.JmsConstants#JMS_MESSAGE_TYPE" }, "CamelJmsRequestTimeout": { "index": 17, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "20000", "description": "The timeout for waiting for a reply when using the InOut Exchange Pattern (in milliseconds).", "constantName": "org.apache.camel.component.jms.JmsConstants#JMS_REQUEST_TIMEOUT" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "text", "bytes", "map", "stream", "null" ], "dependsOn": [ "messageConverter", "transferExchange" ], "byOption": [ { "option": "mapJmsMessage", "value": "false", "carriers": [ "pojo" ], "javaType": "jakarta.jms.Message" }, { "option": "objectMessageEnabled", "value": "true", "carriers": [ "text", "bytes", "map", "stream", "pojo", "null" ] } ], "description": "By the JMS message received: a String for a TextMessage, a byte array for a BytesMessage, a Map for a MapMessage, a stream for a StreamMessage; an ObjectMessage only with objectMessageEnabled" }, + "consumerReply": { "carriers": [ "text", "bytes", "stream", "file", "map" ], "dependsOn": [ "jmsMessageType", "messageConverter", "transferExchange" ], "byOption": [ { "option": "objectMessageEnabled", "value": "true", "carriers": [ "text", "bytes", "stream", "file", "map", "pojo" ] } ], "description": "The reply, sent as the producer sends a message" }, + "producerIn": { "carriers": [ "text", "bytes", "stream", "file", "map" ], "dependsOn": [ "jmsMessageType", "messageConverter", "transferExchange" ], "byOption": [ { "option": "objectMessageEnabled", "value": "true", "carriers": [ "text", "bytes", "stream", "file", "map", "pojo" ] } ], "description": "Text is sent as a TextMessage; bytes, a stream or a file as a BytesMessage; a Map as a MapMessage. Another Serializable object fails unless objectMessageEnabled" }, + "producerOut": { "carriers": [ "unknown" ], "byOption": [ { "option": "exchangePattern", "value": "InOnly", "carriers": [ "unchanged" ] }, { "option": "disableReplyTo", "value": "true", "carriers": [ "unchanged" ] } ], "description": "Unchanged for a message that expects no reply (InOnly). A message that expects one (InOut) gets the body of the reply, as the consumer gives it" } + }, "properties": { "destinationType": { "index": 0, "kind": "path", "displayName": "Destination Type", "group": "common", "label": "", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "queue", "topic", "temp-queue", "temp-topic" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "queue", "description": "The kind of destination to use" }, "destinationName": { "index": 1, "kind": "path", "displayName": "Destination Name", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Name of the queue or topic to use as destination" }, diff --git a/components/camel-amqp/src/main/java/org/apache/camel/component/amqp/AMQPEndpoint.java b/components/camel-amqp/src/main/java/org/apache/camel/component/amqp/AMQPEndpoint.java index e39ea41759d7e..9ed3d154c6132 100644 --- a/components/camel-amqp/src/main/java/org/apache/camel/component/amqp/AMQPEndpoint.java +++ b/components/camel-amqp/src/main/java/org/apache/camel/component/amqp/AMQPEndpoint.java @@ -18,6 +18,9 @@ import org.apache.camel.Category; import org.apache.camel.component.jms.JmsEndpoint; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.UriEndpoint; /** @@ -28,6 +31,42 @@ */ @UriEndpoint(firstVersion = "1.2.0", scheme = "amqp", extendsScheme = "jms", title = "AMQP", syntax = "amqp:destinationType:destinationName", category = { Category.MESSAGING }) +@BodyTypes(consumerOut = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.MAP, Carrier.STREAM, Carrier.NULL }, + dependsOn = { "messageConverter", "transferExchange" }, + byOption = { + @BodyType.When(option = "mapJmsMessage", value = "false", carrier = Carrier.POJO, + javaType = "jakarta.jms.Message"), + @BodyType.When(option = "objectMessageEnabled", value = "true", + carrier = { + Carrier.TEXT, Carrier.BYTES, Carrier.MAP, + Carrier.STREAM, Carrier.POJO, Carrier.NULL }) }, + description = "By the JMS message received: a String for a TextMessage, a byte array for a" + + " BytesMessage, a Map for a MapMessage, a stream for a StreamMessage;" + + " an ObjectMessage only with objectMessageEnabled"), + consumerReply = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, Carrier.MAP }, + dependsOn = { "jmsMessageType", "messageConverter", "transferExchange" }, + byOption = @BodyType.When(option = "objectMessageEnabled", value = "true", + carrier = { + Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, + Carrier.FILE, Carrier.MAP, Carrier.POJO }), + description = "The reply, sent as the producer sends a message"), + producerIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, Carrier.MAP }, + dependsOn = { "jmsMessageType", "messageConverter", "transferExchange" }, + byOption = @BodyType.When(option = "objectMessageEnabled", value = "true", + carrier = { + Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, + Carrier.FILE, Carrier.MAP, Carrier.POJO }), + description = "Text is sent as a TextMessage; bytes, a stream or a file as a BytesMessage;" + + " a Map as a MapMessage. Another Serializable object fails unless" + + " objectMessageEnabled"), + producerOut = @BodyType(value = Carrier.UNKNOWN, + byOption = { + @BodyType.When(option = "exchangePattern", value = "InOnly", + carrier = Carrier.UNCHANGED), + @BodyType.When(option = "disableReplyTo", value = "true", + carrier = Carrier.UNCHANGED) }, + description = "Unchanged for a message that expects no reply (InOnly). A message that" + + " expects one (InOut) gets the body of the reply, as the consumer gives it")) public class AMQPEndpoint extends JmsEndpoint { @Override diff --git a/components/camel-aws/camel-aws2-sqs/src/generated/resources/META-INF/org/apache/camel/component/aws2/sqs/aws2-sqs.json b/components/camel-aws/camel-aws2-sqs/src/generated/resources/META-INF/org/apache/camel/component/aws2/sqs/aws2-sqs.json index 71cc592888ed6..8ff172f1e1f3b 100644 --- a/components/camel-aws/camel-aws2-sqs/src/generated/resources/META-INF/org/apache/camel/component/aws2/sqs/aws2-sqs.json +++ b/components/camel-aws/camel-aws2-sqs/src/generated/resources/META-INF/org/apache/camel/component/aws2/sqs/aws2-sqs.json @@ -93,6 +93,11 @@ "CamelAwsSqsFailedMessageCount": { "index": 12, "kind": "header", "displayName": "", "group": "sendBatchMessage", "label": "sendBatchMessage", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The number of failed messages in a batch send operation.", "constantName": "org.apache.camel.component.aws2.sqs.Sqs2Constants#FAILED_MESSAGE_COUNT" }, "CamelAwsSqsSuccessfulMessageCount": { "index": 13, "kind": "header", "displayName": "", "group": "sendBatchMessage", "label": "sendBatchMessage", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The number of successful messages in a batch send operation.", "constantName": "org.apache.camel.component.aws2.sqs.Sqs2Constants#SUCCESSFUL_MESSAGE_COUNT" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "text" ], "javaType": "java.lang.String", "description": "The body of the SQS message" }, + "producerIn": { "carriers": [ "text" ], "byOption": [ { "option": "operation", "value": "sendBatchMessage", "carriers": [ "list", "text" ] } ], "description": "The body of the message to send, as text, so a Map or a POJO is sent as its toString(): marshal it first. sendBatchMessage takes a List of text, or text that batchSeparator splits" }, + "producerOut": { "carriers": [ "unchanged" ], "byOption": [ { "option": "operation", "value": "sendBatchMessage", "carriers": [ "pojo" ], "javaType": "software.amazon.awssdk.services.sqs.model.SendMessageBatchResponse" }, { "option": "operation", "value": "deleteMessage", "carriers": [ "pojo" ], "javaType": "software.amazon.awssdk.services.sqs.model.DeleteMessageResponse" }, { "option": "operation", "value": "listQueues", "carriers": [ "pojo" ], "javaType": "software.amazon.awssdk.services.sqs.model.ListQueuesResponse" }, { "option": "operation", "value": "purgeQueue", "carriers": [ "pojo" ], "javaType": "software.amazon.awssdk.services.sqs.model.PurgeQueueResponse" }, { "option": "operation", "value": "deleteQueue", "carriers": [ "pojo" ], "javaType": "software.amazon.awssdk.services.sqs.model.DeleteQueueResponse" } ], "description": "Unchanged for a send, with the id of the message in a header; the response of the AWS SDK for the other operations" } + }, "properties": { "queueNameOrArn": { "index": 0, "kind": "path", "displayName": "Queue Name Or Arn", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Queue name or ARN" }, "amazonAWSHost": { "index": 1, "kind": "parameter", "displayName": "Amazon AWSHost", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "amazonaws.com", "configurationClass": "org.apache.camel.component.aws2.sqs.Sqs2Configuration", "configurationField": "configuration", "description": "The hostname of the Amazon AWS cloud." }, diff --git a/components/camel-aws/camel-aws2-sqs/src/main/java/org/apache/camel/component/aws2/sqs/Sqs2Endpoint.java b/components/camel-aws/camel-aws2-sqs/src/main/java/org/apache/camel/component/aws2/sqs/Sqs2Endpoint.java index 14b3316f0e4e8..c4f5fe9575899 100644 --- a/components/camel-aws/camel-aws2-sqs/src/main/java/org/apache/camel/component/aws2/sqs/Sqs2Endpoint.java +++ b/components/camel-aws/camel-aws2-sqs/src/main/java/org/apache/camel/component/aws2/sqs/Sqs2Endpoint.java @@ -33,6 +33,9 @@ import org.apache.camel.clock.Clock; import org.apache.camel.component.aws2.sqs.client.Sqs2ClientFactory; import org.apache.camel.spi.*; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.support.MonotonicClock; import org.apache.camel.support.ResourceHelper; import org.apache.camel.support.ScheduledPollEndpoint; @@ -60,6 +63,30 @@ @UriEndpoint(firstVersion = "3.1.0", scheme = "aws2-sqs", title = "AWS Simple Queue Service (SQS)", syntax = "aws2-sqs:queueNameOrArn", category = { Category.CLOUD, Category.MESSAGING }, headersClass = Sqs2Constants.class) +@BodyTypes(consumerOut = @BodyType(value = Carrier.TEXT, javaType = "java.lang.String", + description = "The body of the SQS message"), + producerIn = @BodyType(value = Carrier.TEXT, + byOption = @BodyType.When(option = "operation", value = "sendBatchMessage", + carrier = { Carrier.LIST, Carrier.TEXT }), + description = "The body of the message to send, as text, so a Map or a POJO is sent as" + + " its toString(): marshal it first. sendBatchMessage takes a List of" + + " text, or text that batchSeparator splits"), + producerOut = @BodyType(value = Carrier.UNCHANGED, + byOption = { + @BodyType.When(option = "operation", value = "sendBatchMessage", + carrier = Carrier.POJO, + javaType = "software.amazon.awssdk.services.sqs.model.SendMessageBatchResponse"), + @BodyType.When(option = "operation", value = "deleteMessage", + carrier = Carrier.POJO, + javaType = "software.amazon.awssdk.services.sqs.model.DeleteMessageResponse"), + @BodyType.When(option = "operation", value = "listQueues", carrier = Carrier.POJO, + javaType = "software.amazon.awssdk.services.sqs.model.ListQueuesResponse"), + @BodyType.When(option = "operation", value = "purgeQueue", carrier = Carrier.POJO, + javaType = "software.amazon.awssdk.services.sqs.model.PurgeQueueResponse"), + @BodyType.When(option = "operation", value = "deleteQueue", carrier = Carrier.POJO, + javaType = "software.amazon.awssdk.services.sqs.model.DeleteQueueResponse") }, + description = "Unchanged for a send, with the id of the message in a header; the response" + + " of the AWS SDK for the other operations")) public class Sqs2Endpoint extends ScheduledPollEndpoint implements HeaderFilterStrategyAware, EndpointServiceLocation { private static final Logger LOG = LoggerFactory.getLogger(Sqs2Endpoint.class); diff --git a/components/camel-csv/src/generated/resources/META-INF/org/apache/camel/dataformat/csv/csv.json b/components/camel-csv/src/generated/resources/META-INF/org/apache/camel/dataformat/csv/csv.json index c1044cee0dd0c..6e696b52703e8 100644 --- a/components/camel-csv/src/generated/resources/META-INF/org/apache/camel/dataformat/csv/csv.json +++ b/components/camel-csv/src/generated/resources/META-INF/org/apache/camel/dataformat/csv/csv.json @@ -15,6 +15,12 @@ "modelName": "csv", "modelJavaType": "org.apache.camel.model.dataformat.CsvDataFormat" }, + "bodyTypes": { + "marshalIn": { "carriers": [ "map", "list" ], "description": "One row as a Map, or the rows as a List (or an Iterator) of Maps or Lists" }, + "marshalOut": { "carriers": [ "stream" ], "format": "csv", "description": "The CSV in the stream cache, or a byte array when stream caching is off" }, + "unmarshalIn": { "carriers": [ "text", "bytes", "stream", "file" ], "format": "csv" }, + "unmarshalOut": { "carriers": [ "list" ], "dependsOn": [ "recordConverterRef" ], "byOption": [ { "option": "lazyLoad", "value": "true", "carriers": [ "list" ], "javaType": "java.util.Iterator" } ], "description": "A List of the rows, each a List of its values; with useMaps or useOrderedMaps, a Map of the header names and the values" } + }, "properties": { "id": { "index": 0, "kind": "attribute", "displayName": "Id", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The id of this node" }, "format": { "index": 1, "kind": "attribute", "displayName": "Format", "group": "common", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "DEFAULT", "EXCEL", "INFORMIX_UNLOAD", "INFORMIX_UNLOAD_CSV", "MONGODB_CSV", "MONGODB_TSV", "MYSQL", "ORACLE", "POSTGRESQL_CSV", "POSTGRESQL_TEXT", "RFC4180" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "DEFAULT", "description": "The format to use." }, diff --git a/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvDataFormat.java b/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvDataFormat.java index 5e443a58c6444..3baa7fe3c30e7 100644 --- a/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvDataFormat.java +++ b/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvDataFormat.java @@ -21,6 +21,9 @@ import java.util.StringJoiner; import org.apache.camel.Exchange; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.DataFormat; import org.apache.camel.spi.DataFormatName; import org.apache.camel.spi.annotations.Dataformat; @@ -36,6 +39,16 @@ * Autogeneration can be disabled. In this case, only the fields defined in csvConfig are written on the output. */ @Dataformat("csv") +@BodyTypes(marshalIn = @BodyType(value = { Carrier.MAP, Carrier.LIST }, + description = "One row as a Map, or the rows as a List (or an Iterator) of Maps or Lists"), + marshalOut = @BodyType(value = Carrier.STREAM, format = "csv", + description = "The CSV in the stream cache, or a byte array when stream caching is off"), + unmarshalIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }, format = "csv"), + unmarshalOut = @BodyType(value = Carrier.LIST, dependsOn = "recordConverterRef", + byOption = @BodyType.When(option = "lazyLoad", value = "true", carrier = Carrier.LIST, + javaType = "java.util.Iterator"), + description = "A List of the rows, each a List of its values; with useMaps or" + + " useOrderedMaps, a Map of the header names and the values")) public class CsvDataFormat extends ServiceSupport implements DataFormat, DataFormatName { // CSV format options private CSVFormat csvFormat = CSVFormat.DEFAULT; diff --git a/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/ftp.json b/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/ftp.json index e56b6813c06bd..8d0a3be6738aa 100644 --- a/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/ftp.json +++ b/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/ftp.json @@ -50,6 +50,11 @@ "CamelFtpReplyCode": { "index": 15, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "important": true, "description": "The FTP client reply code", "constantName": "org.apache.camel.component.file.remote.FtpConstants#FTP_REPLY_CODE" }, "CamelFtpReplyString": { "index": 16, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "important": true, "description": "The FTP client reply string", "constantName": "org.apache.camel.component.file.remote.FtpConstants#FTP_REPLY_STRING" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "file" ], "dependsOn": [ "preMove" ], "byOption": [ { "option": "streamDownload", "value": "true", "carriers": [ "stream" ] }, { "option": "download", "value": "false", "carriers": [ "null" ] } ], "description": "The file as read: a RemoteFile, which converts to text, bytes or a stream; a stream with streamDownload; with preMove its content, a byte array" }, + "producerIn": { "carriers": [ "text", "bytes", "stream", "file" ], "byOption": [ { "option": "allowNullBody", "value": "true", "carriers": [ "null", "text", "bytes", "stream", "file" ] } ], "description": "What converts to an InputStream: text, bytes, a stream or a file. A Map or a List fails: marshal it first" }, + "producerOut": { "carriers": [ "unchanged" ] } + }, "properties": { "host": { "index": 0, "kind": "path", "displayName": "Host", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.file.remote.FtpConfiguration", "configurationField": "configuration", "description": "Hostname of the FTP server" }, "port": { "index": 1, "kind": "path", "displayName": "Port", "group": "common", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.file.remote.FtpConfiguration", "configurationField": "configuration", "description": "Port of the FTP server" }, diff --git a/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/ftps.json b/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/ftps.json index 075f91d010d24..ba1fade434f6b 100644 --- a/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/ftps.json +++ b/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/ftps.json @@ -51,6 +51,11 @@ "CamelFtpReplyCode": { "index": 15, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "important": true, "description": "The FTP client reply code", "constantName": "org.apache.camel.component.file.remote.FtpConstants#FTP_REPLY_CODE" }, "CamelFtpReplyString": { "index": 16, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "important": true, "description": "The FTP client reply string", "constantName": "org.apache.camel.component.file.remote.FtpConstants#FTP_REPLY_STRING" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "file" ], "dependsOn": [ "preMove" ], "byOption": [ { "option": "streamDownload", "value": "true", "carriers": [ "stream" ] }, { "option": "download", "value": "false", "carriers": [ "null" ] } ], "description": "The file as read: a RemoteFile, which converts to text, bytes or a stream; a stream with streamDownload; with preMove its content, a byte array" }, + "producerIn": { "carriers": [ "text", "bytes", "stream", "file" ], "byOption": [ { "option": "allowNullBody", "value": "true", "carriers": [ "null", "text", "bytes", "stream", "file" ] } ], "description": "What converts to an InputStream: text, bytes, a stream or a file. A Map or a List fails: marshal it first" }, + "producerOut": { "carriers": [ "unchanged" ] } + }, "properties": { "host": { "index": 0, "kind": "path", "displayName": "Host", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.file.remote.FtpsConfiguration", "configurationField": "configuration", "description": "Hostname of the FTP server" }, "port": { "index": 1, "kind": "path", "displayName": "Port", "group": "common", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.file.remote.FtpsConfiguration", "configurationField": "configuration", "description": "Port of the FTP server" }, diff --git a/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/sftp.json b/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/sftp.json index 11096138d4192..a0471a76cb2f0 100644 --- a/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/sftp.json +++ b/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/sftp.json @@ -55,6 +55,11 @@ "CamelFtpReplyCode": { "index": 15, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "important": true, "description": "The FTP client reply code", "constantName": "org.apache.camel.component.file.remote.FtpConstants#FTP_REPLY_CODE" }, "CamelFtpReplyString": { "index": 16, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "important": true, "description": "The FTP client reply string", "constantName": "org.apache.camel.component.file.remote.FtpConstants#FTP_REPLY_STRING" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "file" ], "dependsOn": [ "preMove" ], "byOption": [ { "option": "streamDownload", "value": "true", "carriers": [ "stream" ] }, { "option": "download", "value": "false", "carriers": [ "null" ] } ], "description": "The file as read: a RemoteFile, which converts to text, bytes or a stream; a stream with streamDownload; with preMove its content, a byte array" }, + "producerIn": { "carriers": [ "text", "bytes", "stream", "file" ], "byOption": [ { "option": "allowNullBody", "value": "true", "carriers": [ "null", "text", "bytes", "stream", "file" ] } ], "description": "What converts to an InputStream: text, bytes, a stream or a file. A Map or a List fails: marshal it first" }, + "producerOut": { "carriers": [ "unchanged" ] } + }, "properties": { "host": { "index": 0, "kind": "path", "displayName": "Host", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.file.remote.SftpConfiguration", "configurationField": "configuration", "description": "Hostname of the FTP server" }, "port": { "index": 1, "kind": "path", "displayName": "Port", "group": "common", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.file.remote.SftpConfiguration", "configurationField": "configuration", "description": "Port of the FTP server" }, diff --git a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpEndpoint.java b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpEndpoint.java index 6c0d8bae52b8a..2abdf1d757ba9 100644 --- a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpEndpoint.java +++ b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpEndpoint.java @@ -33,6 +33,9 @@ import org.apache.camel.component.file.remote.RemoteFileConfiguration.PathSeparator; import org.apache.camel.component.file.remote.strategy.FtpProcessStrategyFactory; import org.apache.camel.component.file.strategy.FileMoveExistingStrategy; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.ClassResolver; import org.apache.camel.spi.Metadata; import org.apache.camel.spi.UriEndpoint; @@ -56,6 +59,20 @@ + "startingDirectoryMustHaveAccess,chmodDirectory,forceWrites,copyAndDeleteOnRenameFail," + "renameUsingCopy,synchronous") @ManagedResource(description = "Managed FtpEndpoint") +@BodyTypes(consumerOut = @BodyType(value = Carrier.FILE, dependsOn = "preMove", + byOption = { + @BodyType.When(option = "streamDownload", value = "true", carrier = Carrier.STREAM), + @BodyType.When(option = "download", value = "false", carrier = Carrier.NULL) }, + description = "The file as read: a RemoteFile, which converts to text, bytes or a stream; a" + + " stream with streamDownload; with preMove its content, a byte array"), + producerIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }, + byOption = @BodyType.When(option = "allowNullBody", value = "true", + carrier = { + Carrier.NULL, Carrier.TEXT, Carrier.BYTES, + Carrier.STREAM, Carrier.FILE }), + description = "What converts to an InputStream: text, bytes, a stream or a file. A Map or" + + " a List fails: marshal it first"), + producerOut = @BodyType(Carrier.UNCHANGED)) public class FtpEndpoint extends RemoteFileEndpoint { private static final Logger LOG = LoggerFactory.getLogger(FtpEndpoint.class); diff --git a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpsEndpoint.java b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpsEndpoint.java index 37251328723b2..5a80fbc506799 100644 --- a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpsEndpoint.java +++ b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpsEndpoint.java @@ -30,6 +30,9 @@ import org.apache.camel.Category; import org.apache.camel.api.management.ManagedResource; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.Metadata; import org.apache.camel.spi.UriEndpoint; import org.apache.camel.spi.UriParam; @@ -54,6 +57,20 @@ + "startingDirectoryMustHaveAccess,chmodDirectory,forceWrites,copyAndDeleteOnRenameFail," + "renameUsingCopy,synchronous") @ManagedResource(description = "Managed FtpsEndpoint") +@BodyTypes(consumerOut = @BodyType(value = Carrier.FILE, dependsOn = "preMove", + byOption = { + @BodyType.When(option = "streamDownload", value = "true", carrier = Carrier.STREAM), + @BodyType.When(option = "download", value = "false", carrier = Carrier.NULL) }, + description = "The file as read: a RemoteFile, which converts to text, bytes or a stream; a" + + " stream with streamDownload; with preMove its content, a byte array"), + producerIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }, + byOption = @BodyType.When(option = "allowNullBody", value = "true", + carrier = { + Carrier.NULL, Carrier.TEXT, Carrier.BYTES, + Carrier.STREAM, Carrier.FILE }), + description = "What converts to an InputStream: text, bytes, a stream or a file. A Map or" + + " a List fails: marshal it first"), + producerOut = @BodyType(Carrier.UNCHANGED)) public class FtpsEndpoint extends FtpEndpoint { private static final Logger LOG = LoggerFactory.getLogger(FtpsEndpoint.class); diff --git a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/SftpEndpoint.java b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/SftpEndpoint.java index 7c3bda7a0e864..6ff2ab3c28e90 100644 --- a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/SftpEndpoint.java +++ b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/SftpEndpoint.java @@ -24,6 +24,9 @@ import org.apache.camel.component.file.GenericFileProducer; import org.apache.camel.component.file.remote.strategy.SftpProcessStrategyFactory; import org.apache.camel.component.file.strategy.FileMoveExistingStrategy; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.Metadata; import org.apache.camel.spi.UriEndpoint; import org.apache.camel.spi.UriParam; @@ -37,6 +40,20 @@ + "directoryMustExist,extendedAttributes,probeContentType,startingDirectoryMustExist," + "startingDirectoryMustHaveAccess,forceWrites,copyAndDeleteOnRenameFail," + "renameUsingCopy,synchronous") +@BodyTypes(consumerOut = @BodyType(value = Carrier.FILE, dependsOn = "preMove", + byOption = { + @BodyType.When(option = "streamDownload", value = "true", carrier = Carrier.STREAM), + @BodyType.When(option = "download", value = "false", carrier = Carrier.NULL) }, + description = "The file as read: a RemoteFile, which converts to text, bytes or a stream; a" + + " stream with streamDownload; with preMove its content, a byte array"), + producerIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }, + byOption = @BodyType.When(option = "allowNullBody", value = "true", + carrier = { + Carrier.NULL, Carrier.TEXT, Carrier.BYTES, + Carrier.STREAM, Carrier.FILE }), + description = "What converts to an InputStream: text, bytes, a stream or a file. A Map or" + + " a List fails: marshal it first"), + producerOut = @BodyType(Carrier.UNCHANGED)) public class SftpEndpoint extends RemoteFileEndpoint { @UriParam diff --git a/components/camel-groovy/src/generated/resources/META-INF/org/apache/camel/language/groovy/groovy.json b/components/camel-groovy/src/generated/resources/META-INF/org/apache/camel/language/groovy/groovy.json index db276ac056830..fd58e1de9825c 100644 --- a/components/camel-groovy/src/generated/resources/META-INF/org/apache/camel/language/groovy/groovy.json +++ b/components/camel-groovy/src/generated/resources/META-INF/org/apache/camel/language/groovy/groovy.json @@ -15,6 +15,10 @@ "modelName": "groovy", "modelJavaType": "org.apache.camel.model.language.GroovyExpression" }, + "bodyTypes": { + "expressionIn": { "carriers": [ "any" ], "description": "The body as it is, in the variable body" }, + "expressionOut": { "carriers": [ "unknown" ], "description": "What the script returns" } + }, "properties": { "id": { "index": 0, "kind": "attribute", "displayName": "Id", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The id of this node." }, "expression": { "index": 1, "kind": "value", "displayName": "Expression", "group": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The expression value in your chosen language syntax." }, diff --git a/components/camel-groovy/src/main/java/org/apache/camel/language/groovy/GroovyLanguage.java b/components/camel-groovy/src/main/java/org/apache/camel/language/groovy/GroovyLanguage.java index 667bc5adb7fca..1fda88d90b5e1 100644 --- a/components/camel-groovy/src/main/java/org/apache/camel/language/groovy/GroovyLanguage.java +++ b/components/camel-groovy/src/main/java/org/apache/camel/language/groovy/GroovyLanguage.java @@ -32,6 +32,9 @@ import org.apache.camel.Ordered; import org.apache.camel.RuntimeCamelException; import org.apache.camel.Service; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.CamelEvent; import org.apache.camel.spi.CompilePreProcessor; import org.apache.camel.spi.EventNotifier; @@ -49,6 +52,8 @@ import org.slf4j.LoggerFactory; @Language("groovy") +@BodyTypes(expressionIn = @BodyType(value = Carrier.ANY, description = "The body as it is, in the variable body"), + expressionOut = @BodyType(value = Carrier.UNKNOWN, description = "What the script returns")) public class GroovyLanguage extends TypedLanguageSupport implements ScriptingLanguage, Service { private static final Logger LOG = LoggerFactory.getLogger(GroovyLanguage.class); diff --git a/components/camel-jacksonxml/src/generated/resources/META-INF/org/apache/camel/component/jacksonxml/jacksonXml.json b/components/camel-jacksonxml/src/generated/resources/META-INF/org/apache/camel/component/jacksonxml/jacksonXml.json index 1eca4610e6271..e01c38e3002ee 100644 --- a/components/camel-jacksonxml/src/generated/resources/META-INF/org/apache/camel/component/jacksonxml/jacksonXml.json +++ b/components/camel-jacksonxml/src/generated/resources/META-INF/org/apache/camel/component/jacksonxml/jacksonXml.json @@ -15,6 +15,12 @@ "modelName": "jacksonXml", "modelJavaType": "org.apache.camel.model.dataformat.JacksonXMLDataFormat" }, + "bodyTypes": { + "marshalIn": { "carriers": [ "any" ], "description": "A Map, a List or a POJO to write as XML. Text is written as an XML value, not as it is" }, + "marshalOut": { "carriers": [ "stream" ], "format": "xml", "description": "The XML in the stream cache, or a byte array when stream caching is off" }, + "unmarshalIn": { "carriers": [ "text", "bytes", "stream", "file" ], "format": "xml" }, + "unmarshalOut": { "carriers": [ "map" ], "dependsOn": [ "unmarshalType", "collectionType", "allowUnmarshallType" ], "byOption": [ { "option": "useList", "value": "true", "carriers": [ "list" ] } ], "description": "A Map (LinkedHashMap) of the XML; an object of the class unmarshalType names" } + }, "properties": { "id": { "index": 0, "kind": "attribute", "displayName": "Id", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The id of this node" }, "xmlMapper": { "index": 1, "kind": "attribute", "displayName": "Xml Mapper", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Lookup and use the existing XmlMapper with the given id." }, diff --git a/components/camel-jacksonxml/src/main/java/org/apache/camel/component/jacksonxml/JacksonXMLDataFormat.java b/components/camel-jacksonxml/src/main/java/org/apache/camel/component/jacksonxml/JacksonXMLDataFormat.java index 5e4567427e425..e421d9bcdeb11 100644 --- a/components/camel-jacksonxml/src/main/java/org/apache/camel/component/jacksonxml/JacksonXMLDataFormat.java +++ b/components/camel-jacksonxml/src/main/java/org/apache/camel/component/jacksonxml/JacksonXMLDataFormat.java @@ -44,6 +44,9 @@ import org.apache.camel.CamelContextAware; import org.apache.camel.Exchange; import org.apache.camel.WrappedFile; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.DataFormat; import org.apache.camel.spi.DataFormatContentTypeHeader; import org.apache.camel.spi.DataFormatName; @@ -60,6 +63,17 @@ * Jackson to marshal to and from XML. */ @Dataformat("jacksonXml") +@BodyTypes(marshalIn = @BodyType(value = Carrier.ANY, + description = "A Map, a List or a POJO to write as XML. Text is written as an XML value," + + " not as it is"), + marshalOut = @BodyType(value = Carrier.STREAM, format = "xml", + description = "The XML in the stream cache, or a byte array when stream caching is off"), + unmarshalIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }, format = "xml"), + unmarshalOut = @BodyType(value = Carrier.MAP, + dependsOn = { "unmarshalType", "collectionType", "allowUnmarshallType" }, + byOption = @BodyType.When(option = "useList", value = "true", carrier = Carrier.LIST), + description = "A Map (LinkedHashMap) of the XML; an object of the class unmarshalType" + + " names")) public class JacksonXMLDataFormat extends ServiceSupport implements DataFormat, DataFormatName, DataFormatContentTypeHeader, CamelContextAware { diff --git a/components/camel-jms/src/generated/resources/META-INF/org/apache/camel/component/jms/jms.json b/components/camel-jms/src/generated/resources/META-INF/org/apache/camel/component/jms/jms.json index 7307e4f146f23..43327061d9863 100644 --- a/components/camel-jms/src/generated/resources/META-INF/org/apache/camel/component/jms/jms.json +++ b/components/camel-jms/src/generated/resources/META-INF/org/apache/camel/component/jms/jms.json @@ -154,6 +154,12 @@ "CamelJmsMessageType": { "index": 16, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "org.apache.camel.component.jms.JmsMessageType", "enum": [ "Bytes", "Map", "Object", "Stream", "Text", "Blob" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The message type.", "constantName": "org.apache.camel.component.jms.JmsConstants#JMS_MESSAGE_TYPE" }, "CamelJmsRequestTimeout": { "index": 17, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "20000", "description": "The timeout for waiting for a reply when using the InOut Exchange Pattern (in milliseconds).", "constantName": "org.apache.camel.component.jms.JmsConstants#JMS_REQUEST_TIMEOUT" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "text", "bytes", "map", "stream", "null" ], "dependsOn": [ "messageConverter", "transferExchange" ], "byOption": [ { "option": "mapJmsMessage", "value": "false", "carriers": [ "pojo" ], "javaType": "jakarta.jms.Message" }, { "option": "objectMessageEnabled", "value": "true", "carriers": [ "text", "bytes", "map", "stream", "pojo", "null" ] } ], "description": "By the JMS message received: a String for a TextMessage, a byte array for a BytesMessage, a Map for a MapMessage, a stream for a StreamMessage; an ObjectMessage only with objectMessageEnabled" }, + "consumerReply": { "carriers": [ "text", "bytes", "stream", "file", "map" ], "dependsOn": [ "jmsMessageType", "messageConverter", "transferExchange" ], "byOption": [ { "option": "objectMessageEnabled", "value": "true", "carriers": [ "text", "bytes", "stream", "file", "map", "pojo" ] } ], "description": "The reply, sent as the producer sends a message" }, + "producerIn": { "carriers": [ "text", "bytes", "stream", "file", "map" ], "dependsOn": [ "jmsMessageType", "messageConverter", "transferExchange" ], "byOption": [ { "option": "objectMessageEnabled", "value": "true", "carriers": [ "text", "bytes", "stream", "file", "map", "pojo" ] } ], "description": "Text is sent as a TextMessage; bytes, a stream or a file as a BytesMessage; a Map as a MapMessage. Another Serializable object fails unless objectMessageEnabled" }, + "producerOut": { "carriers": [ "unknown" ], "byOption": [ { "option": "exchangePattern", "value": "InOnly", "carriers": [ "unchanged" ] }, { "option": "disableReplyTo", "value": "true", "carriers": [ "unchanged" ] } ], "description": "Unchanged for a message that expects no reply (InOnly). A message that expects one (InOut) gets the body of the reply, as the consumer gives it" } + }, "properties": { "destinationType": { "index": 0, "kind": "path", "displayName": "Destination Type", "group": "common", "label": "", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "queue", "topic", "temp-queue", "temp-topic" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "queue", "description": "The kind of destination to use" }, "destinationName": { "index": 1, "kind": "path", "displayName": "Destination Name", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Name of the queue or topic to use as destination" }, diff --git a/components/camel-jms/src/main/java/org/apache/camel/component/jms/JmsEndpoint.java b/components/camel-jms/src/main/java/org/apache/camel/component/jms/JmsEndpoint.java index 8c18b428ee573..67a8ec51e5312 100644 --- a/components/camel-jms/src/main/java/org/apache/camel/component/jms/JmsEndpoint.java +++ b/components/camel-jms/src/main/java/org/apache/camel/component/jms/JmsEndpoint.java @@ -39,6 +39,9 @@ import org.apache.camel.api.management.ManagedAttribute; import org.apache.camel.api.management.ManagedResource; import org.apache.camel.spi.BeanIntrospection; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.EndpointServiceLocation; import org.apache.camel.spi.HeaderFilterStrategy; import org.apache.camel.spi.HeaderFilterStrategyAware; @@ -73,6 +76,42 @@ @UriEndpoint(firstVersion = "1.0.0", scheme = "jms", title = "JMS", syntax = "jms:destinationType:destinationName", category = { Category.MESSAGING }, headersClass = JmsConstants.class) @Metadata(excludeProperties = "bridgeErrorHandler") +@BodyTypes(consumerOut = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.MAP, Carrier.STREAM, Carrier.NULL }, + dependsOn = { "messageConverter", "transferExchange" }, + byOption = { + @BodyType.When(option = "mapJmsMessage", value = "false", carrier = Carrier.POJO, + javaType = "jakarta.jms.Message"), + @BodyType.When(option = "objectMessageEnabled", value = "true", + carrier = { + Carrier.TEXT, Carrier.BYTES, Carrier.MAP, + Carrier.STREAM, Carrier.POJO, Carrier.NULL }) }, + description = "By the JMS message received: a String for a TextMessage, a byte array for a" + + " BytesMessage, a Map for a MapMessage, a stream for a StreamMessage;" + + " an ObjectMessage only with objectMessageEnabled"), + consumerReply = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, Carrier.MAP }, + dependsOn = { "jmsMessageType", "messageConverter", "transferExchange" }, + byOption = @BodyType.When(option = "objectMessageEnabled", value = "true", + carrier = { + Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, + Carrier.FILE, Carrier.MAP, Carrier.POJO }), + description = "The reply, sent as the producer sends a message"), + producerIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, Carrier.MAP }, + dependsOn = { "jmsMessageType", "messageConverter", "transferExchange" }, + byOption = @BodyType.When(option = "objectMessageEnabled", value = "true", + carrier = { + Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, + Carrier.FILE, Carrier.MAP, Carrier.POJO }), + description = "Text is sent as a TextMessage; bytes, a stream or a file as a BytesMessage;" + + " a Map as a MapMessage. Another Serializable object fails unless" + + " objectMessageEnabled"), + producerOut = @BodyType(value = Carrier.UNKNOWN, + byOption = { + @BodyType.When(option = "exchangePattern", value = "InOnly", + carrier = Carrier.UNCHANGED), + @BodyType.When(option = "disableReplyTo", value = "true", + carrier = Carrier.UNCHANGED) }, + description = "Unchanged for a message that expects no reply (InOnly). A message that" + + " expects one (InOut) gets the body of the reply, as the consumer gives it")) public class JmsEndpoint extends DefaultEndpoint implements AsyncEndpoint, HeaderFilterStrategyAware, MultipleConsumersSupport, EndpointServiceLocation { diff --git a/components/camel-jq/src/generated/resources/META-INF/org/apache/camel/language/jq/jq.json b/components/camel-jq/src/generated/resources/META-INF/org/apache/camel/language/jq/jq.json index 8d658803476a5..781f11d2f1794 100644 --- a/components/camel-jq/src/generated/resources/META-INF/org/apache/camel/language/jq/jq.json +++ b/components/camel-jq/src/generated/resources/META-INF/org/apache/camel/language/jq/jq.json @@ -15,6 +15,10 @@ "modelName": "jq", "modelJavaType": "org.apache.camel.model.language.JqExpression" }, + "bodyTypes": { + "expressionIn": { "carriers": [ "text", "bytes", "stream", "file", "map" ], "format": "json", "description": "JSON as text, bytes, a stream or a file, or a Map" }, + "expressionOut": { "carriers": [ "pojo", "list", "null" ], "dependsOn": [ "resultType" ], "description": "A JsonNode; a List of JsonNode when the filter gives several, null when it gives none" } + }, "properties": { "id": { "index": 0, "kind": "attribute", "displayName": "Id", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The id of this node." }, "expression": { "index": 1, "kind": "value", "displayName": "Expression", "group": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The expression value in your chosen language syntax." }, diff --git a/components/camel-jq/src/main/java/org/apache/camel/language/jq/JqLanguage.java b/components/camel-jq/src/main/java/org/apache/camel/language/jq/JqLanguage.java index e21bfd984aca1..3944e84c8d266 100644 --- a/components/camel-jq/src/main/java/org/apache/camel/language/jq/JqLanguage.java +++ b/components/camel-jq/src/main/java/org/apache/camel/language/jq/JqLanguage.java @@ -22,11 +22,19 @@ import net.thisptr.jackson.jq.module.loaders.BuiltinModuleLoader; import org.apache.camel.Expression; import org.apache.camel.StaticService; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.annotations.Language; import org.apache.camel.support.CamelContextHelper; import org.apache.camel.support.SingleInputTypedLanguageSupport; @Language("jq") +@BodyTypes(expressionIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, Carrier.MAP }, + format = "json", description = "JSON as text, bytes, a stream or a file, or a Map"), + expressionOut = @BodyType(value = { Carrier.POJO, Carrier.LIST, Carrier.NULL }, dependsOn = "resultType", + description = "A JsonNode; a List of JsonNode when the filter gives several, null when it" + + " gives none")) public class JqLanguage extends SingleInputTypedLanguageSupport implements StaticService { private Scope rootScope; diff --git a/components/camel-jsonpath/src/generated/resources/META-INF/org/apache/camel/jsonpath/jsonpath.json b/components/camel-jsonpath/src/generated/resources/META-INF/org/apache/camel/jsonpath/jsonpath.json index af55d48104eaa..e3aeffd210b31 100644 --- a/components/camel-jsonpath/src/generated/resources/META-INF/org/apache/camel/jsonpath/jsonpath.json +++ b/components/camel-jsonpath/src/generated/resources/META-INF/org/apache/camel/jsonpath/jsonpath.json @@ -15,6 +15,10 @@ "modelName": "jsonpath", "modelJavaType": "org.apache.camel.model.language.JsonPathExpression" }, + "bodyTypes": { + "expressionIn": { "carriers": [ "any" ], "description": "JSON as text, bytes, a stream or a file, or parsed: a Map or a List. A POJO is read through Jackson" }, + "expressionOut": { "carriers": [ "unknown" ], "byOption": [ { "option": "writeAsString", "value": "true", "carriers": [ "text", "list" ] } ], "description": "The value the path selects: a Map for an object, a List for an array or for a path with a wildcard or a filter, otherwise text, a number or a boolean" } + }, "properties": { "id": { "index": 0, "kind": "attribute", "displayName": "Id", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The id of this node." }, "expression": { "index": 1, "kind": "value", "displayName": "Expression", "group": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The expression value in your chosen language syntax." }, diff --git a/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathLanguage.java b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathLanguage.java index 6b5df842e8dad..be5917ef5e01e 100644 --- a/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathLanguage.java +++ b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathLanguage.java @@ -25,6 +25,9 @@ import org.apache.camel.Expression; import org.apache.camel.Predicate; import org.apache.camel.jsonpath.easypredicate.EasyPredicateParser; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.PropertyConfigurer; import org.apache.camel.spi.annotations.Language; import org.apache.camel.support.ExpressionAdapter; @@ -32,6 +35,15 @@ import org.apache.camel.support.component.PropertyConfigurerSupport; @Language("jsonpath") +@BodyTypes(expressionIn = @BodyType(value = Carrier.ANY, + description = "JSON as text, bytes, a stream or a file, or parsed: a Map or a List. A POJO" + + " is read through Jackson"), + expressionOut = @BodyType(value = Carrier.UNKNOWN, + byOption = @BodyType.When(option = "writeAsString", value = "true", + carrier = { Carrier.TEXT, Carrier.LIST }), + description = "The value the path selects: a Map for an object, a List for an array or" + + " for a path with a wildcard or a filter, otherwise text, a number or a" + + " boolean")) public class JsonPathLanguage extends SingleInputTypedLanguageSupport implements PropertyConfigurer { private boolean suppressExceptions; diff --git a/components/camel-kafka/src/generated/resources/META-INF/org/apache/camel/component/kafka/kafka.json b/components/camel-kafka/src/generated/resources/META-INF/org/apache/camel/component/kafka/kafka.json index f40e398b911ba..d1dafe51f2f12 100644 --- a/components/camel-kafka/src/generated/resources/META-INF/org/apache/camel/component/kafka/kafka.json +++ b/components/camel-kafka/src/generated/resources/META-INF/org/apache/camel/component/kafka/kafka.json @@ -169,6 +169,11 @@ "CamelKafkaRecordMeta": { "index": 11, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "List", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The metadata (only configured if recordMetadata endpoint parameter is true)", "constantName": "org.apache.camel.component.kafka.KafkaConstants#KAFKA_RECORD_META" }, "CamelKafkaManualCommit": { "index": 12, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "org.apache.camel.component.kafka.consumer.KafkaManualCommit", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Can be used for forcing manual offset commit when using Kafka consumer.", "constantName": "org.apache.camel.component.kafka.KafkaConstants#MANUAL_COMMIT" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "text", "null" ], "dependsOn": [ "valueDeserializer" ], "byOption": [ { "option": "valueDeserializer", "value": "org.apache.kafka.common.serialization.ByteArrayDeserializer", "carriers": [ "bytes", "null" ], "javaType": "byte[]" }, { "option": "batching", "value": "true", "carriers": [ "list" ] } ], "description": "The value of the record: a String with the default valueDeserializer (StringDeserializer), null for a tombstone. With batching, a List of the exchanges of the records" }, + "producerIn": { "carriers": [ "any" ], "dependsOn": [ "valueSerializer" ], "description": "The value of the record, converted for the valueSerializer: text with the default StringSerializer, so a Map or a POJO is sent as its toString(): marshal it first. With useIterator, an Iterable or an Iterator is sent as one record per element" }, + "producerOut": { "carriers": [ "unchanged" ] } + }, "properties": { "topic": { "index": 0, "kind": "path", "displayName": "Topic", "group": "common", "label": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "Name of the topic to use. On the consumer you can use comma to separate multiple topics. A producer can only send a message to a single topic." }, "additionalProperties": { "index": 1, "kind": "parameter", "displayName": "Additional Properties", "group": "common", "label": "common", "required": false, "type": "object", "javaType": "java.util.Map", "prefix": "additionalProperties.", "multiValue": true, "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "Sets additional properties for either kafka consumer or kafka producer in case they can't be set directly on the camel configurations (e.g.: new Kafka properties that are not reflected yet in Camel configurations), the properties have to be prefixed with additionalProperties., e.g.: additionalProperties.transactional.id=12345&additionalProperties.schema.registry.url=http:\/\/localhost:8811\/avro. If the properties are set in the application.properties file, they must be prefixed with camel.component.kafka.additional-properties (camel.component.kafka-share.additional-properties for the Kafka Share component) followed by the property name enclosed in square brackets, for example the delivery.timeout.ms property in square brackets. This is a multi-value option with prefix: additionalProperties." }, diff --git a/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaEndpoint.java b/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaEndpoint.java index 30746c1de1731..85645fbcfe208 100644 --- a/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaEndpoint.java +++ b/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaEndpoint.java @@ -27,6 +27,9 @@ import org.apache.camel.Producer; import org.apache.camel.component.kafka.consumer.KafkaManualCommit; import org.apache.camel.component.kafka.consumer.KafkaManualCommitFactory; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.ClassResolver; import org.apache.camel.spi.EndpointServiceLocation; import org.apache.camel.spi.UriEndpoint; @@ -48,6 +51,21 @@ */ @UriEndpoint(firstVersion = "2.13.0", scheme = "kafka", title = "Kafka", syntax = "kafka:topic", category = { Category.MESSAGING }, headersClass = KafkaConstants.class) +@BodyTypes(consumerOut = @BodyType(value = { Carrier.TEXT, Carrier.NULL }, dependsOn = "valueDeserializer", + byOption = { + @BodyType.When(option = "valueDeserializer", + value = "org.apache.kafka.common.serialization.ByteArrayDeserializer", + carrier = { Carrier.BYTES, Carrier.NULL }, javaType = "byte[]"), + @BodyType.When(option = "batching", value = "true", carrier = Carrier.LIST) }, + description = "The value of the record: a String with the default valueDeserializer" + + " (StringDeserializer), null for a tombstone. With batching, a List of" + + " the exchanges of the records"), + producerIn = @BodyType(value = Carrier.ANY, dependsOn = "valueSerializer", + description = "The value of the record, converted for the valueSerializer: text with the" + + " default StringSerializer, so a Map or a POJO is sent as its toString():" + + " marshal it first. With useIterator, an Iterable or an Iterator is sent" + + " as one record per element"), + producerOut = @BodyType(Carrier.UNCHANGED)) public class KafkaEndpoint extends DefaultEndpoint implements MultipleConsumersSupport, EndpointServiceLocation { private static final Logger LOG = LoggerFactory.getLogger(KafkaEndpoint.class); diff --git a/components/camel-log/src/generated/resources/META-INF/org/apache/camel/component/log/log.json b/components/camel-log/src/generated/resources/META-INF/org/apache/camel/component/log/log.json index 1780d6316af2e..bf19483c839cb 100644 --- a/components/camel-log/src/generated/resources/META-INF/org/apache/camel/component/log/log.json +++ b/components/camel-log/src/generated/resources/META-INF/org/apache/camel/component/log/log.json @@ -30,6 +30,10 @@ "autowiredEnabled": { "index": 2, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which then gets configured on the component. This can be used for automatic configuring JDBC data sources, JMS connection factories, AWS Clients, etc." }, "exchangeFormatter": { "index": 3, "kind": "property", "displayName": "Exchange Formatter", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.ExchangeFormatter", "deprecated": false, "autowired": true, "secret": false, "description": "Sets a custom ExchangeFormatter to convert the Exchange to a String suitable for logging. If not specified, we default to DefaultExchangeFormatter." } }, + "bodyTypes": { + "producerIn": { "carriers": [ "any" ] }, + "producerOut": { "carriers": [ "unchanged" ] } + }, "properties": { "loggerName": { "index": 0, "kind": "path", "displayName": "Logger Name", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Name of the logging category to use" }, "groupActiveOnly": { "index": 1, "kind": "parameter", "displayName": "Group Active Only", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "If true, will hide stats when no new messages have been received for a time interval, if false, show stats regardless of message traffic." }, diff --git a/components/camel-log/src/main/java/org/apache/camel/component/log/LogEndpoint.java b/components/camel-log/src/main/java/org/apache/camel/component/log/LogEndpoint.java index 3f8eff9127211..ce89ba2e3c422 100644 --- a/components/camel-log/src/main/java/org/apache/camel/component/log/LogEndpoint.java +++ b/components/camel-log/src/main/java/org/apache/camel/component/log/LogEndpoint.java @@ -22,6 +22,9 @@ import org.apache.camel.LoggingLevel; import org.apache.camel.Processor; import org.apache.camel.Producer; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.CamelLogger; import org.apache.camel.spi.ExchangeFormatter; import org.apache.camel.spi.MaskingFormatter; @@ -45,6 +48,7 @@ @UriEndpoint(firstVersion = "1.1.0", scheme = "log", title = "Log Data", remote = false, internal = true, syntax = "log:loggerName", producerOnly = true, category = { Category.CORE, Category.MONITORING }) +@BodyTypes(producerIn = @BodyType(Carrier.ANY), producerOut = @BodyType(Carrier.UNCHANGED)) public class LogEndpoint extends ProcessorEndpoint implements LineNumberAware { private volatile Processor logger; diff --git a/components/camel-mock/src/generated/resources/META-INF/org/apache/camel/component/mock/mock.json b/components/camel-mock/src/generated/resources/META-INF/org/apache/camel/component/mock/mock.json index eadcc424ddeb2..4982ad8453e7f 100644 --- a/components/camel-mock/src/generated/resources/META-INF/org/apache/camel/component/mock/mock.json +++ b/components/camel-mock/src/generated/resources/META-INF/org/apache/camel/component/mock/mock.json @@ -30,6 +30,10 @@ "autowiredEnabled": { "index": 2, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which then gets configured on the component. This can be used for automatic configuring JDBC data sources, JMS connection factories, AWS Clients, etc." }, "exchangeFormatter": { "index": 3, "kind": "property", "displayName": "Exchange Formatter", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.ExchangeFormatter", "deprecated": false, "autowired": true, "secret": false, "description": "To use a custom ExchangeFormatter to format the Exchange into a String suitable for logging." } }, + "bodyTypes": { + "producerIn": { "carriers": [ "any" ] }, + "producerOut": { "carriers": [ "unchanged" ] } + }, "properties": { "name": { "index": 0, "kind": "path", "displayName": "Name", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Name of mock endpoint" }, "assertPeriod": { "index": 1, "kind": "parameter", "displayName": "Assert Period", "group": "producer", "label": "producer", "required": false, "type": "duration", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "description": "Sets a grace period after which the mock will re-assert to ensure the preliminary assertion is still valid. This is used, for example, to assert that exactly a number of messages arrive. For example, if the expected count was set to 5, then the assertion is satisfied when five or more messages arrive. To ensure that exactly 5 messages arrive, then you would need to wait a little period to ensure no further message arrives. This is what you can use this method for. By default, this period is disabled." }, diff --git a/components/camel-mock/src/main/java/org/apache/camel/component/mock/MockEndpoint.java b/components/camel-mock/src/main/java/org/apache/camel/component/mock/MockEndpoint.java index 6ac3b02ca1e5c..f3d9e83d1eb0d 100644 --- a/components/camel-mock/src/main/java/org/apache/camel/component/mock/MockEndpoint.java +++ b/components/camel-mock/src/main/java/org/apache/camel/component/mock/MockEndpoint.java @@ -49,6 +49,9 @@ import org.apache.camel.Processor; import org.apache.camel.Producer; import org.apache.camel.RuntimeCamelException; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.BrowsableEndpoint; import org.apache.camel.spi.HeadersMapFactory; import org.apache.camel.spi.InterceptSendToEndpoint; @@ -97,6 +100,7 @@ */ @UriEndpoint(firstVersion = "1.0.0", scheme = "mock", title = "Mock", syntax = "mock:name", producerOnly = true, remote = false, internal = true, category = { Category.CORE, Category.TESTING }, lenientProperties = true) +@BodyTypes(producerIn = @BodyType(Carrier.ANY), producerOut = @BodyType(Carrier.UNCHANGED)) public class MockEndpoint extends DefaultEndpoint implements BrowsableEndpoint, NotifyBuilderMatcher { private static final Logger LOG = LoggerFactory.getLogger(MockEndpoint.class); diff --git a/components/camel-platform-http/src/generated/resources/META-INF/org/apache/camel/component/platform/http/platform-http.json b/components/camel-platform-http/src/generated/resources/META-INF/org/apache/camel/component/platform/http/platform-http.json index e60f0367259fb..3c00abc0f66aa 100644 --- a/components/camel-platform-http/src/generated/resources/META-INF/org/apache/camel/component/platform/http/platform-http.json +++ b/components/camel-platform-http/src/generated/resources/META-INF/org/apache/camel/component/platform/http/platform-http.json @@ -33,6 +33,10 @@ "engine": { "index": 5, "kind": "property", "displayName": "Engine", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.component.platform.http.spi.PlatformHttpEngine", "deprecated": false, "autowired": false, "secret": false, "description": "An HTTP Server engine implementation to serve the requests" }, "headerFilterStrategy": { "index": 6, "kind": "property", "displayName": "Header Filter Strategy", "group": "filter", "label": "filter", "required": false, "type": "object", "javaType": "org.apache.camel.spi.HeaderFilterStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom org.apache.camel.spi.HeaderFilterStrategy to filter header to and from Camel message." } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "stream", "bytes", "null", "map", "file" ], "byOption": [ { "option": "useBodyHandler", "value": "false", "carriers": [ "pojo" ] } ], "description": "The request body: the stream cache with the Vert.x engine, a byte array with Spring Boot; null for a request without a body, such as a GET; a Map of the fields of a form (populateBodyWithForm); a File for an upload" }, + "consumerReply": { "carriers": [ "null", "text", "bytes", "stream" ], "description": "The response body: text, bytes, a stream or no body. A Map or a List fails: marshal it first" } + }, "properties": { "path": { "index": 0, "kind": "path", "displayName": "Path", "group": "consumer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The path under which this endpoint serves the HTTP requests, for proxy use 'proxy'" }, "consumes": { "index": 1, "kind": "parameter", "displayName": "Consumes", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The content type this endpoint accepts as an input, such as application\/xml or application\/json. null or *\/* mean no restriction." }, diff --git a/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/PlatformHttpEndpoint.java b/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/PlatformHttpEndpoint.java index c2c67935bff6d..c910d2ddce89e 100644 --- a/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/PlatformHttpEndpoint.java +++ b/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/PlatformHttpEndpoint.java @@ -34,6 +34,9 @@ import org.apache.camel.component.platform.http.spi.PlatformHttpSecurityHandler; import org.apache.camel.http.base.HttpHeaderFilterStrategy; import org.apache.camel.http.base.OAuthHttpSecuritySupport; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.EndpointServiceLocation; import org.apache.camel.spi.HeaderFilterStrategy; import org.apache.camel.spi.HeaderFilterStrategyAware; @@ -51,6 +54,16 @@ @UriEndpoint(firstVersion = "3.0.0", scheme = "platform-http", title = "Platform HTTP", syntax = "platform-http:path", category = { Category.HTTP }, consumerOnly = true) @Metadata(annotations = { "protocol=http" }) +@BodyTypes(consumerOut = @BodyType(value = { Carrier.STREAM, Carrier.BYTES, Carrier.NULL, Carrier.MAP, Carrier.FILE }, + byOption = @BodyType.When(option = "useBodyHandler", value = "false", + carrier = Carrier.POJO), + description = "The request body: the stream cache with the Vert.x engine, a byte array" + + " with Spring Boot; null for a request without a body, such as a GET; a" + + " Map of the fields of a form (populateBodyWithForm); a File for an" + + " upload"), + consumerReply = @BodyType(value = { Carrier.NULL, Carrier.TEXT, Carrier.BYTES, Carrier.STREAM }, + description = "The response body: text, bytes, a stream or no body. A Map or a List" + + " fails: marshal it first")) public class PlatformHttpEndpoint extends DefaultEndpoint implements AsyncEndpoint, HeaderFilterStrategyAware, EndpointServiceLocation { diff --git a/components/camel-quartz/src/generated/resources/META-INF/org/apache/camel/component/quartz/quartz.json b/components/camel-quartz/src/generated/resources/META-INF/org/apache/camel/component/quartz/quartz.json index d56eb70d9f853..8b8cc7acc2acb 100644 --- a/components/camel-quartz/src/generated/resources/META-INF/org/apache/camel/component/quartz/quartz.json +++ b/components/camel-quartz/src/generated/resources/META-INF/org/apache/camel/component/quartz/quartz.json @@ -37,6 +37,9 @@ "autoStartScheduler": { "index": 10, "kind": "property", "displayName": "Auto Start Scheduler", "group": "scheduler", "label": "scheduler", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether the scheduler should be auto started. This option is default true" }, "interruptJobsOnShutdown": { "index": 11, "kind": "property", "displayName": "Interrupt Jobs On Shutdown", "group": "scheduler", "label": "scheduler", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to interrupt jobs on shutdown, which forces the scheduler to shut down quicker and attempt to interrupt any running jobs. If this is enabled, then any running jobs can fail due to being interrupted. When a job is interrupted then Camel will mark the exchange to stop to continue routing and set java.util.concurrent.RejectedExecutionException as caused exception. Therefore, use this with care, as its often better to allow Camel jobs to complete and shutdown gracefully." } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "null" ], "description": "No body: the message only starts the route" } + }, "properties": { "groupName": { "index": 0, "kind": "path", "displayName": "Group Name", "group": "consumer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "Camel", "description": "The quartz group name to use. The combination of group name and trigger name should be unique." }, "triggerName": { "index": 1, "kind": "path", "displayName": "Trigger Name", "group": "consumer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The quartz trigger name to use. The combination of group name and trigger name should be unique." }, diff --git a/components/camel-quartz/src/main/java/org/apache/camel/component/quartz/QuartzEndpoint.java b/components/camel-quartz/src/main/java/org/apache/camel/component/quartz/QuartzEndpoint.java index 7748cdc5d5647..1b71b5f21b8c1 100644 --- a/components/camel-quartz/src/main/java/org/apache/camel/component/quartz/QuartzEndpoint.java +++ b/components/camel-quartz/src/main/java/org/apache/camel/component/quartz/QuartzEndpoint.java @@ -30,6 +30,9 @@ import org.apache.camel.Processor; import org.apache.camel.Producer; import org.apache.camel.Route; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.Metadata; import org.apache.camel.spi.UriEndpoint; import org.apache.camel.spi.UriParam; @@ -61,6 +64,7 @@ */ @UriEndpoint(firstVersion = "2.12.0", scheme = "quartz", title = "Quartz", syntax = "quartz:groupName/triggerName", remote = false, consumerOnly = true, category = { Category.SCHEDULING }) +@BodyTypes(consumerOut = @BodyType(value = Carrier.NULL, description = "No body: the message only starts the route")) public class QuartzEndpoint extends DefaultEndpoint { private static final Logger LOG = LoggerFactory.getLogger(QuartzEndpoint.class); diff --git a/components/camel-rest-openapi/src/generated/resources/META-INF/org/apache/camel/component/rest/openapi/rest-openapi.json b/components/camel-rest-openapi/src/generated/resources/META-INF/org/apache/camel/component/rest/openapi/rest-openapi.json index 22417ab779dfc..f175ff957c19b 100644 --- a/components/camel-rest-openapi/src/generated/resources/META-INF/org/apache/camel/component/rest/openapi/rest-openapi.json +++ b/components/camel-rest-openapi/src/generated/resources/META-INF/org/apache/camel/component/rest/openapi/rest-openapi.json @@ -47,6 +47,8 @@ "useGlobalSslContextParameters": { "index": 20, "kind": "property", "displayName": "Use Global Ssl Context Parameters", "group": "security", "label": "security", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Enable usage of global SSL context parameters." } }, "bodyTypes": { + "consumerOut": { "carriers": [ "stream", "bytes", "null", "map", "file" ], "dependsOn": [ "consumerComponentName" ], "description": "The request body as the HTTP consumer gives it (platform-http by default). With the json binding mode of the rest configuration, the object of the schema (bindingPackageScan), or a Map or a List" }, + "consumerReply": { "carriers": [ "null", "text", "bytes", "stream" ], "dependsOn": [ "consumerComponentName" ], "description": "The response body as the HTTP consumer writes it. With the json binding mode of the rest configuration, any object, which the binding writes as JSON" }, "producerIn": { "carriers": [ "null", "text", "bytes", "stream", "file" ], "dependsOn": [ "componentName" ], "description": "The request body of the operation, which the http component sends: text, bytes, a stream or a file. A Map or a List fails: marshal it first" }, "producerOut": { "carriers": [ "bytes", "stream", "null" ], "dependsOn": [ "componentName" ], "description": "The response body, as the http component gives it: a byte array or the stream cache; null for a response without a body" } }, diff --git a/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpoint.java b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpoint.java index 4f1c00d080713..bdc1e883077c2 100644 --- a/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpoint.java +++ b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpoint.java @@ -95,7 +95,17 @@ */ @UriEndpoint(firstVersion = "3.1.0", scheme = "rest-openapi", title = "REST OpenApi", syntax = "rest-openapi:specificationUri#operationId", category = { Category.REST, Category.API }) -@BodyTypes(producerIn = @BodyType(value = { Carrier.NULL, Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }, +@BodyTypes(consumerOut = @BodyType(value = { Carrier.STREAM, Carrier.BYTES, Carrier.NULL, Carrier.MAP, Carrier.FILE }, + dependsOn = "consumerComponentName", + description = "The request body as the HTTP consumer gives it (platform-http by default)." + + " With the json binding mode of the rest configuration, the object of the" + + " schema (bindingPackageScan), or a Map or a List"), + consumerReply = @BodyType(value = { Carrier.NULL, Carrier.TEXT, Carrier.BYTES, Carrier.STREAM }, + dependsOn = "consumerComponentName", + description = "The response body as the HTTP consumer writes it. With the json binding" + + " mode of the rest configuration, any object, which the binding writes as" + + " JSON"), + producerIn = @BodyType(value = { Carrier.NULL, Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }, dependsOn = "componentName", description = "The request body of the operation, which the http component sends: text," + " bytes, a stream or a file. A Map or a List fails: marshal it first"), diff --git a/components/camel-rest/src/generated/resources/META-INF/org/apache/camel/component/rest/rest.json b/components/camel-rest/src/generated/resources/META-INF/org/apache/camel/component/rest/rest.json index 041ff7467c2e6..8ad7a7dbc0771 100644 --- a/components/camel-rest/src/generated/resources/META-INF/org/apache/camel/component/rest/rest.json +++ b/components/camel-rest/src/generated/resources/META-INF/org/apache/camel/component/rest/rest.json @@ -43,6 +43,12 @@ "Accept": { "index": 4, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The media type such as: 'text\/xml', or 'application\/json' this REST service accepts.", "constantName": "org.apache.camel.component.rest.RestConstants#ACCEPT" }, "CamelHttpResponseCode": { "index": 5, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "important": true, "description": "The http response code", "constantName": "org.apache.camel.component.rest.RestConstants#HTTP_RESPONSE_CODE" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "stream", "bytes", "null", "map", "file" ], "dependsOn": [ "consumerComponentName" ], "byOption": [ { "option": "bindingMode", "value": "json", "carriers": [ "map", "list", "pojo" ] } ], "description": "The request body as the HTTP consumer gives it (platform-http by default). With bindingMode json, the object of inType, or a Map or a List without inType" }, + "consumerReply": { "carriers": [ "null", "text", "bytes", "stream" ], "dependsOn": [ "consumerComponentName" ], "byOption": [ { "option": "bindingMode", "value": "json", "carriers": [ "any" ] } ], "description": "The response body as the HTTP consumer writes it. With bindingMode json, any object, which the binding writes as JSON" }, + "producerIn": { "carriers": [ "null", "text", "bytes", "stream", "file" ], "dependsOn": [ "producerComponentName" ], "byOption": [ { "option": "bindingMode", "value": "json", "carriers": [ "any" ] } ], "description": "The request body the HTTP producer sends (the http component by default). With bindingMode json, any object, which the binding writes as JSON" }, + "producerOut": { "carriers": [ "bytes", "stream", "null" ], "dependsOn": [ "producerComponentName", "outType" ], "description": "The response body as the HTTP producer gives it; with bindingMode json and outType, an object of outType" } + }, "properties": { "method": { "index": 0, "kind": "path", "displayName": "Method", "group": "common", "label": "common", "required": true, "type": "enum", "javaType": "java.lang.String", "enum": [ "get", "post", "put", "delete", "patch", "head", "trace", "connect", "options" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "HTTP method to use." }, "path": { "index": 1, "kind": "path", "displayName": "Path", "group": "common", "label": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The base path, can use * as path suffix to support wildcard HTTP route matching." }, diff --git a/components/camel-rest/src/main/java/org/apache/camel/component/rest/RestEndpoint.java b/components/camel-rest/src/main/java/org/apache/camel/component/rest/RestEndpoint.java index e4275bca1c72e..66b43a1957e16 100644 --- a/components/camel-rest/src/main/java/org/apache/camel/component/rest/RestEndpoint.java +++ b/components/camel-rest/src/main/java/org/apache/camel/component/rest/RestEndpoint.java @@ -26,6 +26,9 @@ import org.apache.camel.NoSuchBeanException; import org.apache.camel.Processor; import org.apache.camel.Producer; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.Metadata; import org.apache.camel.spi.RestConfiguration; import org.apache.camel.spi.RestConsumerFactory; @@ -55,6 +58,27 @@ @Metadata(annotations = { "protocol=http", }) +@BodyTypes(consumerOut = @BodyType(value = { Carrier.STREAM, Carrier.BYTES, Carrier.NULL, Carrier.MAP, Carrier.FILE }, + dependsOn = "consumerComponentName", + byOption = @BodyType.When(option = "bindingMode", value = "json", + carrier = { Carrier.MAP, Carrier.LIST, Carrier.POJO }), + description = "The request body as the HTTP consumer gives it (platform-http by" + + " default). With bindingMode json, the object of inType, or a Map or a" + + " List without inType"), + consumerReply = @BodyType(value = { Carrier.NULL, Carrier.TEXT, Carrier.BYTES, Carrier.STREAM }, + dependsOn = "consumerComponentName", + byOption = @BodyType.When(option = "bindingMode", value = "json", carrier = Carrier.ANY), + description = "The response body as the HTTP consumer writes it. With bindingMode json," + + " any object, which the binding writes as JSON"), + producerIn = @BodyType(value = { Carrier.NULL, Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }, + dependsOn = "producerComponentName", + byOption = @BodyType.When(option = "bindingMode", value = "json", carrier = Carrier.ANY), + description = "The request body the HTTP producer sends (the http component by default)." + + " With bindingMode json, any object, which the binding writes as JSON"), + producerOut = @BodyType(value = { Carrier.BYTES, Carrier.STREAM, Carrier.NULL }, + dependsOn = { "producerComponentName", "outType" }, + description = "The response body as the HTTP producer gives it; with bindingMode json and" + + " outType, an object of outType")) public class RestEndpoint extends DefaultEndpoint { public static final String[] DEFAULT_REST_CONSUMER_COMPONENTS diff --git a/components/camel-scheduler/src/generated/resources/META-INF/org/apache/camel/component/scheduler/scheduler.json b/components/camel-scheduler/src/generated/resources/META-INF/org/apache/camel/component/scheduler/scheduler.json index 491fc0b24d373..83c5065eeca03 100644 --- a/components/camel-scheduler/src/generated/resources/META-INF/org/apache/camel/component/scheduler/scheduler.json +++ b/components/camel-scheduler/src/generated/resources/META-INF/org/apache/camel/component/scheduler/scheduler.json @@ -34,6 +34,9 @@ "headers": { "CamelMessageTimestamp": { "index": 0, "kind": "header", "displayName": "", "group": "consumer", "label": "", "required": false, "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The timestamp of the message", "constantName": "org.apache.camel.component.scheduler.SchedulerConstants#MESSAGE_TIMESTAMP" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "null" ], "description": "No body: the message only starts the route" } + }, "properties": { "name": { "index": 0, "kind": "path", "displayName": "Name", "group": "consumer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The name of the scheduler" }, "includeMetadata": { "index": 1, "kind": "parameter", "displayName": "Include Metadata", "group": "consumer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to include metadata in the exchange such as fired time, timer name, timer count etc." }, diff --git a/components/camel-scheduler/src/main/java/org/apache/camel/component/scheduler/SchedulerEndpoint.java b/components/camel-scheduler/src/main/java/org/apache/camel/component/scheduler/SchedulerEndpoint.java index 54ab073da275d..ac6aa6a500c9c 100644 --- a/components/camel-scheduler/src/main/java/org/apache/camel/component/scheduler/SchedulerEndpoint.java +++ b/components/camel-scheduler/src/main/java/org/apache/camel/component/scheduler/SchedulerEndpoint.java @@ -22,6 +22,9 @@ import org.apache.camel.Consumer; import org.apache.camel.Processor; import org.apache.camel.Producer; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.Metadata; import org.apache.camel.spi.UriEndpoint; import org.apache.camel.spi.UriParam; @@ -37,6 +40,7 @@ @UriEndpoint(firstVersion = "2.15.0", scheme = "scheduler", title = "Scheduler", syntax = "scheduler:name", remote = false, consumerOnly = true, category = { Category.CORE, Category.SCHEDULING }, headersClass = SchedulerConstants.class) +@BodyTypes(consumerOut = @BodyType(value = Carrier.NULL, description = "No body: the message only starts the route")) public class SchedulerEndpoint extends ScheduledPollEndpoint { @UriPath diff --git a/components/camel-snakeyaml/src/generated/resources/META-INF/org/apache/camel/component/snakeyaml/snakeYaml.json b/components/camel-snakeyaml/src/generated/resources/META-INF/org/apache/camel/component/snakeyaml/snakeYaml.json index cc69504fdca03..a24fa2186d37c 100644 --- a/components/camel-snakeyaml/src/generated/resources/META-INF/org/apache/camel/component/snakeyaml/snakeYaml.json +++ b/components/camel-snakeyaml/src/generated/resources/META-INF/org/apache/camel/component/snakeyaml/snakeYaml.json @@ -15,6 +15,12 @@ "modelName": "yaml", "modelJavaType": "org.apache.camel.model.dataformat.YAMLDataFormat" }, + "bodyTypes": { + "marshalIn": { "carriers": [ "any" ], "description": "A Map, a List or a POJO to write as YAML" }, + "marshalOut": { "carriers": [ "stream" ], "format": "yaml", "description": "The YAML in the stream cache, or a byte array when stream caching is off" }, + "unmarshalIn": { "carriers": [ "text", "bytes", "stream", "file" ], "format": "yaml" }, + "unmarshalOut": { "carriers": [ "map", "list" ], "dependsOn": [ "unmarshalType" ], "description": "A Map (LinkedHashMap) for a mapping, a List (ArrayList) for a sequence; a document that is a single value gives that value" } + }, "properties": { "id": { "index": 0, "kind": "attribute", "displayName": "Id", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The id of this node" }, "library": { "index": 1, "kind": "attribute", "displayName": "Library", "group": "common", "required": false, "type": "enum", "javaType": "org.apache.camel.model.dataformat.YAMLLibrary", "enum": [ "SnakeYAML" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "SnakeYAML", "description": "Which yaml library to use. By default it is SnakeYAML." }, diff --git a/components/camel-snakeyaml/src/main/java/org/apache/camel/component/snakeyaml/SnakeYAMLDataFormat.java b/components/camel-snakeyaml/src/main/java/org/apache/camel/component/snakeyaml/SnakeYAMLDataFormat.java index 7957afe3e8826..56205efa65b3e 100644 --- a/components/camel-snakeyaml/src/main/java/org/apache/camel/component/snakeyaml/SnakeYAMLDataFormat.java +++ b/components/camel-snakeyaml/src/main/java/org/apache/camel/component/snakeyaml/SnakeYAMLDataFormat.java @@ -25,6 +25,9 @@ import org.apache.camel.CamelContext; import org.apache.camel.CamelContextAware; import org.apache.camel.Exchange; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.DataFormat; import org.apache.camel.spi.DataFormatName; import org.apache.camel.spi.annotations.Dataformat; @@ -47,6 +50,13 @@ * SnakeYAML */ @Dataformat("snakeYaml") +@BodyTypes(marshalIn = @BodyType(value = Carrier.ANY, description = "A Map, a List or a POJO to write as YAML"), + marshalOut = @BodyType(value = Carrier.STREAM, format = "yaml", + description = "The YAML in the stream cache, or a byte array when stream caching is off"), + unmarshalIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }, format = "yaml"), + unmarshalOut = @BodyType(value = { Carrier.MAP, Carrier.LIST }, dependsOn = "unmarshalType", + description = "A Map (LinkedHashMap) for a mapping, a List (ArrayList) for a sequence; a" + + " document that is a single value gives that value")) public final class SnakeYAMLDataFormat extends ServiceSupport implements DataFormat, DataFormatName, CamelContextAware { private CamelContext camelContext; diff --git a/components/camel-xpath/src/generated/resources/META-INF/org/apache/camel/language/xpath/xpath.json b/components/camel-xpath/src/generated/resources/META-INF/org/apache/camel/language/xpath/xpath.json index 0a6a706898992..e7e50b9357a1e 100644 --- a/components/camel-xpath/src/generated/resources/META-INF/org/apache/camel/language/xpath/xpath.json +++ b/components/camel-xpath/src/generated/resources/META-INF/org/apache/camel/language/xpath/xpath.json @@ -15,6 +15,10 @@ "modelName": "xpath", "modelJavaType": "org.apache.camel.model.language.XPathExpression" }, + "bodyTypes": { + "expressionIn": { "carriers": [ "text", "bytes", "stream", "file", "pojo" ], "format": "xml", "description": "XML as text, bytes, a stream or a file, or a DOM" }, + "expressionOut": { "carriers": [ "pojo" ], "javaType": "org.w3c.dom.NodeList", "dependsOn": [ "resultType" ], "byOption": [ { "option": "resultQName", "value": "STRING", "carriers": [ "text" ], "javaType": "java.lang.String" }, { "option": "resultQName", "value": "NUMBER", "carriers": [ "value" ], "javaType": "java.lang.Double" }, { "option": "resultQName", "value": "BOOLEAN", "carriers": [ "value" ], "javaType": "java.lang.Boolean" }, { "option": "resultQName", "value": "NODE", "carriers": [ "pojo" ], "javaType": "org.w3c.dom.Node" } ], "description": "The nodes the expression selects" } + }, "properties": { "id": { "index": 0, "kind": "attribute", "displayName": "Id", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The id of this node." }, "expression": { "index": 1, "kind": "value", "displayName": "Expression", "group": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The expression value in your chosen language syntax." }, diff --git a/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPathLanguage.java b/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPathLanguage.java index 2aca26d203812..7487f7f91317d 100644 --- a/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPathLanguage.java +++ b/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPathLanguage.java @@ -25,6 +25,9 @@ import org.apache.camel.CamelContext; import org.apache.camel.Expression; import org.apache.camel.Predicate; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.PropertyConfigurer; import org.apache.camel.spi.annotations.Language; import org.apache.camel.support.SingleInputTypedLanguageSupport; @@ -34,6 +37,19 @@ * XPath language. */ @Language("xpath") +@BodyTypes(expressionIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, Carrier.POJO }, + format = "xml", description = "XML as text, bytes, a stream or a file, or a DOM"), + expressionOut = @BodyType(value = Carrier.POJO, javaType = "org.w3c.dom.NodeList", dependsOn = "resultType", + byOption = { + @BodyType.When(option = "resultQName", value = "STRING", carrier = Carrier.TEXT, + javaType = "java.lang.String"), + @BodyType.When(option = "resultQName", value = "NUMBER", carrier = Carrier.VALUE, + javaType = "java.lang.Double"), + @BodyType.When(option = "resultQName", value = "BOOLEAN", carrier = Carrier.VALUE, + javaType = "java.lang.Boolean"), + @BodyType.When(option = "resultQName", value = "NODE", carrier = Carrier.POJO, + javaType = "org.w3c.dom.Node") }, + description = "The nodes the expression selects")) public class XPathLanguage extends SingleInputTypedLanguageSupport implements PropertyConfigurer { private QName resultQName; private Class documentType; diff --git a/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/constant/constant.json b/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/constant/constant.json index e1c1c40cb2ea8..9889e6d5b957e 100644 --- a/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/constant/constant.json +++ b/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/constant/constant.json @@ -15,6 +15,9 @@ "modelName": "constant", "modelJavaType": "org.apache.camel.model.language.ConstantExpression" }, + "bodyTypes": { + "expressionOut": { "carriers": [ "text" ], "javaType": "java.lang.String", "dependsOn": [ "resultType" ], "description": "The text as it is written; resource:classpath:name loads the text of the resource" } + }, "properties": { "id": { "index": 0, "kind": "attribute", "displayName": "Id", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The id of this node." }, "expression": { "index": 1, "kind": "value", "displayName": "Expression", "group": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The expression value in your chosen language syntax." }, diff --git a/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json b/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json index 6de66be485c43..5648229337930 100644 --- a/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json +++ b/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json @@ -15,6 +15,10 @@ "modelName": "simple", "modelJavaType": "org.apache.camel.model.language.SimpleExpression" }, + "bodyTypes": { + "expressionIn": { "carriers": [ "any" ] }, + "expressionOut": { "carriers": [ "unknown" ], "description": "A text with functions gives text. A single function, such as ${body} or ${header.foo}, gives its value as it is" } + }, "properties": { "id": { "index": 0, "kind": "attribute", "displayName": "Id", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The id of this node." }, "expression": { "index": 1, "kind": "value", "displayName": "Expression", "group": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The expression value in your chosen language syntax." }, diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/constant/ConstantLanguage.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/constant/ConstantLanguage.java index f15bb94fd69c3..738723733d011 100644 --- a/core/camel-core-languages/src/main/java/org/apache/camel/language/constant/ConstantLanguage.java +++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/constant/ConstantLanguage.java @@ -20,6 +20,9 @@ import org.apache.camel.NoTypeConversionAvailableException; import org.apache.camel.Predicate; import org.apache.camel.RuntimeCamelException; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.annotations.Language; import org.apache.camel.support.ExpressionToPredicateAdapter; import org.apache.camel.support.LanguageSupport; @@ -29,6 +32,9 @@ * A language for constant expressions. */ @Language("constant") +@BodyTypes(expressionOut = @BodyType(value = Carrier.TEXT, javaType = "java.lang.String", dependsOn = "resultType", + description = "The text as it is written; resource:classpath:name loads the text of the" + + " resource")) public class ConstantLanguage extends LanguageSupport { public static Expression constant(Object value) { diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleLanguage.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleLanguage.java index ddfd3f7c4b0d2..a43ebbb47d4af 100644 --- a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleLanguage.java +++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleLanguage.java @@ -23,6 +23,9 @@ import org.apache.camel.Expression; import org.apache.camel.Predicate; import org.apache.camel.StaticService; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.annotations.Language; import org.apache.camel.support.CamelContextHelper; import org.apache.camel.support.ExpressionAdapter; @@ -40,6 +43,10 @@ * The Camel simple language. */ @Language(value = "simple", functionsClass = SimpleConstants.class, operatorsClass = SimpleOperatorConstants.class) +@BodyTypes(expressionIn = @BodyType(Carrier.ANY), + expressionOut = @BodyType(value = Carrier.UNKNOWN, + description = "A text with functions gives text. A single function, such as ${body} or" + + " ${header.foo}, gives its value as it is")) public class SimpleLanguage extends LanguageSupport implements StaticService { private static final Logger LOG = LoggerFactory.getLogger(SimpleLanguage.class); diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc index d22ffd4ca0b3f..618d6a540959f 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc @@ -1267,7 +1267,7 @@ its `application.properties` key as `configKey`. `camel_catalog_doc` without `optionsFilter` now lists the required options and the first 20 other options of a component in full, and the rest by name in `moreOptions` (`limit` lists more in full), and an answer is cut at -16 000 characters with a marker. An option of both the endpoint and the component is listed once, with `scope` +18 000 characters with a marker. An option of both the endpoint and the component is listed once, with `scope` set to `endpoint,component` and its `configKey`, instead of twice, and `matchedOptions` counts option names. A name that only matches a word of an EIP title (such as `retry`) no longer returns that EIP: the answer is a not-found with suggestions. diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-mcp.adoc b/docs/user-manual/modules/ROOT/pages/camel-jbang-mcp.adoc index b39a4bd9e1fe1..305f64e8ccd0b 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-jbang-mcp.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-mcp.adoc @@ -315,8 +315,11 @@ project `directory` as an argument, the runtime tools take the integration `name (`mail.*` of smtp) are listed in `prefixOptions`. A filter that matches no option says so, with the closest option names. The answer lists the `sections` of the component page, those that contain the filter first, and `docPage=
` returns one. A full key (`camel.server.port`, `camel.jbang.dependencies`) answers - its group of `application.properties` keys, the key first. An answer is cut at 16 000 characters, with a - marker that says what was left out. + its group of `application.properties` keys, the key first. A component, data format or language that + declares its body types has them in `bodyTypes`, a line per side: what a `from:` gives the route + (`consumerOut`), what a `to:` can send (`producerIn`) and the body after it (`producerOut`), and so on (see + xref:camel-catalog.adoc#_body_types_in_the_catalog[Body types in the Catalog]). An answer is cut at 18 000 + characters, with a marker that says what was left out. `includeOptions` picks the options listed: `common` (the default) leaves out the deprecated and advanced ones and says how many with `omittedOptions`, `required` lists the required ones, `all` everything, `false` none; `optionsFilter` matches a keyword in the option names, descriptions and groups and searches all diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocs.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocs.java index d69c883fbab17..b6777140ce3b7 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocs.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocs.java @@ -39,6 +39,7 @@ import org.apache.camel.tooling.model.ApiReferenceModel; import org.apache.camel.tooling.model.BaseModel; import org.apache.camel.tooling.model.BaseOptionModel; +import org.apache.camel.tooling.model.BodyTypeModel; import org.apache.camel.tooling.model.ComponentModel; import org.apache.camel.tooling.model.DataFormatModel; import org.apache.camel.tooling.model.EipModel; @@ -66,12 +67,10 @@ public final class CatalogDocs { /** The sub-pages of a language's documentation (simple has functions, operators, ognl and advanced). */ private static final List LANGUAGE_DOC_PAGES = List.of("functions", "operators", "ognl", "advanced"); - static final String BODY_TYPES_HINT = "the message body with the default options. consumerOut: the body a from:" - + " gives the route; consumerReply: the bodies it can reply with; producerIn:" - + " the bodies a to: can send; producerOut: the body after the to:; marshal," - + " unmarshal and expression In/Out: the same for a data format and a" - + " language. Carriers: text, bytes, stream, file, value, map, list, pojo," - + " null, unchanged, any, unknown. A side not listed is not known"; + static final String BODY_TYPES_HINT = "the body with the default options: consumerOut is what a from: gives the" + + " route, consumerReply what it can reply with, producerIn what a to: can" + + " send, producerOut the body after the to: (and so on for marshal, unmarshal" + + " and expression); a side not listed is not known"; /** * The rules a small model gets wrong most: functions live inside the placeholder, operators between placeholders. @@ -1635,11 +1634,49 @@ private static void addCommonModelFields(JsonObject result, BaseModel model) } if (!model.getBodyTypes().isEmpty()) { // what the body is after the step and what it needs, which the options do not say (CAMEL-24961) - result.put("bodyTypes", JsonMapper.asJsonObjectBodyTypes(model.getBodyTypes())); + result.put("bodyTypes", bodyTypes(model.getBodyTypes())); result.put("bodyTypesHint", BODY_TYPES_HINT); } } + /** + * The body types a line per side, as compact as the options are: the carriers, the Java type, the format, the + * description, the body for other option values, and the options that make it not known (CAMEL-24961). + */ + static JsonObject bodyTypes(Map bodyTypes) { + JsonObject answer = new JsonObject(); + for (Map.Entry entry : bodyTypes.entrySet()) { + BodyTypeModel side = entry.getValue(); + StringBuilder line = new StringBuilder(carriers(side.getCarriers(), side.getJavaType())); + if (side.getFormat() != null) { + line.append(", as ").append(side.getFormat()); + } + if (side.getDescription() != null) { + line.append(". ").append(side.getDescription()); + } + if (!side.getByOption().isEmpty()) { + List values = new ArrayList<>(); + for (BodyTypeModel.OptionBodyTypeModel option : side.getByOption()) { + values.add(option.getOption() + "=" + option.getValue() + ": " + + carriers(option.getCarriers(), option.getJavaType())); + } + line.append(". With ").append(String.join("; ", values)); + } + if (!side.getDependsOn().isEmpty()) { + line.append(". Not known when ").append(String.join(" or ", side.getDependsOn())).append(" is set"); + } + answer.put(entry.getKey(), line.toString()); + } + return answer; + } + + private static String carriers(List carriers, String javaType) { + String answer = carriers.size() == 1 + ? carriers.get(0) + : String.join(", ", carriers.subList(0, carriers.size() - 1)) + " or " + carriers.get(carriers.size() - 1); + return javaType != null ? answer + " (" + javaType + ")" : answer; + } + /** * The rules of an endpoint URI for this component, spelled out with its own path parts: what a small model gets * wrong most is a path option written as a query parameter or the other way round, and the YAML form. @@ -1696,9 +1733,10 @@ static String usage(ComponentModel model) { /** * The longest answer, in characters: activemq listed 88 options in 38 K characters, which a client rejected or - * spilled to a file instead of showing it to the model (CAMEL-25541). + * spilled to a file instead of showing it to the model (CAMEL-25541). Raised from 16000 for the body types + * (CAMEL-24961), which the jms components have on four sides. */ - static final int MAX_ANSWER_LENGTH = 16000; + static final int MAX_ANSWER_LENGTH = 18000; /** How much of the text of a page a cut answer keeps at least, before its lists are cut. */ static final int MIN_DOC_KEPT = 8000; diff --git a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java index 87de31b39b658..51fc53d6d0bfc 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java +++ b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java @@ -135,21 +135,29 @@ void componentOptionsSayWhetherTheyArePathOrQueryAndTheUriRulesComeAlong() throw @Test void theBodyTypesSayWhatTheBodyIsAfterAStep() throws Exception { - // CAMEL-24961: what the options of a component do not say + // CAMEL-24961: what the options of a component do not say, a line per side JsonObject xslt = catalogDoc(Map.of("name", "xslt", "kind", "component", "includeOptions", false)); - JsonObject out = ((JsonObject) xslt.get("bodyTypes")).getMap("producerOut"); - assertEquals(List.of("text"), out.getCollection("carriers")); - assertEquals("java.lang.String", out.getString("javaType")); - assertTrue(xslt.getString("bodyTypesHint").contains("A side not listed is not known")); + JsonObject sides = (JsonObject) xslt.get("bodyTypes"); + assertEquals("text, bytes, stream, file or pojo, as xml. The XML to transform: text, bytes, a stream, a file, a" + + " DOM or another javax.xml.transform.Source. A Map, a List or no body fails. With" + + " failOnNullBody=false: null, text, bytes, stream, file or pojo. Not known when source is set", + sides.getString("producerIn")); + assertTrue(sides.getString("producerOut").startsWith("text (java.lang.String). The result of the transformation." + + " With output=bytes: bytes (byte[]); output=DOM: pojo")); + assertTrue(xslt.getString("bodyTypesHint").contains("a side not listed is not known")); JsonObject json = catalogDoc(Map.of("name", "json", "kind", "dataformat", "includeOptions", false)); - JsonObject unmarshalOut = ((JsonObject) json.get("bodyTypes")).getMap("unmarshalOut"); - assertEquals(List.of("map", "list"), unmarshalOut.getCollection("carriers")); + assertTrue(((JsonObject) json.get("bodyTypes")).getString("unmarshalOut").startsWith("map or list. ")); + + // a language: what its expressions read, and their result + JsonObject xpath = catalogDoc(Map.of("name", "xpath", "kind", "language", "includeOptions", false)); + assertTrue(((JsonObject) xpath.get("bodyTypes")).getString("expressionOut") + .startsWith("pojo (org.w3c.dom.NodeList). The nodes the expression selects. With resultQName=STRING: text")); // a component that does not declare them has none, rather than a guess - JsonObject kafka = catalogDoc(Map.of("name", "kafka", "kind", "component", "includeOptions", false)); - assertNull(kafka.get("bodyTypes")); - assertNull(kafka.get("bodyTypesHint")); + JsonObject mongodb = catalogDoc(Map.of("name", "mongodb", "kind", "component", "includeOptions", false)); + assertNull(mongodb.get("bodyTypes")); + assertNull(mongodb.get("bodyTypesHint")); } @Test From 82f4901169e5db3f71800a78a98c4058b333b975 Mon Sep 17 00:00:00 2001 From: Claus Ibsen Date: Wed, 21 Oct 2026 14:40:19 +0200 Subject: [PATCH 3/9] CAMEL-24961: camel-catalog - the body types of paho-mqtt5 and mongodb paho-mqtt5: the consumer gives the payload as a byte array, the producer sends what converts to one and leaves the body unchanged. mongodb: the consumer gives a Document (or null for a change without its full document with changeStreams); the producer takes a document or a query, and leaves a body by operation: a Document or null for a find of one, a List for findAll, findDistinct and aggregate, a Long for count, the result of the driver for the other writes. Read from the code that sets the body, and checked against a mosquitto and a mongodb container started with camel infra. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claus Ibsen --- .../camel/catalog/components/mongodb.json | 5 ++ .../camel/catalog/components/paho-mqtt5.json | 5 ++ .../camel/component/mongodb/mongodb.json | 5 ++ .../component/mongodb/MongoDbEndpoint.java | 53 +++++++++++++++++++ .../component/paho/mqtt5/paho-mqtt5.json | 5 ++ .../paho/mqtt5/PahoMqtt5Endpoint.java | 8 +++ .../core/commands/ai/CatalogDocsTest.java | 6 +-- 7 files changed, 84 insertions(+), 3 deletions(-) diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/mongodb.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/mongodb.json index 62ad7c16ba705..6bfb9c23245ef 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/mongodb.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/mongodb.json @@ -58,6 +58,11 @@ "CamelMongoDbReturnDocumentType": { "index": 24, "kind": "header", "displayName": "", "group": "producer update one and return", "label": "producer update one and return", "required": false, "javaType": "com.mongodb.client.model.ReturnDocument", "enum": [ "BEFORE", "AFTER" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Indicates which document to return, the document before or after an update and return atomic operation.", "constantName": "org.apache.camel.component.mongodb.MongoDbConstants#RETURN_DOCUMENT" }, "CamelMongoDbOperationOption": { "index": 25, "kind": "header", "displayName": "", "group": "producer update one and options", "label": "producer update one and options", "required": false, "javaType": "Object", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Options to use. When set, options set in the headers will be ignored.", "constantName": "org.apache.camel.component.mongodb.MongoDbConstants#OPTIONS" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "pojo" ], "javaType": "org.bson.Document", "byOption": [ { "option": "consumerType", "value": "changeStreams", "carriers": [ "pojo", "null" ] } ], "description": "The document added to the capped collection; with changeStreams the full document of the change, null when the change has none (an update without fullDocument=updateLookup)" }, + "producerIn": { "carriers": [ "any" ], "description": "A document or a query: a Document, a Map or JSON text; a List of them for insert, aggregate and update" }, + "producerOut": { "carriers": [ "unknown" ], "dependsOn": [ "outputType", "writeResultAsHeader" ], "byOption": [ { "option": "operation", "value": "findById", "carriers": [ "pojo", "null" ], "javaType": "org.bson.Document" }, { "option": "operation", "value": "findOneByQuery", "carriers": [ "pojo", "null" ], "javaType": "org.bson.Document" }, { "option": "operation", "value": "findOneAndDelete", "carriers": [ "pojo", "null" ], "javaType": "org.bson.Document" }, { "option": "operation", "value": "findOneAndReplace", "carriers": [ "pojo", "null" ], "javaType": "org.bson.Document" }, { "option": "operation", "value": "findOneAndUpdate", "carriers": [ "pojo", "null" ], "javaType": "org.bson.Document" }, { "option": "operation", "value": "findAll", "carriers": [ "list" ] }, { "option": "operation", "value": "findDistinct", "carriers": [ "list" ] }, { "option": "operation", "value": "aggregate", "carriers": [ "list" ] }, { "option": "operation", "value": "count", "carriers": [ "value" ], "javaType": "java.lang.Long" }, { "option": "operation", "value": "insert", "carriers": [ "pojo", "list" ] }, { "option": "operation", "value": "save", "carriers": [ "pojo" ] }, { "option": "operation", "value": "update", "carriers": [ "pojo" ], "javaType": "com.mongodb.client.result.UpdateResult" }, { "option": "operation", "value": "remove", "carriers": [ "pojo" ], "javaType": "com.mongodb.client.result.DeleteResult" }, { "option": "operation", "value": "bulkWrite", "carriers": [ "pojo" ], "javaType": "com.mongodb.bulk.BulkWriteResult" }, { "option": "operation", "value": "command", "carriers": [ "pojo" ], "javaType": "org.bson.Document" }, { "option": "operation", "value": "getDbStats", "carriers": [ "pojo" ], "javaType": "org.bson.Document" }, { "option": "operation", "value": "getColStats", "carriers": [ "pojo" ], "javaType": "org.bson.Document" } ], "description": "By the operation: a Document or null for a find of one, a List for findAll, findDistinct and aggregate, a Long for count, the document added for insert, the result of the driver for the other writes, and a Document for command and the stats" } + }, "properties": { "connectionBean": { "index": 0, "kind": "path", "displayName": "Connection Bean", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Sets the connection bean reference used to lookup a client for connecting to a database if no hosts parameter is present." }, "collection": { "index": 1, "kind": "parameter", "displayName": "Collection", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "endpointIdentity": true, "description": "Sets the name of the MongoDB collection to bind to this endpoint" }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/paho-mqtt5.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/paho-mqtt5.json index 3959801c5ee4e..0f670d9baa037 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/paho-mqtt5.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/paho-mqtt5.json @@ -66,6 +66,11 @@ "CamelPahoMqtt5OverrideTopic": { "index": 4, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The name of topic to override and send to instead of topic specified on endpoint.", "constantName": "org.apache.camel.component.paho.mqtt5.PahoMqtt5Constants#CAMEL_PAHO_OVERRIDE_TOPIC" }, "CamelPahoMqtt5MsgProperties": { "index": 5, "kind": "header", "displayName": "", "group": "producer", "label": "consumer,producer", "required": false, "javaType": "org.eclipse.paho.mqttv5.common.packet.MqttProperties", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Consumer: The properties set on the incoming message. Producer: The properties to be set on the outgoing message.", "constantName": "org.apache.camel.component.paho.mqtt5.PahoMqtt5Constants#CAMEL_PAHO_MSG_PROPERTIES" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "bytes" ], "javaType": "byte[]", "description": "The payload of the MQTT message" }, + "producerIn": { "carriers": [ "text", "bytes", "stream", "file" ], "description": "What converts to a byte array: text, bytes, a stream or a file, sent as the payload" }, + "producerOut": { "carriers": [ "unchanged" ] } + }, "properties": { "topic": { "index": 0, "kind": "path", "displayName": "Topic", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Name of the topic" }, "automaticReconnect": { "index": 1, "kind": "parameter", "displayName": "Automatic Reconnect", "group": "common", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "configurationClass": "org.apache.camel.component.paho.mqtt5.PahoMqtt5Configuration", "configurationField": "configuration", "description": "Sets whether the client will automatically attempt to reconnect to the server if the connection is lost. If set to false, the client will not attempt to automatically reconnect to the server in the event that the connection is lost. If set to true, in the event that the connection is lost, the client will attempt to reconnect to the server. It will initially wait 1 second before it attempts to reconnect, for every failed reconnect attempt, the delay will double until it is at 2 minutes at which point the delay will stay at 2 minutes." }, diff --git a/components/camel-mongodb/src/generated/resources/META-INF/org/apache/camel/component/mongodb/mongodb.json b/components/camel-mongodb/src/generated/resources/META-INF/org/apache/camel/component/mongodb/mongodb.json index 62ad7c16ba705..6bfb9c23245ef 100644 --- a/components/camel-mongodb/src/generated/resources/META-INF/org/apache/camel/component/mongodb/mongodb.json +++ b/components/camel-mongodb/src/generated/resources/META-INF/org/apache/camel/component/mongodb/mongodb.json @@ -58,6 +58,11 @@ "CamelMongoDbReturnDocumentType": { "index": 24, "kind": "header", "displayName": "", "group": "producer update one and return", "label": "producer update one and return", "required": false, "javaType": "com.mongodb.client.model.ReturnDocument", "enum": [ "BEFORE", "AFTER" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Indicates which document to return, the document before or after an update and return atomic operation.", "constantName": "org.apache.camel.component.mongodb.MongoDbConstants#RETURN_DOCUMENT" }, "CamelMongoDbOperationOption": { "index": 25, "kind": "header", "displayName": "", "group": "producer update one and options", "label": "producer update one and options", "required": false, "javaType": "Object", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Options to use. When set, options set in the headers will be ignored.", "constantName": "org.apache.camel.component.mongodb.MongoDbConstants#OPTIONS" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "pojo" ], "javaType": "org.bson.Document", "byOption": [ { "option": "consumerType", "value": "changeStreams", "carriers": [ "pojo", "null" ] } ], "description": "The document added to the capped collection; with changeStreams the full document of the change, null when the change has none (an update without fullDocument=updateLookup)" }, + "producerIn": { "carriers": [ "any" ], "description": "A document or a query: a Document, a Map or JSON text; a List of them for insert, aggregate and update" }, + "producerOut": { "carriers": [ "unknown" ], "dependsOn": [ "outputType", "writeResultAsHeader" ], "byOption": [ { "option": "operation", "value": "findById", "carriers": [ "pojo", "null" ], "javaType": "org.bson.Document" }, { "option": "operation", "value": "findOneByQuery", "carriers": [ "pojo", "null" ], "javaType": "org.bson.Document" }, { "option": "operation", "value": "findOneAndDelete", "carriers": [ "pojo", "null" ], "javaType": "org.bson.Document" }, { "option": "operation", "value": "findOneAndReplace", "carriers": [ "pojo", "null" ], "javaType": "org.bson.Document" }, { "option": "operation", "value": "findOneAndUpdate", "carriers": [ "pojo", "null" ], "javaType": "org.bson.Document" }, { "option": "operation", "value": "findAll", "carriers": [ "list" ] }, { "option": "operation", "value": "findDistinct", "carriers": [ "list" ] }, { "option": "operation", "value": "aggregate", "carriers": [ "list" ] }, { "option": "operation", "value": "count", "carriers": [ "value" ], "javaType": "java.lang.Long" }, { "option": "operation", "value": "insert", "carriers": [ "pojo", "list" ] }, { "option": "operation", "value": "save", "carriers": [ "pojo" ] }, { "option": "operation", "value": "update", "carriers": [ "pojo" ], "javaType": "com.mongodb.client.result.UpdateResult" }, { "option": "operation", "value": "remove", "carriers": [ "pojo" ], "javaType": "com.mongodb.client.result.DeleteResult" }, { "option": "operation", "value": "bulkWrite", "carriers": [ "pojo" ], "javaType": "com.mongodb.bulk.BulkWriteResult" }, { "option": "operation", "value": "command", "carriers": [ "pojo" ], "javaType": "org.bson.Document" }, { "option": "operation", "value": "getDbStats", "carriers": [ "pojo" ], "javaType": "org.bson.Document" }, { "option": "operation", "value": "getColStats", "carriers": [ "pojo" ], "javaType": "org.bson.Document" } ], "description": "By the operation: a Document or null for a find of one, a List for findAll, findDistinct and aggregate, a Long for count, the document added for insert, the result of the driver for the other writes, and a Document for command and the stats" } + }, "properties": { "connectionBean": { "index": 0, "kind": "path", "displayName": "Connection Bean", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Sets the connection bean reference used to lookup a client for connecting to a database if no hosts parameter is present." }, "collection": { "index": 1, "kind": "parameter", "displayName": "Collection", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "endpointIdentity": true, "description": "Sets the name of the MongoDB collection to bind to this endpoint" }, diff --git a/components/camel-mongodb/src/main/java/org/apache/camel/component/mongodb/MongoDbEndpoint.java b/components/camel-mongodb/src/main/java/org/apache/camel/component/mongodb/MongoDbEndpoint.java index 774d095c2b9b2..feb7ebc4fd87a 100644 --- a/components/camel-mongodb/src/main/java/org/apache/camel/component/mongodb/MongoDbEndpoint.java +++ b/components/camel-mongodb/src/main/java/org/apache/camel/component/mongodb/MongoDbEndpoint.java @@ -40,6 +40,9 @@ import org.apache.camel.Consumer; import org.apache.camel.Processor; import org.apache.camel.Producer; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.EndpointServiceLocation; import org.apache.camel.spi.Metadata; import org.apache.camel.spi.StateRepository; @@ -68,6 +71,56 @@ */ @UriEndpoint(firstVersion = "2.19.0", scheme = "mongodb", title = "MongoDB", syntax = "mongodb:connectionBean", category = { Category.DATABASE }, headersClass = MongoDbConstants.class) +@BodyTypes(consumerOut = @BodyType(value = Carrier.POJO, javaType = "org.bson.Document", + byOption = @BodyType.When(option = "consumerType", value = "changeStreams", + carrier = { Carrier.POJO, Carrier.NULL }), + description = "The document added to the capped collection; with changeStreams the full" + + " document of the change, null when the change has none (an update" + + " without fullDocument=updateLookup)"), + producerIn = @BodyType(value = Carrier.ANY, + description = "A document or a query: a Document, a Map or JSON text; a List of them for" + + " insert, aggregate and update"), + producerOut = @BodyType(value = Carrier.UNKNOWN, dependsOn = { "outputType", "writeResultAsHeader" }, + byOption = { + @BodyType.When(option = "operation", value = "findById", + carrier = { Carrier.POJO, Carrier.NULL }, + javaType = "org.bson.Document"), + @BodyType.When(option = "operation", value = "findOneByQuery", + carrier = { Carrier.POJO, Carrier.NULL }, + javaType = "org.bson.Document"), + @BodyType.When(option = "operation", value = "findOneAndDelete", + carrier = { Carrier.POJO, Carrier.NULL }, + javaType = "org.bson.Document"), + @BodyType.When(option = "operation", value = "findOneAndReplace", + carrier = { Carrier.POJO, Carrier.NULL }, + javaType = "org.bson.Document"), + @BodyType.When(option = "operation", value = "findOneAndUpdate", + carrier = { Carrier.POJO, Carrier.NULL }, + javaType = "org.bson.Document"), + @BodyType.When(option = "operation", value = "findAll", carrier = Carrier.LIST), + @BodyType.When(option = "operation", value = "findDistinct", carrier = Carrier.LIST), + @BodyType.When(option = "operation", value = "aggregate", carrier = Carrier.LIST), + @BodyType.When(option = "operation", value = "count", carrier = Carrier.VALUE, + javaType = "java.lang.Long"), + @BodyType.When(option = "operation", value = "insert", + carrier = { Carrier.POJO, Carrier.LIST }), + @BodyType.When(option = "operation", value = "save", carrier = Carrier.POJO), + @BodyType.When(option = "operation", value = "update", carrier = Carrier.POJO, + javaType = "com.mongodb.client.result.UpdateResult"), + @BodyType.When(option = "operation", value = "remove", carrier = Carrier.POJO, + javaType = "com.mongodb.client.result.DeleteResult"), + @BodyType.When(option = "operation", value = "bulkWrite", carrier = Carrier.POJO, + javaType = "com.mongodb.bulk.BulkWriteResult"), + @BodyType.When(option = "operation", value = "command", carrier = Carrier.POJO, + javaType = "org.bson.Document"), + @BodyType.When(option = "operation", value = "getDbStats", carrier = Carrier.POJO, + javaType = "org.bson.Document"), + @BodyType.When(option = "operation", value = "getColStats", carrier = Carrier.POJO, + javaType = "org.bson.Document") }, + description = "By the operation: a Document or null for a find of one, a List for" + + " findAll, findDistinct and aggregate, a Long for count, the document" + + " added for insert, the result of the driver for the other writes, and a" + + " Document for command and the stats")) public class MongoDbEndpoint extends DefaultEndpoint implements EndpointServiceLocation { private static final Logger LOG = LoggerFactory.getLogger(MongoDbEndpoint.class); diff --git a/components/camel-paho-mqtt5/src/generated/resources/META-INF/org/apache/camel/component/paho/mqtt5/paho-mqtt5.json b/components/camel-paho-mqtt5/src/generated/resources/META-INF/org/apache/camel/component/paho/mqtt5/paho-mqtt5.json index 3959801c5ee4e..0f670d9baa037 100644 --- a/components/camel-paho-mqtt5/src/generated/resources/META-INF/org/apache/camel/component/paho/mqtt5/paho-mqtt5.json +++ b/components/camel-paho-mqtt5/src/generated/resources/META-INF/org/apache/camel/component/paho/mqtt5/paho-mqtt5.json @@ -66,6 +66,11 @@ "CamelPahoMqtt5OverrideTopic": { "index": 4, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The name of topic to override and send to instead of topic specified on endpoint.", "constantName": "org.apache.camel.component.paho.mqtt5.PahoMqtt5Constants#CAMEL_PAHO_OVERRIDE_TOPIC" }, "CamelPahoMqtt5MsgProperties": { "index": 5, "kind": "header", "displayName": "", "group": "producer", "label": "consumer,producer", "required": false, "javaType": "org.eclipse.paho.mqttv5.common.packet.MqttProperties", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Consumer: The properties set on the incoming message. Producer: The properties to be set on the outgoing message.", "constantName": "org.apache.camel.component.paho.mqtt5.PahoMqtt5Constants#CAMEL_PAHO_MSG_PROPERTIES" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "bytes" ], "javaType": "byte[]", "description": "The payload of the MQTT message" }, + "producerIn": { "carriers": [ "text", "bytes", "stream", "file" ], "description": "What converts to a byte array: text, bytes, a stream or a file, sent as the payload" }, + "producerOut": { "carriers": [ "unchanged" ] } + }, "properties": { "topic": { "index": 0, "kind": "path", "displayName": "Topic", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Name of the topic" }, "automaticReconnect": { "index": 1, "kind": "parameter", "displayName": "Automatic Reconnect", "group": "common", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "configurationClass": "org.apache.camel.component.paho.mqtt5.PahoMqtt5Configuration", "configurationField": "configuration", "description": "Sets whether the client will automatically attempt to reconnect to the server if the connection is lost. If set to false, the client will not attempt to automatically reconnect to the server in the event that the connection is lost. If set to true, in the event that the connection is lost, the client will attempt to reconnect to the server. It will initially wait 1 second before it attempts to reconnect, for every failed reconnect attempt, the delay will double until it is at 2 minutes at which point the delay will stay at 2 minutes." }, diff --git a/components/camel-paho-mqtt5/src/main/java/org/apache/camel/component/paho/mqtt5/PahoMqtt5Endpoint.java b/components/camel-paho-mqtt5/src/main/java/org/apache/camel/component/paho/mqtt5/PahoMqtt5Endpoint.java index de690231832c7..70dea3a4066c8 100644 --- a/components/camel-paho-mqtt5/src/main/java/org/apache/camel/component/paho/mqtt5/PahoMqtt5Endpoint.java +++ b/components/camel-paho-mqtt5/src/main/java/org/apache/camel/component/paho/mqtt5/PahoMqtt5Endpoint.java @@ -24,6 +24,9 @@ import org.apache.camel.Consumer; import org.apache.camel.Processor; import org.apache.camel.Producer; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.EndpointServiceLocation; import org.apache.camel.spi.Metadata; import org.apache.camel.spi.UriEndpoint; @@ -44,6 +47,11 @@ @UriEndpoint(firstVersion = "3.8.0", scheme = "paho-mqtt5", title = "Paho MQTT 5", category = { Category.MESSAGING, Category.IOT }, syntax = "paho-mqtt5:topic", headersClass = PahoMqtt5Constants.class) +@BodyTypes(consumerOut = @BodyType(value = Carrier.BYTES, javaType = "byte[]", description = "The payload of the MQTT message"), + producerIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }, + description = "What converts to a byte array: text, bytes, a stream or a file, sent as" + + " the payload"), + producerOut = @BodyType(Carrier.UNCHANGED)) public class PahoMqtt5Endpoint extends DefaultEndpoint implements EndpointServiceLocation { // Configuration members diff --git a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java index 51fc53d6d0bfc..ecae2e67fc50f 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java +++ b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java @@ -155,9 +155,9 @@ void theBodyTypesSayWhatTheBodyIsAfterAStep() throws Exception { .startsWith("pojo (org.w3c.dom.NodeList). The nodes the expression selects. With resultQName=STRING: text")); // a component that does not declare them has none, rather than a guess - JsonObject mongodb = catalogDoc(Map.of("name", "mongodb", "kind", "component", "includeOptions", false)); - assertNull(mongodb.get("bodyTypes")); - assertNull(mongodb.get("bodyTypesHint")); + JsonObject jdbc = catalogDoc(Map.of("name", "jdbc", "kind", "component", "includeOptions", false)); + assertNull(jdbc.get("bodyTypes")); + assertNull(jdbc.get("bodyTypesHint")); } @Test From cd44efe04037751f682f4dafe27c6b027928b9e9 Mon Sep 17 00:00:00 2001 From: Claus Ibsen Date: Wed, 21 Oct 2026 16:18:06 +0200 Subject: [PATCH 4/9] CAMEL-24961: camel-catalog - an iterator carrier, and the build rejects an option value without carriers From the review of PR 28241: - An iterator is read once and has no size and no index, so tooling that reads list as a List accepts ${body.size()} or ${body[0]} on it, which fail at runtime. The new iterator carrier names it: sql with outputType=StreamList gives an iterator, not a list. - A @BodyType.When with no carriers (carrier = {}) wrote "carriers": [] into the catalog. The build now rejects it, as it does a side without carriers. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claus Ibsen --- .../org/apache/camel/catalog/components/sql.json | 4 ++-- .../META-INF/org/apache/camel/component/sql/sql.json | 4 ++-- .../java/org/apache/camel/component/sql/SqlEndpoint.java | 8 ++++---- .../generated/java/org/apache/camel/spi/BodyType.java | 9 +++++++-- docs/user-manual/modules/ROOT/pages/camel-catalog.adoc | 6 +++--- .../camel/dsl/jbang/core/commands/ai/CatalogDocs.java | 5 +++-- .../org/apache/camel/tooling/model/BodyTypeModel.java | 6 +++--- .../apache/camel/maven/packaging/BodyTypesHelper.java | 8 ++++++-- .../maven/packaging/EndpointSchemaGeneratorMojoTest.java | 2 +- .../packaging/endpoint/SomeEndpointWithBadBodyTypes.java | 6 ++++++ .../src/main/java/org/apache/camel/spi/BodyType.java | 9 +++++++-- 11 files changed, 44 insertions(+), 23 deletions(-) diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/sql.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/sql.json index 27ec6331908f9..371e35003ff24 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/sql.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/sql.json @@ -49,9 +49,9 @@ "CamelSqlDataSource": { "index": 8, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "javax.sql.DataSource", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "To use a specific DataSource to operate on another database than the pre configured DataSource on the component", "constantName": "org.apache.camel.component.sql.SqlConstants#SQL_DATA_SOURCE" } }, "bodyTypes": { - "consumerOut": { "carriers": [ "map" ], "javaType": "java.util.Map", "dependsOn": [ "outputClass", "outputHeader" ], "byOption": [ { "option": "useIterator", "value": "false", "carriers": [ "list" ] }, { "option": "outputType", "value": "SelectOne", "carriers": [ "map", "value" ] }, { "option": "outputType", "value": "StreamList", "carriers": [ "list" ], "javaType": "java.util.Iterator" } ], "description": "A row of the query: a Map of the column names and their values" }, + "consumerOut": { "carriers": [ "map" ], "javaType": "java.util.Map", "dependsOn": [ "outputClass", "outputHeader" ], "byOption": [ { "option": "useIterator", "value": "false", "carriers": [ "list" ] }, { "option": "outputType", "value": "SelectOne", "carriers": [ "map", "value" ] }, { "option": "outputType", "value": "StreamList", "carriers": [ "iterator" ] } ], "description": "A row of the query: a Map of the column names and their values" }, "producerIn": { "carriers": [ "any" ], "description": "The parameters of the query: a Map for the named ones, a List or an array for the # ones; the query itself with useMessageBodyForSql" }, - "producerOut": { "carriers": [ "list", "unchanged" ], "dependsOn": [ "outputClass", "outputHeader", "noop" ], "byOption": [ { "option": "outputType", "value": "SelectOne", "carriers": [ "map", "value", "null" ] }, { "option": "outputType", "value": "StreamList", "carriers": [ "list" ], "javaType": "java.util.Iterator" } ], "description": "The rows of a select: a List of Maps of the column names and their values. A query that is not a select leaves the body unchanged, with the count in the CamelSqlUpdateCount header" } + "producerOut": { "carriers": [ "list", "unchanged" ], "dependsOn": [ "outputClass", "outputHeader", "noop" ], "byOption": [ { "option": "outputType", "value": "SelectOne", "carriers": [ "map", "value", "null" ] }, { "option": "outputType", "value": "StreamList", "carriers": [ "iterator" ] } ], "description": "The rows of a select: a List of Maps of the column names and their values. A query that is not a select leaves the body unchanged, with the count in the CamelSqlUpdateCount header" } }, "properties": { "query": { "index": 0, "kind": "path", "displayName": "Query", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "supportFileReference": true, "largeInput": true, "inputLanguage": "sql", "description": "Sets the SQL query to perform. You can externalize the query by using file: or classpath: as prefix and specify the location of the file." }, diff --git a/components/camel-sql/src/generated/resources/META-INF/org/apache/camel/component/sql/sql.json b/components/camel-sql/src/generated/resources/META-INF/org/apache/camel/component/sql/sql.json index 27ec6331908f9..371e35003ff24 100644 --- a/components/camel-sql/src/generated/resources/META-INF/org/apache/camel/component/sql/sql.json +++ b/components/camel-sql/src/generated/resources/META-INF/org/apache/camel/component/sql/sql.json @@ -49,9 +49,9 @@ "CamelSqlDataSource": { "index": 8, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "javax.sql.DataSource", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "To use a specific DataSource to operate on another database than the pre configured DataSource on the component", "constantName": "org.apache.camel.component.sql.SqlConstants#SQL_DATA_SOURCE" } }, "bodyTypes": { - "consumerOut": { "carriers": [ "map" ], "javaType": "java.util.Map", "dependsOn": [ "outputClass", "outputHeader" ], "byOption": [ { "option": "useIterator", "value": "false", "carriers": [ "list" ] }, { "option": "outputType", "value": "SelectOne", "carriers": [ "map", "value" ] }, { "option": "outputType", "value": "StreamList", "carriers": [ "list" ], "javaType": "java.util.Iterator" } ], "description": "A row of the query: a Map of the column names and their values" }, + "consumerOut": { "carriers": [ "map" ], "javaType": "java.util.Map", "dependsOn": [ "outputClass", "outputHeader" ], "byOption": [ { "option": "useIterator", "value": "false", "carriers": [ "list" ] }, { "option": "outputType", "value": "SelectOne", "carriers": [ "map", "value" ] }, { "option": "outputType", "value": "StreamList", "carriers": [ "iterator" ] } ], "description": "A row of the query: a Map of the column names and their values" }, "producerIn": { "carriers": [ "any" ], "description": "The parameters of the query: a Map for the named ones, a List or an array for the # ones; the query itself with useMessageBodyForSql" }, - "producerOut": { "carriers": [ "list", "unchanged" ], "dependsOn": [ "outputClass", "outputHeader", "noop" ], "byOption": [ { "option": "outputType", "value": "SelectOne", "carriers": [ "map", "value", "null" ] }, { "option": "outputType", "value": "StreamList", "carriers": [ "list" ], "javaType": "java.util.Iterator" } ], "description": "The rows of a select: a List of Maps of the column names and their values. A query that is not a select leaves the body unchanged, with the count in the CamelSqlUpdateCount header" } + "producerOut": { "carriers": [ "list", "unchanged" ], "dependsOn": [ "outputClass", "outputHeader", "noop" ], "byOption": [ { "option": "outputType", "value": "SelectOne", "carriers": [ "map", "value", "null" ] }, { "option": "outputType", "value": "StreamList", "carriers": [ "iterator" ] } ], "description": "The rows of a select: a List of Maps of the column names and their values. A query that is not a select leaves the body unchanged, with the count in the CamelSqlUpdateCount header" } }, "properties": { "query": { "index": 0, "kind": "path", "displayName": "Query", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "supportFileReference": true, "largeInput": true, "inputLanguage": "sql", "description": "Sets the SQL query to perform. You can externalize the query by using file: or classpath: as prefix and specify the location of the file." }, diff --git a/components/camel-sql/src/main/java/org/apache/camel/component/sql/SqlEndpoint.java b/components/camel-sql/src/main/java/org/apache/camel/component/sql/SqlEndpoint.java index ece6dd8ce371c..5ea866d040d28 100644 --- a/components/camel-sql/src/main/java/org/apache/camel/component/sql/SqlEndpoint.java +++ b/components/camel-sql/src/main/java/org/apache/camel/component/sql/SqlEndpoint.java @@ -41,8 +41,8 @@ @BodyType.When(option = "useIterator", value = "false", carrier = Carrier.LIST), @BodyType.When(option = "outputType", value = "SelectOne", carrier = { Carrier.MAP, Carrier.VALUE }), - @BodyType.When(option = "outputType", value = "StreamList", carrier = Carrier.LIST, - javaType = "java.util.Iterator") }, + @BodyType.When(option = "outputType", value = "StreamList", + carrier = Carrier.ITERATOR) }, description = "A row of the query: a Map of the column names and their values"), producerIn = @BodyType(value = Carrier.ANY, description = "The parameters of the query: a Map for the named ones, a List or an array" @@ -52,8 +52,8 @@ byOption = { @BodyType.When(option = "outputType", value = "SelectOne", carrier = { Carrier.MAP, Carrier.VALUE, Carrier.NULL }), - @BodyType.When(option = "outputType", value = "StreamList", carrier = Carrier.LIST, - javaType = "java.util.Iterator") }, + @BodyType.When(option = "outputType", value = "StreamList", + carrier = Carrier.ITERATOR) }, description = "The rows of a select: a List of Maps of the column names and their values." + " A query that is not a select leaves the body unchanged, with the count" + " in the CamelSqlUpdateCount header")) diff --git a/core/camel-api/src/generated/java/org/apache/camel/spi/BodyType.java b/core/camel-api/src/generated/java/org/apache/camel/spi/BodyType.java index 366a81f7e7116..8f027971b231d 100644 --- a/core/camel-api/src/generated/java/org/apache/camel/spi/BodyType.java +++ b/core/camel-api/src/generated/java/org/apache/camel/spi/BodyType.java @@ -100,7 +100,7 @@ /** * The kinds of Java object a body is in. The runtime sorts the bodies it records into kinds as well (null, payload, * data and collection): text, bytes, a stream, a file and a value are a payload, a map and a pojo are data, a list - * is a collection. + * and an iterator are a collection. */ enum Carrier { @@ -118,8 +118,13 @@ enum Carrier { VALUE("value"), /** A java.util.Map, such as a JSON object parsed or a row of a query. */ MAP("map"), - /** A List, another Collection, an array or an Iterator, such as a JSON array parsed or the rows of a query. */ + /** A List, another Collection or an array, such as a JSON array parsed or the rows of a query. */ LIST("list"), + /** + * An Iterator, which is read once, element by element, and has no size and no index: the rows of a query that + * streams them, or of a CSV file read lazily. + */ + ITERATOR("iterator"), /** Another Java object, such as a DOM Document or a class a payload is unmarshalled into. */ POJO("pojo"), /** The body as it was before the step: for the body after a step only. */ diff --git a/docs/user-manual/modules/ROOT/pages/camel-catalog.adoc b/docs/user-manual/modules/ROOT/pages/camel-catalog.adoc index 78348be8b710e..ce268d3ee666a 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-catalog.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-catalog.adoc @@ -145,9 +145,9 @@ one entry per side: |=== Each side has the _carriers_ of the body, the kinds of Java object it is in: `null`, `text`, `bytes`, `stream`, -`file`, `value`, `map`, `list` and `pojo`; `unchanged` for a step that leaves the body as it was, `any` for a step that -takes any body, and `unknown` for a body that depends on what the other system sends or on a class the user -configures. A side can also have the Java type when the body is always of that type, the format of the payload (json, +`file`, `value`, `map`, `list`, `iterator` (read once, with no size and no index) and `pojo`; `unchanged` for a step +that leaves the body as it was, `any` for a step that takes any body, and `unknown` for a body that depends on what +the other system sends or on a class the user configures. A side can also have the Java type when the body is always of that type, the format of the payload (json, xml, csv, yaml, text or binary), the options the body depends on, the body for other values of an option, and a description: diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocs.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocs.java index d69c883fbab17..b9b988c5372a5 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocs.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocs.java @@ -70,8 +70,9 @@ public final class CatalogDocs { + " gives the route; consumerReply: the bodies it can reply with; producerIn:" + " the bodies a to: can send; producerOut: the body after the to:; marshal," + " unmarshal and expression In/Out: the same for a data format and a" - + " language. Carriers: text, bytes, stream, file, value, map, list, pojo," - + " null, unchanged, any, unknown. A side not listed is not known"; + + " language. Carriers: text, bytes, stream, file, value, map, list," + + " iterator (no size, no index), pojo, null, unchanged, any, unknown. A side" + + " not listed is not known"; /** * The rules a small model gets wrong most: functions live inside the placeholder, operators between placeholders. diff --git a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/BodyTypeModel.java b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/BodyTypeModel.java index bde3c29c0090a..794c505ecc347 100644 --- a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/BodyTypeModel.java +++ b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/BodyTypeModel.java @@ -23,9 +23,9 @@ * One side of the body types of a component, data format or language: the body a step needs, or the body it leaves * after it, with the default options (CAMEL-24961). *

- * The carriers are the kinds of Java object the body is in: null, text, bytes, stream, file, value, map, list and pojo; - * unchanged for a step that leaves the body as it was, any for a step that takes any body, and unknown for a body that - * depends on what the other system sends or on a class the user configures. + * The carriers are the kinds of Java object the body is in: null, text, bytes, stream, file, value, map, list, iterator + * and pojo; unchanged for a step that leaves the body as it was, any for a step that takes any body, and unknown for a + * body that depends on what the other system sends or on a class the user configures. */ public class BodyTypeModel { diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/BodyTypesHelper.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/BodyTypesHelper.java index 02e23fbd3b8bc..2894db39c3729 100644 --- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/BodyTypesHelper.java +++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/BodyTypesHelper.java @@ -137,10 +137,14 @@ private static void checkOption(Class owner, String side, String option, Set< } /** - * UNCHANGED is for the body after a step, such as a sql query that leaves it unchanged unless it is a select. ANY - * is for the bodies a step needs. ANY and UNKNOWN stand alone. + * A body has a carrier: an option value without one would say nothing. UNCHANGED is for the body after a step, such + * as a sql query that leaves it unchanged unless it is a select. ANY is for the bodies a step needs. ANY and + * UNKNOWN stand alone. */ private static void check(Class owner, String side, Carrier[] carriers) { + if (carriers.length == 0) { + throw new IllegalArgumentException(owner.getName() + ": the @BodyTypes " + side + " has no carriers"); + } List list = Arrays.asList(carriers); boolean in = IN.contains(side.split(" ")[0]); if (in && list.contains(Carrier.UNCHANGED)) { diff --git a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojoTest.java b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojoTest.java index 5fa0723fa8f5c..5fa2fe71426d0 100644 --- a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojoTest.java +++ b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojoTest.java @@ -278,7 +278,7 @@ void testEndpointWithoutBodyTypes() throws Exception { SomeEndpointWithBadBodyTypes.DataFormatSide.class, SomeEndpointWithBadBodyTypes.ProducerSideOfConsumer.class, SomeEndpointWithBadBodyTypes.UnchangedInput.class, SomeEndpointWithBadBodyTypes.AnyOutput.class, SomeEndpointWithBadBodyTypes.UnknownWithOthers.class, SomeEndpointWithBadBodyTypes.NoCarriers.class, - SomeEndpointWithBadBodyTypes.UnknownOption.class }) + SomeEndpointWithBadBodyTypes.UnknownOption.class, SomeEndpointWithBadBodyTypes.OptionValueWithoutCarriers.class }) void testEndpointWithBadBodyTypes(Class clazz) { addEndpointOptions("output"); model.setConsumerOnly(clazz.getAnnotation(UriEndpoint.class).consumerOnly()); diff --git a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointWithBadBodyTypes.java b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointWithBadBodyTypes.java index cd66ff0c96706..9346bf17990e0 100644 --- a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointWithBadBodyTypes.java +++ b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointWithBadBodyTypes.java @@ -63,4 +63,10 @@ public static class NoCarriers { @BodyTypes(producerOut = @BodyType(value = Carrier.TEXT, dependsOn = "nope")) public static class UnknownOption { } + + @UriEndpoint(scheme = "some", syntax = "some", title = "some") + @BodyTypes(producerOut = @BodyType(value = Carrier.TEXT, + byOption = @BodyType.When(option = "output", value = "bytes", carrier = {}))) + public static class OptionValueWithoutCarriers { + } } diff --git a/tooling/spi-annotations/src/main/java/org/apache/camel/spi/BodyType.java b/tooling/spi-annotations/src/main/java/org/apache/camel/spi/BodyType.java index 366a81f7e7116..8f027971b231d 100644 --- a/tooling/spi-annotations/src/main/java/org/apache/camel/spi/BodyType.java +++ b/tooling/spi-annotations/src/main/java/org/apache/camel/spi/BodyType.java @@ -100,7 +100,7 @@ /** * The kinds of Java object a body is in. The runtime sorts the bodies it records into kinds as well (null, payload, * data and collection): text, bytes, a stream, a file and a value are a payload, a map and a pojo are data, a list - * is a collection. + * and an iterator are a collection. */ enum Carrier { @@ -118,8 +118,13 @@ enum Carrier { VALUE("value"), /** A java.util.Map, such as a JSON object parsed or a row of a query. */ MAP("map"), - /** A List, another Collection, an array or an Iterator, such as a JSON array parsed or the rows of a query. */ + /** A List, another Collection or an array, such as a JSON array parsed or the rows of a query. */ LIST("list"), + /** + * An Iterator, which is read once, element by element, and has no size and no index: the rows of a query that + * streams them, or of a CSV file read lazily. + */ + ITERATOR("iterator"), /** Another Java object, such as a DOM Document or a class a payload is unmarshalled into. */ POJO("pojo"), /** The body as it was before the step: for the body after a step only. */ From daec2065d0989e7204c2ba775c6ca215c3912ff2 Mon Sep 17 00:00:00 2001 From: Claus Ibsen Date: Wed, 21 Oct 2026 16:20:33 +0200 Subject: [PATCH 5/9] CAMEL-24961: camel-catalog - csv with lazyLoad gives an iterator, and camel_catalog_doc says unknown for no carriers From the review of PR 28242: - The csv data format with lazyLoad=true gives an iterator, with the iterator carrier of PR 28241, not a list. - camel_catalog_doc says unknown for a body without carriers, instead of failing. The build rejects it now, but a catalog read from elsewhere may have one. - Sqs2Endpoint imports org.apache.camel.spi.* already: only the import of the nested BodyType.Carrier stays. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claus Ibsen --- .../org/apache/camel/catalog/dataformats/csv.json | 2 +- .../camel/component/aws2/sqs/Sqs2Endpoint.java | 2 -- .../org/apache/camel/dataformat/csv/csv.json | 2 +- .../apache/camel/dataformat/csv/CsvDataFormat.java | 4 ++-- .../dsl/jbang/core/commands/ai/CatalogDocs.java | 4 ++++ .../jbang/core/commands/ai/CatalogDocsTest.java | 14 ++++++++++++++ 6 files changed, 22 insertions(+), 6 deletions(-) diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/csv.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/csv.json index 6e696b52703e8..2b073a8fdfe26 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/csv.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/csv.json @@ -19,7 +19,7 @@ "marshalIn": { "carriers": [ "map", "list" ], "description": "One row as a Map, or the rows as a List (or an Iterator) of Maps or Lists" }, "marshalOut": { "carriers": [ "stream" ], "format": "csv", "description": "The CSV in the stream cache, or a byte array when stream caching is off" }, "unmarshalIn": { "carriers": [ "text", "bytes", "stream", "file" ], "format": "csv" }, - "unmarshalOut": { "carriers": [ "list" ], "dependsOn": [ "recordConverterRef" ], "byOption": [ { "option": "lazyLoad", "value": "true", "carriers": [ "list" ], "javaType": "java.util.Iterator" } ], "description": "A List of the rows, each a List of its values; with useMaps or useOrderedMaps, a Map of the header names and the values" } + "unmarshalOut": { "carriers": [ "list" ], "dependsOn": [ "recordConverterRef" ], "byOption": [ { "option": "lazyLoad", "value": "true", "carriers": [ "iterator" ] } ], "description": "A List of the rows, each a List of its values; with useMaps or useOrderedMaps, a Map of the header names and the values" } }, "properties": { "id": { "index": 0, "kind": "attribute", "displayName": "Id", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The id of this node" }, diff --git a/components/camel-aws/camel-aws2-sqs/src/main/java/org/apache/camel/component/aws2/sqs/Sqs2Endpoint.java b/components/camel-aws/camel-aws2-sqs/src/main/java/org/apache/camel/component/aws2/sqs/Sqs2Endpoint.java index c4f5fe9575899..7e226d8d92a1e 100644 --- a/components/camel-aws/camel-aws2-sqs/src/main/java/org/apache/camel/component/aws2/sqs/Sqs2Endpoint.java +++ b/components/camel-aws/camel-aws2-sqs/src/main/java/org/apache/camel/component/aws2/sqs/Sqs2Endpoint.java @@ -33,9 +33,7 @@ import org.apache.camel.clock.Clock; import org.apache.camel.component.aws2.sqs.client.Sqs2ClientFactory; import org.apache.camel.spi.*; -import org.apache.camel.spi.BodyType; import org.apache.camel.spi.BodyType.Carrier; -import org.apache.camel.spi.BodyTypes; import org.apache.camel.support.MonotonicClock; import org.apache.camel.support.ResourceHelper; import org.apache.camel.support.ScheduledPollEndpoint; diff --git a/components/camel-csv/src/generated/resources/META-INF/org/apache/camel/dataformat/csv/csv.json b/components/camel-csv/src/generated/resources/META-INF/org/apache/camel/dataformat/csv/csv.json index 6e696b52703e8..2b073a8fdfe26 100644 --- a/components/camel-csv/src/generated/resources/META-INF/org/apache/camel/dataformat/csv/csv.json +++ b/components/camel-csv/src/generated/resources/META-INF/org/apache/camel/dataformat/csv/csv.json @@ -19,7 +19,7 @@ "marshalIn": { "carriers": [ "map", "list" ], "description": "One row as a Map, or the rows as a List (or an Iterator) of Maps or Lists" }, "marshalOut": { "carriers": [ "stream" ], "format": "csv", "description": "The CSV in the stream cache, or a byte array when stream caching is off" }, "unmarshalIn": { "carriers": [ "text", "bytes", "stream", "file" ], "format": "csv" }, - "unmarshalOut": { "carriers": [ "list" ], "dependsOn": [ "recordConverterRef" ], "byOption": [ { "option": "lazyLoad", "value": "true", "carriers": [ "list" ], "javaType": "java.util.Iterator" } ], "description": "A List of the rows, each a List of its values; with useMaps or useOrderedMaps, a Map of the header names and the values" } + "unmarshalOut": { "carriers": [ "list" ], "dependsOn": [ "recordConverterRef" ], "byOption": [ { "option": "lazyLoad", "value": "true", "carriers": [ "iterator" ] } ], "description": "A List of the rows, each a List of its values; with useMaps or useOrderedMaps, a Map of the header names and the values" } }, "properties": { "id": { "index": 0, "kind": "attribute", "displayName": "Id", "group": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The id of this node" }, diff --git a/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvDataFormat.java b/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvDataFormat.java index 3baa7fe3c30e7..d90ee300b0c8d 100644 --- a/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvDataFormat.java +++ b/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvDataFormat.java @@ -45,8 +45,8 @@ description = "The CSV in the stream cache, or a byte array when stream caching is off"), unmarshalIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }, format = "csv"), unmarshalOut = @BodyType(value = Carrier.LIST, dependsOn = "recordConverterRef", - byOption = @BodyType.When(option = "lazyLoad", value = "true", carrier = Carrier.LIST, - javaType = "java.util.Iterator"), + byOption = @BodyType.When(option = "lazyLoad", value = "true", + carrier = Carrier.ITERATOR), description = "A List of the rows, each a List of its values; with useMaps or" + " useOrderedMaps, a Map of the header names and the values")) public class CsvDataFormat extends ServiceSupport implements DataFormat, DataFormatName { diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocs.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocs.java index b6777140ce3b7..cf59e42214687 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocs.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocs.java @@ -1671,6 +1671,10 @@ static JsonObject bodyTypes(Map bodyTypes) { } private static String carriers(List carriers, String javaType) { + if (carriers.isEmpty()) { + // the build rejects a side without carriers, but a catalog read from elsewhere may have one + return "unknown"; + } String answer = carriers.size() == 1 ? carriers.get(0) : String.join(", ", carriers.subList(0, carriers.size() - 1)) + " or " + carriers.get(carriers.size() - 1); diff --git a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java index ecae2e67fc50f..b7a7c781a109c 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java +++ b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java @@ -28,6 +28,7 @@ import org.apache.camel.impl.DefaultCamelContext; import org.apache.camel.support.DefaultExchange; import org.apache.camel.support.ExchangeHelper; +import org.apache.camel.tooling.model.BodyTypeModel; import org.apache.camel.tooling.model.EipModel; import org.apache.camel.util.json.JsonArray; import org.apache.camel.util.json.JsonObject; @@ -160,6 +161,19 @@ void theBodyTypesSayWhatTheBodyIsAfterAStep() throws Exception { assertNull(jdbc.get("bodyTypesHint")); } + @Test + void aBodyWithoutCarriersIsUnknown() { + // the build rejects it, but a catalog read from elsewhere may have one: the line says unknown + BodyTypeModel side = new BodyTypeModel(); + side.setCarriers(List.of("text")); + BodyTypeModel.OptionBodyTypeModel bytes = new BodyTypeModel.OptionBodyTypeModel(); + bytes.setOption("output"); + bytes.setValue("bytes"); + side.getByOption().add(bytes); + JsonObject lines = CatalogDocs.bodyTypes(Map.of(BodyTypeModel.PRODUCER_OUT, side)); + assertEquals("text. With output=bytes: unknown", lines.getString(BodyTypeModel.PRODUCER_OUT)); + } + @Test void endpointArgumentValidatesAUriAgainstTheComponent() throws Exception { JsonObject bad = catalogDoc(Map.of("endpoint", "timer:tick?period=5s&fixedRte=true&repeatCount=abc")); From 0be66c57fb11079e313c218964806f4043094da5 Mon Sep 17 00:00:00 2001 From: Claus Ibsen Date: Wed, 21 Oct 2026 20:46:31 +0200 Subject: [PATCH 6/9] CAMEL-24961: Rename When.carrier to carriers, as the catalog names it The attribute holds an array, and the catalog JSON and BodyTypeModel call the same field carriers. The Javadoc of BodyTypeModel.isUnknown() says that a side with no carriers is unknown too. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claus Ibsen --- .../org/apache/camel/component/file/FileEndpoint.java | 2 +- .../camel/component/jackson/JacksonDataFormat.java | 2 +- .../org/apache/camel/component/seda/SedaEndpoint.java | 2 +- .../org/apache/camel/component/sql/SqlEndpoint.java | 10 +++++----- .../org/apache/camel/component/xslt/XsltEndpoint.java | 8 ++++---- .../generated/java/org/apache/camel/spi/BodyType.java | 2 +- .../org/apache/camel/tooling/model/BodyTypeModel.java | 4 +++- .../apache/camel/maven/packaging/BodyTypesHelper.java | 4 ++-- .../endpoint/SomeEndpointWithBadBodyTypes.java | 2 +- .../packaging/endpoint/SomeEndpointWithBodyTypes.java | 2 +- .../src/main/java/org/apache/camel/spi/BodyType.java | 2 +- 11 files changed, 21 insertions(+), 19 deletions(-) diff --git a/components/camel-file/src/main/java/org/apache/camel/component/file/FileEndpoint.java b/components/camel-file/src/main/java/org/apache/camel/component/file/FileEndpoint.java index 283e0b32e9e80..97c70ff2f7dc4 100644 --- a/components/camel-file/src/main/java/org/apache/camel/component/file/FileEndpoint.java +++ b/components/camel-file/src/main/java/org/apache/camel/component/file/FileEndpoint.java @@ -54,7 +54,7 @@ + " converts to text, bytes or a stream"), producerIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }, byOption = @BodyType.When(option = "allowNullBody", value = "true", - carrier = { + carriers = { Carrier.NULL, Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }), description = "What converts to an InputStream: text, bytes, a stream or a file. A Map or" diff --git a/components/camel-jackson/src/main/java/org/apache/camel/component/jackson/JacksonDataFormat.java b/components/camel-jackson/src/main/java/org/apache/camel/component/jackson/JacksonDataFormat.java index 6519c2d6a2ae0..4afc8fb1cbe30 100644 --- a/components/camel-jackson/src/main/java/org/apache/camel/component/jackson/JacksonDataFormat.java +++ b/components/camel-jackson/src/main/java/org/apache/camel/component/jackson/JacksonDataFormat.java @@ -45,7 +45,7 @@ unmarshalIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }, format = "json"), unmarshalOut = @BodyType(value = { Carrier.MAP, Carrier.LIST }, dependsOn = { "unmarshalType", "collectionType", "allowUnmarshallType" }, - byOption = @BodyType.When(option = "useList", value = "true", carrier = Carrier.LIST), + byOption = @BodyType.When(option = "useList", value = "true", carriers = Carrier.LIST), description = "A Map (LinkedHashMap) for a JSON object, a List (ArrayList) for an array;" + " an object of the class unmarshalType names")) public class JacksonDataFormat extends AbstractJacksonDataFormat { diff --git a/components/camel-seda/src/main/java/org/apache/camel/component/seda/SedaEndpoint.java b/components/camel-seda/src/main/java/org/apache/camel/component/seda/SedaEndpoint.java index 7cedba92ad122..973bd1404bbd1 100644 --- a/components/camel-seda/src/main/java/org/apache/camel/component/seda/SedaEndpoint.java +++ b/components/camel-seda/src/main/java/org/apache/camel/component/seda/SedaEndpoint.java @@ -66,7 +66,7 @@ producerIn = @BodyType(Carrier.ANY), producerOut = @BodyType(value = Carrier.UNKNOWN, byOption = @BodyType.When(option = "waitForTaskToComplete", value = "Never", - carrier = Carrier.UNCHANGED), + carriers = Carrier.UNCHANGED), description = "Unchanged for a message that expects no reply (InOnly). A message that" + " expects one (InOut), or waitForTaskToComplete=Always, waits for the" + " body the seda route ends with")) diff --git a/components/camel-sql/src/main/java/org/apache/camel/component/sql/SqlEndpoint.java b/components/camel-sql/src/main/java/org/apache/camel/component/sql/SqlEndpoint.java index 5ea866d040d28..5a7798e61770e 100644 --- a/components/camel-sql/src/main/java/org/apache/camel/component/sql/SqlEndpoint.java +++ b/components/camel-sql/src/main/java/org/apache/camel/component/sql/SqlEndpoint.java @@ -38,11 +38,11 @@ @BodyTypes(consumerOut = @BodyType(value = Carrier.MAP, javaType = "java.util.Map", dependsOn = { "outputClass", "outputHeader" }, byOption = { - @BodyType.When(option = "useIterator", value = "false", carrier = Carrier.LIST), + @BodyType.When(option = "useIterator", value = "false", carriers = Carrier.LIST), @BodyType.When(option = "outputType", value = "SelectOne", - carrier = { Carrier.MAP, Carrier.VALUE }), + carriers = { Carrier.MAP, Carrier.VALUE }), @BodyType.When(option = "outputType", value = "StreamList", - carrier = Carrier.ITERATOR) }, + carriers = Carrier.ITERATOR) }, description = "A row of the query: a Map of the column names and their values"), producerIn = @BodyType(value = Carrier.ANY, description = "The parameters of the query: a Map for the named ones, a List or an array" @@ -51,9 +51,9 @@ dependsOn = { "outputClass", "outputHeader", "noop" }, byOption = { @BodyType.When(option = "outputType", value = "SelectOne", - carrier = { Carrier.MAP, Carrier.VALUE, Carrier.NULL }), + carriers = { Carrier.MAP, Carrier.VALUE, Carrier.NULL }), @BodyType.When(option = "outputType", value = "StreamList", - carrier = Carrier.ITERATOR) }, + carriers = Carrier.ITERATOR) }, description = "The rows of a select: a List of Maps of the column names and their values." + " A query that is not a select leaves the body unchanged, with the count" + " in the CamelSqlUpdateCount header")) diff --git a/components/camel-xslt/src/main/java/org/apache/camel/component/xslt/XsltEndpoint.java b/components/camel-xslt/src/main/java/org/apache/camel/component/xslt/XsltEndpoint.java index 17c699c6e21e0..c02124dc45695 100644 --- a/components/camel-xslt/src/main/java/org/apache/camel/component/xslt/XsltEndpoint.java +++ b/components/camel-xslt/src/main/java/org/apache/camel/component/xslt/XsltEndpoint.java @@ -66,18 +66,18 @@ @BodyTypes(producerIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, Carrier.POJO }, format = "xml", dependsOn = "source", byOption = @BodyType.When(option = "failOnNullBody", value = "false", - carrier = { + carriers = { Carrier.NULL, Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, Carrier.POJO }), description = "The XML to transform: text, bytes, a stream, a file, a DOM or another" + " javax.xml.transform.Source. A Map, a List or no body fails"), producerOut = @BodyType(value = Carrier.TEXT, javaType = "java.lang.String", dependsOn = "resultHandlerFactory", byOption = { - @BodyType.When(option = "output", value = "bytes", carrier = Carrier.BYTES, + @BodyType.When(option = "output", value = "bytes", carriers = Carrier.BYTES, javaType = "byte[]"), - @BodyType.When(option = "output", value = "DOM", carrier = Carrier.POJO, + @BodyType.When(option = "output", value = "DOM", carriers = Carrier.POJO, javaType = "org.w3c.dom.Document"), - @BodyType.When(option = "output", value = "file", carrier = Carrier.FILE, + @BodyType.When(option = "output", value = "file", carriers = Carrier.FILE, javaType = "java.io.File") }, description = "The result of the transformation")) public class XsltEndpoint extends ProcessorEndpoint { diff --git a/core/camel-api/src/generated/java/org/apache/camel/spi/BodyType.java b/core/camel-api/src/generated/java/org/apache/camel/spi/BodyType.java index 8f027971b231d..2d2cc3b927240 100644 --- a/core/camel-api/src/generated/java/org/apache/camel/spi/BodyType.java +++ b/core/camel-api/src/generated/java/org/apache/camel/spi/BodyType.java @@ -89,7 +89,7 @@ /** * The carriers of the body with this value. */ - Carrier[] carrier(); + Carrier[] carriers(); /** * The Java type of the body with this value, when it is always of this type. diff --git a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/BodyTypeModel.java b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/BodyTypeModel.java index 794c505ecc347..d9097160e19c6 100644 --- a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/BodyTypeModel.java +++ b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/BodyTypeModel.java @@ -132,7 +132,9 @@ public boolean isUnchanged() { } /** - * Whether the body is not known without the configuration or the data of the other system. + * Whether the body is not known without the configuration or the data of the other system. A side with no carriers + * is not known either: the build rejects one, but a catalog from elsewhere, such as JSON written by hand, can have + * it. */ public boolean isUnknown() { return carriers.isEmpty() || carriers.contains("unknown"); diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/BodyTypesHelper.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/BodyTypesHelper.java index 2894db39c3729..fcb3789aa78a5 100644 --- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/BodyTypesHelper.java +++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/BodyTypesHelper.java @@ -93,7 +93,7 @@ static void addBodyTypes(BaseModel model, BodyTypes bodyTypes, Class owner } for (BodyType.When when : bodyType.byOption()) { checkOption(owner, side, when.option(), options); - check(owner, side + " " + when.option() + "=" + when.value(), when.carrier()); + check(owner, side + " " + when.option() + "=" + when.value(), when.carriers()); } model.getBodyTypes().put(side, asModel(bodyType)); } @@ -171,7 +171,7 @@ private static BodyTypeModel asModel(BodyType bodyType) { BodyTypeModel.OptionBodyTypeModel option = new BodyTypeModel.OptionBodyTypeModel(); option.setOption(when.option()); option.setValue(when.value()); - option.setCarriers(names(when.carrier())); + option.setCarriers(names(when.carriers())); option.setJavaType(emptyAsNull(when.javaType())); model.getByOption().add(option); } diff --git a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointWithBadBodyTypes.java b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointWithBadBodyTypes.java index 9346bf17990e0..cccd18faa7155 100644 --- a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointWithBadBodyTypes.java +++ b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointWithBadBodyTypes.java @@ -66,7 +66,7 @@ public static class UnknownOption { @UriEndpoint(scheme = "some", syntax = "some", title = "some") @BodyTypes(producerOut = @BodyType(value = Carrier.TEXT, - byOption = @BodyType.When(option = "output", value = "bytes", carrier = {}))) + byOption = @BodyType.When(option = "output", value = "bytes", carriers = {}))) public static class OptionValueWithoutCarriers { } } diff --git a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointWithBodyTypes.java b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointWithBodyTypes.java index 18c10606a6461..3473dc6f438cf 100644 --- a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointWithBodyTypes.java +++ b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointWithBodyTypes.java @@ -28,6 +28,6 @@ producerOut = @BodyType(value = Carrier.TEXT, javaType = "java.lang.String", format = "xml", dependsOn = "resultHandlerFactory", byOption = @BodyType.When(option = "output", value = "bytes", - carrier = Carrier.BYTES, javaType = "byte[]"))) + carriers = Carrier.BYTES, javaType = "byte[]"))) public class SomeEndpointWithBodyTypes { } diff --git a/tooling/spi-annotations/src/main/java/org/apache/camel/spi/BodyType.java b/tooling/spi-annotations/src/main/java/org/apache/camel/spi/BodyType.java index 8f027971b231d..2d2cc3b927240 100644 --- a/tooling/spi-annotations/src/main/java/org/apache/camel/spi/BodyType.java +++ b/tooling/spi-annotations/src/main/java/org/apache/camel/spi/BodyType.java @@ -89,7 +89,7 @@ /** * The carriers of the body with this value. */ - Carrier[] carrier(); + Carrier[] carriers(); /** * The Java type of the body with this value, when it is always of this type. From 15aaf465fa04661557ef380fa689d3c98d7b1e07 Mon Sep 17 00:00:00 2001 From: Claus Ibsen Date: Wed, 21 Oct 2026 20:48:33 +0200 Subject: [PATCH 7/9] CAMEL-24961: Use When.carriers in the annotations of slice 2 Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claus Ibsen --- .../component/activemq/ActiveMQEndpoint.java | 12 +++--- .../component/activemq6/ActiveMQEndpoint.java | 12 +++--- .../camel/component/amqp/AMQPEndpoint.java | 12 +++--- .../component/aws2/sqs/Sqs2Endpoint.java | 12 +++--- .../camel/dataformat/csv/CsvDataFormat.java | 2 +- .../component/file/remote/FtpEndpoint.java | 6 +-- .../component/file/remote/FtpsEndpoint.java | 6 +-- .../component/file/remote/SftpEndpoint.java | 6 +-- .../jacksonxml/JacksonXMLDataFormat.java | 2 +- .../camel/component/jms/JmsEndpoint.java | 12 +++--- .../camel/jsonpath/JsonPathLanguage.java | 2 +- .../camel/component/kafka/KafkaEndpoint.java | 4 +- .../component/mongodb/MongoDbEndpoint.java | 37 ++++++++++--------- .../platform/http/PlatformHttpEndpoint.java | 2 +- .../camel/component/rest/RestEndpoint.java | 6 +-- .../camel/language/xpath/XPathLanguage.java | 8 ++-- 16 files changed, 71 insertions(+), 70 deletions(-) diff --git a/components/camel-activemq/src/main/java/org/apache/camel/component/activemq/ActiveMQEndpoint.java b/components/camel-activemq/src/main/java/org/apache/camel/component/activemq/ActiveMQEndpoint.java index 1e0ca6e7876f6..34c534d2014d5 100644 --- a/components/camel-activemq/src/main/java/org/apache/camel/component/activemq/ActiveMQEndpoint.java +++ b/components/camel-activemq/src/main/java/org/apache/camel/component/activemq/ActiveMQEndpoint.java @@ -42,10 +42,10 @@ @BodyTypes(consumerOut = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.MAP, Carrier.STREAM, Carrier.NULL }, dependsOn = { "messageConverter", "transferExchange" }, byOption = { - @BodyType.When(option = "mapJmsMessage", value = "false", carrier = Carrier.POJO, + @BodyType.When(option = "mapJmsMessage", value = "false", carriers = Carrier.POJO, javaType = "jakarta.jms.Message"), @BodyType.When(option = "objectMessageEnabled", value = "true", - carrier = { + carriers = { Carrier.TEXT, Carrier.BYTES, Carrier.MAP, Carrier.STREAM, Carrier.POJO, Carrier.NULL }) }, description = "By the JMS message received: a String for a TextMessage, a byte array for a" @@ -54,14 +54,14 @@ consumerReply = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, Carrier.MAP }, dependsOn = { "jmsMessageType", "messageConverter", "transferExchange" }, byOption = @BodyType.When(option = "objectMessageEnabled", value = "true", - carrier = { + carriers = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, Carrier.MAP, Carrier.POJO }), description = "The reply, sent as the producer sends a message"), producerIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, Carrier.MAP }, dependsOn = { "jmsMessageType", "messageConverter", "transferExchange" }, byOption = @BodyType.When(option = "objectMessageEnabled", value = "true", - carrier = { + carriers = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, Carrier.MAP, Carrier.POJO }), description = "Text is sent as a TextMessage; bytes, a stream or a file as a BytesMessage;" @@ -70,9 +70,9 @@ producerOut = @BodyType(value = Carrier.UNKNOWN, byOption = { @BodyType.When(option = "exchangePattern", value = "InOnly", - carrier = Carrier.UNCHANGED), + carriers = Carrier.UNCHANGED), @BodyType.When(option = "disableReplyTo", value = "true", - carrier = Carrier.UNCHANGED) }, + carriers = Carrier.UNCHANGED) }, description = "Unchanged for a message that expects no reply (InOnly). A message that" + " expects one (InOut) gets the body of the reply, as the consumer gives it")) public class ActiveMQEndpoint extends JmsEndpoint implements EndpointServiceLocation { diff --git a/components/camel-activemq6/src/main/java/org/apache/camel/component/activemq6/ActiveMQEndpoint.java b/components/camel-activemq6/src/main/java/org/apache/camel/component/activemq6/ActiveMQEndpoint.java index 2ebf48a77856e..57373f8b7cde3 100644 --- a/components/camel-activemq6/src/main/java/org/apache/camel/component/activemq6/ActiveMQEndpoint.java +++ b/components/camel-activemq6/src/main/java/org/apache/camel/component/activemq6/ActiveMQEndpoint.java @@ -42,10 +42,10 @@ @BodyTypes(consumerOut = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.MAP, Carrier.STREAM, Carrier.NULL }, dependsOn = { "messageConverter", "transferExchange" }, byOption = { - @BodyType.When(option = "mapJmsMessage", value = "false", carrier = Carrier.POJO, + @BodyType.When(option = "mapJmsMessage", value = "false", carriers = Carrier.POJO, javaType = "jakarta.jms.Message"), @BodyType.When(option = "objectMessageEnabled", value = "true", - carrier = { + carriers = { Carrier.TEXT, Carrier.BYTES, Carrier.MAP, Carrier.STREAM, Carrier.POJO, Carrier.NULL }) }, description = "By the JMS message received: a String for a TextMessage, a byte array for a" @@ -54,14 +54,14 @@ consumerReply = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, Carrier.MAP }, dependsOn = { "jmsMessageType", "messageConverter", "transferExchange" }, byOption = @BodyType.When(option = "objectMessageEnabled", value = "true", - carrier = { + carriers = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, Carrier.MAP, Carrier.POJO }), description = "The reply, sent as the producer sends a message"), producerIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, Carrier.MAP }, dependsOn = { "jmsMessageType", "messageConverter", "transferExchange" }, byOption = @BodyType.When(option = "objectMessageEnabled", value = "true", - carrier = { + carriers = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, Carrier.MAP, Carrier.POJO }), description = "Text is sent as a TextMessage; bytes, a stream or a file as a BytesMessage;" @@ -70,9 +70,9 @@ producerOut = @BodyType(value = Carrier.UNKNOWN, byOption = { @BodyType.When(option = "exchangePattern", value = "InOnly", - carrier = Carrier.UNCHANGED), + carriers = Carrier.UNCHANGED), @BodyType.When(option = "disableReplyTo", value = "true", - carrier = Carrier.UNCHANGED) }, + carriers = Carrier.UNCHANGED) }, description = "Unchanged for a message that expects no reply (InOnly). A message that" + " expects one (InOut) gets the body of the reply, as the consumer gives it")) public class ActiveMQEndpoint extends JmsEndpoint implements EndpointServiceLocation { diff --git a/components/camel-amqp/src/main/java/org/apache/camel/component/amqp/AMQPEndpoint.java b/components/camel-amqp/src/main/java/org/apache/camel/component/amqp/AMQPEndpoint.java index 9ed3d154c6132..4216a71c9529f 100644 --- a/components/camel-amqp/src/main/java/org/apache/camel/component/amqp/AMQPEndpoint.java +++ b/components/camel-amqp/src/main/java/org/apache/camel/component/amqp/AMQPEndpoint.java @@ -34,10 +34,10 @@ @BodyTypes(consumerOut = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.MAP, Carrier.STREAM, Carrier.NULL }, dependsOn = { "messageConverter", "transferExchange" }, byOption = { - @BodyType.When(option = "mapJmsMessage", value = "false", carrier = Carrier.POJO, + @BodyType.When(option = "mapJmsMessage", value = "false", carriers = Carrier.POJO, javaType = "jakarta.jms.Message"), @BodyType.When(option = "objectMessageEnabled", value = "true", - carrier = { + carriers = { Carrier.TEXT, Carrier.BYTES, Carrier.MAP, Carrier.STREAM, Carrier.POJO, Carrier.NULL }) }, description = "By the JMS message received: a String for a TextMessage, a byte array for a" @@ -46,14 +46,14 @@ consumerReply = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, Carrier.MAP }, dependsOn = { "jmsMessageType", "messageConverter", "transferExchange" }, byOption = @BodyType.When(option = "objectMessageEnabled", value = "true", - carrier = { + carriers = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, Carrier.MAP, Carrier.POJO }), description = "The reply, sent as the producer sends a message"), producerIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, Carrier.MAP }, dependsOn = { "jmsMessageType", "messageConverter", "transferExchange" }, byOption = @BodyType.When(option = "objectMessageEnabled", value = "true", - carrier = { + carriers = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, Carrier.MAP, Carrier.POJO }), description = "Text is sent as a TextMessage; bytes, a stream or a file as a BytesMessage;" @@ -62,9 +62,9 @@ producerOut = @BodyType(value = Carrier.UNKNOWN, byOption = { @BodyType.When(option = "exchangePattern", value = "InOnly", - carrier = Carrier.UNCHANGED), + carriers = Carrier.UNCHANGED), @BodyType.When(option = "disableReplyTo", value = "true", - carrier = Carrier.UNCHANGED) }, + carriers = Carrier.UNCHANGED) }, description = "Unchanged for a message that expects no reply (InOnly). A message that" + " expects one (InOut) gets the body of the reply, as the consumer gives it")) public class AMQPEndpoint extends JmsEndpoint { diff --git a/components/camel-aws/camel-aws2-sqs/src/main/java/org/apache/camel/component/aws2/sqs/Sqs2Endpoint.java b/components/camel-aws/camel-aws2-sqs/src/main/java/org/apache/camel/component/aws2/sqs/Sqs2Endpoint.java index 7e226d8d92a1e..961073d19076b 100644 --- a/components/camel-aws/camel-aws2-sqs/src/main/java/org/apache/camel/component/aws2/sqs/Sqs2Endpoint.java +++ b/components/camel-aws/camel-aws2-sqs/src/main/java/org/apache/camel/component/aws2/sqs/Sqs2Endpoint.java @@ -65,23 +65,23 @@ description = "The body of the SQS message"), producerIn = @BodyType(value = Carrier.TEXT, byOption = @BodyType.When(option = "operation", value = "sendBatchMessage", - carrier = { Carrier.LIST, Carrier.TEXT }), + carriers = { Carrier.LIST, Carrier.TEXT }), description = "The body of the message to send, as text, so a Map or a POJO is sent as" + " its toString(): marshal it first. sendBatchMessage takes a List of" + " text, or text that batchSeparator splits"), producerOut = @BodyType(value = Carrier.UNCHANGED, byOption = { @BodyType.When(option = "operation", value = "sendBatchMessage", - carrier = Carrier.POJO, + carriers = Carrier.POJO, javaType = "software.amazon.awssdk.services.sqs.model.SendMessageBatchResponse"), @BodyType.When(option = "operation", value = "deleteMessage", - carrier = Carrier.POJO, + carriers = Carrier.POJO, javaType = "software.amazon.awssdk.services.sqs.model.DeleteMessageResponse"), - @BodyType.When(option = "operation", value = "listQueues", carrier = Carrier.POJO, + @BodyType.When(option = "operation", value = "listQueues", carriers = Carrier.POJO, javaType = "software.amazon.awssdk.services.sqs.model.ListQueuesResponse"), - @BodyType.When(option = "operation", value = "purgeQueue", carrier = Carrier.POJO, + @BodyType.When(option = "operation", value = "purgeQueue", carriers = Carrier.POJO, javaType = "software.amazon.awssdk.services.sqs.model.PurgeQueueResponse"), - @BodyType.When(option = "operation", value = "deleteQueue", carrier = Carrier.POJO, + @BodyType.When(option = "operation", value = "deleteQueue", carriers = Carrier.POJO, javaType = "software.amazon.awssdk.services.sqs.model.DeleteQueueResponse") }, description = "Unchanged for a send, with the id of the message in a header; the response" + " of the AWS SDK for the other operations")) diff --git a/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvDataFormat.java b/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvDataFormat.java index d90ee300b0c8d..911b89cef6fd1 100644 --- a/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvDataFormat.java +++ b/components/camel-csv/src/main/java/org/apache/camel/dataformat/csv/CsvDataFormat.java @@ -46,7 +46,7 @@ unmarshalIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }, format = "csv"), unmarshalOut = @BodyType(value = Carrier.LIST, dependsOn = "recordConverterRef", byOption = @BodyType.When(option = "lazyLoad", value = "true", - carrier = Carrier.ITERATOR), + carriers = Carrier.ITERATOR), description = "A List of the rows, each a List of its values; with useMaps or" + " useOrderedMaps, a Map of the header names and the values")) public class CsvDataFormat extends ServiceSupport implements DataFormat, DataFormatName { diff --git a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpEndpoint.java b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpEndpoint.java index 2abdf1d757ba9..48417c25b72fc 100644 --- a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpEndpoint.java +++ b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpEndpoint.java @@ -61,13 +61,13 @@ @ManagedResource(description = "Managed FtpEndpoint") @BodyTypes(consumerOut = @BodyType(value = Carrier.FILE, dependsOn = "preMove", byOption = { - @BodyType.When(option = "streamDownload", value = "true", carrier = Carrier.STREAM), - @BodyType.When(option = "download", value = "false", carrier = Carrier.NULL) }, + @BodyType.When(option = "streamDownload", value = "true", carriers = Carrier.STREAM), + @BodyType.When(option = "download", value = "false", carriers = Carrier.NULL) }, description = "The file as read: a RemoteFile, which converts to text, bytes or a stream; a" + " stream with streamDownload; with preMove its content, a byte array"), producerIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }, byOption = @BodyType.When(option = "allowNullBody", value = "true", - carrier = { + carriers = { Carrier.NULL, Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }), description = "What converts to an InputStream: text, bytes, a stream or a file. A Map or" diff --git a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpsEndpoint.java b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpsEndpoint.java index 5a80fbc506799..079079926c9a4 100644 --- a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpsEndpoint.java +++ b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpsEndpoint.java @@ -59,13 +59,13 @@ @ManagedResource(description = "Managed FtpsEndpoint") @BodyTypes(consumerOut = @BodyType(value = Carrier.FILE, dependsOn = "preMove", byOption = { - @BodyType.When(option = "streamDownload", value = "true", carrier = Carrier.STREAM), - @BodyType.When(option = "download", value = "false", carrier = Carrier.NULL) }, + @BodyType.When(option = "streamDownload", value = "true", carriers = Carrier.STREAM), + @BodyType.When(option = "download", value = "false", carriers = Carrier.NULL) }, description = "The file as read: a RemoteFile, which converts to text, bytes or a stream; a" + " stream with streamDownload; with preMove its content, a byte array"), producerIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }, byOption = @BodyType.When(option = "allowNullBody", value = "true", - carrier = { + carriers = { Carrier.NULL, Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }), description = "What converts to an InputStream: text, bytes, a stream or a file. A Map or" diff --git a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/SftpEndpoint.java b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/SftpEndpoint.java index 6ff2ab3c28e90..199e5d28bdaa7 100644 --- a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/SftpEndpoint.java +++ b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/SftpEndpoint.java @@ -42,13 +42,13 @@ + "renameUsingCopy,synchronous") @BodyTypes(consumerOut = @BodyType(value = Carrier.FILE, dependsOn = "preMove", byOption = { - @BodyType.When(option = "streamDownload", value = "true", carrier = Carrier.STREAM), - @BodyType.When(option = "download", value = "false", carrier = Carrier.NULL) }, + @BodyType.When(option = "streamDownload", value = "true", carriers = Carrier.STREAM), + @BodyType.When(option = "download", value = "false", carriers = Carrier.NULL) }, description = "The file as read: a RemoteFile, which converts to text, bytes or a stream; a" + " stream with streamDownload; with preMove its content, a byte array"), producerIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }, byOption = @BodyType.When(option = "allowNullBody", value = "true", - carrier = { + carriers = { Carrier.NULL, Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }), description = "What converts to an InputStream: text, bytes, a stream or a file. A Map or" diff --git a/components/camel-jacksonxml/src/main/java/org/apache/camel/component/jacksonxml/JacksonXMLDataFormat.java b/components/camel-jacksonxml/src/main/java/org/apache/camel/component/jacksonxml/JacksonXMLDataFormat.java index e421d9bcdeb11..097f6a2748be5 100644 --- a/components/camel-jacksonxml/src/main/java/org/apache/camel/component/jacksonxml/JacksonXMLDataFormat.java +++ b/components/camel-jacksonxml/src/main/java/org/apache/camel/component/jacksonxml/JacksonXMLDataFormat.java @@ -71,7 +71,7 @@ unmarshalIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }, format = "xml"), unmarshalOut = @BodyType(value = Carrier.MAP, dependsOn = { "unmarshalType", "collectionType", "allowUnmarshallType" }, - byOption = @BodyType.When(option = "useList", value = "true", carrier = Carrier.LIST), + byOption = @BodyType.When(option = "useList", value = "true", carriers = Carrier.LIST), description = "A Map (LinkedHashMap) of the XML; an object of the class unmarshalType" + " names")) public class JacksonXMLDataFormat extends ServiceSupport diff --git a/components/camel-jms/src/main/java/org/apache/camel/component/jms/JmsEndpoint.java b/components/camel-jms/src/main/java/org/apache/camel/component/jms/JmsEndpoint.java index 67a8ec51e5312..1d77420af22df 100644 --- a/components/camel-jms/src/main/java/org/apache/camel/component/jms/JmsEndpoint.java +++ b/components/camel-jms/src/main/java/org/apache/camel/component/jms/JmsEndpoint.java @@ -79,10 +79,10 @@ @BodyTypes(consumerOut = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.MAP, Carrier.STREAM, Carrier.NULL }, dependsOn = { "messageConverter", "transferExchange" }, byOption = { - @BodyType.When(option = "mapJmsMessage", value = "false", carrier = Carrier.POJO, + @BodyType.When(option = "mapJmsMessage", value = "false", carriers = Carrier.POJO, javaType = "jakarta.jms.Message"), @BodyType.When(option = "objectMessageEnabled", value = "true", - carrier = { + carriers = { Carrier.TEXT, Carrier.BYTES, Carrier.MAP, Carrier.STREAM, Carrier.POJO, Carrier.NULL }) }, description = "By the JMS message received: a String for a TextMessage, a byte array for a" @@ -91,14 +91,14 @@ consumerReply = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, Carrier.MAP }, dependsOn = { "jmsMessageType", "messageConverter", "transferExchange" }, byOption = @BodyType.When(option = "objectMessageEnabled", value = "true", - carrier = { + carriers = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, Carrier.MAP, Carrier.POJO }), description = "The reply, sent as the producer sends a message"), producerIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, Carrier.MAP }, dependsOn = { "jmsMessageType", "messageConverter", "transferExchange" }, byOption = @BodyType.When(option = "objectMessageEnabled", value = "true", - carrier = { + carriers = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, Carrier.MAP, Carrier.POJO }), description = "Text is sent as a TextMessage; bytes, a stream or a file as a BytesMessage;" @@ -107,9 +107,9 @@ producerOut = @BodyType(value = Carrier.UNKNOWN, byOption = { @BodyType.When(option = "exchangePattern", value = "InOnly", - carrier = Carrier.UNCHANGED), + carriers = Carrier.UNCHANGED), @BodyType.When(option = "disableReplyTo", value = "true", - carrier = Carrier.UNCHANGED) }, + carriers = Carrier.UNCHANGED) }, description = "Unchanged for a message that expects no reply (InOnly). A message that" + " expects one (InOut) gets the body of the reply, as the consumer gives it")) public class JmsEndpoint extends DefaultEndpoint diff --git a/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathLanguage.java b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathLanguage.java index be5917ef5e01e..880b0bd42336b 100644 --- a/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathLanguage.java +++ b/components/camel-jsonpath/src/main/java/org/apache/camel/jsonpath/JsonPathLanguage.java @@ -40,7 +40,7 @@ + " is read through Jackson"), expressionOut = @BodyType(value = Carrier.UNKNOWN, byOption = @BodyType.When(option = "writeAsString", value = "true", - carrier = { Carrier.TEXT, Carrier.LIST }), + carriers = { Carrier.TEXT, Carrier.LIST }), description = "The value the path selects: a Map for an object, a List for an array or" + " for a path with a wildcard or a filter, otherwise text, a number or a" + " boolean")) diff --git a/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaEndpoint.java b/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaEndpoint.java index 85645fbcfe208..b56c238d99bb1 100644 --- a/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaEndpoint.java +++ b/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaEndpoint.java @@ -55,8 +55,8 @@ byOption = { @BodyType.When(option = "valueDeserializer", value = "org.apache.kafka.common.serialization.ByteArrayDeserializer", - carrier = { Carrier.BYTES, Carrier.NULL }, javaType = "byte[]"), - @BodyType.When(option = "batching", value = "true", carrier = Carrier.LIST) }, + carriers = { Carrier.BYTES, Carrier.NULL }, javaType = "byte[]"), + @BodyType.When(option = "batching", value = "true", carriers = Carrier.LIST) }, description = "The value of the record: a String with the default valueDeserializer" + " (StringDeserializer), null for a tombstone. With batching, a List of" + " the exchanges of the records"), diff --git a/components/camel-mongodb/src/main/java/org/apache/camel/component/mongodb/MongoDbEndpoint.java b/components/camel-mongodb/src/main/java/org/apache/camel/component/mongodb/MongoDbEndpoint.java index feb7ebc4fd87a..6b2515bd629c4 100644 --- a/components/camel-mongodb/src/main/java/org/apache/camel/component/mongodb/MongoDbEndpoint.java +++ b/components/camel-mongodb/src/main/java/org/apache/camel/component/mongodb/MongoDbEndpoint.java @@ -73,7 +73,7 @@ category = { Category.DATABASE }, headersClass = MongoDbConstants.class) @BodyTypes(consumerOut = @BodyType(value = Carrier.POJO, javaType = "org.bson.Document", byOption = @BodyType.When(option = "consumerType", value = "changeStreams", - carrier = { Carrier.POJO, Carrier.NULL }), + carriers = { Carrier.POJO, Carrier.NULL }), description = "The document added to the capped collection; with changeStreams the full" + " document of the change, null when the change has none (an update" + " without fullDocument=updateLookup)"), @@ -83,39 +83,40 @@ producerOut = @BodyType(value = Carrier.UNKNOWN, dependsOn = { "outputType", "writeResultAsHeader" }, byOption = { @BodyType.When(option = "operation", value = "findById", - carrier = { Carrier.POJO, Carrier.NULL }, + carriers = { Carrier.POJO, Carrier.NULL }, javaType = "org.bson.Document"), @BodyType.When(option = "operation", value = "findOneByQuery", - carrier = { Carrier.POJO, Carrier.NULL }, + carriers = { Carrier.POJO, Carrier.NULL }, javaType = "org.bson.Document"), @BodyType.When(option = "operation", value = "findOneAndDelete", - carrier = { Carrier.POJO, Carrier.NULL }, + carriers = { Carrier.POJO, Carrier.NULL }, javaType = "org.bson.Document"), @BodyType.When(option = "operation", value = "findOneAndReplace", - carrier = { Carrier.POJO, Carrier.NULL }, + carriers = { Carrier.POJO, Carrier.NULL }, javaType = "org.bson.Document"), @BodyType.When(option = "operation", value = "findOneAndUpdate", - carrier = { Carrier.POJO, Carrier.NULL }, + carriers = { Carrier.POJO, Carrier.NULL }, javaType = "org.bson.Document"), - @BodyType.When(option = "operation", value = "findAll", carrier = Carrier.LIST), - @BodyType.When(option = "operation", value = "findDistinct", carrier = Carrier.LIST), - @BodyType.When(option = "operation", value = "aggregate", carrier = Carrier.LIST), - @BodyType.When(option = "operation", value = "count", carrier = Carrier.VALUE, + @BodyType.When(option = "operation", value = "findAll", carriers = Carrier.LIST), + @BodyType.When(option = "operation", value = "findDistinct", + carriers = Carrier.LIST), + @BodyType.When(option = "operation", value = "aggregate", carriers = Carrier.LIST), + @BodyType.When(option = "operation", value = "count", carriers = Carrier.VALUE, javaType = "java.lang.Long"), @BodyType.When(option = "operation", value = "insert", - carrier = { Carrier.POJO, Carrier.LIST }), - @BodyType.When(option = "operation", value = "save", carrier = Carrier.POJO), - @BodyType.When(option = "operation", value = "update", carrier = Carrier.POJO, + carriers = { Carrier.POJO, Carrier.LIST }), + @BodyType.When(option = "operation", value = "save", carriers = Carrier.POJO), + @BodyType.When(option = "operation", value = "update", carriers = Carrier.POJO, javaType = "com.mongodb.client.result.UpdateResult"), - @BodyType.When(option = "operation", value = "remove", carrier = Carrier.POJO, + @BodyType.When(option = "operation", value = "remove", carriers = Carrier.POJO, javaType = "com.mongodb.client.result.DeleteResult"), - @BodyType.When(option = "operation", value = "bulkWrite", carrier = Carrier.POJO, + @BodyType.When(option = "operation", value = "bulkWrite", carriers = Carrier.POJO, javaType = "com.mongodb.bulk.BulkWriteResult"), - @BodyType.When(option = "operation", value = "command", carrier = Carrier.POJO, + @BodyType.When(option = "operation", value = "command", carriers = Carrier.POJO, javaType = "org.bson.Document"), - @BodyType.When(option = "operation", value = "getDbStats", carrier = Carrier.POJO, + @BodyType.When(option = "operation", value = "getDbStats", carriers = Carrier.POJO, javaType = "org.bson.Document"), - @BodyType.When(option = "operation", value = "getColStats", carrier = Carrier.POJO, + @BodyType.When(option = "operation", value = "getColStats", carriers = Carrier.POJO, javaType = "org.bson.Document") }, description = "By the operation: a Document or null for a find of one, a List for" + " findAll, findDistinct and aggregate, a Long for count, the document" diff --git a/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/PlatformHttpEndpoint.java b/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/PlatformHttpEndpoint.java index c910d2ddce89e..2227077b50dd5 100644 --- a/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/PlatformHttpEndpoint.java +++ b/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/PlatformHttpEndpoint.java @@ -56,7 +56,7 @@ @Metadata(annotations = { "protocol=http" }) @BodyTypes(consumerOut = @BodyType(value = { Carrier.STREAM, Carrier.BYTES, Carrier.NULL, Carrier.MAP, Carrier.FILE }, byOption = @BodyType.When(option = "useBodyHandler", value = "false", - carrier = Carrier.POJO), + carriers = Carrier.POJO), description = "The request body: the stream cache with the Vert.x engine, a byte array" + " with Spring Boot; null for a request without a body, such as a GET; a" + " Map of the fields of a form (populateBodyWithForm); a File for an" diff --git a/components/camel-rest/src/main/java/org/apache/camel/component/rest/RestEndpoint.java b/components/camel-rest/src/main/java/org/apache/camel/component/rest/RestEndpoint.java index 66b43a1957e16..d83a749de8f37 100644 --- a/components/camel-rest/src/main/java/org/apache/camel/component/rest/RestEndpoint.java +++ b/components/camel-rest/src/main/java/org/apache/camel/component/rest/RestEndpoint.java @@ -61,18 +61,18 @@ @BodyTypes(consumerOut = @BodyType(value = { Carrier.STREAM, Carrier.BYTES, Carrier.NULL, Carrier.MAP, Carrier.FILE }, dependsOn = "consumerComponentName", byOption = @BodyType.When(option = "bindingMode", value = "json", - carrier = { Carrier.MAP, Carrier.LIST, Carrier.POJO }), + carriers = { Carrier.MAP, Carrier.LIST, Carrier.POJO }), description = "The request body as the HTTP consumer gives it (platform-http by" + " default). With bindingMode json, the object of inType, or a Map or a" + " List without inType"), consumerReply = @BodyType(value = { Carrier.NULL, Carrier.TEXT, Carrier.BYTES, Carrier.STREAM }, dependsOn = "consumerComponentName", - byOption = @BodyType.When(option = "bindingMode", value = "json", carrier = Carrier.ANY), + byOption = @BodyType.When(option = "bindingMode", value = "json", carriers = Carrier.ANY), description = "The response body as the HTTP consumer writes it. With bindingMode json," + " any object, which the binding writes as JSON"), producerIn = @BodyType(value = { Carrier.NULL, Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }, dependsOn = "producerComponentName", - byOption = @BodyType.When(option = "bindingMode", value = "json", carrier = Carrier.ANY), + byOption = @BodyType.When(option = "bindingMode", value = "json", carriers = Carrier.ANY), description = "The request body the HTTP producer sends (the http component by default)." + " With bindingMode json, any object, which the binding writes as JSON"), producerOut = @BodyType(value = { Carrier.BYTES, Carrier.STREAM, Carrier.NULL }, diff --git a/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPathLanguage.java b/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPathLanguage.java index 7487f7f91317d..1e846164b355b 100644 --- a/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPathLanguage.java +++ b/components/camel-xpath/src/main/java/org/apache/camel/language/xpath/XPathLanguage.java @@ -41,13 +41,13 @@ format = "xml", description = "XML as text, bytes, a stream or a file, or a DOM"), expressionOut = @BodyType(value = Carrier.POJO, javaType = "org.w3c.dom.NodeList", dependsOn = "resultType", byOption = { - @BodyType.When(option = "resultQName", value = "STRING", carrier = Carrier.TEXT, + @BodyType.When(option = "resultQName", value = "STRING", carriers = Carrier.TEXT, javaType = "java.lang.String"), - @BodyType.When(option = "resultQName", value = "NUMBER", carrier = Carrier.VALUE, + @BodyType.When(option = "resultQName", value = "NUMBER", carriers = Carrier.VALUE, javaType = "java.lang.Double"), - @BodyType.When(option = "resultQName", value = "BOOLEAN", carrier = Carrier.VALUE, + @BodyType.When(option = "resultQName", value = "BOOLEAN", carriers = Carrier.VALUE, javaType = "java.lang.Boolean"), - @BodyType.When(option = "resultQName", value = "NODE", carrier = Carrier.POJO, + @BodyType.When(option = "resultQName", value = "NODE", carriers = Carrier.POJO, javaType = "org.w3c.dom.Node") }, description = "The nodes the expression selects")) public class XPathLanguage extends SingleInputTypedLanguageSupport implements PropertyConfigurer { From 07e849749739ee4b523771b653d4f05c5c61a45f Mon Sep 17 00:00:00 2001 From: Claus Ibsen Date: Thu, 22 Oct 2026 07:50:35 +0200 Subject: [PATCH 8/9] CAMEL-24961: camel-yaml-dsl-maven-plugin - ignore the bodyTypes of the data format and language schemas Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claus Ibsen --- .../java/org/apache/camel/maven/dsl/yaml/support/Schema.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/support/Schema.java b/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/support/Schema.java index ece10f183c110..cc7b9bf26e2a5 100644 --- a/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/support/Schema.java +++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/support/Schema.java @@ -30,6 +30,8 @@ public class Schema { public ObjectNode properties; @JsonIgnore public ObjectNode exchangeProperties; + @JsonIgnore + public ObjectNode bodyTypes; @JsonProperty public ObjectNode functions; @JsonProperty From 4017dd4f313b1cd4b1774af3bd1e673b3e38a09f Mon Sep 17 00:00:00 2001 From: Claus Ibsen Date: Thu, 22 Oct 2026 09:48:28 +0200 Subject: [PATCH 9/9] CAMEL-24961: camel-jbang - the options that make a body type not known read as a list, as the carriers do Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claus Ibsen --- .../dsl/jbang/core/commands/ai/CatalogDocs.java | 12 ++++++++---- .../dsl/jbang/core/commands/ai/CatalogDocsTest.java | 5 +++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocs.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocs.java index cf59e42214687..7aa5bfedab5d3 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocs.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocs.java @@ -1663,7 +1663,7 @@ static JsonObject bodyTypes(Map bodyTypes) { line.append(". With ").append(String.join("; ", values)); } if (!side.getDependsOn().isEmpty()) { - line.append(". Not known when ").append(String.join(" or ", side.getDependsOn())).append(" is set"); + line.append(". Not known when ").append(orList(side.getDependsOn())).append(" is set"); } answer.put(entry.getKey(), line.toString()); } @@ -1675,12 +1675,16 @@ private static String carriers(List carriers, String javaType) { // the build rejects a side without carriers, but a catalog read from elsewhere may have one return "unknown"; } - String answer = carriers.size() == 1 - ? carriers.get(0) - : String.join(", ", carriers.subList(0, carriers.size() - 1)) + " or " + carriers.get(carriers.size() - 1); + String answer = orList(carriers); return javaType != null ? answer + " (" + javaType + ")" : answer; } + private static String orList(List values) { + return values.size() == 1 + ? values.get(0) + : String.join(", ", values.subList(0, values.size() - 1)) + " or " + values.get(values.size() - 1); + } + /** * The rules of an endpoint URI for this component, spelled out with its own path parts: what a small model gets * wrong most is a path option written as a query parameter or the other way round, and the YAML form. diff --git a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java index b7a7c781a109c..51a1730eb6771 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java +++ b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java @@ -147,6 +147,11 @@ void theBodyTypesSayWhatTheBodyIsAfterAStep() throws Exception { + " With output=bytes: bytes (byte[]); output=DOM: pojo")); assertTrue(xslt.getString("bodyTypesHint").contains("a side not listed is not known")); + // the options that make a side not known read as a list + JsonObject jms = catalogDoc(Map.of("name", "jms", "kind", "component", "includeOptions", false)); + assertTrue(((JsonObject) jms.get("bodyTypes")).getString("producerIn") + .endsWith(". Not known when jmsMessageType, messageConverter or transferExchange is set")); + JsonObject json = catalogDoc(Map.of("name", "json", "kind", "dataformat", "includeOptions", false)); assertTrue(((JsonObject) json.get("bodyTypes")).getString("unmarshalOut").startsWith("map or list. "));