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 64ab57e4dc34 CAMEL-26254: camel-ibm-secrets-manager - getSecret gives 
the Secret for every other secret type, createKVSecret takes a JSON object, and 
a Map or a POJO is stored as JSON in an arbitrary secret (#28973)
64ab57e4dc34 is described below

commit 64ab57e4dc34b96d49a676a4724650753e049115
Author: Andrea Cosentino <[email protected]>
AuthorDate: Mon Nov 2 23:21:48 2026 +0100

    CAMEL-26254: camel-ibm-secrets-manager - getSecret gives the Secret for 
every other secret type, createKVSecret takes a JSON object, and a Map or a 
POJO is stored as JSON in an arbitrary secret (#28973)
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 .../catalog/components/ibm-secrets-manager.json    |   4 +-
 .../docs/ibm-secrets-manager-component.adoc        |  70 ++-
 .../ibm/secrets/manager/ibm-secrets-manager.json   |   4 +-
 .../main/docs/ibm-secrets-manager-component.adoc   |  70 ++-
 .../secrets/manager/IBMSecretsManagerEndpoint.java |  27 +-
 .../secrets/manager/IBMSecretsManagerProducer.java | 118 ++++-
 .../manager/IBMSecretsManagerProducerBodyTest.java | 504 +++++++++++++++++++++
 .../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc    |   8 +
 8 files changed, 767 insertions(+), 38 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ibm-secrets-manager.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ibm-secrets-manager.json
index fa2ea7fd7a2c..4dc4884604fb 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ibm-secrets-manager.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ibm-secrets-manager.json
@@ -36,8 +36,8 @@
     "CamelIbmSecretsManagerSecretData": { "index": 5, "kind": "header", 
"displayName": "", "group": "producer", "label": "producer", "required": false, 
"javaType": "java.util.Map", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Set the secret data (Map) 
for KV secret update operation.", "constantName": 
"org.apache.camel.component.ibm.secrets.manager.IBMSecretsManagerConstants#SECRET_DATA"
 }
   },
   "bodyTypes": {
-    "producerIn": { "carriers": [ "text", "bytes", "stream", "file" ], 
"byOption": [ { "option": "operation", "value": "createKVSecret", "carriers": [ 
"map" ] }, { "option": "operation", "value": "updateSecret", "carriers": [ 
"text", "bytes", "stream", "file", "map" ] }, { "option": "operation", "value": 
"getSecret", "carriers": [ "any" ] }, { "option": "operation", "value": 
"deleteSecret", "carriers": [ "any" ] }, { "option": "operation", "value": 
"listSecrets", "carriers": [ "any" ] }, [...]
-    "producerOut": { "carriers": [ "text" ], "javaType": "java.lang.String", 
"byOption": [ { "option": "operation", "value": "getSecret", "carriers": [ 
"text", "map", "pojo" ] }, { "option": "operation", "value": "deleteSecret", 
"carriers": [ "unchanged" ] }, { "option": "operation", "value": "listSecrets", 
"carriers": [ "pojo" ], "javaType": 
"com.ibm.cloud.secrets_manager_sdk.secrets_manager.v2.model.SecretMetadataPaginatedCollection"
 }, { "option": "operation", "value": "updateSecret", [...]
+    "producerIn": { "carriers": [ "text", "bytes", "stream", "file", "map", 
"list", "pojo" ], "byOption": [ { "option": "operation", "value": 
"createKVSecret", "carriers": [ "map", "text", "bytes", "stream", "file" ] }, { 
"option": "operation", "value": "updateSecret", "carriers": [ "text", "bytes", 
"stream", "file", "map", "list", "pojo" ] }, { "option": "operation", "value": 
"getSecret", "carriers": [ "any" ] }, { "option": "operation", "value": 
"deleteSecret", "carriers": [ "any" ] }, [...]
+    "producerOut": { "carriers": [ "text" ], "javaType": "java.lang.String", 
"byOption": [ { "option": "operation", "value": "getSecret", "carriers": [ 
"text", "map", "pojo" ] }, { "option": "operation", "value": "deleteSecret", 
"carriers": [ "unchanged" ] }, { "option": "operation", "value": "listSecrets", 
"carriers": [ "pojo" ], "javaType": 
"com.ibm.cloud.secrets_manager_sdk.secrets_manager.v2.model.SecretMetadataPaginatedCollection"
 }, { "option": "operation", "value": "updateSecret", [...]
   },
   "properties": {
     "label": { "index": 0, "kind": "path", "displayName": "Label", "group": 
"producer", "label": "", "required": true, "type": "string", "javaType": 
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "configurationClass": 
"org.apache.camel.component.ibm.secrets.manager.IBMSecretsManagerConfiguration",
 "configurationField": "configuration", "description": "Logical name" },
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ibm-secrets-manager-component.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ibm-secrets-manager-component.adoc
index 43a9e69fa908..e4231c850eee 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ibm-secrets-manager-component.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ibm-secrets-manager-component.adoc
@@ -48,6 +48,22 @@ The component supports operations at the producer level. 
Specifically, it provid
 * `updateSecret` - Updates an existing secret (creates a new version)
 * `listSecretVersions` - Lists all versions of a specific secret
 
+The payload of an arbitrary secret is the message body as text: bytes, a 
stream, a file, XML (a DOM node or a
+`Source`) or a `ByteBuffer` are read as text. A `Map`, a `List` or another 
object (POJO) is stored as JSON.
+
+The data of a key-value secret is a `Map` of the keys and values (or a body 
that converts to a `Map`), or a JSON
+object, which can be text, bytes, a stream or a file.
+
+`getSecret` gives the payload text of an arbitrary secret and the `Map` of a 
key-value secret. For the other secret
+types, such as `service_credentials`, `username_password`, `iam_credentials`, 
`imported_cert`, `public_cert` or
+`private_cert`, it gives the `Secret` of the IBM SDK, whose getters return the 
fields of the secret type, for example
+`getUsername()` and `getPassword()`.
+
+`updateSecret` supports arbitrary and key-value secrets: it creates a new 
version with the payload of the
+`CamelIbmSecretsManagerSecretPayload` header or the message body for an 
arbitrary secret, and with the data of the
+`CamelIbmSecretsManagerSecretData` header or the message body for a key-value 
secret. It fails for the other secret
+types.
+
 == Examples
 
 === Producer Operations
@@ -106,7 +122,59 @@ YAML::
 
 ==== Create a Key-Value Secret
 
-._Java-only: requires programmatic Map construction_
+The keys and values can be given as a JSON object:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:createKVSecret")
+    .setHeader("CamelIbmSecretsManagerSecretName", constant("database-creds"))
+    .setBody(constant("{\"username\": \"admin\", \"password\": 
\"secret123\"}"))
+    
.to("ibm-secrets-manager://label?operation=createKVSecret&token={{token}}&serviceUrl={{serviceUrl}}");
+----
+
+XML::
++
+[source,xml]
+----
+<route>
+  <from uri="direct:createKVSecret"/>
+  <setHeader name="CamelIbmSecretsManagerSecretName">
+    <constant>database-creds</constant>
+  </setHeader>
+  <setBody>
+    <constant>{"username": "admin", "password": "secret123"}</constant>
+  </setBody>
+  <to 
uri="ibm-secrets-manager://label?operation=createKVSecret&amp;token={{token}}&amp;serviceUrl={{serviceUrl}}"/>
+</route>
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+    from:
+      uri: direct:createKVSecret
+      steps:
+        - setHeader:
+            name: CamelIbmSecretsManagerSecretName
+            constant: database-creds
+        - setBody:
+            constant: '{"username": "admin", "password": "secret123"}'
+        - to:
+            uri: ibm-secrets-manager://label
+            parameters:
+              operation: createKVSecret
+              token: "{{token}}"
+              serviceUrl: "{{serviceUrl}}"
+----
+====
+
+or as a `Map`:
 
 [source,java]
 ----
diff --git 
a/components/camel-ibm/camel-ibm-secrets-manager/src/generated/resources/META-INF/org/apache/camel/component/ibm/secrets/manager/ibm-secrets-manager.json
 
b/components/camel-ibm/camel-ibm-secrets-manager/src/generated/resources/META-INF/org/apache/camel/component/ibm/secrets/manager/ibm-secrets-manager.json
index fa2ea7fd7a2c..4dc4884604fb 100644
--- 
a/components/camel-ibm/camel-ibm-secrets-manager/src/generated/resources/META-INF/org/apache/camel/component/ibm/secrets/manager/ibm-secrets-manager.json
+++ 
b/components/camel-ibm/camel-ibm-secrets-manager/src/generated/resources/META-INF/org/apache/camel/component/ibm/secrets/manager/ibm-secrets-manager.json
@@ -36,8 +36,8 @@
     "CamelIbmSecretsManagerSecretData": { "index": 5, "kind": "header", 
"displayName": "", "group": "producer", "label": "producer", "required": false, 
"javaType": "java.util.Map", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Set the secret data (Map) 
for KV secret update operation.", "constantName": 
"org.apache.camel.component.ibm.secrets.manager.IBMSecretsManagerConstants#SECRET_DATA"
 }
   },
   "bodyTypes": {
-    "producerIn": { "carriers": [ "text", "bytes", "stream", "file" ], 
"byOption": [ { "option": "operation", "value": "createKVSecret", "carriers": [ 
"map" ] }, { "option": "operation", "value": "updateSecret", "carriers": [ 
"text", "bytes", "stream", "file", "map" ] }, { "option": "operation", "value": 
"getSecret", "carriers": [ "any" ] }, { "option": "operation", "value": 
"deleteSecret", "carriers": [ "any" ] }, { "option": "operation", "value": 
"listSecrets", "carriers": [ "any" ] }, [...]
-    "producerOut": { "carriers": [ "text" ], "javaType": "java.lang.String", 
"byOption": [ { "option": "operation", "value": "getSecret", "carriers": [ 
"text", "map", "pojo" ] }, { "option": "operation", "value": "deleteSecret", 
"carriers": [ "unchanged" ] }, { "option": "operation", "value": "listSecrets", 
"carriers": [ "pojo" ], "javaType": 
"com.ibm.cloud.secrets_manager_sdk.secrets_manager.v2.model.SecretMetadataPaginatedCollection"
 }, { "option": "operation", "value": "updateSecret", [...]
+    "producerIn": { "carriers": [ "text", "bytes", "stream", "file", "map", 
"list", "pojo" ], "byOption": [ { "option": "operation", "value": 
"createKVSecret", "carriers": [ "map", "text", "bytes", "stream", "file" ] }, { 
"option": "operation", "value": "updateSecret", "carriers": [ "text", "bytes", 
"stream", "file", "map", "list", "pojo" ] }, { "option": "operation", "value": 
"getSecret", "carriers": [ "any" ] }, { "option": "operation", "value": 
"deleteSecret", "carriers": [ "any" ] }, [...]
+    "producerOut": { "carriers": [ "text" ], "javaType": "java.lang.String", 
"byOption": [ { "option": "operation", "value": "getSecret", "carriers": [ 
"text", "map", "pojo" ] }, { "option": "operation", "value": "deleteSecret", 
"carriers": [ "unchanged" ] }, { "option": "operation", "value": "listSecrets", 
"carriers": [ "pojo" ], "javaType": 
"com.ibm.cloud.secrets_manager_sdk.secrets_manager.v2.model.SecretMetadataPaginatedCollection"
 }, { "option": "operation", "value": "updateSecret", [...]
   },
   "properties": {
     "label": { "index": 0, "kind": "path", "displayName": "Label", "group": 
"producer", "label": "", "required": true, "type": "string", "javaType": 
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "configurationClass": 
"org.apache.camel.component.ibm.secrets.manager.IBMSecretsManagerConfiguration",
 "configurationField": "configuration", "description": "Logical name" },
diff --git 
a/components/camel-ibm/camel-ibm-secrets-manager/src/main/docs/ibm-secrets-manager-component.adoc
 
b/components/camel-ibm/camel-ibm-secrets-manager/src/main/docs/ibm-secrets-manager-component.adoc
index 43a9e69fa908..e4231c850eee 100644
--- 
a/components/camel-ibm/camel-ibm-secrets-manager/src/main/docs/ibm-secrets-manager-component.adoc
+++ 
b/components/camel-ibm/camel-ibm-secrets-manager/src/main/docs/ibm-secrets-manager-component.adoc
@@ -48,6 +48,22 @@ The component supports operations at the producer level. 
Specifically, it provid
 * `updateSecret` - Updates an existing secret (creates a new version)
 * `listSecretVersions` - Lists all versions of a specific secret
 
+The payload of an arbitrary secret is the message body as text: bytes, a 
stream, a file, XML (a DOM node or a
+`Source`) or a `ByteBuffer` are read as text. A `Map`, a `List` or another 
object (POJO) is stored as JSON.
+
+The data of a key-value secret is a `Map` of the keys and values (or a body 
that converts to a `Map`), or a JSON
+object, which can be text, bytes, a stream or a file.
+
+`getSecret` gives the payload text of an arbitrary secret and the `Map` of a 
key-value secret. For the other secret
+types, such as `service_credentials`, `username_password`, `iam_credentials`, 
`imported_cert`, `public_cert` or
+`private_cert`, it gives the `Secret` of the IBM SDK, whose getters return the 
fields of the secret type, for example
+`getUsername()` and `getPassword()`.
+
+`updateSecret` supports arbitrary and key-value secrets: it creates a new 
version with the payload of the
+`CamelIbmSecretsManagerSecretPayload` header or the message body for an 
arbitrary secret, and with the data of the
+`CamelIbmSecretsManagerSecretData` header or the message body for a key-value 
secret. It fails for the other secret
+types.
+
 == Examples
 
 === Producer Operations
@@ -106,7 +122,59 @@ YAML::
 
 ==== Create a Key-Value Secret
 
-._Java-only: requires programmatic Map construction_
+The keys and values can be given as a JSON object:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:createKVSecret")
+    .setHeader("CamelIbmSecretsManagerSecretName", constant("database-creds"))
+    .setBody(constant("{\"username\": \"admin\", \"password\": 
\"secret123\"}"))
+    
.to("ibm-secrets-manager://label?operation=createKVSecret&token={{token}}&serviceUrl={{serviceUrl}}");
+----
+
+XML::
++
+[source,xml]
+----
+<route>
+  <from uri="direct:createKVSecret"/>
+  <setHeader name="CamelIbmSecretsManagerSecretName">
+    <constant>database-creds</constant>
+  </setHeader>
+  <setBody>
+    <constant>{"username": "admin", "password": "secret123"}</constant>
+  </setBody>
+  <to 
uri="ibm-secrets-manager://label?operation=createKVSecret&amp;token={{token}}&amp;serviceUrl={{serviceUrl}}"/>
+</route>
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+    from:
+      uri: direct:createKVSecret
+      steps:
+        - setHeader:
+            name: CamelIbmSecretsManagerSecretName
+            constant: database-creds
+        - setBody:
+            constant: '{"username": "admin", "password": "secret123"}'
+        - to:
+            uri: ibm-secrets-manager://label
+            parameters:
+              operation: createKVSecret
+              token: "{{token}}"
+              serviceUrl: "{{serviceUrl}}"
+----
+====
+
+or as a `Map`:
 
 [source,java]
 ----
diff --git 
a/components/camel-ibm/camel-ibm-secrets-manager/src/main/java/org/apache/camel/component/ibm/secrets/manager/IBMSecretsManagerEndpoint.java
 
b/components/camel-ibm/camel-ibm-secrets-manager/src/main/java/org/apache/camel/component/ibm/secrets/manager/IBMSecretsManagerEndpoint.java
index fbaada0b20a0..3bfa113cbbec 100644
--- 
a/components/camel-ibm/camel-ibm-secrets-manager/src/main/java/org/apache/camel/component/ibm/secrets/manager/IBMSecretsManagerEndpoint.java
+++ 
b/components/camel-ibm/camel-ibm-secrets-manager/src/main/java/org/apache/camel/component/ibm/secrets/manager/IBMSecretsManagerEndpoint.java
@@ -38,26 +38,30 @@ import org.apache.camel.support.DefaultEndpoint;
                      Category.CLOUD, Category.CLOUD },
              producerOnly = true,
              headersClass = IBMSecretsManagerConstants.class)
-@BodyTypes(producerIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, 
Carrier.STREAM, Carrier.FILE },
+@BodyTypes(producerIn = @BodyType(value = {
+        Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, 
Carrier.MAP, Carrier.LIST, Carrier.POJO },
                                   byOption = {
                                           @BodyType.When(option = "operation", 
value = "createKVSecret",
-                                                         carriers = 
Carrier.MAP),
+                                                         carriers = {
+                                                                 Carrier.MAP, 
Carrier.TEXT, Carrier.BYTES, Carrier.STREAM,
+                                                                 Carrier.FILE 
}),
                                           @BodyType.When(option = "operation", 
value = "updateSecret",
                                                          carriers = {
                                                                  Carrier.TEXT, 
Carrier.BYTES, Carrier.STREAM, Carrier.FILE,
-                                                                 Carrier.MAP 
}),
+                                                                 Carrier.MAP, 
Carrier.LIST, Carrier.POJO }),
                                           @BodyType.When(option = "operation", 
value = "getSecret", carriers = Carrier.ANY),
                                           @BodyType.When(option = "operation", 
value = "deleteSecret", carriers = Carrier.ANY),
                                           @BodyType.When(option = "operation", 
value = "listSecrets", carriers = Carrier.ANY),
                                           @BodyType.When(option = "operation", 
value = "listSecretVersions",
                                                          carriers = 
Carrier.ANY) },
                                   description = "The payload of the secret for 
createArbitrarySecret, the default operation, as"
-                                                + " text: bytes, a stream or a 
file are read as text, a Map or a POJO is stored as"
-                                                + " its toString(), and no 
body fails. createKVSecret needs a Map of the keys and"
-                                                + " values: a JSON text fails, 
so unmarshal it first. updateSecret takes text for"
-                                                + " an arbitrary secret and a 
Map for a kv secret, unless the"
+                                                + " text: bytes, a stream, a 
file or XML are read as text, a Map, a List or a POJO"
+                                                + " is stored as JSON, and no 
body fails. createKVSecret needs the keys and values"
+                                                + " as a Map or as a JSON 
object, which can be text, bytes, a stream or a file."
+                                                + " updateSecret takes the 
same bodies for an arbitrary and a kv secret, unless the"
                                                 + " 
CamelIbmSecretsManagerSecretPayload or CamelIbmSecretsManagerSecretData header"
-                                                + " is set. The other 
operations take the secret id from a header"),
+                                                + " is set, and fails for the 
other secret types. The other operations take the"
+                                                + " secret id from a header"),
            producerOut = @BodyType(value = Carrier.TEXT, javaType = 
"java.lang.String",
                                    byOption = {
                                            @BodyType.When(option = 
"operation", value = "getSecret",
@@ -73,9 +77,10 @@ import org.apache.camel.support.DefaultEndpoint;
                                                           carriers = 
Carrier.POJO,
                                                           javaType = 
"com.ibm.cloud.secrets_manager_sdk.secrets_manager.v2.model.SecretVersionMetadataCollection")
 },
                                    description = "The id of the new secret for 
createArbitrarySecret and createKVSecret. getSecret"
-                                                 + " gives the payload text of 
an arbitrary secret, the Map of a kv secret, or the"
-                                                 + " Secret of the IBM SDK for 
service credentials, and fails for other secret"
-                                                 + " types. updateSecret gives 
the SecretVersion of the IBM SDK, the list"
+                                                 + " gives the payload text of 
an arbitrary secret, the Map of a kv secret, and the"
+                                                 + " Secret of the IBM SDK for 
the other secret types, such as service credentials,"
+                                                 + " username and password, 
IAM credentials or certificates. updateSecret gives the"
+                                                 + " SecretVersion of the IBM 
SDK, the list"
                                                  + " operations the collection 
of the IBM SDK, and deleteSecret leaves the body"
                                                  + " unchanged"))
 public class IBMSecretsManagerEndpoint extends DefaultEndpoint {
diff --git 
a/components/camel-ibm/camel-ibm-secrets-manager/src/main/java/org/apache/camel/component/ibm/secrets/manager/IBMSecretsManagerProducer.java
 
b/components/camel-ibm/camel-ibm-secrets-manager/src/main/java/org/apache/camel/component/ibm/secrets/manager/IBMSecretsManagerProducer.java
index 44d777aea3b0..e99d5302d091 100644
--- 
a/components/camel-ibm/camel-ibm-secrets-manager/src/main/java/org/apache/camel/component/ibm/secrets/manager/IBMSecretsManagerProducer.java
+++ 
b/components/camel-ibm/camel-ibm-secrets-manager/src/main/java/org/apache/camel/component/ibm/secrets/manager/IBMSecretsManagerProducer.java
@@ -16,16 +16,37 @@
  */
 package org.apache.camel.component.ibm.secrets.manager;
 
+import java.io.File;
+import java.io.InputStream;
+import java.io.Reader;
+import java.nio.ByteBuffer;
+import java.nio.file.Path;
 import java.util.Map;
+import java.util.UUID;
 
+import javax.xml.transform.Source;
+
+import org.w3c.dom.Node;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.ibm.cloud.sdk.core.http.Response;
 import com.ibm.cloud.secrets_manager_sdk.secrets_manager.v2.model.*;
 import org.apache.camel.Endpoint;
 import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.camel.StreamCache;
+import org.apache.camel.WrappedFile;
 import org.apache.camel.support.DefaultProducer;
 import org.apache.camel.util.ObjectHelper;
 
 public class IBMSecretsManagerProducer extends DefaultProducer {
+
+    private static final ObjectMapper MAPPER = new ObjectMapper();
+    private static final TypeReference<Map<String, Object>> MAP_TYPE = new 
TypeReference<>() {
+    };
+
     public IBMSecretsManagerProducer(final Endpoint endpoint) {
         super(endpoint);
     }
@@ -70,7 +91,7 @@ public class IBMSecretsManagerProducer extends 
DefaultProducer {
                     "Secret Name must be specified. Set the header '" + 
IBMSecretsManagerConstants.SECRET_NAME
                                                + "' with the desired secret 
name.");
         }
-        String payload = exchange.getMessage().getBody(String.class);
+        String payload = arbitraryPayload(exchange.getMessage());
         if (ObjectHelper.isEmpty(payload)) {
             throw new IllegalArgumentException(
                     "Secret payload must be provided in the message body for 
createArbitrarySecret operation.");
@@ -97,10 +118,10 @@ public class IBMSecretsManagerProducer extends 
DefaultProducer {
                     "Secret Name must be specified. Set the header '" + 
IBMSecretsManagerConstants.SECRET_NAME
                                                + "' with the desired secret 
name.");
         }
-        Map<String, Object> data = exchange.getMessage().getBody(Map.class);
+        Map<String, Object> data = kvData(exchange.getMessage());
         if (ObjectHelper.isEmpty(data)) {
             throw new IllegalArgumentException(
-                    "Secret data must be provided as a Map in the message body 
for createKVSecret operation.");
+                    "Secret data must be provided as a Map or a JSON object in 
the message body for createKVSecret operation.");
         }
         kvSecretResourceBuilder.data(data);
         kvSecretResourceBuilder.secretType(KVSecretPrototype.SecretType.KV);
@@ -125,21 +146,16 @@ public class IBMSecretsManagerProducer extends 
DefaultProducer {
         }
         Response<Secret> getResp = 
getEndpoint().getSecretManager().getSecret(getSecretOptionsBuilder.build()).execute();
 
-        String secretType = getResp.getResult().getSecretType();
-        switch (secretType) {
-            case "arbitrary":
-                
exchange.getMessage().setBody(getResp.getResult().getPayload());
-                break;
-            case "kv":
-                exchange.getMessage().setBody(getResp.getResult().getData());
-                break;
-            case "service_credentials":
-                exchange.getMessage().setBody(getResp.getResult());
-                break;
-            default:
-                throw new IllegalArgumentException(
-                        "Unsupported secret type '" + secretType
-                                                   + "'. Supported types are: 
arbitrary, kv, service_credentials.");
+        Secret secret = getResp.getResult();
+        String secretType = secret.getSecretType();
+        if (Secret.SecretType.ARBITRARY.equals(secretType)) {
+            exchange.getMessage().setBody(secret.getPayload());
+        } else if (Secret.SecretType.KV.equals(secretType)) {
+            exchange.getMessage().setBody(secret.getData());
+        } else {
+            // service_credentials, username_password, iam_credentials, 
imported_cert, public_cert, private_cert and
+            // custom_credentials: the Secret of the IBM SDK, with the fields 
of its type
+            exchange.getMessage().setBody(secret);
         }
     }
 
@@ -172,13 +188,17 @@ public class IBMSecretsManagerProducer extends 
DefaultProducer {
                 .build();
         Response<Secret> getResp = 
getEndpoint().getSecretManager().getSecret(getSecretOptions).execute();
         String secretType = getResp.getResult().getSecretType();
+        if (secretType == null) {
+            throw new IllegalArgumentException(
+                    "The secret has no secret type. The updateSecret operation 
supports arbitrary and kv secrets.");
+        }
 
         SecretVersionPrototype secretVersionPrototype;
         switch (secretType) {
             case "arbitrary":
                 String payload = 
exchange.getMessage().getHeader(IBMSecretsManagerConstants.SECRET_PAYLOAD, 
String.class);
                 if (ObjectHelper.isEmpty(payload)) {
-                    payload = exchange.getMessage().getBody(String.class);
+                    payload = arbitraryPayload(exchange.getMessage());
                 }
                 if (ObjectHelper.isEmpty(payload)) {
                     throw new IllegalArgumentException("Secret payload must be 
specified for arbitrary secret update");
@@ -190,7 +210,7 @@ public class IBMSecretsManagerProducer extends 
DefaultProducer {
             case "kv":
                 Map<String, Object> data = 
exchange.getMessage().getHeader(IBMSecretsManagerConstants.SECRET_DATA, 
Map.class);
                 if (ObjectHelper.isEmpty(data)) {
-                    data = exchange.getMessage().getBody(Map.class);
+                    data = kvData(exchange.getMessage());
                 }
                 if (ObjectHelper.isEmpty(data)) {
                     throw new IllegalArgumentException("Secret data must be 
specified for KV secret update");
@@ -200,7 +220,9 @@ public class IBMSecretsManagerProducer extends 
DefaultProducer {
                 secretVersionPrototype = kvBuilder.build();
                 break;
             default:
-                throw new IllegalArgumentException("Unsupported secret type 
for update: " + secretType);
+                throw new IllegalArgumentException(
+                        "Unsupported secret type for update: " + secretType
+                                                   + ". The updateSecret 
operation supports arbitrary and kv secrets.");
         }
 
         CreateSecretVersionOptions createSecretVersionOptions = new 
CreateSecretVersionOptions.Builder()
@@ -228,6 +250,60 @@ public class IBMSecretsManagerProducer extends 
DefaultProducer {
         exchange.getMessage().setBody(result.getResult());
     }
 
+    /**
+     * The payload of an arbitrary secret. Text, bytes, a stream, a file, XML 
(a DOM node or a Source) or a ByteBuffer
+     * are read as text with the type converter, and a number, a boolean, a 
character, an enum or a UUID is its text. A
+     * Map, a List or another object is stored as JSON, rather than as its 
toString().
+     */
+    private static String arbitraryPayload(Message message) {
+        Object body = message.getBody();
+        if (body == null || isText(body)) {
+            return message.getBody(String.class);
+        }
+        try {
+            return MAPPER.writeValueAsString(body);
+        } catch (JsonProcessingException e) {
+            throw new IllegalArgumentException(
+                    "The message body of type " + body.getClass().getName()
+                                               + " cannot be stored as JSON in 
an arbitrary secret. Convert it to text first.",
+                    e);
+        }
+    }
+
+    /**
+     * The keys and values of a kv secret: a Map (also a body that the type 
converter turns into a Map), or a JSON
+     * object as text, bytes, a stream or a file.
+     */
+    @SuppressWarnings("unchecked")
+    private static Map<String, Object> kvData(Message message) {
+        Map<String, Object> data = message.getBody(Map.class);
+        Object body = message.getBody();
+        if (data != null || body == null) {
+            return data;
+        }
+        String json = message.getBody(String.class);
+        if (ObjectHelper.isEmpty(json)) {
+            return null;
+        }
+        try {
+            return MAPPER.readValue(json, MAP_TYPE);
+        } catch (JsonProcessingException e) {
+            // the cause is not kept, as the message of the JSON parser can 
quote the secret
+            throw new IllegalArgumentException(
+                    "Secret data must be provided as a Map or a JSON object in 
the message body for a kv secret,"
+                                               + " but the body of type " + 
body.getClass().getName()
+                                               + " is not a JSON object");
+        }
+    }
+
+    private static boolean isText(Object body) {
+        return body instanceof CharSequence || body instanceof byte[] || body 
instanceof InputStream
+                || body instanceof Reader || body instanceof StreamCache || 
body instanceof File || body instanceof Path
+                || body instanceof WrappedFile || body instanceof Node || body 
instanceof Source
+                || body instanceof ByteBuffer || body instanceof Number || 
body instanceof Boolean
+                || body instanceof Character || body instanceof Enum<?> || 
body instanceof UUID;
+    }
+
     @Override
     public IBMSecretsManagerEndpoint getEndpoint() {
         return (IBMSecretsManagerEndpoint) super.getEndpoint();
diff --git 
a/components/camel-ibm/camel-ibm-secrets-manager/src/test/java/org/apache/camel/component/ibm/secrets/manager/IBMSecretsManagerProducerBodyTest.java
 
b/components/camel-ibm/camel-ibm-secrets-manager/src/test/java/org/apache/camel/component/ibm/secrets/manager/IBMSecretsManagerProducerBodyTest.java
new file mode 100644
index 000000000000..8e93e5dd2346
--- /dev/null
+++ 
b/components/camel-ibm/camel-ibm-secrets-manager/src/test/java/org/apache/camel/component/ibm/secrets/manager/IBMSecretsManagerProducerBodyTest.java
@@ -0,0 +1,504 @@
+/*
+ * 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.ibm.secrets.manager;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.dom.DOMSource;
+
+import org.w3c.dom.Document;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.ibm.cloud.sdk.core.security.NoAuthAuthenticator;
+import com.ibm.cloud.secrets_manager_sdk.secrets_manager.v2.SecretsManager;
+import com.ibm.cloud.secrets_manager_sdk.secrets_manager.v2.model.Secret;
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpServer;
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+import org.apache.camel.TypeConversionException;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.support.TypeConverterSupport;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * The bodies the producer takes and gives, with the IBM Secrets Manager SDK 
against a JDK HttpServer that plays the
+ * service and records the requests.
+ */
+class IBMSecretsManagerProducerBodyTest extends CamelTestSupport {
+
+    private static final ObjectMapper MAPPER = new ObjectMapper();
+
+    private static final String SECRETS_PATH = "/api/v2/secrets";
+
+    // the secrets the server knows, by id, which is also the secret type
+    private static final Map<String, String> SECRETS = Map.of(
+            "arbitrary", "{\"payload\":\"s3cr3t\"}",
+            "kv", 
"{\"data\":{\"username\":\"admin\",\"password\":\"s3cr3t\"}}",
+            "username_password", 
"{\"username\":\"admin\",\"password\":\"s3cr3t\"}",
+            "iam_credentials", 
"{\"api_key\":\"my-api-key\",\"service_id\":\"my-service-id\"}",
+            "imported_cert", "{\"certificate\":\"-----BEGIN 
CERTIFICATE-----\",\"common_name\":\"example.com\"}",
+            "public_cert", "{\"certificate\":\"-----BEGIN 
CERTIFICATE-----\",\"common_name\":\"example.com\"}",
+            "private_cert", "{\"certificate\":\"-----BEGIN 
CERTIFICATE-----\",\"common_name\":\"example.com\"}",
+            "service_credentials", 
"{\"credentials\":{\"apikey\":\"my-api-key\"}}",
+            "custom_credentials", 
"{\"credentials_content\":{\"token\":\"my-token\"}}");
+
+    private static final Map<String, String> REQUESTS = new 
ConcurrentHashMap<>();
+    private static HttpServer server;
+
+    @BeforeAll
+    static void startServer() throws IOException {
+        server = HttpServer.create(new InetSocketAddress("localhost", 0), 0);
+        server.createContext("/", IBMSecretsManagerProducerBodyTest::handle);
+        server.start();
+    }
+
+    @AfterAll
+    static void stopServer() {
+        if (server != null) {
+            server.stop(0);
+        }
+    }
+
+    private static void handle(HttpExchange exchange) throws IOException {
+        String method = exchange.getRequestMethod();
+        String path = exchange.getRequestURI().getPath();
+        String request = new String(exchange.getRequestBody().readAllBytes(), 
StandardCharsets.UTF_8);
+        REQUESTS.put(method + " " + path, request);
+
+        String response = null;
+        int status = 200;
+        if ("GET".equals(method) && path.startsWith(SECRETS_PATH + "/")) {
+            // get a secret: the id is the type
+            String id = path.substring(SECRETS_PATH.length() + 1);
+            String fields = SECRETS.get(id);
+            if (fields != null) {
+                response = secret(id, id, fields);
+            }
+        } else if ("POST".equals(method) && path.equals(SECRETS_PATH)) {
+            // create a secret
+            String type = 
MAPPER.readTree(request).path("secret_type").asText();
+            response = secret("new-secret-id", type, "{}");
+            status = 201;
+        } else if ("POST".equals(method) && path.startsWith(SECRETS_PATH + 
"/") && path.endsWith("/versions")) {
+            // create a version of a secret
+            String id = path.substring(SECRETS_PATH.length() + 1, 
path.length() - "/versions".length());
+            ObjectNode version = MAPPER.createObjectNode();
+            version.put("id", "new-version-id");
+            version.put("secret_id", id);
+            version.put("secret_type", id);
+            response = version.toString();
+            status = 201;
+        }
+        if (response == null) {
+            exchange.sendResponseHeaders(404, -1);
+            exchange.close();
+            return;
+        }
+        byte[] bytes = response.getBytes(StandardCharsets.UTF_8);
+        exchange.getResponseHeaders().add("Content-Type", "application/json");
+        exchange.sendResponseHeaders(status, bytes.length);
+        try (OutputStream out = exchange.getResponseBody()) {
+            out.write(bytes);
+        }
+    }
+
+    private static String secret(String id, String type, String fields) throws 
IOException {
+        ObjectNode secret = (ObjectNode) MAPPER.readTree(fields);
+        secret.put("id", id);
+        secret.put("name", "my-secret");
+        secret.put("secret_type", type);
+        return secret.toString();
+    }
+
+    @BeforeEach
+    void useTestServer() {
+        REQUESTS.clear();
+        // the endpoint is started with an IAM client: use a client without 
authentication that calls the test server
+        SecretsManager client = new SecretsManager("test", new 
NoAuthAuthenticator());
+        client.setServiceUrl("http://localhost:"; + 
server.getAddress().getPort());
+        context.getEndpoint(endpointUri(), 
IBMSecretsManagerEndpoint.class).setSecretManager(client);
+    }
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        CamelContext context = super.createCamelContext();
+        // a body that becomes a Map only through a type converter
+        context.getTypeConverterRegistry().addTypeConverter(Map.class, 
KeyValue.class, new TypeConverterSupport() {
+            @Override
+            public <T> T convertTo(Class<T> type, Exchange exchange, Object 
value) throws TypeConversionException {
+                KeyValue keyValue = (KeyValue) value;
+                return type.cast(Map.of(keyValue.key(), keyValue.value()));
+            }
+        });
+        return context;
+    }
+
+    private static String endpointUri() {
+        return 
"ibm-secrets-manager:test?token=test-token&serviceUrl=http://localhost:"; + 
server.getAddress().getPort();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:secrets").to(endpointUri());
+            }
+        };
+    }
+
+    // 1. getSecret and updateSecret for the other secret types
+
+    @ParameterizedTest
+    @ValueSource(strings = {
+            "username_password", "iam_credentials", "imported_cert", 
"public_cert", "private_cert", "service_credentials",
+            "custom_credentials" })
+    void getSecretGivesTheSecretOfTheOtherTypes(String type) {
+        Exchange result = getSecret(type);
+
+        Secret secret = assertInstanceOf(Secret.class, 
result.getMessage().getBody());
+        assertEquals(type, secret.getSecretType());
+        assertEquals(type, secret.getId());
+    }
+
+    @Test
+    void getSecretGivesTheUsernameAndPasswordOfAUsernamePasswordSecret() {
+        Secret secret = assertInstanceOf(Secret.class, 
getSecret("username_password").getMessage().getBody());
+
+        assertEquals("admin", secret.getUsername());
+        assertEquals("s3cr3t", secret.getPassword());
+    }
+
+    @Test
+    void getSecretGivesTheApiKeyOfAnIamCredentialsSecret() {
+        Secret secret = assertInstanceOf(Secret.class, 
getSecret("iam_credentials").getMessage().getBody());
+
+        assertEquals("my-api-key", secret.getApiKey());
+    }
+
+    @Test
+    void getSecretGivesTheCertificateOfAnImportedCertificate() {
+        Secret secret = assertInstanceOf(Secret.class, 
getSecret("imported_cert").getMessage().getBody());
+
+        assertEquals("-----BEGIN CERTIFICATE-----", secret.getCertificate());
+    }
+
+    @Test
+    void getSecretGivesThePayloadOfAnArbitrarySecretAndTheMapOfAKvSecret() {
+        assertEquals("s3cr3t", getSecret("arbitrary").getMessage().getBody());
+        assertEquals(Map.of("username", "admin", "password", "s3cr3t"), 
getSecret("kv").getMessage().getBody());
+    }
+
+    @Test
+    void updateSecretFailsClearlyForTheOtherTypes() {
+        Exchange result = updateSecret("username_password", "new-password");
+
+        IllegalArgumentException e = 
assertInstanceOf(IllegalArgumentException.class, result.getException());
+        assertTrue(e.getMessage().contains("username_password"), 
e.getMessage());
+        assertTrue(e.getMessage().contains("supports arbitrary and kv 
secrets"), e.getMessage());
+        assertNull(REQUESTS.get("POST " + SECRETS_PATH + 
"/username_password/versions"), "no version is created");
+    }
+
+    // 2. createKVSecret and a JSON object
+
+    @Test
+    void createKVSecretTakesAJsonObjectText() throws Exception {
+        Exchange result = createKVSecret("{\"username\": \"admin\", 
\"password\": \"s3cr3t\", \"port\": 5432}");
+
+        assertEquals("new-secret-id", result.getMessage().getBody());
+        JsonNode request = request("POST " + SECRETS_PATH);
+        assertEquals("kv", request.get("secret_type").asText());
+        assertEquals(MAPPER.readTree("{\"username\": \"admin\", \"password\": 
\"s3cr3t\", \"port\": 5432}"),
+                request.get("data"));
+    }
+
+    @Test
+    void createKVSecretTakesAJsonObjectAsBytes() throws Exception {
+        createKVSecret("{\"username\": 
\"admin\"}".getBytes(StandardCharsets.UTF_8));
+
+        assertEquals(MAPPER.readTree("{\"username\": \"admin\"}"), 
request("POST " + SECRETS_PATH).get("data"));
+    }
+
+    @Test
+    void createKVSecretTakesAJsonObjectAsAStream() throws Exception {
+        createKVSecret(new ByteArrayInputStream("{\"username\": 
\"admin\"}".getBytes(StandardCharsets.UTF_8)));
+
+        assertEquals(MAPPER.readTree("{\"username\": \"admin\"}"), 
request("POST " + SECRETS_PATH).get("data"));
+    }
+
+    @Test
+    void createKVSecretTakesAMap() throws Exception {
+        createKVSecret(Map.of("username", "admin"));
+
+        assertEquals(MAPPER.readTree("{\"username\": \"admin\"}"), 
request("POST " + SECRETS_PATH).get("data"));
+    }
+
+    @Test
+    void createKVSecretTakesABodyThatConvertsToAMap() throws Exception {
+        createKVSecret(new KeyValue("username", "admin"));
+
+        assertEquals(MAPPER.readTree("{\"username\": \"admin\"}"), 
request("POST " + SECRETS_PATH).get("data"));
+    }
+
+    @Test
+    void createKVSecretFailsForJsonThatIsNotAnObject() {
+        Exchange result = template.send("direct:secrets", exchange -> {
+            exchange.getIn().setHeader(IBMSecretsManagerConstants.OPERATION, 
IBMSecretsManagerOperation.createKVSecret);
+            exchange.getIn().setHeader(IBMSecretsManagerConstants.SECRET_NAME, 
"my-secret");
+            exchange.getIn().setBody("[\"admin\", \"s3cr3t\"]");
+        });
+
+        IllegalArgumentException e = 
assertInstanceOf(IllegalArgumentException.class, result.getException());
+        assertTrue(e.getMessage().contains("is not a JSON object"), 
e.getMessage());
+        assertNull(REQUESTS.get("POST " + SECRETS_PATH), "no secret is 
created");
+    }
+
+    @Test
+    void createKVSecretDoesNotQuoteATextThatIsNotJson() {
+        Exchange result = template.send("direct:secrets", exchange -> {
+            exchange.getIn().setHeader(IBMSecretsManagerConstants.OPERATION, 
IBMSecretsManagerOperation.createKVSecret);
+            exchange.getIn().setHeader(IBMSecretsManagerConstants.SECRET_NAME, 
"my-secret");
+            exchange.getIn().setBody("s3cr3t-password");
+        });
+
+        IllegalArgumentException e = 
assertInstanceOf(IllegalArgumentException.class, result.getException());
+        assertTrue(e.getMessage().contains("is not a JSON object"), 
e.getMessage());
+        // the error is logged: it must not quote the secret, as the message 
of the JSON parser does
+        StringWriter trace = new StringWriter();
+        e.printStackTrace(new PrintWriter(trace));
+        assertFalse(trace.toString().contains("s3cr3t"), trace.toString());
+    }
+
+    @Test
+    void updateSecretOfAKvSecretTakesAJsonObjectText() throws Exception {
+        Exchange result = updateSecret("kv", "{\"password\": 
\"new-password\"}");
+
+        assertNoException(result);
+        assertEquals(MAPPER.readTree("{\"password\": \"new-password\"}"),
+                request("POST " + SECRETS_PATH + "/kv/versions").get("data"));
+    }
+
+    // 3. createArbitrarySecret and updateSecret store a Map or a POJO as JSON
+
+    @Test
+    void createArbitrarySecretStoresAMapAsJson() throws Exception {
+        Map<String, Object> body = new LinkedHashMap<>();
+        body.put("username", "admin");
+        body.put("port", 5432);
+
+        createArbitrarySecret(body);
+
+        assertEquals(MAPPER.readTree("{\"username\": \"admin\", \"port\": 
5432}"), payloadOf("POST " + SECRETS_PATH));
+    }
+
+    @Test
+    void createArbitrarySecretStoresAPojoAsJson() throws Exception {
+        createArbitrarySecret(new DatabaseCredentials("admin", "s3cr3t"));
+
+        assertEquals(MAPPER.readTree("{\"username\": \"admin\", \"password\": 
\"s3cr3t\"}"),
+                payloadOf("POST " + SECRETS_PATH));
+    }
+
+    @Test
+    void createArbitrarySecretStoresAListAsJson() throws Exception {
+        createArbitrarySecret(List.of("admin", "s3cr3t"));
+
+        assertEquals(MAPPER.readTree("[\"admin\", \"s3cr3t\"]"), 
payloadOf("POST " + SECRETS_PATH));
+    }
+
+    @Test
+    void createArbitrarySecretReadsTextBytesAndNumbersAsText() throws 
Exception {
+        createArbitrarySecret("s3cr3t");
+        assertEquals("s3cr3t", request("POST " + 
SECRETS_PATH).get("payload").asText());
+
+        createArbitrarySecret("s3cr3t-bytes".getBytes(StandardCharsets.UTF_8));
+        assertEquals("s3cr3t-bytes", request("POST " + 
SECRETS_PATH).get("payload").asText());
+
+        createArbitrarySecret(new 
ByteArrayInputStream("s3cr3t".getBytes(StandardCharsets.UTF_8)));
+        assertEquals("s3cr3t", request("POST " + 
SECRETS_PATH).get("payload").asText());
+
+        createArbitrarySecret(1234);
+        assertEquals("1234", request("POST " + 
SECRETS_PATH).get("payload").asText());
+    }
+
+    @Test
+    void createArbitrarySecretReadsADomDocumentAndASourceAsXml() throws 
Exception {
+        Document document = 
DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
+        
document.appendChild(document.createElement("user")).setTextContent("admin");
+        String xml = context.getTypeConverter().convertTo(String.class, 
document);
+        assertTrue(xml.contains("<user>admin</user>"), xml);
+
+        createArbitrarySecret(document);
+        assertEquals(xml, request("POST " + 
SECRETS_PATH).get("payload").asText(), "the XML, not a JSON string");
+
+        createArbitrarySecret(new DOMSource(document));
+        assertEquals(xml, request("POST " + 
SECRETS_PATH).get("payload").asText(), "the XML, not a JSON string");
+    }
+
+    @Test
+    void createArbitrarySecretReadsAByteBufferAsText() throws Exception {
+        
createArbitrarySecret(ByteBuffer.wrap("s3cr3t".getBytes(StandardCharsets.UTF_8)));
+
+        assertEquals("s3cr3t", request("POST " + 
SECRETS_PATH).get("payload").asText(), "the text, not Base64");
+    }
+
+    @Test
+    void createArbitrarySecretReadsAUuidAsText() throws Exception {
+        UUID uuid = UUID.randomUUID();
+
+        createArbitrarySecret(uuid);
+
+        assertEquals(uuid.toString(), request("POST " + 
SECRETS_PATH).get("payload").asText(), "the text, not quoted");
+    }
+
+    @Test
+    void createArbitrarySecretFailsWithoutABody() {
+        Exchange result = template.send("direct:secrets", exchange -> {
+            exchange.getIn().setHeader(IBMSecretsManagerConstants.SECRET_NAME, 
"my-secret");
+        });
+
+        IllegalArgumentException e = 
assertInstanceOf(IllegalArgumentException.class, result.getException());
+        assertTrue(e.getMessage().startsWith("Secret payload must be 
provided"), e.getMessage());
+    }
+
+    @Test
+    void updateSecretOfAnArbitrarySecretStoresAMapAsJson() throws Exception {
+        Exchange result = updateSecret("arbitrary", Map.of("username", 
"admin"));
+
+        assertNoException(result);
+        assertEquals(MAPPER.readTree("{\"username\": \"admin\"}"), 
payloadOf("POST " + SECRETS_PATH + "/arbitrary/versions"));
+    }
+
+    private Exchange getSecret(String id) {
+        Exchange result = template.send("direct:secrets", exchange -> {
+            exchange.getIn().setHeader(IBMSecretsManagerConstants.OPERATION, 
IBMSecretsManagerOperation.getSecret);
+            exchange.getIn().setHeader(IBMSecretsManagerConstants.SECRET_ID, 
id);
+        });
+        assertNoException(result);
+        return result;
+    }
+
+    private Exchange updateSecret(String id, Object body) {
+        return template.send("direct:secrets", exchange -> {
+            exchange.getIn().setHeader(IBMSecretsManagerConstants.OPERATION, 
IBMSecretsManagerOperation.updateSecret);
+            exchange.getIn().setHeader(IBMSecretsManagerConstants.SECRET_ID, 
id);
+            exchange.getIn().setBody(body);
+        });
+    }
+
+    private Exchange createKVSecret(Object body) {
+        Exchange result = template.send("direct:secrets", exchange -> {
+            exchange.getIn().setHeader(IBMSecretsManagerConstants.OPERATION, 
IBMSecretsManagerOperation.createKVSecret);
+            exchange.getIn().setHeader(IBMSecretsManagerConstants.SECRET_NAME, 
"my-secret");
+            exchange.getIn().setBody(body);
+        });
+        assertNoException(result);
+        return result;
+    }
+
+    private Exchange createArbitrarySecret(Object body) {
+        Exchange result = template.send("direct:secrets", exchange -> {
+            exchange.getIn().setHeader(IBMSecretsManagerConstants.SECRET_NAME, 
"my-secret");
+            exchange.getIn().setBody(body);
+        });
+        assertNoException(result);
+        assertEquals("new-secret-id", result.getMessage().getBody());
+        return result;
+    }
+
+    private static JsonNode request(String key) throws IOException {
+        String body = REQUESTS.get(key);
+        if (body == null) {
+            throw new AssertionError("no request " + key + " in " + 
REQUESTS.keySet());
+        }
+        return MAPPER.readTree(body);
+    }
+
+    /**
+     * The payload of the secret or version the request creates, read as JSON.
+     */
+    private static JsonNode payloadOf(String key) throws IOException {
+        String payload = request(key).get("payload").asText();
+        try {
+            return MAPPER.readTree(payload);
+        } catch (JsonProcessingException e) {
+            throw new AssertionError("the payload is not JSON: " + payload, e);
+        }
+    }
+
+    private static void assertNoException(Exchange result) {
+        if (result.getException() != null) {
+            throw new AssertionError("the exchange failed: " + 
result.getException(), result.getException());
+        }
+    }
+
+    record KeyValue(String key, String value) {
+    }
+
+    static final class DatabaseCredentials {
+
+        private final String username;
+        private final String password;
+
+        DatabaseCredentials(String username, String password) {
+            this.username = username;
+            this.password = password;
+        }
+
+        public String getUsername() {
+            return username;
+        }
+
+        public String getPassword() {
+            return password;
+        }
+
+        @Override
+        public String toString() {
+            return "DatabaseCredentials[username=" + username + "]";
+        }
+    }
+}
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 d01e5531afdc..d140e03fee39 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
@@ -2808,6 +2808,14 @@ nothing was stored and the exchange completed without an 
error. The requests are
 the answer like the other state operations: the states are stored, and a 
request that Dapr rejects (or a sidecar that
 cannot be reached) fails the exchange.
 
+=== camel-ibm-secrets-manager - a Map or a POJO body is stored as JSON in an 
arbitrary secret
+
+The `createArbitrarySecret` operation, and the `updateSecret` operation for an 
arbitrary secret, now store a `Map`, a
+`List` or another object (POJO) in the message body as JSON. Before, the 
payload was the `toString()` of the body, such
+as `{username=admin}`. Text, bytes, a stream, a file, XML (a DOM node or a 
`Source`), a `ByteBuffer`, a `UUID`, a number,
+a boolean, a character or an enum is stored as text, as before. A route that 
relied on the `toString()` form must convert the body to text
+before the call.
+
 === camel-pubnub - the producer uses its configured channel
 
 The pubnub producer no longer reads the `CamelPubNubChannel` header. This 
header is set by the pubnub consumer to the

Reply via email to