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

davsclaus pushed a commit to branch feature/CAMEL-25750-body-type-dump
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 7f89abbf98c0a55e16b705fd7b2011074b4d5c80
Author: Claus Ibsen <[email protected]>
AuthorDate: Tue Oct 20 14:51:18 2026 +0200

    CAMEL-25750: camel-management, camel-console - the body types over JMX, in 
the route coverage dump and in a body-type console
    
    So the body types a workload brought to each step can be read and kept, 
such as after a test run:
    - JMX: each processor has BodyTypes and BodyKinds, and each route counts 
its steps reached with
      more than one type (VariedBodyTypes) and with more than one kind 
(MixedBodyKinds). The route
      console reads its counts from there.
    - The route coverage dump (dumpRoutesCoverageAsXml, written by the Camel 
test kits) has bodyTypes
      and bodyKinds attributes on each step a message reached.
    - The new body-type dev console lists those steps per route, with varied 
and filter options.
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../apache/camel/catalog/dev-consoles-openapi.json |  98 +++++++++++++
 .../apache/camel/catalog/dev-consoles.properties   |   1 +
 .../camel/catalog/dev-consoles/body-type.json      | 108 +++++++++++++++
 .../apache/camel/catalog/docs/message-history.adoc |  11 ++
 .../org/apache/camel/dev-console/body-type.json    | 108 +++++++++++++++
 .../org/apache/camel/dev-console/body-type         |   2 +
 .../org/apache/camel/dev-consoles.properties       |   2 +-
 .../camel/impl/console/BodyTypeDevConsole.java     | 152 +++++++++++++++++++++
 .../apache/camel/impl/console/RouteDevConsole.java |  32 +----
 .../impl/console/RouteStructureDevConsole.java     |   8 +-
 .../camel/impl/console/BodyTypeDevConsoleTest.java | 109 +++++++++++++++
 .../docs/modules/eips/pages/message-history.adoc   |  11 ++
 .../management/mbean/ManagedProcessorMBean.java    |   6 +
 .../api/management/mbean/ManagedRouteMBean.java    |   6 +
 .../camel/management/mbean/ManagedProcessor.java   |  21 +++
 .../camel/management/mbean/ManagedRoute.java       |  21 +++
 .../management/mbean/RouteCoverageXmlParser.java   |   8 ++
 .../camel/management/ManagedBodyTypesTest.java     | 114 ++++++++++++++++
 18 files changed, 786 insertions(+), 32 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles-openapi.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles-openapi.json
index 49f33a6b703d..3e50feb68773 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles-openapi.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles-openapi.json
@@ -624,6 +624,104 @@
                                }
                        }
                },
