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 de10963e2e64 CAMEL-26510: camel-micrometer - a counter increment or 
decrement that does not give a number fails the exchange (#29049)
de10963e2e64 is described below

commit de10963e2e64a38075e96cd4bbb558e722282440
Author: Claus Ibsen <[email protected]>
AuthorDate: Tue Nov 3 08:16:05 2026 +0100

    CAMEL-26510: camel-micrometer - a counter increment or decrement that does 
not give a number fails the exchange (#29049)
    
    * 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.
    
    * CAMEL-26510: camel-micrometer - the counter error message names only …
    
    …the configured increment or decrement option
    
    The message said "The increment (${body}) or decrement (null) ..." and so 
named
    an option that is not configured. It now names the configured option(s):
    "The increment (...)", "The decrement (...)" or, when both are configured,
    "The increment (...) and decrement (...)", followed by "of counter X did not
    evaluate to a number". Fixed a test comment that described the old 
behaviour.
    
    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      |  16 +++
 .../component/micrometer/CounterRouteTest.java     | 110 +++++++++++++++++++++
 .../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc    |   7 ++
 5 files changed, 139 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..c14967af142f 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,8 +49,24 @@ 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(
+                    describeOptions(endpoint.getIncrement(), 
endpoint.getDecrement()) + " of counter "
+                                               + counter.getId().getName() + " 
did not evaluate to a number");
         } else {
             counter.increment();
         }
     }
+
+    private static String describeOptions(String increment, String decrement) {
+        // name only the options that are configured
+        if (increment != null && decrement != null) {
+            return "The increment (" + increment + ") and decrement (" + 
decrement + ")";
+        } else if (increment != null) {
+            return "The increment (" + increment + ")";
+        } else {
+            return "The decrement (" + decrement + ")";
+        }
+    }
 }
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..60ed279d235b 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,18 @@ 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;
+
+    @Produce("direct:in-8")
+    private ProducerTemplate producer8;
+
     @BindToRegistry(METRICS_REGISTRY_NAME)
     private MeterRegistry registry = new SimpleMeterRegistry();
 
@@ -85,6 +103,22 @@ 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");
+
+                from("direct:in-8")
+                        .to("micrometer:counter:H?decrement=${body}")
+                        .to("mock:out");
             }
         };
     }
@@ -136,4 +170,80 @@ 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 (the exchange fails with 
IllegalArgumentException)
+        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}) and 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 testDecrementFromBodyNotNumber() throws Exception {
+        endpoint.expectedMessageCount(0);
+
+        CamelExecutionException e = assertThrows(CamelExecutionException.class,
+                () -> producer8.sendBody(Map.of("count", 5)));
+        assertInstanceOf(IllegalArgumentException.class, e.getCause());
+        assertEquals("The decrement (${body}) of counter H did not evaluate to 
a number",
+                e.getCause().getMessage());
+
+        assertEquals(0.0D, registry.find("H").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}) 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 7d2f65887d2e..b4535f50c030 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
@@ -994,6 +994,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