This is an automated email from the ASF dual-hosted git repository. luigidemasi pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel.git
commit 9687273ecdeddc2c7d7f5c77c12747a0e5cb4eaa Author: Luigi De Masi <[email protected]> AuthorDate: Thu Sep 24 09:21:08 2026 +0200 CAMEL-24977: Add context to invalid semantic numeric values Report the question, field and YAML source location when threshold or uncertainty cannot be parsed as a number. Preserve the original parsing cause. Clarify that raw property configurers receive expanded placeholders only when the caller requests placeholder resolution. Test both fields through the route loader without schema validation, including the resource name and exact source position in the diagnostic. Co-authored-by: Codex <[email protected]> Signed-off-by: Luigi De Masi <[email protected]> --- .../org/apache/camel/spi/PropertyConfigurer.java | 7 +++--- .../SemanticDefinitionDeserializer.java | 18 ++++++++++++--- .../camel/dsl/yaml/SemanticQuestionTest.java | 27 ++++++++++++++++++++++ 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/PropertyConfigurer.java b/core/camel-api/src/main/java/org/apache/camel/spi/PropertyConfigurer.java index a0929a3238f1..de1e867889ec 100644 --- a/core/camel-api/src/main/java/org/apache/camel/spi/PropertyConfigurer.java +++ b/core/camel-api/src/main/java/org/apache/camel/spi/PropertyConfigurer.java @@ -38,9 +38,10 @@ import org.apache.camel.CamelContext; public interface PropertyConfigurer { /** - * Optionally binds a value before resolving bean or class references. Property placeholders have already been - * resolved. This allows an option that owns reference resolution and lifecycle to retain the reference text. - * Returning false leaves the normal binding behavior unchanged. + * Optionally binds a value before resolving bean or class references. Property placeholders are resolved before + * this call when placeholder resolution was requested by the caller. This allows an option that owns reference + * resolution and lifecycle to retain the reference text. Returning false leaves the normal binding behavior + * unchanged. * * @param camelContext the Camel context * @param target the target instance diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/SemanticDefinitionDeserializer.java b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/SemanticDefinitionDeserializer.java index 64d93f41b03b..dd5bb10375a7 100644 --- a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/SemanticDefinitionDeserializer.java +++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/SemanticDefinitionDeserializer.java @@ -25,6 +25,7 @@ import java.util.Set; import org.apache.camel.CamelContext; import org.apache.camel.dsl.yaml.common.YamlDeserializationContext; import org.apache.camel.dsl.yaml.common.YamlDeserializerSupport; +import org.apache.camel.dsl.yaml.common.exception.YamlDeserializationException; import org.apache.camel.semantic.SemanticQuestion; import org.apache.camel.semantic.SemanticQuestions; import org.apache.camel.spi.CamelContextCustomizer; @@ -110,13 +111,24 @@ public class SemanticDefinitionDeserializer extends YamlDeserializerSupport impl : SemanticQuestion.UncertaintyPolicy.FAIL; result.put(name, new SemanticQuestion( type, asText(values.get("instructions")), asText(values.get("state")), - criteria, levels, number(values, "threshold", 0.5), number(values, "uncertainty", 0), policy)); + criteria, levels, number(values, name, "threshold", 0.5), number(values, name, "uncertainty", 0), policy)); }); return result; } - private static double number(Map<String, Node> values, String name, double fallback) { - return values.containsKey(name) ? Double.parseDouble(asText(values.get(name))) : fallback; + private static double number(Map<String, Node> values, String question, String name, double fallback) { + if (!values.containsKey(name)) { + return fallback; + } + Node node = values.get(name); + String raw = asText(node); + try { + return Double.parseDouble(raw); + } catch (NumberFormatException e) { + throw new YamlDeserializationException( + node, + "Invalid numeric value for '" + name + "' in semantic question '" + question + "': " + raw, e); + } } private static Map<String, Node> fields(Node node) { diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/SemanticQuestionTest.java b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/SemanticQuestionTest.java index 4b71a86826ae..5633c8726685 100644 --- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/SemanticQuestionTest.java +++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/SemanticQuestionTest.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.dsl.yaml.common.exception.YamlDeserializationException; import org.apache.camel.dsl.yaml.support.YamlTestSupport; import org.apache.camel.language.semantic.SemanticLanguage; import org.apache.camel.semantic.SemanticAdapter; @@ -34,6 +35,8 @@ import org.apache.camel.support.ResourceHelper; import org.apache.camel.support.RouteWatcherReloadStrategy; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -206,4 +209,28 @@ class SemanticQuestionTest extends YamlTestSupport { assertThatThrownBy(() -> loadRoutesNoValidate(declarations("${body}").replace("type: choice", "type: score"))) .hasStackTraceContaining("Node type map is invalid, expected array"); } + + @ParameterizedTest + @ValueSource(strings = { "threshold", "uncertainty" }) + void invalidNumericValuesIdentifyQuestionFieldAndLocation(String field) { + String yaml = """ + - semantic: + question: + spam: + type: boolean + instructions: Is this spam? + %s: abc + """.formatted(field); + assertThatThrownBy(() -> loadRoutesNoValidate(yaml)) + .hasMessageContaining("route-0.yaml") + .hasRootCauseInstanceOf(NumberFormatException.class) + .cause().isInstanceOfSatisfying(YamlDeserializationException.class, error -> { + assertThat(error).hasMessageContaining( + "Invalid numeric value for '" + field + "' in semantic question 'spam': abc"); + assertThat(error.getProblemMark()).hasValueSatisfying(mark -> { + assertThat(mark.getLine()).isEqualTo(5); + assertThat(mark.getColumn()).isEqualTo(8 + field.length() + 2); + }); + }); + } }
