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 2328bb522ec7 CAMEL-24696: camel-core-model - inheritErrorHandler is a 
String so property placeholders resolve at route startup
2328bb522ec7 is described below

commit 2328bb522ec792c2c6336b58a708bf5d71ca6e9f
Author: Claus Ibsen <[email protected]>
AuthorDate: Tue Sep 15 09:07:15 2026 +0200

    CAMEL-24696: camel-core-model - inheritErrorHandler is a String so property 
placeholders resolve at route startup
    
    CircuitBreakerDefinition.inheritErrorHandler and
    FailoverLoadBalancerDefinition.inheritErrorHandler were the last two model
    attributes still converted with Boolean.valueOf while deserializing, so a
    property placeholder was never resolved and inheritErrorHandler: "{{flag}}"
    silently evaluated to false - while camel validate accepted it since
    CAMEL-24697 exposed the attribute in the YAML schema.
    
    Both attributes are now String with @Metadata(javaType = 
"java.lang.Boolean")
    like every other scalar attribute; ProcessorReifier and LoadBalanceReifier
    parse the value with the CamelContext at route startup. camel-jta and
    camel-kamelet follow, the fluent DSL keeps its boolean signatures and gains 
a
    CircuitBreakerDefinition.inheritErrorHandler(String) overload, and the
    generated parsers/writers/XSDs are regenerated. Documented in the 4.23
    upgrade guide.
    
    The failover(..., inheritErrorHandler=false, ...) runtime regression found
    while analysing this is tracked separately as CAMEL-24749.
    
    Closes #26449
    
    Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
---
 .../apache/camel/catalog/schemas/camel-spring.xsd  |  4 +-
 .../apache/camel/catalog/schemas/camel-xml-io.xsd  |  4 +-
 .../org/apache/camel/jta/JtaTransactionPolicy.java |  2 +-
 .../apache/camel/component/kamelet/Kamelet.java    |  4 +-
 ...silienceInheritErrorHandlerPlaceholderTest.java | 64 ++++++++++++++++++++++
 .../camel/model/CircuitBreakerDefinition.java      | 22 +++++++-
 .../apache/camel/model/LoadBalanceDefinition.java  |  4 +-
 .../apache/camel/model/ProcessorDefinition.java    |  6 +-
 .../FailoverLoadBalancerDefinition.java            |  6 +-
 .../apache/camel/reifier/LoadBalanceReifier.java   |  2 +-
 .../org/apache/camel/reifier/ProcessorReifier.java |  2 +-
 .../apache/camel/java/out/JavaDslModelWriter.java  |  4 +-
 .../java/org/apache/camel/xml/in/ModelParser.java  |  4 +-
 .../java/org/apache/camel/xml/out/ModelWriter.java |  4 +-
 .../org/apache/camel/yaml/out/YamlModelWriter.java |  4 +-
 .../ROOT/pages/camel-4x-upgrade-guide-4_23.adoc    | 34 ++++++++++++
 .../dsl/yaml/deserializers/ModelDeserializers.java |  4 +-
 .../camel/dsl/yaml/validator/YamlValidator.java    |  7 ++-
 .../validator/YamlValidatorSchemaGroupsTest.java   |  6 ++
 .../inherit-error-handler-placeholder.yaml         | 35 ++++++++++++
 .../camel/dsl/yaml/CircuitBreakerTest.groovy       | 22 ++++++++
 .../apache/camel/dsl/yaml/LoadBalanceTest.groovy   | 30 ++++++++++
 22 files changed, 241 insertions(+), 33 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
index 5ee9404250ed..fb02523a244b 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
@@ -4539,7 +4539,7 @@ Refers to a circuit breaker configuration to use for 
configuring the circuit bre
             </xs:documentation>
           </xs:annotation>
         </xs:attribute>
