[
https://issues.apache.org/jira/browse/CAMEL-26589?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Andrea Cosentino reassigned CAMEL-26589:
----------------------------------------
Assignee: Andrea Cosentino (was: Guillaume Nodet)
> Data type contract fails on byte[] bodies, breaking kamelets with dataTypes
> ---------------------------------------------------------------------------
>
> Key: CAMEL-26589
> URL: https://issues.apache.org/jira/browse/CAMEL-26589
> Project: Camel
> Issue Type: Bug
> Components: camel-core, camel-kamelet, camel-yaml-dsl
> Affects Versions: 4.18.1, 4.19.0, 4.22.2
> Reporter: Andrea Cosentino
> Assignee: Andrea Cosentino
> Priority: Major
>
> When a route has a data type contract ({{inputType}} / {{outputType}}) and
> the message body is a Java array such as {{byte[]}}, {{ContractAdvice}} fails
> the exchange even though no conversion is needed:
> {noformat}
> org.apache.camel.CamelExecutionException: Failed to convert body to 'java:[B'
> content using type conversion for null on the exchange: Exchange[...]
> at
> org.apache.camel.processor.transformer.TypeConverterTransformer.transform(TypeConverterTransformer.java:73)
> at
> org.apache.camel.processor.ContractAdvice.convertIfRequired(ContractAdvice.java:157)
> at
> org.apache.camel.processor.ContractAdvice.doTransform(ContractAdvice.java:127)
> at
> org.apache.camel.processor.ContractAdvice.after(ContractAdvice.java:101)
> ...
> Caused by: java.lang.ClassNotFoundException: [B
> at
> org.apache.camel.impl.engine.DefaultClassResolver.resolveMandatoryClass(DefaultClassResolver.java:136)
> at
> org.apache.camel.processor.transformer.TypeConverterTransformer.transform(TypeConverterTransformer.java:61)
> {noformat}
> Since CAMEL-23174 (4.18.1, 4.19.0), {{KameletRoutesBuilderLoader}} turns a
> Kamelet's {{spec.dataTypes.in/out}} into the route {{inputType}} /
> {{outputType}}. Every Kamelet that declares {{dataTypes}} therefore hits this
> whenever its body is an array, which is the normal case for binary sources
> such as {{aws-s3-source}}.
> h3. Cause
> # When a route has a contract, {{RouteReifier}} calls
> {{camelContext.setUseDataType(true)}}. From then on
> {{MessageSupport.setBody}} tags every body with {{new
> DataType(body.getClass())}}. For a {{byte[]}} body that is {{java:\[B}},
> because {{byte[].class.getName()}} is {{\[B}}.
> # {{ContractAdvice.after}} -> {{doTransform}} -> {{convertIfRequired(message,
> from)}} with {{from = java:\[B}} builds a
> {{TypeConverterTransformer(DataType)}}, which calls
> {{ClassResolver.resolveMandatoryClass("\[B")}} before it checks whether the
> body already has that type.
> # {{DefaultClassResolver}} -> {{ObjectHelper.loadClass}} only uses
> {{ClassLoader.loadClass(name)}}, which cannot load an array class from its
> binary name ({{Class.forName("\[B", false, loader)}} can).
> {{ObjectHelper.loadSimpleType}} knows {{byte[]}} but not {{\[B}}. The lookup
> fails and the exchange is marked failed.
> This logic is unchanged between 4.18.0 and 4.22.0 (code inspection), so
> explicit route contracts with array bodies presumably fail on older versions
> too. What CAMEL-23174 changed is that every Kamelet declaring {{dataTypes}}
> now gets such a contract.
> h3. Reproducer (Camel 4.22.0, plain JVM)
> {code:java}
> from("direct:contract").outputType("application-octet-stream").log("in
> contract route");
> Exchange e = template.send("direct:contract", ex ->
> ex.getMessage().setBody("hello".getBytes()));
> // e.getException(): CamelExecutionException: Failed to convert body to
> 'java:[B' ... Caused by: ClassNotFoundException: [B
> // the same route with a String body succeeds
> {code}
> Kamelet variant, loaded from {{classpath:kamelets/bytes-source.kamelet.yaml}}
> and consumed with {{from("kamelet:bytes-source")}}. Sending a {{byte[]}} to
> {{direct:kamelet-in}} fails the same way; a {{String}} body succeeds.
> {noformat}
> apiVersion: camel.apache.org/v1
> kind: Kamelet
> metadata:
> name: bytes-source
> spec:
> definition:
> title: "Bytes Source"
> properties: {}
> dataTypes:
> out:
> default: binary
> types:
> binary:
> format: "application-octet-stream"
> mediaType: application/octet-stream
> template:
> from:
> uri: "direct:kamelet-in"
> steps:
> - to: "kamelet:sink"
> {noformat}
> h3. Impact
> * The contract is checked when the Kamelet route completes, so the steps
> after {{kamelet:sink}} run first; only then is the exchange marked failed and
> the consumer takes its failure path.
> * Verified with {{aws-s3-source}} (Kamelets 4.22.0, default
> {{includeBody=true}}, so the body is {{byte[]}}): every exchange fails at the
> end of the Kamelet route, {{AWS2S3Consumer}} runs {{processRollback}} instead
> of {{processCommit}}, {{deleteAfterRead}} never deletes, and the same objects
> are consumed again on every poll. It is logged at WARN only ("Exchange
> failed, so rolling back message status"), so it is easy to miss. Reported
> downstream in
> [apache/camel-kafka-connector#1849|https://github.com/apache/camel-kafka-connector/issues/1849].
> * 19 Kamelets in the 4.22.0 catalog declare {{dataTypes}}. The sources with a
> binary default output are the most likely to carry {{byte[]}}: aws-s3-source,
> azure-eventhubs-source, azure-storage-blob-source,
> azure-storage-datalake-source, azure-storage-files-source,
> google-storage-source and ibm-cos-source. Only aws-s3-source was verified.
> h3. Possible fixes
> * Resolve binary array names in {{ObjectHelper.loadClass}} /
> {{DefaultClassResolver}}, for example by falling back to
> {{Class.forName(name, false, loader)}} for names starting with {{\[}}.
> * Keep the {{Class}} in {{DataType(Class)}}, so {{TypeConverterTransformer}}
> does not have to resolve it by name.
> * Skip the conversion in {{TypeConverterTransformer}} /
> {{ContractAdvice.convertIfRequired}} when the body is already an instance of
> the requested type.
> Tests should cover a {{byte[]}} body for both a route contract and a Kamelet
> with {{dataTypes}}.
> h3. Workaround
> Use the component endpoint directly instead of the Kamelet, so no contract is
> added (verified with {{aws2-s3:...?deleteAfterRead=true}}).
> ----
> _Claude Code on behalf of Andrea Cosentino_
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)