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,

Reply via email to