This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch CAMEL-26510-micrometer-counter-no-number in repository https://gitbox.apache.org/repos/asf/camel.git
commit f15f2c50580d9f81b76d1464399151a4b81c4fcb Author: Claus Ibsen <[email protected]> AuthorDate: Tue Nov 3 07:21:38 2026 +0100 CAMEL-26510: camel-micrometer - a counter increment or decrement that does not give a number fails the exchange CounterProducer fell through to counter.increment() (adds 1) when the increment or decrement option was set, but neither the options nor the CamelMetricsCounterIncrement/Decrement headers gave a number, such as increment=${body} with a Map or null body, or a missing header. It now fails the exchange with an IllegalArgumentException and does not update the counter. A counter with neither option still adds 1. Same fix as CAMEL-26195 for camel-opentelemetry-metrics. Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- .../camel/catalog/docs/micrometer-component.adoc | 3 + .../src/main/docs/micrometer-component.adoc | 3 + .../component/micrometer/CounterProducer.java | 6 ++ .../component/micrometer/CounterRouteTest.java | 89 ++++++++++++++++++++++ .../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc | 7 ++ 5 files changed, 108 insertions(+) diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/micrometer-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/micrometer-component.adoc index c2464b67c616..1149d43c4d2a 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/micrometer-component.adoc +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/micrometer-component.adoc @@ -173,6 +173,9 @@ micrometer:counter:name[?options] If neither `increment` or `decrement` is defined then value of the counter will be incremented by one. If `increment` and `decrement` are both defined only increment operation is called. +If `increment` or `decrement` is defined, but neither of them gives a number (for example `+increment=${body}+` with +a `Map`, a POJO or a `null` body, or `+increment=${header.amount}+` with no such header), the exchange fails with an +`IllegalArgumentException`, and the counter is not updated. [tabs] ==== diff --git a/components/camel-micrometer/src/main/docs/micrometer-component.adoc b/components/camel-micrometer/src/main/docs/micrometer-component.adoc index c2464b67c616..1149d43c4d2a 100644 --- a/components/camel-micrometer/src/main/docs/micrometer-component.adoc +++ b/components/camel-micrometer/src/main/docs/micrometer-component.adoc @@ -173,6 +173,9 @@ micrometer:counter:name[?options] If neither `increment` or `decrement` is defined then value of the counter will be incremented by one. If `increment` and `decrement` are both defined only increment operation is called. +If `increment` or `decrement` is defined, but neither of them gives a number (for example `+increment=${body}+` with +a `Map`, a POJO or a `null` body, or `+increment=${header.amount}+` with no such header), the exchange fails with an +`IllegalArgumentException`, and the counter is not updated. [tabs] ==== diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/CounterProducer.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/CounterProducer.java index c50173042ffe..fd2779a39dfe 100644 --- a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/CounterProducer.java +++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/CounterProducer.java @@ -49,6 +49,12 @@ public class CounterProducer extends AbstractMicrometerProducer<Counter> { counter.increment(finalIncrement); } else if (finalDecrement != null) { counter.increment(-finalDecrement); + } else if (endpoint.getIncrement() != null || endpoint.getDecrement() != null) { + // an increment or decrement is configured, but it did not give a number (such as a Map or null body) + throw new IllegalArgumentException( + "The increment (" + endpoint.getIncrement() + ") or decrement (" + endpoint.getDecrement() + + ") of counter " + counter.getId().getName() + + " did not evaluate to a number"); } else { counter.increment(); } diff --git a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/CounterRouteTest.java b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/CounterRouteTest.java index 9feb4a31cccb..6e03fc9404af 100644 --- a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/CounterRouteTest.java +++ b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/CounterRouteTest.java @@ -16,10 +16,14 @@ */ package org.apache.camel.component.micrometer; +import java.util.List; +import java.util.Map; + import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.apache.camel.BindToRegistry; +import org.apache.camel.CamelExecutionException; import org.apache.camel.EndpointInject; import org.apache.camel.Produce; import org.apache.camel.ProducerTemplate; @@ -37,6 +41,8 @@ import static org.apache.camel.component.micrometer.MicrometerConstants.HEADER_C import static org.apache.camel.component.micrometer.MicrometerConstants.HEADER_METRIC_NAME; import static org.apache.camel.component.micrometer.MicrometerConstants.METRICS_REGISTRY_NAME; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; @CamelSpringTest public class CounterRouteTest extends CamelSpringTestSupport { @@ -56,6 +62,15 @@ public class CounterRouteTest extends CamelSpringTestSupport { @Produce("direct:in-4") private ProducerTemplate producer4; + @Produce("direct:in-5") + private ProducerTemplate producer5; + + @Produce("direct:in-6") + private ProducerTemplate producer6; + + @Produce("direct:in-7") + private ProducerTemplate producer7; + @BindToRegistry(METRICS_REGISTRY_NAME) private MeterRegistry registry = new SimpleMeterRegistry(); @@ -85,6 +100,18 @@ public class CounterRouteTest extends CamelSpringTestSupport { from("direct:in-4") .to("micrometer:counter:D?increment=${body.length}&tags.a=${body.length}") .to("mock:out"); + + from("direct:in-5") + .to("micrometer:counter:E?increment=${body}") + .to("mock:out"); + + from("direct:in-6") + .to("micrometer:counter:F?increment=${header.inc}&decrement=${header.dec}") + .to("mock:out"); + + from("direct:in-7") + .to("micrometer:counter:G") + .to("mock:out"); } }; } @@ -136,4 +163,66 @@ public class CounterRouteTest extends CamelSpringTestSupport { assertEquals(Integer.toString(message.length()), counter.getId().getTag("a")); endpoint.assertIsSatisfied(); } + + @Test + public void testIncrementFromBody() throws Exception { + endpoint.expectedMessageCount(1); + producer5.sendBody("42"); + assertEquals(42.0D, registry.find("E").counter().count(), 0.01D); + endpoint.assertIsSatisfied(); + } + + @Test + public void testIncrementFromBodyNotNumber() throws Exception { + endpoint.expectedMessageCount(0); + + assertIncrementFails(Map.of("count", 5)); + assertIncrementFails(List.of(new Object())); + assertIncrementFails(new Object()); + assertIncrementFails(null); + + // the counter is not updated (it was incremented by 1 for each of them) + assertEquals(0.0D, registry.find("E").counter().count(), 0.01D); + endpoint.assertIsSatisfied(); + } + + @Test + public void testIncrementOrDecrementFromHeader() throws Exception { + endpoint.expectedMessageCount(2); + producer6.sendBodyAndHeader(null, "inc", 10.0D); + producer6.sendBodyAndHeader(null, "dec", 3.0D); + assertEquals(7.0D, registry.find("F").counter().count(), 0.01D); + endpoint.assertIsSatisfied(); + } + + @Test + public void testIncrementOrDecrementFromHeaderMissing() throws Exception { + endpoint.expectedMessageCount(0); + + CamelExecutionException e = assertThrows(CamelExecutionException.class, + () -> producer6.sendBody("Hello")); + assertInstanceOf(IllegalArgumentException.class, e.getCause()); + assertEquals("The increment (${header.inc}) or decrement (${header.dec}) of counter F did not evaluate to a number", + e.getCause().getMessage()); + + assertEquals(0.0D, registry.find("F").counter().count(), 0.01D); + endpoint.assertIsSatisfied(); + } + + @Test + public void testNoIncrementOrDecrement() throws Exception { + endpoint.expectedMessageCount(2); + producer7.sendBody(new Object()); + producer7.sendBody(null); + assertEquals(2.0D, registry.find("G").counter().count(), 0.01D); + endpoint.assertIsSatisfied(); + } + + private void assertIncrementFails(Object body) { + CamelExecutionException e = assertThrows(CamelExecutionException.class, + () -> producer5.sendBody(body)); + assertInstanceOf(IllegalArgumentException.class, e.getCause()); + assertEquals("The increment (${body}) or decrement (null) of counter E did not evaluate to a number", + e.getCause().getMessage()); + } } diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc index 10f5a47c1b89..e7d8b92295e9 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc @@ -966,6 +966,13 @@ header, as with `collect=false`. An explicit `collect=true` still fails. The `da options of the flink component are now used by endpoints that do not set them, as `dataSet` and `dataSetCallback` already were. +=== camel-micrometer - a counter increment or decrement that does not give a number fails the exchange + +A `counter` with the `increment` or `decrement` option (such as `+increment=${body}+` or `+increment=${header.amount}+`) +added 1 to the counter when the expression did not give a number, for example a `Map`, a POJO or a `null` body, or a +missing header. It now fails the exchange with an `IllegalArgumentException`, and the counter is not updated. A +counter with neither option still adds 1. + === camel-mina-sftp - the local work file is deleted when the exchange is done With `localWorkDirectory`, camel-mina-sftp now deletes the local work file of a consumed file when the exchange is done,