+               "\/q\/dev\/body-type": {
+                       "get": {
+                               "summary": "Body Type",
+                               "description": "The body types each step of the 
routes has been reached with",
+                               "operationId": "body-type",
+                               "parameters": [
+                                       {
+                                               "name": "filter",
+                                               "in": "query",
+                                               "description": "Filters the 
routes matching by route id, route uri, and source location",
+                                               "required": false,
+                                               "schema": {
+                                                       "type": "string"
+                                               }
+                                       },
+                                       {
+                                               "name": "varied",
+                                               "in": "query",
+                                               "description": "Whether to 
include only the steps that have been reached with more than one body type",
+                                               "required": false,
+                                               "schema": {
+                                                       "type": "boolean",
+                                                       "default": false
+                                               }
+                                       }
+                               ],
+                               "responses": {
+                                       "200": {
+                                               "description": "Body Type 
output",
+                                               "content": {
+                                                       "application\/json": {
+                                                               "schema": {
+                                                                       "type": 
"object",
+                                                                       
"properties": {
+                                                                               
"routes": {
+                                                                               
        "type": "array",
+                                                                               
        "items": {
+                                                                               
                "type": "object",
+                                                                               
                "properties": {
+                                                                               
                        "routeId": {
+                                                                               
                                "type": "string",
+                                                                               
                                "description": "The route ID"
+                                                                               
                        },
+                                                                               
                        "source": {
+                                                                               
                                "type": "string",
+                                                                               
                                "description": "The source location (only 
present when known)"
+                                                                               
                        },
+                                                                               
                        "steps": {
+                                                                               
                                "type": "array",
+                                                                               
                                "items": {
+                                                                               
                                        "type": "object",
+                                                                               
                                        "properties": {
+                                                                               
                                                "id": {
+                                                                               
                                                        "type": "string",
+                                                                               
                                                        "description": "The 
processor ID"
+                                                                               
                                                },
+                                                                               
                                                "type": {
+                                                                               
                                                        "type": "string",
+                                                                               
                                                        "description": "The 
processor type"
+                                                                               
                                                },
+                                                                               
                                                "line": {
+                                                                               
                                                        "type": "integer",
+                                                                               
                                                        "description": "The 
source line number (only present when known)"
+                                                                               
                                                },
+                                                                               
                                                "code": {
+                                                                               
                                                        "type": "string",
+                                                                               
                                                        "description": "The 
code representation of this processor"
+                                                                               
                                                },
+                                                                               
                                                "bodyTypes": {
+                                                                               
                                                        "type": "array",
+                                                                               
                                                        "items": {
+                                                                               
                                                                "type": "string"
+                                                                               
                                                        },
+                                                                               
                                                        "description": "The 
body types this processor has been reached with, across the messages, in the 
order first seen: more than one means the type varied"
+                                                                               
                                                },
+                                                                               
                                                "bodyKinds": {
+                                                                               
                                                        "type": "array",
+                                                                               
                                                        "items": {
+                                                                               
                                                                "type": "string"
+                                                                               
                                                        },
+                                                                               
                                                        "description": "The 
kinds of the bodies this processor has been reached with: null, payload (text, 
bytes, a stream or a file), data (a Map, a List or an object) or iterator. More 
than one means the step needs attention"
+                                                                               
                                                }
+                                                                               
                                        }
+                                                                               
                                },
+                                                                               
                                "description": "The steps of the route that a 
message has reached, with their body types"
+                                                                               
                        }
+                                                                               
                }
+                                                                               
        },
+                                                                               
        "description": "The routes with a step that a message has reached (the 
message history records the body types, such as in the dev profile)"
+                                                                               
}
+                                                                       }
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               },
                "\/q\/dev\/browse": {
                        "get": {
                                "summary": "Browse",
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles.properties
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles.properties
index 0c4a488209c1..d1ffc72f9789 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles.properties
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles.properties
@@ -6,6 +6,7 @@ azure-secrets
 bean
 bean-model
 blocked
+body-type
 browse
 circuit-breaker
 consumer
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/body-type.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/body-type.json
new file mode 100644
index 000000000000..9de50b978451
--- /dev/null
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/body-type.json
@@ -0,0 +1,108 @@
+{
+  "console": {
+    "kind": "console",
+    "group": "camel",
+    "name": "body-type",
+    "title": "Body Type",
+    "description": "The body types each step of the routes has been reached 
with",
+    "deprecated": false,
+    "readOnly": true,
+    "javaType": "org.apache.camel.impl.console.BodyTypeDevConsole",
+    "groupId": "org.apache.camel",
+    "artifactId": "camel-console",
+    "version": "4.24.0-SNAPSHOT"
+  },
+  "options": {
+    "filter": {
+      "index": 0,
+      "kind": "option",
+      "displayName": "Filter",
+      "group": "query",
+      "label": "query",
+      "required": false,
+      "type": "string",
+      "javaType": "java.lang.String",
+      "deprecated": false,
+      "autowired": false,
+      "secret": false,
+      "description": "Filters the routes matching by route id, route uri, and 
source location"
+    },
+    "varied": {
+      "index": 1,
+      "kind": "option",
+      "displayName": "Varied",
+      "group": "query",
+      "label": "query",
+      "required": false,
+      "type": "boolean",
+      "javaType": "java.lang.Boolean",
+      "deprecated": false,
+      "autowired": false,
+      "secret": false,
+      "defaultValue": false,
+      "description": "Whether to include only the steps that have been reached 
with more than one body type"
+    }
+  },
+  "response": {
+    "type": "object",
+    "properties": {
+      "routes": {
+        "type": "array",
+        "items": {
+          "type": "object",
+          "properties": {
+            "routeId": {
+              "type": "string",
+              "description": "The route ID"
+            },
+            "source": {
+              "type": "string",
+              "description": "The source location (only present when known)"
+            },
+            "steps": {
+              "type": "array",
+              "items": {
+                "type": "object",
+                "properties": {
+                  "id": {
+                    "type": "string",
+                    "description": "The processor ID"
+                  },
+                  "type": {
+                    "type": "string",
+                    "description": "The processor type"
+                  },
+                  "line": {
+                    "type": "integer",
+                    "description": "The source line number (only present when 
known)"
+                  },
+                  "code": {
+                    "type": "string",
+                    "description": "The code representation of this processor"
+                  },
+                  "bodyTypes": {
+                    "type": "array",
+                    "items": {
+                      "type": "string"
+                    },
+                    "description": "The body types this processor has been 
reached with, across the messages, in the order first seen: more than one means 
the type varied"
+                  },
+                  "bodyKinds": {
+                    "type": "array",
+                    "items": {
+                      "type": "string"
+                    },
+                    "description": "The kinds of the bodies this processor has 
been reached with: null, payload (text, bytes, a stream or a file), data (a 
Map, a List or an object) or iterator. More than one means the step needs 
attention"
+                  }
+                }
+              },
+              "description": "The steps of the route that a message has 
reached, with their body types"
+            }
+          }
+        },
+        "description": "The routes with a step that a message has reached (the 
message history records the body types, such as in the dev profile)"
+      }
+    }
+  }
+}
+
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/message-history.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/message-history.adoc
index 174e4ad313fe..40d18ec93aec 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/message-history.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/message-history.adoc
@@ -477,6 +477,17 @@ List<BodyTypeRegistry.Kind> kinds = 
registry.getBodyKinds("orders", "to1");
 
 The Camel CLI shows them as well, see 
xref:manual::camel-jbang-diagnostics.adoc[Camel CLI diagnostics].
 
+=== Dumping the body types
+
+Run the messages of the expected workload, such as in a test, and then read 
what each step has been reached with:
+
+* The `body-type` dev console lists the steps that a message has reached, per 
route, with their types and kinds.
+  `varied=true` keeps only the steps reached with more than one type, and 
`filter` selects the routes.
+* Over JMX, each processor has the `BodyTypes` and `BodyKinds` attributes, and 
each route counts its steps reached with
+  more than one type (`VariedBodyTypes`) and with bodies of more than one kind 
(`MixedBodyKinds`).
+* The route coverage dump (`dumpRoutesCoverageAsXml`, which the Camel test 
kits write when route coverage is enabled)
+  has `bodyTypes` and `bodyKinds` attributes on each step a message has 
reached.
+
 == Configuring MessageHistory format
 
 The  Message History format in Camel is controlled by `Java String.format` 
patterns stored as global options on the CamelContext.
diff --git 
a/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/body-type.json
 
b/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/body-type.json
new file mode 100644
index 000000000000..9de50b978451
--- /dev/null
+++ 
b/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/body-type.json
@@ -0,0 +1,108 @@
+{
+  "console": {
+    "kind": "console",
+    "group": "camel",
+    "name": "body-type",
+    "title": "Body Type",
+    "description": "The body types each step of the routes has been reached 
with",
+    "deprecated": false,
+    "readOnly": true,
+    "javaType": "org.apache.camel.impl.console.BodyTypeDevConsole",
+    "groupId": "org.apache.camel",
+    "artifactId": "camel-console",
+    "version": "4.24.0-SNAPSHOT"
+  },
+  "options": {
+    "filter": {
+      "index": 0,
+      "kind": "option",
+      "displayName": "Filter",
+      "group": "query",
+      "label": "query",
+      "required": false,
+      "type": "string",
+      "javaType": "java.lang.String",
+      "deprecated": false,
+      "autowired": false,
+      "secret": false,
+      "description": "Filters the routes matching by route id, route uri, and 
source location"
+    },
+    "varied": {
+      "index": 1,
+      "kind": "option",
+      "displayName": "Varied",
+      "group": "query",
+      "label": "query",
+      "required": false,
+      "type": "boolean",
+      "javaType": "java.lang.Boolean",
+      "deprecated": false,
+      "autowired": false,
+      "secret": false,
+      "defaultValue": false,
+      "description": "Whether to include only the steps that have been reached 
with more than one body type"
+    }
+  },
+  "response": {
+    "type": "object",
+    "properties": {
+      "routes": {
+        "type": "array",
+        "items": {
+          "type": "object",
+          "properties": {
+            "routeId": {
+              "type": "string",
+              "description": "The route ID"
+            },
+            "source": {
+              "type": "string",
+              "description": "The source location (only present when known)"
+            },
+            "steps": {
+              "type": "array",
+              "items": {
+                "type": "object",
+                "properties": {
+                  "id": {
+                    "type": "string",
+                    "description": "The processor ID"
+                  },
+                  "type": {
+                    "type": "string",
+                    "description": "The processor type"
+                  },
+                  "line": {
+                    "type": "integer",
+                    "description": "The source line number (only present when 
known)"
+                  },
+                  "code": {
+                    "type": "string",
+                    "description": "The code representation of this processor"
+                  },
+                  "bodyTypes": {
+                    "type": "array",
+                    "items": {
+                      "type": "string"
+                    },
+                    "description": "The body types this processor has been 
reached with, across the messages, in the order first seen: more than one means 
the type varied"
+                  },
+                  "bodyKinds": {
+                    "type": "array",
+                    "items": {
+                      "type": "string"
+                    },
+                    "description": "The kinds of the bodies this processor has 
been reached with: null, payload (text, bytes, a stream or a file), data (a 
Map, a List or an object) or iterator. More than one means the step needs 
attention"
+                  }
+                }
+              },
+              "description": "The steps of the route that a message has 
reached, with their body types"
+            }
+          }
+        },
+        "description": "The routes with a step that a message has reached (the 
message history records the body types, such as in the dev profile)"
+      }
+    }
+  }
+}
+
diff --git 
a/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/body-type
 
b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/body-type
new file mode 100644
index 000000000000..bb4f0bd228f5
--- /dev/null
+++ 
b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/body-type
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.impl.console.BodyTypeDevConsole
diff --git 
a/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-consoles.properties
 
b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-consoles.properties
index 259ccb5a6d1e..4ecce3e5c9ff 100644
--- 
a/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-consoles.properties
+++ 
b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-consoles.properties
@@ -1,5 +1,5 @@
 # Generated by camel build tools - do NOT edit this file!
-dev-consoles=activity api bean blocked browse circuit-breaker consumer context 
datasource debug endpoint errors eval-language event gc health heap-dump 
heap-histogram inflight internal-tasks java-security jfr-memory-leak jvm log 
memory message-history processor processor-detail producer properties receive 
reload rest rest-spec route route-controller route-dump route-group 
route-structure route-topology send service simple-language source sql-query 
sql-trace startup-recorder system-proper [...]
+dev-consoles=activity api bean blocked body-type browse circuit-breaker 
consumer context datasource debug endpoint errors eval-language event gc health 
heap-dump heap-histogram inflight internal-tasks java-security jfr-memory-leak 
jvm log memory message-history processor processor-detail producer properties 
receive reload rest rest-spec route route-controller route-dump route-group 
route-structure route-topology send service simple-language source sql-query 
sql-trace startup-recorder sys [...]
 groupId=org.apache.camel
 artifactId=camel-console
 version=4.24.0-SNAPSHOT
diff --git 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/BodyTypeDevConsole.java
 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/BodyTypeDevConsole.java
new file mode 100644
index 000000000000..d48fce215651
--- /dev/null
+++ 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/BodyTypeDevConsole.java
@@ -0,0 +1,152 @@
+/*
+ * 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.impl.console;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.camel.NamedRoute;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.ModelDumpLine;
+import org.apache.camel.spi.ModelToStructureDumper;
+import org.apache.camel.spi.annotations.DevConsole;
+import org.apache.camel.support.PluginHelper;
+import org.apache.camel.support.console.AbstractDevConsole;
+import org.apache.camel.util.json.JsonRecordSupport;
+import org.apache.camel.util.json.Jsoner;
+
+import static 
org.apache.camel.impl.console.ConsoleHelper.extractSourceLocationLineNumber;
+import static 
org.apache.camel.impl.console.ConsoleHelper.extractSourceLocationNoLineNumber;
+
+/**
+ * The body types each step of the routes has been reached with, across the 
messages, as the
+ * {@link org.apache.camel.spi.BodyTypeRegistry} keeps them while the message 
history is on (CAMEL-25750).
+ */
+@DevConsole(name = "body-type", description = "The body types each step of the 
routes has been reached with")
+public class BodyTypeDevConsole extends AbstractDevConsole {
+
+    public record StepEntry(
+            @Metadata(description = "The processor ID") String id,
+            @Metadata(description = "The processor type") String type,
+            @Metadata(description = "The source line number (only present when 
known)") Integer line,
+            @Metadata(description = "The code representation of this 
processor") String code,
+            @Metadata(description = "The body types this processor has been 
reached with, across the messages, in the order first seen: more than one means 
the type varied") List<String> bodyTypes,
+            @Metadata(description = "The kinds of the bodies this processor 
has been reached with: null, payload (text, bytes, a stream or a file), data (a 
Map, a List or an object) or iterator. More than one means the step needs 
attention") List<String> bodyKinds) {
+    }
+
+    public record RouteEntry(
+            @Metadata(description = "The route ID") String routeId,
+            @Metadata(description = "The source location (only present when 
known)") String source,
+            @Metadata(description = "The steps of the route that a message has 
reached, with their body types") List<StepEntry> steps) {
+    }
+
+    public record Response(
+            @Metadata(description = "The routes with a step that a message has 
reached (the message history records the body types, such as in the dev 
profile)") List<RouteEntry> routes) {
+    }
+
+    @Metadata(label = "query", description = "Filters the routes matching by 
route id, route uri, and source location",
+              javaType = "java.lang.String")
+    public static final String FILTER = "filter";
+
+    @Metadata(label = "query",
+              description = "Whether to include only the steps that have been 
reached with more than one body type",
+              javaType = "java.lang.Boolean", defaultValue = "false")
+    public static final String VARIED = "varied";
+
+    public BodyTypeDevConsole() {
+        super("camel", "body-type", "Body Type", "The body types each step of 
the routes has been reached with");
+    }
+
+    @Override
+    protected String doCallText(Map<String, Object> options) {
+        List<RouteEntry> routes = routes(options);
+        if (routes.isEmpty()) {
+            return "No step has been reached by a message while the message 
history is on (the dev profile turns it on)\n";
+        }
+        StringBuilder sb = new StringBuilder();
+        for (RouteEntry route : routes) {
+            sb.append(String.format("    Route: %s", route.routeId()));
+            if (route.source() != null) {
+                sb.append(String.format(" (%s)", route.source()));
+            }
+            sb.append("\n\n");
+            int width = 0;
+            for (StepEntry step : route.steps()) {
+                width = Math.max(width, Jsoner.unescape(step.code()).length());
+            }
+            for (StepEntry step : route.steps()) {
+                String num = step.line() != null ? String.format("%4d:  ", 
step.line()) : "       ";
+                String text = String.format("%-" + width + "s   body: %s", 
Jsoner.unescape(step.code()),
+                        RouteStructureDevConsole.bodyTypesText(
+                                new 
RouteStructureDevConsole.LineBodyTypes(step.bodyTypes(), step.bodyKinds())));
+                sb.append(num).append(text).append("\n");
+            }
+            sb.append("\n");
+        }
+        return sb.toString();
+    }
+
+    @Override
+    protected Map<String, Object> doCallJson(Map<String, Object> options) {
+        return JsonRecordSupport.toJsonObject(new Response(routes(options)));
+    }
+
+    private List<RouteEntry> routes(Map<String, Object> options) {
+        final String filter = optionString(options, FILTER);
+        final boolean varied = optionBoolean(options, VARIED, false);
+
+        List<RouteEntry> answer = new ArrayList<>();
+        List<NamedRoute> routes = getCamelContext().getNamedRouteDefinitions();
+        routes.sort((o1, o2) -> 
o1.getRouteId().compareToIgnoreCase(o2.getRouteId()));
+        for (NamedRoute route : routes) {
+            if (!RouteStructureDevConsole.accept(route, filter)) {
+                continue;
+            }
+            List<StepEntry> steps = steps(route.getRouteId(), varied);
+            if (!steps.isEmpty()) {
+                String source = route.getResource() != null
+                        ? 
extractSourceLocationNoLineNumber(route.getResource().getLocation()) : null;
+                answer.add(new RouteEntry(route.getRouteId(), source, steps));
+            }
+        }
+        return answer;
+    }
+
+    private List<StepEntry> steps(String routeId, boolean varied) {
+        List<StepEntry> answer = new ArrayList<>();
+        try {
+            ModelToStructureDumper dumper = 
PluginHelper.getModelToStructureDumper(getCamelContext());
+            List<ModelDumpLine> lines = 
dumper.dumpStructure(getCamelContext(), routeId, false);
+            List<RouteStructureDevConsole.LineBodyTypes> bodyTypes
+                    = 
RouteStructureDevConsole.bodyTypesPerLine(getCamelContext(), routeId, lines);
+            for (int i = 0; i < lines.size(); i++) {
+                RouteStructureDevConsole.LineBodyTypes types = 
bodyTypes.get(i);
+                if (types == null || varied && types.types().size() < 2) {
+                    continue;
+                }
+                ModelDumpLine line = lines.get(i);
+                answer.add(new StepEntry(
+                        line.id(), line.type(), 
extractSourceLocationLineNumber(line.location()), Jsoner.escape(line.code()),
+                        types.types(), types.kinds()));
+            }
+        } catch (Exception e) {
+            // the route has no model to dump, such as a route added without 
one
+        }
+        return answer;
+    }
+}
diff --git 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteDevConsole.java
 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteDevConsole.java
index 6d605a29a771..a05f83d9f399 100644
--- 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteDevConsole.java
+++ 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteDevConsole.java
@@ -32,7 +32,6 @@ import org.apache.camel.Route;
 import org.apache.camel.api.management.ManagedCamelContext;
 import org.apache.camel.api.management.mbean.ManagedProcessorMBean;
 import org.apache.camel.api.management.mbean.ManagedRouteMBean;
-import org.apache.camel.spi.BodyTypeRegistry;
 import org.apache.camel.spi.Metadata;
 import org.apache.camel.spi.annotations.DevConsole;
 import org.apache.camel.support.ExceptionHelper;
@@ -316,12 +315,15 @@ public class RouteDevConsole extends AbstractDevConsole {
             List<ProcessorDevConsole.ProcessorEntry> procList = processors ? 
includeProcessorsJson(mrb) : null;
 
             Route r = getCamelContext().getRoute(mrb.getRouteId());
-            BodyTypeCounts bodyTypes = bodyTypeCounts(mrb.getRouteId());
+            // the steps reached with more than one body type, and with more 
than one kind (CAMEL-25750)
+            int varied = mrb.getVariedBodyTypes();
+            int mixed = mrb.getMixedBodyKinds();
             list.add(new RouteEntry(
                     mrb.getRouteId(), mrb.getRouteGroup(), 
mrb.getNodePrefixId(), mrb.getDescription(), mrb.getNote(),
                     mrb.isCreatedByKamelet(), mrb.isCreatedByRouteTemplate(), 
mrb.getRouteTemplateId(), mrb.getEndpointUri(),
                     mrb.isRemoteEndpoint(), mrb.getSourceLocation(), 
mrb.getTemplatedRouteSource(), mrb.getState(),
-                    r != null && r.supportsSuspension(), mrb.getUptime(), 
bodyTypes.varied(), bodyTypes.mixed(), lastError,
+                    r != null && r.supportsSuspension(), mrb.getUptime(), 
varied > 0 ? varied : null, mixed > 0 ? mixed : null,
+                    lastError,
                     stats, procList));
             return null;
         };
@@ -513,30 +515,6 @@ public class RouteDevConsole extends AbstractDevConsole {
         }
     }
 
-    /**
-     * How many steps of a route have been reached with more than one body 
type, and with bodies of more than one kind
-     * (each null when there are none).
-     */
-    private record BodyTypeCounts(Integer varied, Integer mixed) {
-    }
-
-    private BodyTypeCounts bodyTypeCounts(String routeId) {
-        BodyTypeRegistry registry = 
getCamelContext().getCamelContextExtension().getContextPlugin(BodyTypeRegistry.class);
-        int varied = 0;
-        int mixed = 0;
-        if (registry != null) {
-            for (Map.Entry<String, List<String>> node : 
registry.getBodyTypes(routeId).entrySet()) {
-                if (node.getValue().size() > 1) {
-                    varied++;
-                    if (registry.getBodyKinds(routeId, node.getKey()).size() > 
1) {
-                        mixed++;
-                    }
-                }
-            }
-        }
-        return new BodyTypeCounts(varied > 0 ? varied : null, mixed > 0 ? 
mixed : null);
-    }
-
     public static JsonObject gatherRouteStats(CamelContext camelContext, 
ManagedRouteMBean mrb) {
         return JsonRecordSupport.toJsonObject(buildStatistics(camelContext, 
mrb));
     }
diff --git 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteStructureDevConsole.java
 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteStructureDevConsole.java
index cb95e0bf9916..e320429cc64c 100644
--- 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteStructureDevConsole.java
+++ 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteStructureDevConsole.java
@@ -67,7 +67,7 @@ public class RouteStructureDevConsole extends 
AbstractDevConsole {
     /**
      * The body types and kinds a line of the route structure has been reached 
with.
      */
-    private record LineBodyTypes(List<String> types, List<String> kinds) {
+    record LineBodyTypes(List<String> types, List<String> kinds) {
     }
 
     public record RouteEntry(
@@ -210,7 +210,7 @@ public class RouteStructureDevConsole extends 
AbstractDevConsole {
                 .forEach(task::apply);
     }
 
-    private static boolean accept(NamedRoute route, String filter) {
+    static boolean accept(NamedRoute route, String filter) {
         if (filter == null || filter.isBlank()) {
             return true;
         }
@@ -280,7 +280,7 @@ public class RouteStructureDevConsole extends 
AbstractDevConsole {
      * for the route and its from (the first two lines), and for a processor 
no exchange has reached. The first
      * processor has the body as the from gave it.
      */
-    private static List<LineBodyTypes> bodyTypesPerLine(
+    static List<LineBodyTypes> bodyTypesPerLine(
             CamelContext camelContext, String routeId, List<ModelDumpLine> 
lines) {
         BodyTypeRegistry registry = 
camelContext.getCamelContextExtension().getContextPlugin(BodyTypeRegistry.class);
         List<LineBodyTypes> answer = new ArrayList<>(lines.size());
@@ -313,7 +313,7 @@ public class RouteStructureDevConsole extends 
AbstractDevConsole {
      * The body types by their simple class names, such as {@code String | 
LinkedHashMap}, followed by the kinds when
      * there is more than one: {@code String | LinkedHashMap (payload and 
data)}.
      */
-    private static String bodyTypesText(LineBodyTypes types) {
+    static String bodyTypesText(LineBodyTypes types) {
         StringJoiner sj = new StringJoiner(" | ");
         for (String type : types.types()) {
             sj.add(StringHelper.afterLast(type, ".", type));
diff --git 
a/core/camel-console/src/test/java/org/apache/camel/impl/console/BodyTypeDevConsoleTest.java
 
b/core/camel-console/src/test/java/org/apache/camel/impl/console/BodyTypeDevConsoleTest.java
new file mode 100644
index 000000000000..35b53c142a1e
--- /dev/null
+++ 
b/core/camel-console/src/test/java/org/apache/camel/impl/console/BodyTypeDevConsoleTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.impl.console;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.console.DevConsole;
+import org.apache.camel.support.PluginHelper;
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * The body-type console dumps what each step has been reached with 
(CAMEL-25750).
+ */
+public class BodyTypeDevConsoleTest extends ContextTestSupport {
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                context.setMessageHistory(true);
+                from("direct:start").routeId("orders")
+                        .log("${body}").id("logIt")
+                        .to("mock:result").id("result");
+                from("direct:quiet").routeId("quiet")
+                        .to("mock:quiet").id("quietResult");
+            }
+        };
+    }
+
+    @Test
+    public void testTheStepsAMessageReached() {
+        template.sendBody("direct:start", "Hello World");
+        template.sendBody("direct:start", new LinkedHashMap<>(Map.of("id", 
1)));
+
+        DevConsole con = 
PluginHelper.getDevConsoleResolver(context).resolveDevConsole("body-type");
+        Assertions.assertNotNull(con);
+        Assertions.assertEquals("camel", con.getGroup());
+
+        JsonObject out = (JsonObject) con.call(DevConsole.MediaType.JSON);
+        JsonArray routes = out.getCollection("routes");
+        // the quiet route has not been reached by a message
+        Assertions.assertEquals(1, routes.size());
+        JsonObject route = (JsonObject) routes.get(0);
+        Assertions.assertEquals("orders", route.getString("routeId"));
+        JsonArray steps = route.getCollection("steps");
+        Assertions.assertEquals(2, steps.size());
+        JsonObject result = (JsonObject) steps.get(1);
+        Assertions.assertEquals("result", result.getString("id"));
+        Assertions.assertEquals(List.of("java.lang.String", 
"java.util.LinkedHashMap"), result.getCollection("bodyTypes"));
+        Assertions.assertEquals(List.of("payload", "data"), 
result.getCollection("bodyKinds"));
+
+        String text = (String) con.call(DevConsole.MediaType.TEXT);
+        Assertions.assertTrue(text.contains("Route: orders"), text);
+        Assertions.assertTrue(text.contains("body: String | LinkedHashMap 
(payload and data)"), text);
+    }
+
+    @Test
+    public void testOnlyTheVariedSteps() {
+        template.sendBody("direct:start", "Hello World");
+        template.sendBody("direct:start", "Bye World");
+        template.sendBody("direct:quiet", "Hello");
+
+        DevConsole con = 
PluginHelper.getDevConsoleResolver(context).resolveDevConsole("body-type");
+        JsonObject all = (JsonObject) con.call(DevConsole.MediaType.JSON);
+        Assertions.assertEquals(2, ((JsonArray) 
all.getCollection("routes")).size());
+
+        // every step was reached with text only
+        JsonObject varied = (JsonObject) con.call(DevConsole.MediaType.JSON, 
Map.of(BodyTypeDevConsole.VARIED, "true"));
+        Assertions.assertTrue(((JsonArray) 
varied.getCollection("routes")).isEmpty());
+
+        // a filter by route id
+        JsonObject quiet = (JsonObject) con.call(DevConsole.MediaType.JSON, 
Map.of(BodyTypeDevConsole.FILTER, "quiet"));
+        JsonArray routes = quiet.getCollection("routes");
+        Assertions.assertEquals(1, routes.size());
+        Assertions.assertEquals("quiet", ((JsonObject) 
routes.get(0)).getString("routeId"));
+    }
+
+    @Test
+    public void testNothingReachedYet() {
+        DevConsole con = 
PluginHelper.getDevConsoleResolver(context).resolveDevConsole("body-type");
+        JsonObject out = (JsonObject) con.call(DevConsole.MediaType.JSON);
+        Assertions.assertTrue(((JsonArray) 
out.getCollection("routes")).isEmpty());
+
+        String text = (String) con.call(DevConsole.MediaType.TEXT);
+        Assertions.assertTrue(text.startsWith("No step has been reached"), 
text);
+    }
+}
diff --git 
a/core/camel-core-engine/src/main/docs/modules/eips/pages/message-history.adoc 
b/core/camel-core-engine/src/main/docs/modules/eips/pages/message-history.adoc
index 174e4ad313fe..40d18ec93aec 100644
--- 
a/core/camel-core-engine/src/main/docs/modules/eips/pages/message-history.adoc
+++ 
b/core/camel-core-engine/src/main/docs/modules/eips/pages/message-history.adoc
@@ -477,6 +477,17 @@ List<BodyTypeRegistry.Kind> kinds = 
registry.getBodyKinds("orders", "to1");
 
 The Camel CLI shows them as well, see 
xref:manual::camel-jbang-diagnostics.adoc[Camel CLI diagnostics].
 
+=== Dumping the body types
+
+Run the messages of the expected workload, such as in a test, and then read 
what each step has been reached with:
+
+* The `body-type` dev console lists the steps that a message has reached, per 
route, with their types and kinds.
+  `varied=true` keeps only the steps reached with more than one type, and 
`filter` selects the routes.
+* Over JMX, each processor has the `BodyTypes` and `BodyKinds` attributes, and 
each route counts its steps reached with
+  more than one type (`VariedBodyTypes`) and with bodies of more than one kind 
(`MixedBodyKinds`).
+* The route coverage dump (`dumpRoutesCoverageAsXml`, which the Camel test 
kits write when route coverage is enabled)
+  has `bodyTypes` and `bodyKinds` attributes on each step a message has 
reached.
+
 == Configuring MessageHistory format
 
 The  Message History format in Camel is controlled by `Java String.format` 
patterns stored as global options on the CamelContext.
diff --git 
a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedProcessorMBean.java
 
b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedProcessorMBean.java
index e60a4dc99718..bb76c62e91c3 100644
--- 
a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedProcessorMBean.java
+++ 
b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedProcessorMBean.java
@@ -69,6 +69,12 @@ public interface ManagedProcessorMBean extends 
ManagedPerformanceCounterMBean {
     @ManagedAttribute(description = "Line number of this node in the source 
file (when loaded from a line number aware parser)")
     Integer getSourceLineNumber();
 
+    @ManagedAttribute(description = "The body types this processor has been 
reached with, across the messages, in the order first seen (recorded while the 
message history is on)")
+    String[] getBodyTypes();
+
+    @ManagedAttribute(description = "The kinds of the bodies this processor 
has been reached with: null, payload, data or iterator. More than one needs 
attention: a step that writes fails for data, one that reads fields fails for a 
payload")
+    String[] getBodyKinds();
+
     @ManagedAttribute(description = "Whether this processor supports extended 
JMX information")
     Boolean getSupportExtendedInformation();
 
diff --git 
a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedRouteMBean.java
 
b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedRouteMBean.java
index 25b5c673b98b..f7e8731179a2 100644
--- 
a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedRouteMBean.java
+++ 
b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedRouteMBean.java
@@ -58,6 +58,12 @@ public interface ManagedRouteMBean extends 
ManagedPerformanceCounterMBean {
     @ManagedAttribute(description = "Route Source Location (Short)")
     String getSourceLocationShort();
 
+    @ManagedAttribute(description = "The number of steps that have been 
reached with more than one body type, across the messages (recorded while the 
message history is on)")
+    int getVariedBodyTypes();
+
+    @ManagedAttribute(description = "The number of steps that have been 
reached with bodies of more than one kind (null, payload, data or iterator), 
which need attention")
+    int getMixedBodyKinds();
+
     @ManagedAttribute(description = "The id of the route template (or Kamelet) 
this route was created from")
     String getRouteTemplateId();
 
diff --git 
a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedProcessor.java
 
b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedProcessor.java
index 79a6d2b1338f..74b32085235c 100644
--- 
a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedProcessor.java
+++ 
b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedProcessor.java
@@ -16,6 +16,7 @@
  */
 package org.apache.camel.management.mbean;
 
+import java.util.Locale;
 import java.util.Objects;
 
 import org.apache.camel.CamelContext;
@@ -33,6 +34,7 @@ import org.apache.camel.model.ProcessorDefinition;
 import org.apache.camel.model.ProcessorDefinitionHelper;
 import org.apache.camel.model.RouteDefinition;
 import org.apache.camel.model.StepDefinition;
+import org.apache.camel.spi.BodyTypeRegistry;
 import org.apache.camel.spi.ManagementStrategy;
 import org.apache.camel.spi.NodeIdFactory;
 import org.apache.camel.spi.RouteIdAware;
@@ -144,6 +146,25 @@ public class ManagedProcessor extends 
ManagedPerformanceCounter
         return line >= 0 ? line : null;
     }
 
+    @Override
+    public String[] getBodyTypes() {
+        BodyTypeRegistry registry = 
context.getCamelContextExtension().getContextPlugin(BodyTypeRegistry.class);
+        if (registry == null || getRouteId() == null || getProcessorId() == 
null) {
+            return new String[0];
+        }
+        return registry.getBodyTypes(getRouteId(), 
getProcessorId()).toArray(new String[0]);
+    }
+
+    @Override
+    public String[] getBodyKinds() {
+        BodyTypeRegistry registry = 
context.getCamelContextExtension().getContextPlugin(BodyTypeRegistry.class);
+        if (registry == null || getRouteId() == null || getProcessorId() == 
null) {
+            return new String[0];
+        }
+        return registry.getBodyKinds(getRouteId(), getProcessorId()).stream()
+                .map(kind -> 
kind.name().toLowerCase(Locale.ROOT)).toArray(String[]::new);
+    }
+
     @Override
     public Boolean getSupportExtendedInformation() {
         return false;
diff --git 
a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedRoute.java
 
b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedRoute.java
index 9a54c5331053..ccf087f26c87 100644
--- 
a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedRoute.java
+++ 
b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedRoute.java
@@ -55,6 +55,7 @@ import org.apache.camel.model.Model;
 import org.apache.camel.model.ModelCamelContext;
 import org.apache.camel.model.RouteDefinition;
 import org.apache.camel.model.RoutesDefinition;
+import org.apache.camel.spi.BodyTypeRegistry;
 import org.apache.camel.spi.InflightRepository;
 import org.apache.camel.spi.ManagementAgent;
 import org.apache.camel.spi.ManagementStrategy;
@@ -204,6 +205,26 @@ public class ManagedRoute extends 
ManagedPerformanceCounter implements ManagedRo
         return sourceLocationShort;
     }
 
+    @Override
+    public int getVariedBodyTypes() {
+        BodyTypeRegistry registry = 
context.getCamelContextExtension().getContextPlugin(BodyTypeRegistry.class);
+        if (registry == null || getRouteId() == null) {
+            return 0;
+        }
+        return (int) 
registry.getBodyTypes(getRouteId()).values().stream().filter(types -> 
types.size() > 1).count();
+    }
+
+    @Override
+    public int getMixedBodyKinds() {
+        BodyTypeRegistry registry = 
context.getCamelContextExtension().getContextPlugin(BodyTypeRegistry.class);
+        if (registry == null || getRouteId() == null) {
+            return 0;
+        }
+        String routeId = getRouteId();
+        return (int) registry.getBodyTypes(routeId).keySet().stream()
+                .filter(nodeId -> registry.getBodyKinds(routeId, 
nodeId).size() > 1).count();
+    }
+
     @Override
     public String getRouteTemplateId() {
         return route.getRouteTemplateId();
diff --git 
a/core/camel-management/src/main/java/org/apache/camel/management/mbean/RouteCoverageXmlParser.java
 
b/core/camel-management/src/main/java/org/apache/camel/management/mbean/RouteCoverageXmlParser.java
index 50df46a5730c..00b64f8e5406 100644
--- 
a/core/camel-management/src/main/java/org/apache/camel/management/mbean/RouteCoverageXmlParser.java
+++ 
b/core/camel-management/src/main/java/org/apache/camel/management/mbean/RouteCoverageXmlParser.java
@@ -46,6 +46,8 @@ import 
org.apache.camel.api.management.mbean.ManagedRouteMBean;
  * <ul>
  * <li>exchangesTotal - Total number of exchanges</li>
  * <li>totalProcessingTime - Total processing time in millis</li>
+ * <li>bodyTypes - The body types the step has been reached with (when the 
message history recorded any)</li>
+ * <li>bodyKinds - The kinds of those bodies: null, payload, data or 
iterator</li>
  * </ul>
  * Is included as attributes on the route nodes.
  */
@@ -152,6 +154,12 @@ public final class RouteCoverageXmlParser {
                             el.setAttribute("totalProcessingTime", 
Long.toString(totalTime));
                             int index = processor.getIndex();
                             el.setAttribute("index", Integer.toString(index));
+                            // the body types the step was reached with, and 
their kinds (CAMEL-25750)
+                            String[] types = processor.getBodyTypes();
+                            if (types != null && types.length > 0) {
+                                el.setAttribute("bodyTypes", String.join(",", 
types));
+                                el.setAttribute("bodyKinds", String.join(",", 
processor.getBodyKinds()));
+                            }
                         }
                     }
                 } catch (Exception e) {
diff --git 
a/core/camel-management/src/test/java/org/apache/camel/management/ManagedBodyTypesTest.java
 
b/core/camel-management/src/test/java/org/apache/camel/management/ManagedBodyTypesTest.java
new file mode 100644
index 000000000000..35e07ac452c1
--- /dev/null
+++ 
b/core/camel-management/src/test/java/org/apache/camel/management/ManagedBodyTypesTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.management;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.w3c.dom.Document;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledOnOs;
+import org.junit.jupiter.api.condition.OS;
+
+import static 
org.apache.camel.management.DefaultManagementObjectNameStrategy.TYPE_PROCESSOR;
+import static 
org.apache.camel.management.DefaultManagementObjectNameStrategy.TYPE_ROUTE;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * The body types each step has been reached with, over JMX and in the route 
coverage dump (CAMEL-25750).
+ */
+@DisabledOnOs(OS.AIX)
+public class ManagedBodyTypesTest extends ManagementTestSupport {
+
+    @Test
+    public void testBodyTypesOverJmx() throws Exception {
+        sendTextAndData();
+
+        MBeanServer mbeanServer = getMBeanServer();
+        ObjectName result = getCamelObjectName(TYPE_PROCESSOR, "result");
+        assertArrayEquals(new String[] { "java.lang.String", 
"java.util.LinkedHashMap" },
+                (String[]) mbeanServer.getAttribute(result, "BodyTypes"));
+        assertArrayEquals(new String[] { "payload", "data" }, (String[]) 
mbeanServer.getAttribute(result, "BodyKinds"));
+
+        ObjectName maybeMap = getCamelObjectName(TYPE_PROCESSOR, "maybeMap");
+        assertArrayEquals(new String[] { "java.lang.String" }, (String[]) 
mbeanServer.getAttribute(maybeMap, "BodyTypes"));
+
+        // the route counts its steps: the choice and the mock were reached 
with text and with data
+        ObjectName route = getCamelObjectName(TYPE_ROUTE, "orders");
+        assertEquals(2, mbeanServer.getAttribute(route, "VariedBodyTypes"));
+        assertEquals(2, mbeanServer.getAttribute(route, "MixedBodyKinds"));
+    }
+
+    @Test
+    public void testNoBodyTypesBeforeAMessage() throws Exception {
+        MBeanServer mbeanServer = getMBeanServer();
+        ObjectName result = getCamelObjectName(TYPE_PROCESSOR, "result");
+        assertArrayEquals(new String[0], (String[]) 
mbeanServer.getAttribute(result, "BodyTypes"));
+        assertEquals(0, 
mbeanServer.getAttribute(getCamelObjectName(TYPE_ROUTE, "orders"), 
"MixedBodyKinds"));
+    }
+
+    @Test
+    public void testRouteCoverageHasTheBodyTypes() throws Exception {
+        sendTextAndData();
+
+        String xml = (String) getMBeanServer().invoke(getContextObjectName(), 
"dumpRoutesCoverageAsXml", null, null);
+        log.info(xml);
+
+        
assertTrue(xml.contains("bodyTypes=\"java.lang.String,java.util.LinkedHashMap\""),
 xml);
+        assertTrue(xml.contains("bodyKinds=\"payload,data\""), xml);
+        // should be valid XML
+        Document doc = context.getTypeConverter().convertTo(Document.class, 
xml);
+        assertNotNull(doc);
+    }
+
+    private void sendTextAndData() throws Exception {
+        getMockEndpoint("mock:result").expectedMessageCount(2);
+        template.sendBody("direct:start", "hello");
+        template.sendBody("direct:start", "map");
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                context.setMessageHistory(true);
+
+                from("direct:start").routeId("orders")
+                        .process(e -> {
+                            if ("map".equals(e.getMessage().getBody())) {
+                                e.getMessage().setBody(new 
LinkedHashMap<>(Map.of("id", 1)));
+                            }
+                        }).id("maybeMap")
+                        .choice().id("choice")
+                        .when(body().isInstanceOf(Map.class))
+                        .log("a map").id("onlyMaps")
+                        .end()
+                        .to("mock:result").id("result");
+            }
+        };
+    }
+}

Reply via email to