You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2024/10/24 07:53:52 UTC
(camel) 01/01: CAMEL-21382: camel-core - Tracing with headers that are Map type can cause ClassCastException when dumping as XML
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch trace
in repository https://gitbox.apache.org/repos/asf/camel.git
commit bccd990728253442497b146258ca9255e013adb9
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu Oct 24 09:53:32 2024 +0200
CAMEL-21382: camel-core - Tracing with headers that are Map type can cause ClassCastException when dumping as XML
---
.../impl/debugger/DefaultBacklogDebugger.java | 4 +
.../debugger/DefaultBacklogTracerEventMessage.java | 29 +++--
.../camel/impl/engine/CamelInternalProcessor.java | 3 +
.../management/BacklogTracerHeaderMapTest.java | 131 +++++++++++++++++++++
4 files changed, 156 insertions(+), 11 deletions(-)
diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/DefaultBacklogDebugger.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/DefaultBacklogDebugger.java
index 57bad2b4e6b..51c9025ed05 100644
--- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/DefaultBacklogDebugger.java
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/DefaultBacklogDebugger.java
@@ -830,6 +830,7 @@ public final class DefaultBacklogDebugger extends ServiceSupport implements Back
suspendedBreakpointMessages.computeIfPresent(
nodeId,
(nId, message) -> new DefaultBacklogTracerEventMessage(
+ camelContext,
false, false, message.getUid(), message.getTimestamp(), message.getLocation(), message.getRouteId(),
message.getToNode(),
message.getExchangeId(),
@@ -876,6 +877,7 @@ public final class DefaultBacklogDebugger extends ServiceSupport implements Back
JsonObject data = dumpAsJSonObject(exchange);
BacklogTracerEventMessage msg
= new DefaultBacklogTracerEventMessage(
+ camelContext,
first, false, uid, timestamp, source, routeId, toNode, exchangeId, false, false, data);
suspendedBreakpointMessages.put(nodeId, msg);
@@ -948,6 +950,7 @@ public final class DefaultBacklogDebugger extends ServiceSupport implements Back
JsonObject data = dumpAsJSonObject(exchange);
BacklogTracerEventMessage msg
= new DefaultBacklogTracerEventMessage(
+ camelContext,
false, false, uid, timestamp, source, routeId, toNode, exchangeId, false, false, data);
suspendedBreakpointMessages.put(toNode, msg);
@@ -1037,6 +1040,7 @@ public final class DefaultBacklogDebugger extends ServiceSupport implements Back
JsonObject data = dumpAsJSonObject(exchange);
BacklogTracerEventMessage msg
= new DefaultBacklogTracerEventMessage(
+ camelContext,
false, true, uid, timestamp, source, routeId, toNode, exchangeId, false, false, data);
// we want to capture if there was an exception
if (cause != null) {
diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/DefaultBacklogTracerEventMessage.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/DefaultBacklogTracerEventMessage.java
index b7a6e862c50..157c55dbf83 100644
--- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/DefaultBacklogTracerEventMessage.java
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/DefaultBacklogTracerEventMessage.java
@@ -19,6 +19,7 @@ package org.apache.camel.impl.debugger;
import java.text.SimpleDateFormat;
import java.util.Map;
+import org.apache.camel.CamelContext;
import org.apache.camel.spi.BacklogTracerEventMessage;
import org.apache.camel.support.MessageHelper;
import org.apache.camel.util.StopWatch;
@@ -35,6 +36,7 @@ import static org.apache.camel.support.MessageHelper.dumpExceptionAsJSonObject;
*/
public final class DefaultBacklogTracerEventMessage implements BacklogTracerEventMessage {
+ private final CamelContext camelContext;
private final StopWatch watch;
private final boolean first;
private final boolean last;
@@ -62,9 +64,10 @@ public final class DefaultBacklogTracerEventMessage implements BacklogTracerEven
private long duration;
private boolean done;
- public DefaultBacklogTracerEventMessage(boolean first, boolean last, long uid, long timestamp,
+ public DefaultBacklogTracerEventMessage(CamelContext camelContext, boolean first, boolean last, long uid, long timestamp,
String location, String routeId, String toNode, String exchangeId,
boolean rest, boolean template, JsonObject data) {
+ this.camelContext = camelContext;
this.watch = new StopWatch();
this.first = first;
this.last = last;
@@ -353,11 +356,12 @@ public final class DefaultBacklogTracerEventMessage implements BacklogTracerEven
sb.append(" type=\"").append(type).append("\"");
}
sb.append(">");
- String value = jo.getString("value");
+ Object value = jo.get("value");
if (value != null) {
try {
+ String text = camelContext.getTypeConverter().tryConvertTo(String.class, value);
// must always xml encode
- sb.append(StringHelper.xmlEncode(value));
+ sb.append(StringHelper.xmlEncode(text));
} catch (Exception e) {
// ignore
}
@@ -381,13 +385,14 @@ public final class DefaultBacklogTracerEventMessage implements BacklogTracerEven
sb.append(" type=\"").append(type).append("\"");
}
sb.append(">");
- String value = jo.getString("value");
+ Object value = jo.get("value");
if (value != null) {
try {
+ String text = camelContext.getTypeConverter().tryConvertTo(String.class, value);
// must always xml encode
- sb.append(StringHelper.xmlEncode(value));
+ sb.append(StringHelper.xmlEncode(text));
} catch (Exception e) {
- // ignore as the body is for logging purpose
+ // ignore
}
}
sb.append("</exchangeProperty>\n");
@@ -409,11 +414,12 @@ public final class DefaultBacklogTracerEventMessage implements BacklogTracerEven
sb.append(" type=\"").append(type).append("\"");
}
sb.append(">");
- String value = jo.getString("value");
+ Object value = jo.get("value");
if (value != null) {
try {
+ String text = camelContext.getTypeConverter().tryConvertTo(String.class, value);
// must always xml encode
- sb.append(StringHelper.xmlEncode(value));
+ sb.append(StringHelper.xmlEncode(text));
} catch (Exception e) {
// ignore
}
@@ -440,13 +446,14 @@ public final class DefaultBacklogTracerEventMessage implements BacklogTracerEven
sb.append(" position=\"").append(position).append("\"");
}
sb.append(">");
- String value = jo.getString("value");
+ Object value = jo.get("value");
if (value != null) {
try {
+ String text = camelContext.getTypeConverter().tryConvertTo(String.class, value);
// must always xml encode
- sb.append(StringHelper.xmlEncode(value));
+ sb.append(StringHelper.xmlEncode(text));
} catch (Exception e) {
- // ignore as the body is for logging purpose
+ // ignore
}
}
sb.append("</body>\n");
diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelInternalProcessor.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelInternalProcessor.java
index 870431dd5da..37f0f8b536a 100644
--- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelInternalProcessor.java
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelInternalProcessor.java
@@ -642,6 +642,7 @@ public class CamelInternalProcessor extends DelegateAsyncProcessor implements In
String source = LoggerHelper.getLineNumberLoggerName(routeDefinition);
final long created = exchange.getClock().getCreated();
DefaultBacklogTracerEventMessage pseudoFirst = new DefaultBacklogTracerEventMessage(
+ camelContext,
true, false, backlogTracer.incrementTraceCounter(), created, source, routeId, null, exchangeId,
rest, template, data);
if (exchange.getFromEndpoint() instanceof EndpointServiceLocation esl) {
@@ -654,6 +655,7 @@ public class CamelInternalProcessor extends DelegateAsyncProcessor implements In
}
String source = LoggerHelper.getLineNumberLoggerName(processorDefinition);
DefaultBacklogTracerEventMessage event = new DefaultBacklogTracerEventMessage(
+ camelContext,
false, false, backlogTracer.incrementTraceCounter(), timestamp, source, routeId, toNode, exchangeId,
rest, template, data);
backlogTracer.traceEvent(event);
@@ -679,6 +681,7 @@ public class CamelInternalProcessor extends DelegateAsyncProcessor implements In
true, backlogTracer.isBodyIncludeStreams(), backlogTracer.isBodyIncludeFiles(),
backlogTracer.getBodyMaxChars());
DefaultBacklogTracerEventMessage pseudoLast = new DefaultBacklogTracerEventMessage(
+ camelContext,
false, true, backlogTracer.incrementTraceCounter(), created, source, routeId, null,
exchangeId, rest, template, data);
backlogTracer.traceEvent(pseudoLast);
diff --git a/core/camel-management/src/test/java/org/apache/camel/management/BacklogTracerHeaderMapTest.java b/core/camel-management/src/test/java/org/apache/camel/management/BacklogTracerHeaderMapTest.java
new file mode 100644
index 00000000000..0da7e7c2396
--- /dev/null
+++ b/core/camel-management/src/test/java/org/apache/camel/management/BacklogTracerHeaderMapTest.java
@@ -0,0 +1,131 @@
+/*
+ * 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.List;
+import java.util.Map;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.spi.BacklogTracerEventMessage;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledOnOs;
+import org.junit.jupiter.api.condition.OS;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@DisabledOnOs(OS.AIX)
+public class BacklogTracerHeaderMapTest extends ManagementTestSupport {
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testBacklogTracerEventMessage() throws Exception {
+ MBeanServer mbeanServer = getMBeanServer();
+ ObjectName on
+ = new ObjectName("org.apache.camel:context=" + context.getManagementName() + ",type=tracer,name=BacklogTracer");
+ assertNotNull(on);
+ assertTrue(mbeanServer.isRegistered(on));
+
+ Boolean enabled = (Boolean) mbeanServer.getAttribute(on, "Enabled");
+ assertEquals(Boolean.TRUE, enabled, "Should be enabled");
+
+ Integer size = (Integer) mbeanServer.getAttribute(on, "BacklogSize");
+ assertEquals(100, size.intValue(), "Should be 100");
+
+ Boolean removeOnDump = (Boolean) mbeanServer.getAttribute(on, "RemoveOnDump");
+ assertEquals(Boolean.TRUE, removeOnDump);
+
+ getMockEndpoint("mock:foo").expectedMessageCount(2);
+
+ template.sendBody("direct:start", "Hello World");
+ template.sendBody("direct:start", "Bye World");
+
+ assertMockEndpointsSatisfied();
+
+ List<Exchange> exchanges = getMockEndpoint("mock:foo").getReceivedExchanges();
+
+ List<BacklogTracerEventMessage> events = (List<BacklogTracerEventMessage>) mbeanServer.invoke(on, "dumpTracedMessages",
+ new Object[] { "foo" }, new String[] { "java.lang.String" });
+
+ assertNotNull(events);
+ assertEquals(2, events.size());
+
+ BacklogTracerEventMessage event1 = events.get(0);
+ assertEquals("foo", event1.getToNode());
+ assertEquals(" <message exchangeId=\"" + exchanges.get(0).getExchangeId()
+ + "\" exchangePattern=\"InOnly\" exchangeType=\"org.apache.camel.support.DefaultExchange\" messageType=\"org.apache.camel.support.DefaultMessage\">\n"
+ + " <exchangeProperties>\n"
+ + " <exchangeProperty key=\"CamelToEndpoint\" type=\"java.lang.String\">direct://start</exchangeProperty>\n"
+ + " </exchangeProperties>\n"
+ + " <headers>\n"
+ + " <header key=\"foo\" type=\"java.util.LinkedHashMap\">{one=1, two=2}</header>\n"
+ + " </headers>\n"
+ + " <body type=\"java.lang.String\">Hello World</body>\n"
+ + " </message>",
+ event1.getMessageAsXml());
+
+ assertEquals(String.format(
+ "{\"message\":{\"exchangeId\":\"%s\",\"exchangePattern\":\"InOnly\",\"exchangeType\":\"org.apache.camel.support.DefaultExchange\",\"messageType\":\"org.apache.camel.support.DefaultMessage\",\"exchangeProperties\":[{\"key\":\"CamelToEndpoint\",\"type\":\"java.lang.String\",\"value\":\"direct:\\/\\/start\"}],\"headers\":[{\"key\":\"foo\",\"type\":\"java.util.LinkedHashMap\",\"value\":{\"one\":1,\"two\":2}}],\"body\":{\"type\":\"java.lang.String\",\"value\":\"Hello World\"}}}",
+ exchanges.get(0).getExchangeId()),
+ event1.getMessageAsJSon());
+
+ BacklogTracerEventMessage event2 = events.get(1);
+ assertEquals("foo", event2.getToNode());
+ assertEquals(" <message exchangeId=\"" + exchanges.get(1).getExchangeId()
+ + "\" exchangePattern=\"InOnly\" exchangeType=\"org.apache.camel.support.DefaultExchange\" messageType=\"org.apache.camel.support.DefaultMessage\">\n"
+ + " <exchangeProperties>\n"
+ + " <exchangeProperty key=\"CamelToEndpoint\" type=\"java.lang.String\">direct://start</exchangeProperty>\n"
+ + " </exchangeProperties>\n"
+ + " <headers>\n"
+ + " <header key=\"foo\" type=\"java.util.LinkedHashMap\">{one=1, two=2}</header>\n"
+ + " </headers>\n"
+ + " <body type=\"java.lang.String\">Bye World</body>\n"
+ + " </message>",
+ event2.getMessageAsXml());
+
+ assertEquals(String.format(
+ "{\"message\":{\"exchangeId\":\"%s\",\"exchangePattern\":\"InOnly\",\"exchangeType\":\"org.apache.camel.support.DefaultExchange\",\"messageType\":\"org.apache.camel.support.DefaultMessage\",\"exchangeProperties\":[{\"key\":\"CamelToEndpoint\",\"type\":\"java.lang.String\",\"value\":\"direct:\\/\\/start\"}],\"headers\":[{\"key\":\"foo\",\"type\":\"java.util.LinkedHashMap\",\"value\":{\"one\":1,\"two\":2}}],\"body\":{\"type\":\"java.lang.String\",\"value\":\"Bye World\"}}}",
+ exchanges.get(1).getExchangeId()),
+ event2.getMessageAsJSon());
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ context.setUseBreadcrumb(false);
+ context.setBacklogTracing(true);
+
+ Map<String, Object> map = new LinkedHashMap<>();
+ map.put("one", 1);
+ map.put("two", 2);
+
+ from("direct:start")
+ .setHeader("foo", constant(map)).id("setHeader1")
+ .to("mock:foo").id("foo");
+ }
+ };
+ }
+
+}