This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch brow in repository https://gitbox.apache.org/repos/asf/camel.git
commit 162ae0ea51bb7468e7e141918a07c69dc0a47233 Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Mon Sep 9 07:14:40 2024 +0200 CAMEL-21183: BrowseEndpoint should have limit/filter to optimize returned data from component implementations --- .../mbean/ManagedBrowsableEndpointMBean.java | 11 +- .../management/mbean/ManagedBrowsableEndpoint.java | 58 +++++- .../ManagedBrowsableEndpointAsJSonFileTest.java | 64 ++++++ .../ManagedBrowsableEndpointAsJSonTest.java | 214 +++++++++++++++++++++ 4 files changed, 342 insertions(+), 5 deletions(-) diff --git a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedBrowsableEndpointMBean.java b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedBrowsableEndpointMBean.java index 979f7ae3eca..208be8ccfa2 100644 --- a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedBrowsableEndpointMBean.java +++ b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedBrowsableEndpointMBean.java @@ -21,7 +21,7 @@ import org.apache.camel.api.management.ManagedOperation; public interface ManagedBrowsableEndpointMBean extends ManagedEndpointMBean { - @ManagedAttribute(description = "Maximum number of messages to browse by default.") + @ManagedAttribute(description = "Maximum number of messages to browse by default") int getBrowseLimit(); @ManagedOperation(description = "Current number of Exchanges in Queue") @@ -42,4 +42,13 @@ public interface ManagedBrowsableEndpointMBean extends ManagedEndpointMBean { @ManagedOperation(description = "Gets the range of messages as XML from the queue") String browseRangeMessagesAsXml(Integer fromIndex, Integer toIndex, Boolean includeBody); + @ManagedOperation(description = "Get message as JSon from queue by index") + String browseMessageAsJSon(Integer index, Boolean includeBody); + + @ManagedOperation(description = "Gets all the messages as JSon from the queue") + String browseAllMessagesAsJSon(Boolean includeBody); + + @ManagedOperation(description = "Gets the range of messages as JSon from the queue") + String browseRangeMessagesAsJSon(Integer fromIndex, Integer toIndex, Boolean includeBody); + } diff --git a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedBrowsableEndpoint.java b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedBrowsableEndpoint.java index d5de701daee..0a6a7c36cef 100644 --- a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedBrowsableEndpoint.java +++ b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedBrowsableEndpoint.java @@ -24,6 +24,9 @@ import org.apache.camel.api.management.ManagedResource; import org.apache.camel.api.management.mbean.ManagedBrowsableEndpointMBean; import org.apache.camel.spi.BrowsableEndpoint; import org.apache.camel.support.MessageHelper; +import org.apache.camel.util.json.JsonArray; +import org.apache.camel.util.json.JsonObject; +import org.apache.camel.util.json.Jsoner; @ManagedResource(description = "Managed BrowsableEndpoint") public class ManagedBrowsableEndpoint extends ManagedEndpoint implements ManagedBrowsableEndpointMBean { @@ -79,9 +82,8 @@ public class ManagedBrowsableEndpoint extends ManagedEndpoint implements Managed return null; } - // must use java type with JMX such as java.lang.String - - return exchange.getMessage().getBody(String.class); + Message msg = exchange.getMessage(); + return MessageHelper.extractBodyAsString(msg); } @Override @@ -97,7 +99,6 @@ public class ManagedBrowsableEndpoint extends ManagedEndpoint implements Managed } Message msg = exchange.getMessage(); - return MessageHelper.dumpAsXml(msg, includeBody); } @@ -136,4 +137,53 @@ public class ManagedBrowsableEndpoint extends ManagedEndpoint implements Managed return sb.toString(); } + @Override + public String browseMessageAsJSon(Integer index, Boolean includeBody) { + List<Exchange> exchanges = getEndpoint().getExchanges(); + + if (index >= exchanges.size()) { + return null; + } + Exchange exchange = exchanges.get(index); + if (exchange == null) { + return null; + } + + Message msg = exchange.getMessage(); + return MessageHelper.dumpAsJSon(msg, includeBody); + } + + @Override + public String browseAllMessagesAsJSon(Boolean includeBody) { + return browseRangeMessagesAsJSon(0, Integer.MAX_VALUE, includeBody); + } + + @Override + public String browseRangeMessagesAsJSon(Integer fromIndex, Integer toIndex, Boolean includeBody) { + if (fromIndex == null) { + fromIndex = 0; + } + if (toIndex == null) { + toIndex = Integer.MAX_VALUE; + } + if (fromIndex > toIndex) { + throw new IllegalArgumentException( + "From index cannot be larger than to index, was: " + fromIndex + " > " + toIndex); + } + + List<Exchange> exchanges = getEndpoint().getExchanges(); + if (exchanges.isEmpty()) { + return null; + } + + JsonArray arr = new JsonArray(); + for (int i = fromIndex; i < exchanges.size() && i <= toIndex; i++) { + Exchange exchange = exchanges.get(i); + Message msg = exchange.getMessage(); + JsonObject jo = MessageHelper.dumpAsJSonObject(msg, false, false, includeBody, true, true, true, 128 * 1024); + arr.add(jo); + } + String out = arr.toJson(); + return Jsoner.prettyPrint(out); + } } diff --git a/core/camel-management/src/test/java/org/apache/camel/management/ManagedBrowsableEndpointAsJSonFileTest.java b/core/camel-management/src/test/java/org/apache/camel/management/ManagedBrowsableEndpointAsJSonFileTest.java new file mode 100644 index 00000000000..4abcdcff063 --- /dev/null +++ b/core/camel-management/src/test/java/org/apache/camel/management/ManagedBrowsableEndpointAsJSonFileTest.java @@ -0,0 +1,64 @@ +/* + * 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 javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.apache.camel.Exchange; +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_ENDPOINT; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@DisabledOnOs(OS.AIX) +public class ManagedBrowsableEndpointAsJSonFileTest extends ManagementTestSupport { + + @Test + public void testBrowseableEndpointAsJSonAllIncludeBody() throws Exception { + template.sendBodyAndHeader("direct:start", "Hello World", Exchange.FILE_NAME, "hello.txt"); + + MBeanServer mbeanServer = getMBeanServer(); + + ObjectName name = getCamelObjectName(TYPE_ENDPOINT, "file://" + testDirectory()); + + String out = (String) mbeanServer.invoke(name, "browseAllMessagesAsJSon", new Object[] { true }, + new String[] { "java.lang.Boolean" }); + assertNotNull(out); + log.info(out); + assertTrue(out.contains("\"value\": \"Hello World\"")); + assertTrue(out.contains("\"type\": \"org.apache.camel.component.file.GenericFile\"")); + assertTrue(out.contains("\"value\": \"hello.txt\"")); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + context.setUseBreadcrumb(false); + + from("direct:start").to(fileUri()); + } + }; + } + +} diff --git a/core/camel-management/src/test/java/org/apache/camel/management/ManagedBrowsableEndpointAsJSonTest.java b/core/camel-management/src/test/java/org/apache/camel/management/ManagedBrowsableEndpointAsJSonTest.java new file mode 100644 index 00000000000..b2b5334914c --- /dev/null +++ b/core/camel-management/src/test/java/org/apache/camel/management/ManagedBrowsableEndpointAsJSonTest.java @@ -0,0 +1,214 @@ +/* + * 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.HashMap; +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.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_ENDPOINT; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +@DisabledOnOs(OS.AIX) +public class ManagedBrowsableEndpointAsJSonTest extends ManagementTestSupport { + + @Test + public void testBrowseableEndpointAsJSonIncludeBody() throws Exception { + getMockEndpoint("mock:result").expectedMessageCount(1); + + Map<String, Object> headers = new HashMap<>(); + headers.put("user", false); + headers.put("uid", 123); + headers.put("title", "Camel rocks"); + template.sendBodyAndHeaders("direct:start", "Hello World", headers); + + assertMockEndpointsSatisfied(); + + MBeanServer mbeanServer = getMBeanServer(); + + ObjectName name = getCamelObjectName(TYPE_ENDPOINT, "mock://result"); + + String out = (String) mbeanServer.invoke(name, "browseMessageAsJSon", new Object[] { 0, true }, + new String[] { "java.lang.Integer", "java.lang.Boolean" }); + assertNotNull(out); + log.info(out); + assertTrue(out.contains("\"body\": {")); + assertTrue(out.contains("\"value\": \"Hello World\"")); + } + + @Test + public void testBrowseableEndpointAsJSon() throws Exception { + getMockEndpoint("mock:result").expectedMessageCount(2); + + template.sendBodyAndHeader("direct:start", "Hello World", "foo", 123); + template.sendBodyAndHeader("direct:start", "Bye World", "foo", 456); + + assertMockEndpointsSatisfied(); + + MBeanServer mbeanServer = getMBeanServer(); + + ObjectName name = getCamelObjectName(TYPE_ENDPOINT, "mock://result"); + + String out = (String) mbeanServer.invoke(name, "browseMessageAsJSon", new Object[] { 0, false }, + new String[] { "java.lang.Integer", "java.lang.Boolean" }); + assertNotNull(out); + assertFalse(out.contains("\"body\": {")); + assertTrue(out.contains("\"value\": 123")); + + out = (String) mbeanServer.invoke(name, "browseMessageAsJSon", new Object[] { 1, false }, + new String[] { "java.lang.Integer", "java.lang.Boolean" }); + assertNotNull(out); + assertFalse(out.contains("\"body\": {")); + assertTrue(out.contains("\"value\": 456")); + } + + @Test + public void testBrowseableEndpointAsJSonAllIncludeBody() throws Exception { + getMockEndpoint("mock:result").expectedMessageCount(2); + + template.sendBody("direct:start", "Hello World"); + template.sendBodyAndHeader("direct:start", "Bye World", "foo", 456); + + assertMockEndpointsSatisfied(); + + MBeanServer mbeanServer = getMBeanServer(); + + ObjectName name = getCamelObjectName(TYPE_ENDPOINT, "mock://result"); + + String out = (String) mbeanServer.invoke(name, "browseAllMessagesAsJSon", new Object[] { true }, + new String[] { "java.lang.Boolean" }); + assertNotNull(out); + log.info(out); + + assertTrue(out.contains("\"value\": \"Hello World\"")); + assertTrue(out.contains("\"value\": \"Bye World\"")); + } + + @Test + public void testBrowseableEndpointAsJSonAll() throws Exception { + getMockEndpoint("mock:result").expectedMessageCount(2); + + template.sendBodyAndHeader("direct:start", "Hello World", "foo", 123); + template.sendBodyAndHeader("direct:start", "Bye World", "foo", 456); + + assertMockEndpointsSatisfied(); + + MBeanServer mbeanServer = getMBeanServer(); + + ObjectName name = getCamelObjectName(TYPE_ENDPOINT, "mock://result"); + + String out = (String) mbeanServer.invoke(name, "browseAllMessagesAsJSon", new Object[] { false }, + new String[] { "java.lang.Boolean" }); + assertNotNull(out); + log.info(out); + + assertFalse(out.contains("\"body\": {")); + assertTrue(out.contains("\"value\": 123")); + assertTrue(out.contains("\"value\": 456")); + } + + @Test + public void testBrowseableEndpointAsJSonRangeIncludeBody() throws Exception { + getMockEndpoint("mock:result").expectedMessageCount(3); + + template.sendBody("direct:start", "Hello World"); + template.sendBodyAndHeader("direct:start", "Bye World", "foo", 456); + template.sendBody("direct:start", "Hi Camel"); + + assertMockEndpointsSatisfied(); + + MBeanServer mbeanServer = getMBeanServer(); + + ObjectName name = getCamelObjectName(TYPE_ENDPOINT, "mock://result"); + + String out = (String) mbeanServer.invoke(name, "browseRangeMessagesAsJSon", new Object[] { 0, 1, true }, + new String[] { "java.lang.Integer", "java.lang.Integer", "java.lang.Boolean" }); + assertNotNull(out); + log.info(out); + + assertTrue(out.contains("\"value\": \"Hello World\"")); + assertTrue(out.contains("\"value\": \"Bye World\"")); + } + + @Test + public void testBrowseableEndpointAsJSonRange() throws Exception { + getMockEndpoint("mock:result").expectedMessageCount(3); + + template.sendBodyAndHeader("direct:start", "Hello World", "foo", 123); + template.sendBodyAndHeader("direct:start", "Bye World", "foo", 456); + template.sendBody("direct:start", "Hi Camel"); + + assertMockEndpointsSatisfied(); + + List<Exchange> exchanges = getMockEndpoint("mock:result").getReceivedExchanges(); + + MBeanServer mbeanServer = getMBeanServer(); + + ObjectName name = getCamelObjectName(TYPE_ENDPOINT, "mock://result"); + + String out = (String) mbeanServer.invoke(name, "browseRangeMessagesAsJSon", new Object[] { 0, 1, false }, + new String[] { "java.lang.Integer", "java.lang.Integer", "java.lang.Boolean" }); + assertNotNull(out); + log.info(out); + + assertFalse(out.contains("\"body\": {")); + assertTrue(out.contains("\"value\": 123")); + assertTrue(out.contains("\"value\": 456")); + } + + @Test + public void testBrowseableEndpointAsJSonRangeInvalidIndex() throws Exception { + MBeanServer mbeanServer = getMBeanServer(); + + ObjectName name = getCamelObjectName(TYPE_ENDPOINT, "mock://result"); + + try { + mbeanServer.invoke(name, "browseRangeMessagesAsJSon", new Object[] { 3, 1, false }, + new String[] { "java.lang.Integer", "java.lang.Integer", "java.lang.Boolean" }); + fail("Should have thrown exception"); + } catch (Exception e) { + assertIsInstanceOf(IllegalArgumentException.class, e.getCause()); + assertEquals("From index cannot be larger than to index, was: 3 > 1", e.getCause().getMessage()); + } + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + context.setUseBreadcrumb(false); + + from("direct:start").to("mock:result"); + } + }; + } + +}