This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch camel-4.10.x in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/camel-4.10.x by this push: new d8fcd8a32c2 CAMEL-22195: camel-resilience4j - Fix using record and ignore exceptions (which can be wrapped) and only trigger fallback accordingly. d8fcd8a32c2 is described below commit d8fcd8a32c2f3391d474b63c0ac5601f8851b10e Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Wed Jun 25 08:11:51 2025 +0200 CAMEL-22195: camel-resilience4j - Fix using record and ignore exceptions (which can be wrapped) and only trigger fallback accordingly. --- .../camel/catalog/models/circuitBreaker.json | 3 ++- .../resilience4j/ResilienceProcessor.java | 7 +++++++ .../ResilienceIgnoreExceptionTest.java | 14 +++++++++++++ .../ResilienceRecordExceptionTest.java | 9 +++++++++ .../ResilienceRecordIgnoreExceptionTest.java | 23 ++++++++++++++++------ .../java/org/apache/camel/ExchangePropertyKey.java | 3 +++ .../apache/camel/spi/CircuitBreakerConstants.java | 3 +++ .../org/apache/camel/model/circuitBreaker.json | 3 ++- 8 files changed, 57 insertions(+), 8 deletions(-) diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/circuitBreaker.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/circuitBreaker.json index 575c3a45fbf..0b0aa850821 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/circuitBreaker.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/circuitBreaker.json @@ -27,6 +27,7 @@ "CamelResponseFromFallback": { "index": 1, "kind": "exchangeProperty", "displayName": "Response From Fallback", "label": "producer", "required": false, "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "description": "Whether the exchange was processed by the onFallback by the circuit breaker" }, "CamelResponseShortCircuited": { "index": 2, "kind": "exchangeProperty", "displayName": "Response Short Circuited", "label": "producer", "required": false, "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "description": "Whether the exchange was short circuited by the breaker" }, "CamelResponseTimedOut": { "index": 3, "kind": "exchangeProperty", "displayName": "Response Timed Out", "label": "producer", "required": false, "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "description": "Whether the exchange timed out during processing by the circuit breaker" }, - "CamelResponseRejected": { "index": 4, "kind": "exchangeProperty", "displayName": "Response Rejected", "label": "producer", "required": false, "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "description": "Whether the circuit breaker rejected processing the exchange" } + "CamelResponseRejected": { "index": 4, "kind": "exchangeProperty", "displayName": "Response Rejected", "label": "producer", "required": false, "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "description": "Whether the circuit breaker rejected processing the exchange" }, + "CamelResponseIgnored": { "index": 5, "kind": "exchangeProperty", "displayName": "Response Ignored", "label": "producer", "required": false, "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "description": "Whether the circuit breaker ignored an exception during processing" } } } diff --git a/components/camel-resilience4j/src/main/java/org/apache/camel/component/resilience4j/ResilienceProcessor.java b/components/camel-resilience4j/src/main/java/org/apache/camel/component/resilience4j/ResilienceProcessor.java index c3b0c4d1e5a..8e9bbcf4e4b 100644 --- a/components/camel-resilience4j/src/main/java/org/apache/camel/component/resilience4j/ResilienceProcessor.java +++ b/components/camel-resilience4j/src/main/java/org/apache/camel/component/resilience4j/ResilienceProcessor.java @@ -635,6 +635,10 @@ public class ResilienceProcessor extends AsyncProcessorSupport id, throwable); } // exception should be ignored + exchange.setProperty(ExchangePropertyKey.CIRCUIT_BREAKER_RESPONSE_SUCCESSFUL_EXECUTION, false); + exchange.setProperty(ExchangePropertyKey.CIRCUIT_BREAKER_RESPONSE_FROM_FALLBACK, false); + exchange.setProperty(ExchangePropertyKey.CIRCUIT_BREAKER_RESPONSE_SHORT_CIRCUITED, false); + exchange.setProperty(ExchangePropertyKey.CIRCUIT_BREAKER_RESPONSE_IGNORED, true); exchange.setException(null); return exchange; } @@ -645,6 +649,9 @@ public class ResilienceProcessor extends AsyncProcessorSupport id, throwable); } // exception is a success + exchange.setProperty(ExchangePropertyKey.CIRCUIT_BREAKER_RESPONSE_SUCCESSFUL_EXECUTION, true); + exchange.setProperty(ExchangePropertyKey.CIRCUIT_BREAKER_RESPONSE_FROM_FALLBACK, false); + exchange.setProperty(ExchangePropertyKey.CIRCUIT_BREAKER_RESPONSE_SHORT_CIRCUITED, false); exchange.setException(null); return exchange; } diff --git a/components/camel-resilience4j/src/test/java/org/apache/camel/component/resilience4j/ResilienceIgnoreExceptionTest.java b/components/camel-resilience4j/src/test/java/org/apache/camel/component/resilience4j/ResilienceIgnoreExceptionTest.java index 016e23c4b22..04c004fe877 100644 --- a/components/camel-resilience4j/src/test/java/org/apache/camel/component/resilience4j/ResilienceIgnoreExceptionTest.java +++ b/components/camel-resilience4j/src/test/java/org/apache/camel/component/resilience4j/ResilienceIgnoreExceptionTest.java @@ -21,6 +21,7 @@ import java.io.IOException; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.spi.CircuitBreakerConstants; import org.apache.camel.test.junit5.CamelTestSupport; import org.junit.jupiter.api.Test; @@ -29,6 +30,7 @@ public class ResilienceIgnoreExceptionTest extends CamelTestSupport { @Test public void testHello() throws Exception { getMockEndpoint("mock:result").expectedBodiesReceived("Hello World"); + getMockEndpoint("mock:result").expectedPropertyReceived(CircuitBreakerConstants.RESPONSE_SUCCESSFUL_EXECUTION, true); template.sendBody("direct:start", "Hello World"); MockEndpoint.assertIsSatisfied(context); } @@ -36,6 +38,11 @@ public class ResilienceIgnoreExceptionTest extends CamelTestSupport { @Test public void testFile() throws Exception { getMockEndpoint("mock:result").expectedBodiesReceived("file"); + getMockEndpoint("mock:result").expectedPropertyReceived(CircuitBreakerConstants.RESPONSE_SUCCESSFUL_EXECUTION, false); + getMockEndpoint("mock:result").expectedPropertyReceived(CircuitBreakerConstants.RESPONSE_SHORT_CIRCUITED, false); + getMockEndpoint("mock:result").expectedPropertyReceived(CircuitBreakerConstants.RESPONSE_FROM_FALLBACK, false); + getMockEndpoint("mock:result").expectedPropertyReceived(CircuitBreakerConstants.RESPONSE_IGNORED, true); + getMockEndpoint("mock:result").expectedPropertyReceived(CircuitBreakerConstants.RESPONSE_FROM_FALLBACK, false); template.sendBody("direct:start", "file"); MockEndpoint.assertIsSatisfied(context); } @@ -43,6 +50,8 @@ public class ResilienceIgnoreExceptionTest extends CamelTestSupport { @Test public void testKaboom() throws Exception { getMockEndpoint("mock:result").expectedBodiesReceived("Fallback message"); + getMockEndpoint("mock:result").expectedPropertyReceived(CircuitBreakerConstants.RESPONSE_SUCCESSFUL_EXECUTION, false); + getMockEndpoint("mock:result").expectedPropertyReceived(CircuitBreakerConstants.RESPONSE_FROM_FALLBACK, true); template.sendBody("direct:start", "kaboom"); MockEndpoint.assertIsSatisfied(context); } @@ -50,6 +59,11 @@ public class ResilienceIgnoreExceptionTest extends CamelTestSupport { @Test public void testIo() throws Exception { getMockEndpoint("mock:result").expectedBodiesReceived("io"); + getMockEndpoint("mock:result").expectedPropertyReceived(CircuitBreakerConstants.RESPONSE_SUCCESSFUL_EXECUTION, false); + getMockEndpoint("mock:result").expectedPropertyReceived(CircuitBreakerConstants.RESPONSE_SHORT_CIRCUITED, false); + getMockEndpoint("mock:result").expectedPropertyReceived(CircuitBreakerConstants.RESPONSE_FROM_FALLBACK, false); + getMockEndpoint("mock:result").expectedPropertyReceived(CircuitBreakerConstants.RESPONSE_IGNORED, true); + getMockEndpoint("mock:result").expectedPropertyReceived(CircuitBreakerConstants.RESPONSE_FROM_FALLBACK, false); template.sendBody("direct:start", "io"); MockEndpoint.assertIsSatisfied(context); } diff --git a/components/camel-resilience4j/src/test/java/org/apache/camel/component/resilience4j/ResilienceRecordExceptionTest.java b/components/camel-resilience4j/src/test/java/org/apache/camel/component/resilience4j/ResilienceRecordExceptionTest.java index 72d20cd8058..c4e6046fde2 100644 --- a/components/camel-resilience4j/src/test/java/org/apache/camel/component/resilience4j/ResilienceRecordExceptionTest.java +++ b/components/camel-resilience4j/src/test/java/org/apache/camel/component/resilience4j/ResilienceRecordExceptionTest.java @@ -21,6 +21,7 @@ import java.io.IOException; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.spi.CircuitBreakerConstants; import org.apache.camel.test.junit5.CamelTestSupport; import org.junit.jupiter.api.Test; @@ -29,6 +30,7 @@ public class ResilienceRecordExceptionTest extends CamelTestSupport { @Test public void testHello() throws Exception { getMockEndpoint("mock:result").expectedBodiesReceived("Hello World"); + getMockEndpoint("mock:result").expectedPropertyReceived(CircuitBreakerConstants.RESPONSE_SUCCESSFUL_EXECUTION, true); template.sendBody("direct:start", "Hello World"); MockEndpoint.assertIsSatisfied(context); } @@ -36,6 +38,9 @@ public class ResilienceRecordExceptionTest extends CamelTestSupport { @Test public void testFile() throws Exception { getMockEndpoint("mock:result").expectedBodiesReceived("Fallback message"); + getMockEndpoint("mock:result").expectedPropertyReceived(CircuitBreakerConstants.RESPONSE_SUCCESSFUL_EXECUTION, false); + getMockEndpoint("mock:result").expectedPropertyReceived(CircuitBreakerConstants.RESPONSE_SHORT_CIRCUITED, true); + getMockEndpoint("mock:result").expectedPropertyReceived(CircuitBreakerConstants.RESPONSE_FROM_FALLBACK, true); template.sendBody("direct:start", "file"); MockEndpoint.assertIsSatisfied(context); } @@ -43,6 +48,7 @@ public class ResilienceRecordExceptionTest extends CamelTestSupport { @Test public void testKaboom() throws Exception { getMockEndpoint("mock:result").expectedBodiesReceived("kaboom"); + getMockEndpoint("mock:result").expectedPropertyReceived(CircuitBreakerConstants.RESPONSE_SUCCESSFUL_EXECUTION, true); template.sendBody("direct:start", "kaboom"); MockEndpoint.assertIsSatisfied(context); } @@ -50,6 +56,9 @@ public class ResilienceRecordExceptionTest extends CamelTestSupport { @Test public void testIo() throws Exception { getMockEndpoint("mock:result").expectedBodiesReceived("Fallback message"); + getMockEndpoint("mock:result").expectedPropertyReceived(CircuitBreakerConstants.RESPONSE_SUCCESSFUL_EXECUTION, false); + getMockEndpoint("mock:result").expectedPropertyReceived(CircuitBreakerConstants.RESPONSE_SHORT_CIRCUITED, true); + getMockEndpoint("mock:result").expectedPropertyReceived(CircuitBreakerConstants.RESPONSE_FROM_FALLBACK, true); template.sendBody("direct:start", "io"); MockEndpoint.assertIsSatisfied(context); } diff --git a/components/camel-resilience4j/src/test/java/org/apache/camel/component/resilience4j/ResilienceRecordIgnoreExceptionTest.java b/components/camel-resilience4j/src/test/java/org/apache/camel/component/resilience4j/ResilienceRecordIgnoreExceptionTest.java index 1517d027f4e..0b1f29d86f2 100644 --- a/components/camel-resilience4j/src/test/java/org/apache/camel/component/resilience4j/ResilienceRecordIgnoreExceptionTest.java +++ b/components/camel-resilience4j/src/test/java/org/apache/camel/component/resilience4j/ResilienceRecordIgnoreExceptionTest.java @@ -21,6 +21,7 @@ import java.io.IOException; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.spi.CircuitBreakerConstants; import org.apache.camel.test.junit5.CamelTestSupport; import org.junit.jupiter.api.Test; @@ -29,6 +30,7 @@ public class ResilienceRecordIgnoreExceptionTest extends CamelTestSupport { @Test public void testHello() throws Exception { getMockEndpoint("mock:result").expectedBodiesReceived("Hello World"); + getMockEndpoint("mock:result").expectedPropertyReceived(CircuitBreakerConstants.RESPONSE_SUCCESSFUL_EXECUTION, true); template.sendBody("direct:start", "Hello World"); MockEndpoint.assertIsSatisfied(context); } @@ -36,21 +38,30 @@ public class ResilienceRecordIgnoreExceptionTest extends CamelTestSupport { @Test public void testFile() throws Exception { getMockEndpoint("mock:result").expectedBodiesReceived("file"); + getMockEndpoint("mock:result").expectedPropertyReceived(CircuitBreakerConstants.RESPONSE_SUCCESSFUL_EXECUTION, false); + getMockEndpoint("mock:result").expectedPropertyReceived(CircuitBreakerConstants.RESPONSE_SHORT_CIRCUITED, false); + getMockEndpoint("mock:result").expectedPropertyReceived(CircuitBreakerConstants.RESPONSE_FROM_FALLBACK, false); + getMockEndpoint("mock:result").expectedPropertyReceived(CircuitBreakerConstants.RESPONSE_IGNORED, true); + getMockEndpoint("mock:result").expectedPropertyReceived(CircuitBreakerConstants.RESPONSE_FROM_FALLBACK, false); template.sendBody("direct:start", "file"); MockEndpoint.assertIsSatisfied(context); } @Test - public void testIo() throws Exception { - getMockEndpoint("mock:result").expectedBodiesReceived("Fallback message"); - template.sendBody("direct:start", "io"); + public void testKaboom() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived("kaboom"); + getMockEndpoint("mock:result").expectedPropertyReceived(CircuitBreakerConstants.RESPONSE_SUCCESSFUL_EXECUTION, true); + template.sendBody("direct:start", "kaboom"); MockEndpoint.assertIsSatisfied(context); } @Test - public void testKaboom() throws Exception { - getMockEndpoint("mock:result").expectedBodiesReceived("kaboom"); - template.sendBody("direct:start", "kaboom"); + public void testIo() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived("Fallback message"); + getMockEndpoint("mock:result").expectedPropertyReceived(CircuitBreakerConstants.RESPONSE_SUCCESSFUL_EXECUTION, false); + getMockEndpoint("mock:result").expectedPropertyReceived(CircuitBreakerConstants.RESPONSE_SHORT_CIRCUITED, true); + getMockEndpoint("mock:result").expectedPropertyReceived(CircuitBreakerConstants.RESPONSE_FROM_FALLBACK, true); + template.sendBody("direct:start", "io"); MockEndpoint.assertIsSatisfied(context); } diff --git a/core/camel-api/src/main/java/org/apache/camel/ExchangePropertyKey.java b/core/camel-api/src/main/java/org/apache/camel/ExchangePropertyKey.java index b4232869aa1..56e42160d02 100644 --- a/core/camel-api/src/main/java/org/apache/camel/ExchangePropertyKey.java +++ b/core/camel-api/src/main/java/org/apache/camel/ExchangePropertyKey.java @@ -40,6 +40,7 @@ public enum ExchangePropertyKey { CIRCUIT_BREAKER_RESPONSE_SHORT_CIRCUITED(CircuitBreakerConstants.RESPONSE_SHORT_CIRCUITED), CIRCUIT_BREAKER_RESPONSE_TIMED_OUT(CircuitBreakerConstants.RESPONSE_TIMED_OUT), CIRCUIT_BREAKER_RESPONSE_REJECTED(CircuitBreakerConstants.RESPONSE_REJECTED), + CIRCUIT_BREAKER_RESPONSE_IGNORED(CircuitBreakerConstants.RESPONSE_IGNORED), CLAIM_CHECK_REPOSITORY(Exchange.CLAIM_CHECK_REPOSITORY), CORRELATION_ID(Exchange.CORRELATION_ID), DUPLICATE_MESSAGE(Exchange.DUPLICATE_MESSAGE), @@ -126,6 +127,8 @@ public enum ExchangePropertyKey { return CIRCUIT_BREAKER_RESPONSE_TIMED_OUT; case CircuitBreakerConstants.RESPONSE_REJECTED: return CIRCUIT_BREAKER_RESPONSE_REJECTED; + case CircuitBreakerConstants.RESPONSE_IGNORED: + return CIRCUIT_BREAKER_RESPONSE_IGNORED; case Exchange.CLAIM_CHECK_REPOSITORY: return CLAIM_CHECK_REPOSITORY; case Exchange.CORRELATION_ID: diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/CircuitBreakerConstants.java b/core/camel-api/src/main/java/org/apache/camel/spi/CircuitBreakerConstants.java index 64371505b80..d4421043a27 100644 --- a/core/camel-api/src/main/java/org/apache/camel/spi/CircuitBreakerConstants.java +++ b/core/camel-api/src/main/java/org/apache/camel/spi/CircuitBreakerConstants.java @@ -33,5 +33,8 @@ public interface CircuitBreakerConstants { @Metadata(label = "circuitBreaker", description = "Whether the circuit breaker rejected processing the exchange", javaType = "boolean") String RESPONSE_REJECTED = "CamelCircuitBreakerResponseRejected"; + @Metadata(label = "circuitBreaker", description = "Whether the circuit breaker ignored an exception during processing", + javaType = "boolean") + String RESPONSE_IGNORED = "CamelCircuitBreakerResponseIgnored"; } diff --git a/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/circuitBreaker.json b/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/circuitBreaker.json index 575c3a45fbf..0b0aa850821 100644 --- a/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/circuitBreaker.json +++ b/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/circuitBreaker.json @@ -27,6 +27,7 @@ "CamelResponseFromFallback": { "index": 1, "kind": "exchangeProperty", "displayName": "Response From Fallback", "label": "producer", "required": false, "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "description": "Whether the exchange was processed by the onFallback by the circuit breaker" }, "CamelResponseShortCircuited": { "index": 2, "kind": "exchangeProperty", "displayName": "Response Short Circuited", "label": "producer", "required": false, "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "description": "Whether the exchange was short circuited by the breaker" }, "CamelResponseTimedOut": { "index": 3, "kind": "exchangeProperty", "displayName": "Response Timed Out", "label": "producer", "required": false, "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "description": "Whether the exchange timed out during processing by the circuit breaker" }, - "CamelResponseRejected": { "index": 4, "kind": "exchangeProperty", "displayName": "Response Rejected", "label": "producer", "required": false, "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "description": "Whether the circuit breaker rejected processing the exchange" } + "CamelResponseRejected": { "index": 4, "kind": "exchangeProperty", "displayName": "Response Rejected", "label": "producer", "required": false, "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "description": "Whether the circuit breaker rejected processing the exchange" }, + "CamelResponseIgnored": { "index": 5, "kind": "exchangeProperty", "displayName": "Response Ignored", "label": "producer", "required": false, "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "description": "Whether the circuit breaker ignored an exception during processing" } } }