-        <xs:attribute name="inheritErrorHandler" type="xs:boolean">
+        <xs:attribute name="inheritErrorHandler" type="xs:string">
           <xs:annotation>
             <xs:documentation xml:lang="en">
 <![CDATA[
@@ -6949,7 +6949,7 @@ value: -1
             </xs:documentation>
           </xs:annotation>
         </xs:attribute>
-        <xs:attribute name="inheritErrorHandler" type="xs:boolean">
+        <xs:attribute name="inheritErrorHandler" type="xs:string">
           <xs:annotation>
             <xs:documentation xml:lang="en">
 <![CDATA[
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-xml-io.xsd
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-xml-io.xsd
index dbbcbd1ba490..79d25dfcdd81 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-xml-io.xsd
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-xml-io.xsd
@@ -3649,7 +3649,7 @@ Refers to a circuit breaker configuration to use for 
configuring the circuit bre
             </xs:documentation>
           </xs:annotation>
         </xs:attribute>
-        <xs:attribute name="inheritErrorHandler" type="xs:boolean">
+        <xs:attribute name="inheritErrorHandler" type="xs:string">
           <xs:annotation>
             <xs:documentation xml:lang="en">
 <![CDATA[
@@ -6071,7 +6071,7 @@ value: -1
             </xs:documentation>
           </xs:annotation>
         </xs:attribute>
-        <xs:attribute name="inheritErrorHandler" type="xs:boolean">
+        <xs:attribute name="inheritErrorHandler" type="xs:string">
           <xs:annotation>
             <xs:documentation xml:lang="en">
 <![CDATA[
diff --git 
a/components/camel-jta/src/main/java/org/apache/camel/jta/JtaTransactionPolicy.java
 
b/components/camel-jta/src/main/java/org/apache/camel/jta/JtaTransactionPolicy.java
index 0b9e4c67dfe7..c8b3bf64abf6 100644
--- 
a/components/camel-jta/src/main/java/org/apache/camel/jta/JtaTransactionPolicy.java
+++ 
b/components/camel-jta/src/main/java/org/apache/camel/jta/JtaTransactionPolicy.java
@@ -63,7 +63,7 @@ public abstract class JtaTransactionPolicy implements 
TransactedPolicy {
         // (otherwise the default error handler would be used two times
         // because we inherit it on our own but only in case of a
         // non-transactional error handler)
-        ((ProcessorDefinition<?>) definition).setInheritErrorHandler(false);
+        ((ProcessorDefinition<?>) definition).setInheritErrorHandler("false");
     }
 
     public abstract void run(Runnable runnable) throws Throwable;
diff --git 
a/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/Kamelet.java
 
b/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/Kamelet.java
index d3b3cda8c7ed..9a5acda98d16 100644
--- 
a/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/Kamelet.java
+++ 
b/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/Kamelet.java
@@ -214,8 +214,8 @@ public final class Kamelet {
             // (if ppid is null then it is a source kamelet)
             if (ppid != null) {
                 ProcessorDefinition<?> pro = mcc.getProcessorDefinition(ppid);
-                wrap = pro == null || 
ProcessorDefinitionHelper.shouldWrapInErrorHandler(def.getCamelContext(), pro, 
null,
-                        pro.getInheritErrorHandler());
+                wrap = pro == null || 
ProcessorDefinitionHelper.shouldWrapInErrorHandler(mcc, pro, null,
+                        CamelContextHelper.parseBoolean(mcc, 
pro.getInheritErrorHandler()));
             }
             if (wrap && parent != null && parent.isKamelet() == null) {
                 // do not wrap if the parent is also a kamelet
diff --git 
a/components/camel-resilience4j/src/test/java/org/apache/camel/component/resilience4j/ResilienceInheritErrorHandlerPlaceholderTest.java
 
b/components/camel-resilience4j/src/test/java/org/apache/camel/component/resilience4j/ResilienceInheritErrorHandlerPlaceholderTest.java
new file mode 100644
index 000000000000..3b8c9ca0a9f7
--- /dev/null
+++ 
b/components/camel-resilience4j/src/test/java/org/apache/camel/component/resilience4j/ResilienceInheritErrorHandlerPlaceholderTest.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.resilience4j;
+
+import java.util.Properties;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+/**
+ * The inheritErrorHandler option is a property placeholder that is resolved 
when the route starts (CAMEL-24696).
+ */
+public class ResilienceInheritErrorHandlerPlaceholderTest extends 
CamelTestSupport {
+
+    @Override
+    protected Properties useOverridePropertiesWithPropertiesComponent() {
+        Properties prop = new Properties();
+        prop.setProperty("myInherit", "true");
+        return prop;
+    }
+
+    @Test
+    public void testResiliencePlaceholder() throws Exception {
+        // inheritErrorHandler resolved to true so Camel error handler does 
the 3 redeliveries before the dead letter
+        getMockEndpoint("mock:a").expectedMessageCount(3 + 1);
+        getMockEndpoint("mock:dead").expectedMessageCount(1);
+        getMockEndpoint("mock:result").expectedMessageCount(0);
+
+        template.sendBody("direct:start", "Hello World");
+
+        MockEndpoint.assertIsSatisfied(context);
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                
errorHandler(deadLetterChannel("mock:dead").maximumRedeliveries(3).redeliveryDelay(0));
+
+                from("direct:start").to("log:start")
+                        
.circuitBreaker().inheritErrorHandler("{{myInherit}}").to("mock:a")
+                        .throwException(new 
IllegalArgumentException("Forced")).end().to("log:result").to("mock:result");
+            }
+        };
+    }
+
+}
diff --git 
a/core/camel-core-model/src/main/java/org/apache/camel/model/CircuitBreakerDefinition.java
 
b/core/camel-core-model/src/main/java/org/apache/camel/model/CircuitBreakerDefinition.java
index 320be9797bd5..4dc14c555953 100644
--- 
a/core/camel-core-model/src/main/java/org/apache/camel/model/CircuitBreakerDefinition.java
+++ 
b/core/camel-core-model/src/main/java/org/apache/camel/model/CircuitBreakerDefinition.java
@@ -47,7 +47,7 @@ public class CircuitBreakerDefinition extends 
OutputDefinition<CircuitBreakerDef
     @XmlAttribute
     @Metadata(label = "advanced", javaType = "java.lang.Boolean", defaultValue 
= "false",
               description = "Whether to inherit Camel error handling during 
circuit breaker. By default, Camel error handler is turned off.")
-    private Boolean inheritErrorHandler;
+    private String inheritErrorHandler;
     @XmlElement
     @Metadata(label = "advanced",
               description = "Configures the circuit breaker to use 
Resilience4j with the given configuration.")
@@ -149,12 +149,12 @@ public class CircuitBreakerDefinition extends 
OutputDefinition<CircuitBreakerDef
     }
 
     @Override
-    public Boolean getInheritErrorHandler() {
+    public String getInheritErrorHandler() {
         return inheritErrorHandler;
     }
 
     @Override
-    public void setInheritErrorHandler(Boolean inheritErrorHandler) {
+    public void setInheritErrorHandler(String inheritErrorHandler) {
         this.inheritErrorHandler = inheritErrorHandler;
     }
 
@@ -225,6 +225,22 @@ public class CircuitBreakerDefinition extends 
OutputDefinition<CircuitBreakerDef
      * By default, Camel error handler is turned off.
      */
     public CircuitBreakerDefinition inheritErrorHandler(boolean 
inheritErrorHandler) {
+        return inheritErrorHandler(Boolean.toString(inheritErrorHandler));
+    }
+
+    /**
+     * To turn on or off Camel error handling during circuit breaker.
+     *
+     * If this is enabled then Camel error handler will first trigger if there 
is an error in the circuit breaker, which
+     * allows to let Camel handle redeliveries. If all attempts is failed, 
then after the circuit breaker is finished,
+     * then Camel error handler can handle the error as well such as the dead 
letter channel.
+     *
+     * By default, Camel error handler is turned off.
+     *
+     * @param inheritErrorHandler whether to inherit the error handler, can 
also be a property placeholder that is
+     *                            resolved when the route starts
+     */
+    public CircuitBreakerDefinition inheritErrorHandler(String 
inheritErrorHandler) {
         this.inheritErrorHandler = inheritErrorHandler;
         return this;
     }
diff --git 
a/core/camel-core-model/src/main/java/org/apache/camel/model/LoadBalanceDefinition.java
 
b/core/camel-core-model/src/main/java/org/apache/camel/model/LoadBalanceDefinition.java
index 50cba0714f2b..46bdb0e852d9 100644
--- 
a/core/camel-core-model/src/main/java/org/apache/camel/model/LoadBalanceDefinition.java
+++ 
b/core/camel-core-model/src/main/java/org/apache/camel/model/LoadBalanceDefinition.java
@@ -159,7 +159,7 @@ public class LoadBalanceDefinition extends 
OutputDefinition<LoadBalanceDefinitio
         if (roundRobin) {
             def.setRoundRobin(Boolean.toString(roundRobin));
         }
-        def.setInheritErrorHandler(inheritErrorHandler);
+        def.setInheritErrorHandler(Boolean.toString(inheritErrorHandler));
         setLoadBalancerType(def);
         return this;
     }
@@ -189,7 +189,7 @@ public class LoadBalanceDefinition extends 
OutputDefinition<LoadBalanceDefinitio
         if (sticky) {
             def.setSticky(Boolean.toString(sticky));
         }
-        def.setInheritErrorHandler(inheritErrorHandler);
+        def.setInheritErrorHandler(Boolean.toString(inheritErrorHandler));
         setLoadBalancerType(def);
         return this;
     }
diff --git 
a/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java
 
b/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java
index ef985f54af44..5acd659582cc 100644
--- 
a/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java
+++ 
b/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java
@@ -96,7 +96,7 @@ public abstract class ProcessorDefinition<Type extends 
ProcessorDefinition<Type>
     @XmlTransient
     private final int index;
     @XmlTransient
-    private Boolean inheritErrorHandler; // used for camel-jta
+    private String inheritErrorHandler; // used for camel-jta
 
     protected ProcessorDefinition() {
         // every time we create a definition we should inc the counter
@@ -4507,11 +4507,11 @@ public abstract class ProcessorDefinition<Type extends 
ProcessorDefinition<Type>
         this.disabled = disabled;
     }
 
-    public Boolean getInheritErrorHandler() {
+    public String getInheritErrorHandler() {
         return inheritErrorHandler;
     }
 
-    public void setInheritErrorHandler(Boolean inheritErrorHandler) {
+    public void setInheritErrorHandler(String inheritErrorHandler) {
         this.inheritErrorHandler = inheritErrorHandler;
     }
 
diff --git 
a/core/camel-core-model/src/main/java/org/apache/camel/model/loadbalancer/FailoverLoadBalancerDefinition.java
 
b/core/camel-core-model/src/main/java/org/apache/camel/model/loadbalancer/FailoverLoadBalancerDefinition.java
index a27abb7b129f..b8e7fe8219a4 100644
--- 
a/core/camel-core-model/src/main/java/org/apache/camel/model/loadbalancer/FailoverLoadBalancerDefinition.java
+++ 
b/core/camel-core-model/src/main/java/org/apache/camel/model/loadbalancer/FailoverLoadBalancerDefinition.java
@@ -67,7 +67,7 @@ public class FailoverLoadBalancerDefinition extends 
LoadBalancerDefinition {
               description = "Whether to inherit the error handler."
                             + " If turned off, the load balancer will fail 
over immediately on an error"
                             + " instead of waiting for Camel error handler to 
exhaust retries.")
-    private Boolean inheritErrorHandler;
+    private String inheritErrorHandler;
 
     public FailoverLoadBalancerDefinition() {
     }
@@ -127,11 +127,11 @@ public class FailoverLoadBalancerDefinition extends 
LoadBalancerDefinition {
         this.maximumFailoverAttempts = maximumFailoverAttempts;
     }
 
-    public Boolean getInheritErrorHandler() {
+    public String getInheritErrorHandler() {
         return inheritErrorHandler;
     }
 
-    public void setInheritErrorHandler(Boolean inheritErrorHandler) {
+    public void setInheritErrorHandler(String inheritErrorHandler) {
         this.inheritErrorHandler = inheritErrorHandler;
     }
 
diff --git 
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/LoadBalanceReifier.java
 
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/LoadBalanceReifier.java
index 8211a27531b0..d222a75c4fe5 100644
--- 
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/LoadBalanceReifier.java
+++ 
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/LoadBalanceReifier.java
@@ -62,7 +62,7 @@ public class LoadBalanceReifier extends 
ProcessorReifier<LoadBalanceDefinition>
             loadBalancer.addProcessor(channel);
         }
 
-        Boolean inherit = definition.getInheritErrorHandler();
+        Boolean inherit = parseBoolean(definition.getInheritErrorHandler());
         if (definition.getLoadBalancerType() instanceof 
FailoverLoadBalancerDefinition) {
             // special for failover load balancer where you can configure it to
             // not inherit error handler for its children
diff --git 
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/ProcessorReifier.java
 
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/ProcessorReifier.java
index 0c6a44314633..2d964fa98a2d 100644
--- 
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/ProcessorReifier.java
+++ 
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/ProcessorReifier.java
@@ -656,7 +656,7 @@ public abstract class ProcessorReifier<T extends 
ProcessorDefinition<?>> extends
     }
 
     protected Channel wrapChannel(Processor processor, ProcessorDefinition<?> 
child) throws Exception {
-        return wrapChannel(processor, child, 
definition.getInheritErrorHandler());
+        return wrapChannel(processor, child, 
parseBoolean(definition.getInheritErrorHandler()));
     }
 
     protected Channel wrapChannel(Processor processor, ProcessorDefinition<?> 
child, Boolean inheritErrorHandler)
diff --git 
a/core/camel-java-io/src/generated/java/org/apache/camel/java/out/JavaDslModelWriter.java
 
b/core/camel-java-io/src/generated/java/org/apache/camel/java/out/JavaDslModelWriter.java
index 14d9a9bde751..3b3b14e5315e 100644
--- 
a/core/camel-java-io/src/generated/java/org/apache/camel/java/out/JavaDslModelWriter.java
+++ 
b/core/camel-java-io/src/generated/java/org/apache/camel/java/out/JavaDslModelWriter.java
@@ -1850,7 +1850,7 @@ public class JavaDslModelWriter extends 
JavaDslModelWriterSupport {
     protected void doWriteCircuitBreakerDefinition(StringBuilder sb, 
CircuitBreakerDefinition def) {
         doWriteProcessorDefinitionAttributes(sb, def);
         doWriteAttribute(sb, "configuration", def.getConfiguration(), null);
-        doWriteAttribute(sb, "inheritErrorHandler", 
toString(def.getInheritErrorHandler()), "false");
+        doWriteAttribute(sb, "inheritErrorHandler", 
def.getInheritErrorHandler(), "false");
         doWriteChildElement(sb, "resilience4jConfiguration", 
def.getResilience4jConfiguration(), 
this::doWriteResilience4jConfigurationDefinition);
         doWriteChildElement(sb, "faultToleranceConfiguration", 
def.getFaultToleranceConfiguration(), 
this::doWriteFaultToleranceConfigurationDefinition);
         doWriteChildElement(sb, "onFallback", def.getOnFallback(), 
this::doWriteOnFallbackDefinition);
@@ -3677,7 +3677,7 @@ public class JavaDslModelWriter extends 
JavaDslModelWriterSupport {
         doWriteAttribute(sb, "roundRobin", def.getRoundRobin(), null);
         doWriteAttribute(sb, "sticky", def.getSticky(), null);
         doWriteAttribute(sb, "maximumFailoverAttempts", 
def.getMaximumFailoverAttempts(), "-1");
-        doWriteAttribute(sb, "inheritErrorHandler", 
toString(def.getInheritErrorHandler()), "true");
+        doWriteAttribute(sb, "inheritErrorHandler", 
def.getInheritErrorHandler(), "true");
         doWriteStringList(sb, null, "exception", def.getExceptions());
     }
     protected void doWriteRandomLoadBalancerDefinition(StringBuilder sb, 
RandomLoadBalancerDefinition def) {
diff --git 
a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java 
b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
index 9393502a94eb..73ef9c9d48fc 100644
--- 
a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
+++ 
b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
@@ -296,7 +296,7 @@ public class ModelParser extends BaseParser {
     protected CircuitBreakerDefinition doParseCircuitBreakerDefinition() 
throws IOException, XmlPullParserException {
         return doParse(new CircuitBreakerDefinition(), (def, key, val) -> 
switch (key) {
                 case "configuration": def.setConfiguration(val); yield true;
-                case "inheritErrorHandler": 
def.setInheritErrorHandler(Boolean.valueOf(val)); yield true;
+                case "inheritErrorHandler": def.setInheritErrorHandler(val); 
yield true;
                 default: yield 
processorDefinitionAttributeHandler().accept(def, key, val);
             }, (def, key) -> switch (key) {
                 case "faultToleranceConfiguration": 
def.setFaultToleranceConfiguration(doParseFaultToleranceConfigurationDefinition());
 yield true;
@@ -2431,7 +2431,7 @@ public class ModelParser extends BaseParser {
     }
     protected FailoverLoadBalancerDefinition 
doParseFailoverLoadBalancerDefinition() throws IOException, 
XmlPullParserException {
         return doParse(new FailoverLoadBalancerDefinition(), (def, key, val) 
-> switch (key) {
-                case "inheritErrorHandler": 
def.setInheritErrorHandler(Boolean.valueOf(val)); yield true;
+                case "inheritErrorHandler": def.setInheritErrorHandler(val); 
yield true;
                 case "maximumFailoverAttempts": 
def.setMaximumFailoverAttempts(val); yield true;
                 case "roundRobin": def.setRoundRobin(val); yield true;
                 case "sticky": def.setSticky(val); yield true;
diff --git 
a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
 
b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
index 4fd5f78262cb..b5e89597ba28 100644
--- 
a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
+++ 
b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
@@ -905,7 +905,7 @@ public class ModelWriter extends BaseWriter {
         startElement(name);
         doWriteProcessorDefinitionAttributes(def);
         doWriteAttribute("configuration", def.getConfiguration(), null);
-        doWriteAttribute("inheritErrorHandler", 
toString(def.getInheritErrorHandler()), "false");
+        doWriteAttribute("inheritErrorHandler", def.getInheritErrorHandler(), 
"false");
         doWriteElement("resilience4jConfiguration", 
def.getResilience4jConfiguration(), 
this::doWriteResilience4jConfigurationDefinition);
         doWriteElement("faultToleranceConfiguration", 
def.getFaultToleranceConfiguration(), 
this::doWriteFaultToleranceConfigurationDefinition);
         doWriteList(null, null, def.getOutputs(), 
this::doWriteProcessorDefinitionRef);
@@ -3166,7 +3166,7 @@ public class ModelWriter extends BaseWriter {
         doWriteAttribute("roundRobin", def.getRoundRobin(), null);
         doWriteAttribute("sticky", def.getSticky(), null);
         doWriteAttribute("maximumFailoverAttempts", 
def.getMaximumFailoverAttempts(), "-1");
-        doWriteAttribute("inheritErrorHandler", 
toString(def.getInheritErrorHandler()), "true");
+        doWriteAttribute("inheritErrorHandler", def.getInheritErrorHandler(), 
"true");
         doWriteList(null, "exception", def.getExceptions(), 
this::doWriteString);
         endElement(name);
     }
diff --git 
a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/YamlModelWriter.java
 
b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/YamlModelWriter.java
index af8cd632617c..6090291d50d0 100644
--- 
a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/YamlModelWriter.java
+++ 
b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/YamlModelWriter.java
@@ -900,7 +900,7 @@ public class YamlModelWriter extends YamlModelWriterSupport 
{
         JsonObject jo = new JsonObject();
         doWriteProcessorDefinitionAttributes(jo, def);
         doWriteAttribute(jo, "configuration", def.getConfiguration(), null);
-        doWriteAttribute(jo, "inheritErrorHandler", 
toString(def.getInheritErrorHandler()), "false");
+        doWriteAttribute(jo, "inheritErrorHandler", 
def.getInheritErrorHandler(), "false");
         doWriteChildElement(jo, "resilience4jConfiguration", 
def.getResilience4jConfiguration(), 
this::doWriteResilience4jConfigurationDefinition);
         doWriteChildElement(jo, "faultToleranceConfiguration", 
def.getFaultToleranceConfiguration(), 
this::doWriteFaultToleranceConfigurationDefinition);
         doWriteChildElement(jo, "onFallback", def.getOnFallback(), 
this::doWriteOnFallbackDefinition);
@@ -3164,7 +3164,7 @@ public class YamlModelWriter extends 
YamlModelWriterSupport {
         doWriteAttribute(jo, "roundRobin", def.getRoundRobin(), null);
         doWriteAttribute(jo, "sticky", def.getSticky(), null);
         doWriteAttribute(jo, "maximumFailoverAttempts", 
def.getMaximumFailoverAttempts(), "-1");
-        doWriteAttribute(jo, "inheritErrorHandler", 
toString(def.getInheritErrorHandler()), "true");
+        doWriteAttribute(jo, "inheritErrorHandler", 
def.getInheritErrorHandler(), "true");
         doWriteStringList(jo, null, "exception", def.getExceptions());
         return jo;
     }
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
index e973045d1446..de812b6cca9d 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
@@ -1990,6 +1990,40 @@ The accessors changed accordingly:
 The fluent builder `ParamDefinition.required(Boolean)` is unchanged, and a 
`required(String)` overload
 was added for placeholders. Routes written in XML, YAML or the Java DSL do not 
need any change.
 
+=== camel-core - the inheritErrorHandler attribute on circuitBreaker and 
failoverLoadBalancer is now a String
+
+`CircuitBreakerDefinition.inheritErrorHandler` and 
`FailoverLoadBalancerDefinition.inheritErrorHandler` are now
+declared as `String` instead of `Boolean`, the same way nearly every other 
scalar attribute in the Camel model
+is declared. This allows a property placeholder to be used, which is resolved 
when the route starts:
+
+[source,yaml]
+----
+- route:
+    from:
+      uri: direct:start
+      steps:
+        - circuitBreaker:
+            inheritErrorHandler: "{{myInheritFlag}}"
+            steps:
+              - to:
+                  uri: mock:a
+----
+
+Before this change the value was converted to a `Boolean` while the route was 
being loaded, so
+`+inheritErrorHandler: "{{myInheritFlag}}"+` silently evaluated to `false` 
instead of resolving the placeholder.
+
+The accessors changed accordingly:
+
+* `ProcessorDefinition.getInheritErrorHandler()` and 
`setInheritErrorHandler(...)` now use `String` instead of
+  `Boolean`, and so do the overrides on `CircuitBreakerDefinition` and the 
accessors on
+  `FailoverLoadBalancerDefinition`. Use 
`CamelContextHelper.parseBoolean(camelContext, getInheritErrorHandler())`
+  where a `CamelContext` is available to resolve the value.
+
+The fluent builder `CircuitBreakerDefinition.inheritErrorHandler(boolean)` and 
the `failover(...)` methods on
+`LoadBalanceDefinition` are unchanged, and a 
`CircuitBreakerDefinition.inheritErrorHandler(String)` overload was
+added for placeholders. Routes written in XML, YAML or the Java DSL do not 
need any change; the `inheritErrorHandler`
+attribute in the XML schema is now `xs:string` so a placeholder validates.
+
 === camel-weaviate - removed the unused vector field name header
 
 The `CamelweaviateVectorFieldName` header 
(`WeaviateVectorDbHeaders.VECTOR_FIELD_NAME`) has been removed.
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java
index 416175fdc142..828ccbd0fa72 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java
@@ -2075,7 +2075,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                 }
                 case "inheritErrorHandler": {
                     String val = asText(node);
-                    
target.setInheritErrorHandler(java.lang.Boolean.valueOf(val));
+                    target.setInheritErrorHandler(val);
                     break;
                 }
                 case "onFallback": {
@@ -5330,7 +5330,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                 }
                 case "inheritErrorHandler": {
                     String val = asText(node);
-                    
target.setInheritErrorHandler(java.lang.Boolean.valueOf(val));
+                    target.setInheritErrorHandler(val);
                     break;
                 }
                 case "maximumFailoverAttempts": {
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/YamlValidator.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/YamlValidator.java
index 25351b09f954..366604c23b5a 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/YamlValidator.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/YamlValidator.java
@@ -813,9 +813,10 @@ public class YamlValidator {
      * Everything else stays strict: unknown properties, structure (a map 
where a list is expected), enums, and strings
      * that do not parse as the expected type.
      * <p>
-     * This assumes the runtime defers the conversion for every scalar 
attribute the schema exposes. The few model
-     * attributes that are still converted while deserializing (so a 
placeholder is never resolved for them) are not
-     * reachable from the schema today - see CAMEL-24696 before exposing one 
of them.
+     * This assumes the runtime defers the conversion for every scalar 
attribute the schema exposes. The only model
+     * attribute that is still converted while deserializing (so a placeholder 
is never resolved for it) is
+     * {@code BeanConstructorDefinition.index}, which is a map key and is not 
reachable from the schema - see
+     * CAMEL-24696 before exposing it.
      */
     static boolean isRuntimeAcceptedScalar(Error error) {
         if (!"type".equals(error.getKeyword())) {
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorSchemaGroupsTest.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorSchemaGroupsTest.java
index e8fa22a06836..432cde9cb890 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorSchemaGroupsTest.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorSchemaGroupsTest.java
@@ -97,6 +97,12 @@ public class YamlValidatorSchemaGroupsTest {
         Assertions.assertTrue(report.isEmpty(), "Expected no errors but got: " 
+ messages(report));
     }
 
+    @Test
+    public void testInheritErrorHandlerPlaceholder() throws Exception {
+        List<Error> report = validator.validate(new 
File("src/test/resources/inherit-error-handler-placeholder.yaml"));
+        Assertions.assertTrue(report.isEmpty(), "Expected no errors but got: " 
+ messages(report));
+    }
+
     private static List<String> messages(List<Error> report) {
         return report.stream().map(Error::getMessage).toList();
     }
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/resources/inherit-error-handler-placeholder.yaml
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/resources/inherit-error-handler-placeholder.yaml
new file mode 100644
index 000000000000..52b80634ae05
--- /dev/null
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/resources/inherit-error-handler-placeholder.yaml
@@ -0,0 +1,35 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+- route:
+    from:
+      uri: direct:start
+      steps:
+        - circuitBreaker:
+            inheritErrorHandler: "{{myInherit}}"
+            steps:
+              - to:
+                  uri: mock:a
+        - loadBalance:
+            failoverLoadBalancer:
+              inheritErrorHandler: "{{myInherit}}"
+              maximumFailoverAttempts: "10"
+            steps:
+              - to:
+                  uri: mock:b
+              - to:
+                  uri: mock:c
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/CircuitBreakerTest.groovy
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/CircuitBreakerTest.groovy
index 410e346990af..6c1e8421d4ee 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/CircuitBreakerTest.groovy
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/CircuitBreakerTest.groovy
@@ -60,6 +60,28 @@ class CircuitBreakerTest extends YamlTestSupport {
         }
     }
 
+    def "circuitBreaker inheritErrorHandler placeholder"() {
+        when:
+        // YamlTestSupport validates against the raw schema (without the 
placeholder leniency of YamlValidator),
+        // which types inheritErrorHandler as boolean; the runtime resolves 
the placeholder when the route starts
+        // and YamlValidatorSchemaGroupsTest covers that the validator accepts 
it (CAMEL-24696)
+        loadRoutesNoValidate '''
+                - from:
+                    uri: "direct:start"
+                    steps:
+                      - circuitBreaker:
+                         inheritErrorHandler: "{{myInherit}}"
+                         steps:
+                           - log: "test"
+            '''
+        then:
+        with(context.routeDefinitions[0], RouteDefinition) {
+            with(outputs[0], CircuitBreakerDefinition) {
+                inheritErrorHandler == '{{myInherit}}'
+            }
+        }
+    }
+
     def "circuitBreaker with onFallback steps"() {
         when:
         loadRoutes '''
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/LoadBalanceTest.groovy
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/LoadBalanceTest.groovy
index ada1ac578b4e..3929bb5aee02 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/LoadBalanceTest.groovy
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/LoadBalanceTest.groovy
@@ -18,6 +18,9 @@ package org.apache.camel.dsl.yaml
 
 import org.apache.camel.dsl.yaml.support.YamlTestSupport
 import org.apache.camel.component.mock.MockEndpoint
+import org.apache.camel.model.LoadBalanceDefinition
+import org.apache.camel.model.RouteDefinition
+import org.apache.camel.model.loadbalancer.FailoverLoadBalancerDefinition
 import org.junit.jupiter.api.Assertions
 
 class LoadBalanceTest extends YamlTestSupport {
@@ -56,4 +59,31 @@ class LoadBalanceTest extends YamlTestSupport {
             MockEndpoint.assertIsSatisfied(context)
     }
 
+    def "failoverLoadBalancer inheritErrorHandler placeholder"() {
+        when:
+            // YamlTestSupport validates against the raw schema (without the 
placeholder leniency of YamlValidator),
+            // which types inheritErrorHandler as boolean; the runtime 
resolves the placeholder when the route starts
+            // and YamlValidatorSchemaGroupsTest covers that the validator 
accepts it (CAMEL-24696)
+            loadRoutesNoValidate '''
+                - from:
+                   uri: "direct:start"
+                   steps:
+                     - loadBalance:
+                         failoverLoadBalancer:
+                           inheritErrorHandler: "{{myInherit}}"
+                           maximumFailoverAttempts: "3"
+                         steps:
+                           - to: "mock:x"
+                           - to: "mock:y"
+            '''
+        then:
+            with(context.routeDefinitions[0], RouteDefinition) {
+                with(outputs[0], LoadBalanceDefinition) {
+                    with(loadBalancerType, FailoverLoadBalancerDefinition) {
+                        inheritErrorHandler == '{{myInherit}}'
+                        maximumFailoverAttempts == '3'
+                    }
+                }
+            }
+    }
 }

Reply via email to