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,