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 2021/12/16 05:42:34 UTC
[camel] branch main updated: [CAMEL-17341, CAMEL-17342] BacklogDebugger API enhancements (#6535)
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 b121f58 [CAMEL-17341, CAMEL-17342] BacklogDebugger API enhancements (#6535)
b121f58 is described below
commit b121f58a7b93287c8ce38f24a0508392667702a0
Author: javaduke <Eu...@modusbox.com>
AuthorDate: Wed Dec 15 22:41:51 2021 -0700
[CAMEL-17341, CAMEL-17342] BacklogDebugger API enhancements (#6535)
* Exposing properties and expression evaluator
* Removed unnecessary dependency
* Unit test and docs added
* Improved evaluateExpression, added tests and docs
* Use camel class resolver
* Got rid of the Apache utils and implemented isSerializable() method
* Need initialization of languages other than core
* CAMEL-17342 - exchange properties are optional part of the dumpTracedMessages
* CAMEL-17341 - removed DataSonnet tests to avoid cyclic reference
---
core/camel-management-api/pom.xml | 1 -
.../mbean/ManagedBacklogDebuggerMBean.java | 8 ++
core/camel-management/pom.xml | 1 -
.../management/mbean/ManagedBacklogDebugger.java | 106 ++++++++++++++-
.../camel/management/BacklogDebuggerTest.java | 145 +++++++++++++++++++++
.../modules/ROOT/pages/backlog-debugger.adoc | 3 +
6 files changed, 261 insertions(+), 3 deletions(-)
diff --git a/core/camel-management-api/pom.xml b/core/camel-management-api/pom.xml
index d971dfb..64bf001 100644
--- a/core/camel-management-api/pom.xml
+++ b/core/camel-management-api/pom.xml
@@ -43,7 +43,6 @@
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
-
</dependencies>
<reporting>
diff --git a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedBacklogDebuggerMBean.java b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedBacklogDebuggerMBean.java
index 75c4df1..2a6626a 100644
--- a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedBacklogDebuggerMBean.java
+++ b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedBacklogDebuggerMBean.java
@@ -142,6 +142,9 @@ public interface ManagedBacklogDebuggerMBean {
@ManagedOperation(description = "Dumps the messages in xml format from the suspended breakpoint at the given node")
String dumpTracedMessagesAsXml(String nodeId);
+ @ManagedOperation(description = "Dumps the messages in xml format from the suspended breakpoint at the given node, optionally including the exchange properties")
+ String dumpTracedMessagesAsXml(String nodeId, boolean includeExchangeProperties);
+
@ManagedAttribute(description = "Number of total debugged messages")
long getDebugCounter();
@@ -151,4 +154,9 @@ public interface ManagedBacklogDebuggerMBean {
@ManagedOperation(description = "Used for validating if a given predicate is valid or not")
String validateConditionalBreakpoint(String language, String predicate);
+ @ManagedOperation(description = "Evaluates the expression at a given breakpoint Id")
+ Object evaluateExpressionAtBreakpoint(String id, String language, String expression, String resultType);
+
+ @ManagedOperation(description = "Evaluates the expression at a given breakpoint Id and returns the result as String")
+ String evaluateExpressionAtBreakpoint(String id, String language, String expression);
}
diff --git a/core/camel-management/pom.xml b/core/camel-management/pom.xml
index 8950f05..c806e27 100644
--- a/core/camel-management/pom.xml
+++ b/core/camel-management/pom.xml
@@ -85,7 +85,6 @@
<artifactId>log4j-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>
-
</dependencies>
<build>
diff --git a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedBacklogDebugger.java b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedBacklogDebugger.java
index 9554cd8..22184e0 100644
--- a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedBacklogDebugger.java
+++ b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedBacklogDebugger.java
@@ -16,16 +16,26 @@
*/
package org.apache.camel.management.mbean;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.util.Map;
import java.util.Set;
import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+import org.apache.camel.Expression;
import org.apache.camel.NoTypeConversionAvailableException;
+import org.apache.camel.Predicate;
import org.apache.camel.RuntimeCamelException;
import org.apache.camel.api.management.ManagedResource;
+import org.apache.camel.api.management.mbean.BacklogTracerEventMessage;
import org.apache.camel.api.management.mbean.ManagedBacklogDebuggerMBean;
import org.apache.camel.impl.debugger.BacklogDebugger;
import org.apache.camel.spi.Language;
import org.apache.camel.spi.ManagementStrategy;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.StringHelper;
@ManagedResource(description = "Managed BacklogDebugger")
public class ManagedBacklogDebugger implements ManagedBacklogDebuggerMBean {
@@ -236,7 +246,18 @@ public class ManagedBacklogDebugger implements ManagedBacklogDebuggerMBean {
@Override
public String dumpTracedMessagesAsXml(String nodeId) {
- return backlogDebugger.dumpTracedMessagesAsXml(nodeId);
+ return dumpTracedMessagesAsXml(nodeId, false);
+ }
+
+ @Override
+ public String dumpTracedMessagesAsXml(String nodeId, boolean includeExchangeProperties) {
+ String messageAsXml = backlogDebugger.dumpTracedMessagesAsXml(nodeId);
+ if (messageAsXml != null && includeExchangeProperties) {
+ String closingTag = "</" + BacklogTracerEventMessage.ROOT_TAG + ">";
+ String exchangePropertiesAsXml = dumpExchangePropertiesAsXml(nodeId);
+ messageAsXml = messageAsXml.replace(closingTag, exchangePropertiesAsXml) + "\n" + closingTag;
+ }
+ return messageAsXml;
}
@Override
@@ -274,4 +295,87 @@ public class ManagedBacklogDebugger implements ManagedBacklogDebuggerMBean {
public void setFallbackTimeout(long fallbackTimeout) {
backlogDebugger.setFallbackTimeout(fallbackTimeout);
}
+
+ @Override
+ public String evaluateExpressionAtBreakpoint(String id, String language, String expression) {
+ return evaluateExpressionAtBreakpoint(id, language, expression, "java.lang.String").toString();
+ }
+
+ @Override
+ public Object evaluateExpressionAtBreakpoint(String id, String language, String expression, String resultType) {
+ Exchange suspendedExchange = null;
+ try {
+ Language lan = camelContext.resolveLanguage(language);
+ suspendedExchange = backlogDebugger.getSuspendedExchange(id);
+ if (suspendedExchange != null) {
+ Object result = null;
+ Class resultClass = camelContext.getClassResolver().resolveClass(resultType);
+ if (!Boolean.class.isAssignableFrom(resultClass)) {
+ Expression expr = lan.createExpression(expression);
+ expr.init(camelContext);
+ result = expr.evaluate(suspendedExchange, resultClass);
+ } else {
+ Predicate pred = lan.createPredicate(expression);
+ pred.init(camelContext);
+ result = pred.matches(suspendedExchange);
+ }
+ //Test if result is serializable
+ if (!isSerializable(result)) {
+ String resultStr = suspendedExchange.getContext().getTypeConverter().tryConvertTo(String.class, result);
+ if (resultStr != null) {
+ result = resultStr;
+ }
+ }
+ return result;
+ }
+ } catch (Exception e) {
+ return e;
+ }
+ return null;
+ }
+
+ private boolean isSerializable(Object obj) {
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
+ try (ObjectOutputStream out = new ObjectOutputStream(baos)) {
+ out.writeObject(obj);
+ return true;
+ } catch (final IOException ex) {
+ return false;
+ }
+ }
+
+ private String dumpExchangePropertiesAsXml(String id) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(" <exchangeProperties>\n");
+ Exchange suspendedExchange = backlogDebugger.getSuspendedExchange(id);
+ if (suspendedExchange != null) {
+ Map<String, Object> properties = suspendedExchange.getAllProperties();
+ properties.forEach((propertyName, propertyValue) -> {
+ String type = ObjectHelper.classCanonicalName(propertyValue);
+ sb.append(" <exchangeProperty name=\"").append(propertyName).append("\"");
+ if (type != null) {
+ sb.append(" type=\"").append(type).append("\"");
+ }
+ sb.append(">");
+ // dump property value as XML, use Camel type converter to convert
+ // to String
+ if (propertyValue != null) {
+ try {
+ String xml = suspendedExchange.getContext().getTypeConverter().tryConvertTo(String.class,
+ suspendedExchange, propertyValue);
+ if (xml != null) {
+ // must always xml encode
+ sb.append(StringHelper.xmlEncode(xml));
+ }
+ } catch (Throwable e) {
+ // ignore as the body is for logging purpose
+ }
+ }
+ sb.append("</exchangeProperty>\n");
+ });
+ }
+ sb.append(" </exchangeProperties>");
+ return sb.toString();
+ }
+
}
diff --git a/core/camel-management/src/test/java/org/apache/camel/management/BacklogDebuggerTest.java b/core/camel-management/src/test/java/org/apache/camel/management/BacklogDebuggerTest.java
index 63d1167..401c36d 100644
--- a/core/camel-management/src/test/java/org/apache/camel/management/BacklogDebuggerTest.java
+++ b/core/camel-management/src/test/java/org/apache/camel/management/BacklogDebuggerTest.java
@@ -670,6 +670,150 @@ public class BacklogDebuggerTest extends ManagementTestSupport {
assertEquals(Boolean.FALSE, stepMode, "Should not be in step mode");
}
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testBacklogDebuggerExchangeProperties() throws Exception {
+ MBeanServer mbeanServer = getMBeanServer();
+ ObjectName on = new ObjectName(
+ "org.apache.camel:context=" + context.getManagementName() + ",type=tracer,name=BacklogDebugger");
+ assertNotNull(on);
+ mbeanServer.isRegistered(on);
+
+ Boolean enabled = (Boolean) mbeanServer.getAttribute(on, "Enabled");
+ assertEquals(Boolean.FALSE, enabled, "Should not be enabled");
+
+ // enable debugger
+ mbeanServer.invoke(on, "enableDebugger", null, null);
+
+ enabled = (Boolean) mbeanServer.getAttribute(on, "Enabled");
+ assertEquals(Boolean.TRUE, enabled, "Should be enabled");
+
+ // add breakpoint at bar
+ mbeanServer.invoke(on, "addBreakpoint", new Object[] { "bar" }, new String[] { "java.lang.String" });
+
+ MockEndpoint mock = getMockEndpoint("mock:result");
+ mock.expectedMessageCount(0);
+ mock.setSleepForEmptyTest(100);
+
+ template.sendBody("seda:start", "Hello World");
+
+ assertMockEndpointsSatisfied();
+
+ // wait for breakpoint at bar
+ await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> {
+ Set<String> suspended = (Set<String>) mbeanServer.invoke(on, "getSuspendedBreakpointNodeIds", null, null);
+ assertNotNull(suspended);
+ assertEquals(1, suspended.size());
+ assertEquals("bar", suspended.iterator().next());
+ });
+
+ // there should be an exchange property 'myProperty'
+ String xml = (String) mbeanServer.invoke(on, "dumpTracedMessagesAsXml", new Object[] { "bar", true },
+ new String[] { "java.lang.String", "boolean" });
+ assertNotNull(xml);
+ log.info(xml);
+
+ assertTrue(xml.contains("<exchangeProperty name=\"myProperty\" type=\"java.lang.String\">myValue</exchangeProperty>"),
+ "Should contain myProperty");
+
+ resetMocks();
+ mock.expectedMessageCount(1);
+
+ // resume breakpoint
+ mbeanServer.invoke(on, "resumeBreakpoint", new Object[] { "bar" }, new String[] { "java.lang.String" });
+
+ assertMockEndpointsSatisfied();
+
+ // and no suspended anymore
+ Set<String> nodes = (Set<String>) mbeanServer.invoke(on, "getSuspendedBreakpointNodeIds", null, null);
+ assertNotNull(nodes);
+ assertEquals(0, nodes.size());
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testBacklogDebuggerEvaluateExpression() throws Exception {
+ MBeanServer mbeanServer = getMBeanServer();
+ ObjectName on = new ObjectName(
+ "org.apache.camel:context=" + context.getManagementName() + ",type=tracer,name=BacklogDebugger");
+ assertNotNull(on);
+ mbeanServer.isRegistered(on);
+
+ Boolean enabled = (Boolean) mbeanServer.getAttribute(on, "Enabled");
+ assertEquals(Boolean.FALSE, enabled, "Should not be enabled");
+
+ // enable debugger
+ mbeanServer.invoke(on, "enableDebugger", null, null);
+
+ enabled = (Boolean) mbeanServer.getAttribute(on, "Enabled");
+ assertEquals(Boolean.TRUE, enabled, "Should be enabled");
+
+ // add breakpoint at bar
+ mbeanServer.invoke(on, "addBreakpoint", new Object[] { "bar" }, new String[] { "java.lang.String" });
+
+ MockEndpoint mock = getMockEndpoint("mock:result");
+ mock.expectedMessageCount(0);
+ mock.setSleepForEmptyTest(100);
+
+ template.sendBody("seda:start", "Hello World");
+
+ assertMockEndpointsSatisfied();
+
+ // wait for breakpoint at bar
+ await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> {
+ Set<String> suspended = (Set<String>) mbeanServer.invoke(on, "getSuspendedBreakpointNodeIds", null, null);
+ assertNotNull(suspended);
+ assertEquals(1, suspended.size());
+ assertEquals("bar", suspended.iterator().next());
+ });
+
+ // evaluate expression, should return true
+ Object response = mbeanServer.invoke(on, "evaluateExpressionAtBreakpoint",
+ new Object[] { "bar", "simple", "${body} contains 'Hello'", "java.lang.Boolean" },
+ new String[] { "java.lang.String", "java.lang.String", "java.lang.String", "java.lang.String" });
+
+ assertNotNull(response);
+ log.info(response.toString());
+
+ assertTrue(response.getClass().isAssignableFrom(Boolean.class));
+ assertTrue((Boolean) response);
+
+ // evaluate another expression, should return value
+ response = mbeanServer.invoke(on, "evaluateExpressionAtBreakpoint",
+ new Object[] { "bar", "simple", "${exchangeProperty.myProperty}", "java.lang.String" },
+ new String[] { "java.lang.String", "java.lang.String", "java.lang.String", "java.lang.String" });
+
+ assertNotNull(response);
+ log.info(response.toString());
+
+ assertTrue(response.getClass().isAssignableFrom(String.class));
+ assertEquals("myValue", response);
+
+ // same as before but assume string by default
+ response = mbeanServer.invoke(on, "evaluateExpressionAtBreakpoint",
+ new Object[] { "bar", "simple", "${exchangeProperty.myProperty}" },
+ new String[] { "java.lang.String", "java.lang.String", "java.lang.String" });
+
+ assertNotNull(response);
+ log.info(response.toString());
+
+ assertTrue(response.getClass().isAssignableFrom(String.class));
+ assertEquals("myValue", response);
+
+ resetMocks();
+ mock.expectedMessageCount(1);
+
+ // resume breakpoint
+ mbeanServer.invoke(on, "resumeBreakpoint", new Object[] { "bar" }, new String[] { "java.lang.String" });
+
+ assertMockEndpointsSatisfied();
+
+ // and no suspended anymore
+ Set<String> nodes = (Set<String>) mbeanServer.invoke(on, "getSuspendedBreakpointNodeIds", null, null);
+ assertNotNull(nodes);
+ assertEquals(0, nodes.size());
+ }
+
@Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
@@ -679,6 +823,7 @@ public class BacklogDebuggerTest extends ManagementTestSupport {
context.setDebugging(true);
from("seda:start?concurrentConsumers=2")
+ .setProperty("myProperty", constant("myValue")).id("setProp")
.to("log:foo").id("foo")
.to("log:bar").id("bar")
.transform().constant("Bye World").id("transform")
diff --git a/docs/user-manual/modules/ROOT/pages/backlog-debugger.adoc b/docs/user-manual/modules/ROOT/pages/backlog-debugger.adoc
index 9a4fd97..f26a9f5 100644
--- a/docs/user-manual/modules/ROOT/pages/backlog-debugger.adoc
+++ b/docs/user-manual/modules/ROOT/pages/backlog-debugger.adoc
@@ -36,8 +36,11 @@ NOTE: This requires to enabled JMX by including `camel-management` JAR in the cl
|`disableBreakpoint(nodeId)` |`void` |To disable a breakpoint.
|`disableDebugger` |`void` |To disable the debugger
|`dumpTracedMessagesAsXml(nodeId)` |`String` |To dump the debugged messages from the give node id in XML format.
+|`dumpTracedMessagesAsXml(nodeId, boolean)` |`String` |To dump the debugged messages from the give node id in XML format, optionally including the exchange properties.
|`enableBreakpoint(nodeId)` |`void` |To activate a breakpoint which has been disabled.
|`enableDebugger` |`void` |To enable the debugger
+|`evaluateExpressionAtBreakpoint(nodeId, language, expression)` | `String`|To evaluate an expression in any supported language (e.g. Simple, CSimple, etc.) and convert the result to String
+|`evaluateExpressionAtBreakpoint(nodeId, language, expression, resultType)` | `Object`|To evaluate an expression in any supported language (e.g. Simple, CSimple, etc.) and return the result as a given result type
|`getBreakpoints` |`Set<String>` |To get a set of all the nodes which has a breakpoint added.
|`getDebuggerCounter` |`long` |Gets the total number of debugged messages.
|`getSuspendedBreakpointNodeIds` |`Set<String>` |To get a set of all the nodes which has suspended breakpoints (eg an Exchange at the breakpoint which is suspended).