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 52764e6e0b9 CAMEL-18557: camel-core - Total counter on ContextMBean is too high 52764e6e0b9 is described below commit 52764e6e0b91ccbd740cc905429e9f35b0b65567 Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Mon Sep 26 19:07:33 2022 +0200 CAMEL-18557: camel-core - Total counter on ContextMBean is too high --- .../main/java/org/apache/camel/spi/UnitOfWork.java | 13 ++++ .../camel/impl/engine/DefaultUnitOfWork.java | 5 ++ .../management/mbean/ManagedCamelContext.java | 53 ++++++++++++++ .../ManagedCamelContextTotalCounterTest.java | 85 ++++++++++++++++++++++ .../ROOT/pages/camel-3x-upgrade-guide-3_19.adoc | 8 ++ 5 files changed, 164 insertions(+) diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/UnitOfWork.java b/core/camel-api/src/main/java/org/apache/camel/spi/UnitOfWork.java index e2f25fa1533..b9d28cb530e 100644 --- a/core/camel-api/src/main/java/org/apache/camel/spi/UnitOfWork.java +++ b/core/camel-api/src/main/java/org/apache/camel/spi/UnitOfWork.java @@ -190,6 +190,19 @@ public interface UnitOfWork { */ Route popRoute(); + /** + * Gets the {@link Route} level-of-depth that this {@link UnitOfWork} currently is being routed through. + * <p/> + * Notice that an {@link Exchange} can be routed through multiple routes and thus the level of depth can change over + * time. + * + * If level is 1 then the current route is at the first route (original route). Maybe be <tt>0</tt> if not routed + * through a route currently. + * + * @return the route level-of-depth + */ + int routeStackLevel(); + /** * Whether the unit of work should call the before/after process methods or not. */ diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultUnitOfWork.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultUnitOfWork.java index 09110b55a89..ea1640933a6 100644 --- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultUnitOfWork.java +++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultUnitOfWork.java @@ -348,6 +348,11 @@ public class DefaultUnitOfWork implements UnitOfWork { return routes.poll(); } + @Override + public int routeStackLevel() { + return routes.size(); + } + @Override public boolean isBeforeAfterProcess() { return false; diff --git a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedCamelContext.java b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedCamelContext.java index 511fa3fbb72..2d9e1848aa8 100644 --- a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedCamelContext.java +++ b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedCamelContext.java @@ -32,6 +32,7 @@ import org.w3c.dom.Document; import org.apache.camel.CamelContext; import org.apache.camel.Endpoint; +import org.apache.camel.Exchange; import org.apache.camel.ExtendedCamelContext; import org.apache.camel.ManagementStatisticsLevel; import org.apache.camel.Producer; @@ -52,6 +53,7 @@ import org.apache.camel.model.RoutesDefinition; import org.apache.camel.model.rest.RestDefinition; import org.apache.camel.model.rest.RestsDefinition; import org.apache.camel.spi.ManagementStrategy; +import org.apache.camel.spi.UnitOfWork; @ManagedResource(description = "Managed CamelContext") public class ManagedCamelContext extends ManagedPerformanceCounter implements TimerListener, ManagedCamelContextMBean { @@ -74,6 +76,57 @@ public class ManagedCamelContext extends ManagedPerformanceCounter implements Ti setStatisticsEnabled(enabled); } + @Override + public void completedExchange(Exchange exchange, long time) { + // the camel-context mbean is triggered for every route mbean + // so we must only trigger on the root level, otherwise the context mbean + // total counter will be incorrect. For example if an exchange is routed via 3 routes + // we should only count this as 1 instead of 3. + UnitOfWork uow = exchange.getUnitOfWork(); + if (uow != null) { + int level = uow.routeStackLevel(); + if (level <= 1) { + super.completedExchange(exchange, time); + } + } else { + super.completedExchange(exchange, time); + } + } + + @Override + public void failedExchange(Exchange exchange) { + // the camel-context mbean is triggered for every route mbean + // so we must only trigger on the root level, otherwise the context mbean + // total counter will be incorrect. For example if an exchange is routed via 3 routes + // we should only count this as 1 instead of 3. + UnitOfWork uow = exchange.getUnitOfWork(); + if (uow != null) { + int level = uow.routeStackLevel(); + if (level <= 1) { + super.failedExchange(exchange); + } + } else { + super.failedExchange(exchange); + } + } + + @Override + public void processExchange(Exchange exchange, String type) { + // the camel-context mbean is triggered for every route mbean + // so we must only trigger on the root level, otherwise the context mbean + // total counter will be incorrect. For example if an exchange is routed via 3 routes + // we should only count this as 1 instead of 3. + UnitOfWork uow = exchange.getUnitOfWork(); + if (uow != null) { + int level = uow.routeStackLevel(); + if (level <= 1) { + super.processExchange(exchange, type); + } + } else { + super.processExchange(exchange, type); + } + } + public CamelContext getContext() { return context; } diff --git a/core/camel-management/src/test/java/org/apache/camel/management/ManagedCamelContextTotalCounterTest.java b/core/camel-management/src/test/java/org/apache/camel/management/ManagedCamelContextTotalCounterTest.java new file mode 100644 index 00000000000..5fb6b28cd25 --- /dev/null +++ b/core/camel-management/src/test/java/org/apache/camel/management/ManagedCamelContextTotalCounterTest.java @@ -0,0 +1,85 @@ +/* + * 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.CamelContext; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.impl.engine.ExplicitCamelContextNameStrategy; +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.assertTrue; + +@DisabledOnOs(OS.AIX) +public class ManagedCamelContextTotalCounterTest extends ManagementTestSupport { + + @Override + protected CamelContext createCamelContext() throws Exception { + CamelContext context = super.createCamelContext(); + // to force a different management name than the camel id + context.getManagementNameStrategy().setNamePattern("20-#name#"); + context.setNameStrategy(new ExplicitCamelContextNameStrategy("my-camel-context")); + return context; + } + + @Test + public void testContextTotalCounter() throws Exception { + template.sendBody("direct:a", "Hello World"); + + MBeanServer mbeanServer = getMBeanServer(); + ObjectName on = getContextObjectName(); + + assertTrue(mbeanServer.isRegistered(on), "Should be registered"); + String name = (String) mbeanServer.getAttribute(on, "CamelId"); + assertEquals("my-camel-context", name); + + String managementName = (String) mbeanServer.getAttribute(on, "ManagementName"); + assertEquals("20-my-camel-context", managementName); + + Integer total = (Integer) mbeanServer.getAttribute(on, "TotalRoutes"); + assertEquals(3, total.intValue()); + + // 3 routes but only 1 exchange completed + Long ec = (Long) mbeanServer.getAttribute(on, "ExchangesCompleted"); + assertEquals(1, ec.intValue()); + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + from("direct:a") + .to("log:a") + .to("direct:b"); + + from("direct:b") + .to("log:b") + .to("direct:c"); + + from("direct:c") + .to("log:c"); + } + }; + } + +} diff --git a/docs/user-manual/modules/ROOT/pages/camel-3x-upgrade-guide-3_19.adoc b/docs/user-manual/modules/ROOT/pages/camel-3x-upgrade-guide-3_19.adoc index 90a14b93ca3..6ccb2fe316a 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-3x-upgrade-guide-3_19.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-3x-upgrade-guide-3_19.adoc @@ -12,6 +12,14 @@ Added `addClassLoader` method to `org.apache.camel.spi.ClassResolver`. The default TLS protocol is changed from `TLSv1.2` to `TLSv1.3` in Camel JSSE support. +=== camel-management + +The context MBean (`ManagedCamelContextMBean`) total counter is changed to count only once +while an _exchange_ is being routed through multiple routes. Previously the counter was +a total aggregation of all the routes the _exchange_ was processed. For example if an _exchange_ +is routed via A, B and C; then previously the total counter was +3 (+1 for route A, +1 for route B, +1 for route C). +This is now corrected so the total is +1 on the context MBean. + === camel-main The option `camel.main.eager-classloading` has been removed.