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 278254ec258e CAMEL-25699: camel-protobuf - Keep the content of bytes
fields when converting a message to and from a Map (#27984)
278254ec258e is described below
commit 278254ec258e6b3cd5cdaa38725242ed92ef3b12
Author: allthingssecurity <[email protected]>
AuthorDate: Sat Oct 17 14:43:34 2026 +0530
CAMEL-25699: camel-protobuf - Keep the content of bytes fields when
converting a message to and from a Map (#27984)
ProtobufConverter turned a bytes field into ByteString.toString() when a
message was converted to a Map. That is a description for logging (identity
hash, size and a shortened, escaped preview), not the value, so the content
of every bytes field was lost without an error. In the other direction a
byte[] for a bytes field was passed to Builder.setField unchanged and
rejected by protobuf.
A bytes field now becomes a byte[] in the Map, and a byte[] in a Map is
wrapped with ByteString.copyFrom when the Map is converted to a message.
Enum fields are unchanged. The 4.24 upgrade guide notes the new value type.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
.../camel/catalog/docs/protobuf-dataformat.adoc | 2 +
.../src/main/docs/protobuf-dataformat.adoc | 2 +
.../dataformat/protobuf/ProtobufConverter.java | 10 ++-
.../protobuf/ProtobufBytesFieldMapTest.java | 82 ++++++++++++++++++++++
.../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc | 7 ++
5 files changed, 102 insertions(+), 1 deletion(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/protobuf-dataformat.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/protobuf-dataformat.adoc
index a5c5319d4b93..210acbe359f4 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/protobuf-dataformat.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/protobuf-dataformat.adoc
@@ -63,6 +63,8 @@ data from proto `Message` to `Map`,
you will need to set the `contentTypeFormat` to `native`
and explicitly define the data type `Map` when you get body of the exchange.
For instance: `exchange.getMessage().getBody(Map.class)`.
+In the `Map`, the value of a `bytes` field is a `byte[]`; when a `Map` is
converted to a message, a `bytes` field takes a
+`byte[]` (or a `ByteString`).
== Protobuf overview
diff --git a/components/camel-protobuf/src/main/docs/protobuf-dataformat.adoc
b/components/camel-protobuf/src/main/docs/protobuf-dataformat.adoc
index a5c5319d4b93..210acbe359f4 100644
--- a/components/camel-protobuf/src/main/docs/protobuf-dataformat.adoc
+++ b/components/camel-protobuf/src/main/docs/protobuf-dataformat.adoc
@@ -63,6 +63,8 @@ data from proto `Message` to `Map`,
you will need to set the `contentTypeFormat` to `native`
and explicitly define the data type `Map` when you get body of the exchange.
For instance: `exchange.getMessage().getBody(Map.class)`.
+In the `Map`, the value of a `bytes` field is a `byte[]`; when a `Map` is
converted to a message, a `bytes` field takes a
+`byte[]` (or a `ByteString`).
== Protobuf overview
diff --git
a/components/camel-protobuf/src/main/java/org/apache/camel/dataformat/protobuf/ProtobufConverter.java
b/components/camel-protobuf/src/main/java/org/apache/camel/dataformat/protobuf/ProtobufConverter.java
index a6c62b76611d..8d41bfa8d45a 100644
---
a/components/camel-protobuf/src/main/java/org/apache/camel/dataformat/protobuf/ProtobufConverter.java
+++
b/components/camel-protobuf/src/main/java/org/apache/camel/dataformat/protobuf/ProtobufConverter.java
@@ -21,6 +21,7 @@ import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
+import com.google.protobuf.ByteString;
import com.google.protobuf.Descriptors;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.EnumValueDescriptor;
@@ -88,6 +89,10 @@ public final class ProtobufConverter {
return convertMapToMessage(fieldDescriptor.getMessageType(),
builder.newBuilderForField(fieldDescriptor),
nestedValue);
+ case BYTE_STRING:
+ // a bytes field is given as byte[] in the map (see
convertValueToSuitableFieldType)
+ return inputValue instanceof byte[] bytes ?
ByteString.copyFrom(bytes) : inputValue;
+
default:
return inputValue;
}
@@ -157,9 +162,12 @@ public final class ProtobufConverter {
switch (fieldDescriptor.getJavaType()) {
case ENUM:
- case BYTE_STRING:
result = value.toString();
break;
+ case BYTE_STRING:
+ // ByteString.toString() only describes the value (size and a
shortened text)
+ result = ((ByteString) value).toByteArray();
+ break;
case MESSAGE:
result = convertProtoMessageToMap((Message) value);
break;
diff --git
a/components/camel-protobuf/src/test/java/org/apache/camel/dataformat/protobuf/ProtobufBytesFieldMapTest.java
b/components/camel-protobuf/src/test/java/org/apache/camel/dataformat/protobuf/ProtobufBytesFieldMapTest.java
new file mode 100644
index 000000000000..59e21e0fdefa
--- /dev/null
+++
b/components/camel-protobuf/src/test/java/org/apache/camel/dataformat/protobuf/ProtobufBytesFieldMapTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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.dataformat.protobuf;
+
+import java.util.Map;
+
+import com.google.protobuf.ByteString;
+import com.google.protobuf.BytesValue;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * A {@code bytes} field must keep its value when a message is converted to a
{@link Map} and back. The well-known type
+ * {@link BytesValue} has a single {@code bytes} field named {@code value}.
+ */
+public class ProtobufBytesFieldMapTest extends CamelTestSupport {
+
+ // not valid UTF-8, and longer than the text that ByteString.toString()
shows
+ private static final byte[] DATA = new byte[] {
+ 0, 1, 2, (byte) 0xff, (byte) 0xfe, 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1',
'2', '3', '4', '5', '6', '7', '8', '9', '0',
+ '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9' };
+
+ @Test
+ public void testToMapKeepsTheBytes() {
+ Map<String, Object> map =
ProtobufConverter.toMap(BytesValue.of(ByteString.copyFrom(DATA)));
+
+ assertArrayEquals(DATA, (byte[]) map.get("value"));
+ }
+
+ @Test
+ public void testToProtoAcceptsTheBytesOfToMap() {
+ BytesValue original = BytesValue.of(ByteString.copyFrom(DATA));
+
+ BytesValue back = (BytesValue)
ProtobufConverter.toProto(ProtobufConverter.toMap(original),
+ BytesValue.getDefaultInstance());
+
+ assertEquals(original, back);
+ }
+
+ @Test
+ public void testUnmarshalToMapKeepsTheBytes() throws Exception {
+ MockEndpoint mock = getMockEndpoint("mock:result");
+ mock.expectedMessageCount(1);
+
+ template.sendBody("direct:unmarshal",
BytesValue.of(ByteString.copyFrom(DATA)).toByteArray());
+
+ mock.assertIsSatisfied();
+ Map<?, ?> map =
mock.getReceivedExchanges().get(0).getMessage().getBody(Map.class);
+ assertArrayEquals(DATA, (byte[]) map.get("value"));
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:unmarshal").unmarshal(new
ProtobufDataFormat(BytesValue.getDefaultInstance()))
+ .to("mock:result");
+ }
+ };
+ }
+}
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 84afa10e507b..ff8694f11a3f 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
@@ -1474,3 +1474,10 @@ The consumer now sets the `CamelMiloNodeId` header on
every exchange, including
Stopping a consumer now deletes its monitored items. Previously they stayed on
the server while another endpoint kept
the shared connection open, so a restarted consumer received each value twice.
While the server is unreachable, the
stop waits until the client's next attempt to reconnect has failed.
+
+=== camel-protobuf - a bytes field in a Map
+
+When a protobuf message is converted to a `Map` (for example
`getBody(Map.class)` after unmarshalling), the value of
+a `bytes` field is now a `byte[]` with the content of the field. Previously it
was the `String` that
+`ByteString.toString()` returns, a description such as `<ByteString@1b6d3586
size=4 contents="...">`, which does not
+contain the whole value. When a `Map` is marshalled, a `byte[]` value is
accepted for a `bytes` field.