This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new fae6c3323cdf CAMEL-23883: Kafka producer no longer silently drops 
scalar Jackson ValueNode
fae6c3323cdf is described below

commit fae6c3323cdf97c794224a979fe482ac62c4984a
Author: matteominin <[email protected]>
AuthorDate: Mon Jul 6 18:46:41 2026 +0200

    CAMEL-23883: Kafka producer no longer silently drops scalar Jackson 
ValueNode
    
    With useIterator=true (the default), the Kafka producer splits Iterable 
bodies
    into individual records. Since Jackson JsonNode implements Iterable, scalar 
value
    nodes (e.g. IntNode from a jq transform) were treated as empty iterables — 
send()
    was never called and the message was silently dropped.
    
    Narrow isIterable() so that a JsonNode is only treated as iterable when it 
is a
    container node (ArrayNode/ObjectNode). Scalar nodes now take the 
single-message
    path. Includes unit tests for both scalar and container cases, an 
integration
    test, and an upgrade guide entry.
    
    Closes #24467
---
 .../camel/component/kafka/KafkaProducer.java       |  5 ++
 .../camel/component/kafka/KafkaProducerTest.java   | 31 ++++++++++++
 .../integration/KafkaProducerValueNodeIT.java      | 59 ++++++++++++++++++++++
 .../ROOT/pages/camel-4x-upgrade-guide-4_22.adoc    | 11 ++++
 4 files changed, 106 insertions(+)

diff --git 
a/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaProducer.java
 
b/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaProducer.java
index bfa56a42b898..92e1ab5d8e74 100755
--- 
a/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaProducer.java
+++ 
b/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaProducer.java
@@ -27,6 +27,7 @@ import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
 
+import com.fasterxml.jackson.databind.JsonNode;
 import org.apache.camel.AsyncCallback;
 import org.apache.camel.Exchange;
 import org.apache.camel.Message;
@@ -332,6 +333,10 @@ public class KafkaProducer extends DefaultAsyncProducer 
implements RouteIdAware
     }
 
     private boolean isIterable(Object body) {
+        if (body instanceof JsonNode node) {
+            return node.isContainerNode();
+        }
+
         if (body instanceof Iterable || body instanceof Iterator) {
             return true;
         }
diff --git 
a/components/camel-kafka/src/test/java/org/apache/camel/component/kafka/KafkaProducerTest.java
 
b/components/camel-kafka/src/test/java/org/apache/camel/component/kafka/KafkaProducerTest.java
index 3a455bfa81a8..67db580eb5f4 100755
--- 
a/components/camel-kafka/src/test/java/org/apache/camel/component/kafka/KafkaProducerTest.java
+++ 
b/components/camel-kafka/src/test/java/org/apache/camel/component/kafka/KafkaProducerTest.java
@@ -28,6 +28,9 @@ import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 import java.util.stream.Collectors;
 
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.IntNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
 import org.apache.camel.AggregationStrategy;
 import org.apache.camel.AsyncCallback;
 import org.apache.camel.CamelContext;
@@ -480,6 +483,34 @@ public class KafkaProducerTest {
         assertRecordMetadataExistsForEachAggregatedMessage();
     }
 
+    @Test
+    public void processSendsMessageWithScalarJsonNode() throws Exception {
+        endpoint.getConfiguration().setTopic("sometopic");
+        Mockito.when(exchange.getIn()).thenReturn(in);
+        Mockito.when(exchange.getMessage()).thenReturn(in);
+
+        in.setBody(IntNode.valueOf(42));
+        producer.process(exchange);
+
+        verifySendMessage("sometopic");
+    }
+
+    @Test
+    public void processSendsMessagesWithJsonArrayNode() throws Exception {
+        endpoint.getConfiguration().setTopic("sometopic");
+        Mockito.when(exchange.getIn()).thenReturn(in);
+        Mockito.when(exchange.getMessage()).thenReturn(in);
+
+        ArrayNode node = JsonNodeFactory.instance.arrayNode();
+        node.add(1);
+        node.add(2);
+
+        in.setBody(node);
+        producer.process(exchange);
+
+        verifySendMessages(Arrays.asList("sometopic", "sometopic"), null);
+    }
+
     @SuppressWarnings({ "unchecked", "rawtypes" })
     protected void verifySendMessage(Integer partitionKey, String topic, 
String messageKey) {
         ArgumentCaptor<ProducerRecord> captor = 
ArgumentCaptor.forClass(ProducerRecord.class);
diff --git 
a/components/camel-kafka/src/test/java/org/apache/camel/component/kafka/integration/KafkaProducerValueNodeIT.java
 
b/components/camel-kafka/src/test/java/org/apache/camel/component/kafka/integration/KafkaProducerValueNodeIT.java
new file mode 100644
index 000000000000..8457f5cee511
--- /dev/null
+++ 
b/components/camel-kafka/src/test/java/org/apache/camel/component/kafka/integration/KafkaProducerValueNodeIT.java
@@ -0,0 +1,59 @@
+/*
+ * 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.component.kafka.integration;
+
+import java.util.Collections;
+
+import com.fasterxml.jackson.databind.node.IntNode;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+public class KafkaProducerValueNodeIT extends BaseKafkaTestSupport {
+
+    private static final String TOPIC = "value-node";
+
+    private static final String FROM_URI = "kafka:" + TOPIC
+                                           + 
"?groupId=KafkaProducerValueNodeIT&autoOffsetReset=earliest&keyDeserializer=org.apache.kafka.common.serialization.StringDeserializer&"
+                                           + 
"valueDeserializer=org.apache.kafka.common.serialization.StringDeserializer"
+                                           + 
"&autoCommitIntervalMs=1000&pollTimeoutMs=1000&autoCommitEnable=true";
+
+    @AfterEach
+    public void after() {
+        kafkaAdminClient.deleteTopics(Collections.singletonList(TOPIC));
+    }
+
+    @Test
+    public void scalarJsonNodeIsDelivered() throws Exception {
+        MockEndpoint mock = contextExtension.getMockEndpoint("mock:result");
+        mock.expectedBodiesReceived("5");
+        contextExtension.getProducerTemplate().sendBody("direct:start", 
IntNode.valueOf(5));
+        mock.assertIsSatisfied(5000);
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:start").to("kafka:" + TOPIC + 
"?groupId=KafkaProducerValueNodeIT");
+                from(FROM_URI).to("mock:result");
+            }
+        };
+    }
+}
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
index 19879bae8b3a..b8ae87f73de7 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
@@ -109,3 +109,14 @@ the message, consistent with the inbound header filtering 
already performed by t
 
 Ordinary application headers are unaffected. If a route relied on `Camel*` 
headers being propagated from the MIME
 content, set them explicitly after unmarshalling.
+
+=== camel-kafka - Producer no longer silently drops scalar Jackson nodes
+
+With `useIterator=true`, the Kafka producer splits an `Iterable` body into one 
record per element.
+A Jackson `JsonNode` implements `Iterable`, so a scalar value node (e.g. an 
`IntNode` produced by
+`transform().jq(".my-value")`) was seen as an empty iterable and produced no 
record, silently
+discarding the message with no exception or log.
+
+Scalar value nodes are now sent as a single record. Only container nodes 
(`ArrayNode`, `ObjectNode`)
+are still split, which is unchanged. Any `convertBodyTo(...)` previously used 
as a workaround is no
+longer required.

Reply via email to