[backport camel-spring-boot-4.22.x] CAMEL-24501: Report starter configuration options that cannot be bound - #1960
Merged
Croway merged 3 commits intoSep 9, 2026
Conversation
The generated starter code discarded configuration in two places, in both cases without a log line, so an option that never took effect looked exactly like one that did. The generated *ComponentConverter, *DataFormatConverter and *LanguageConverter classes resolve the bean reference that an option of a complex (object) type is configured with. They returned null for any value that did not start with #, and for a value naming a bean that does not exist, so a typo in the bean id produced a component with the option unset. The generated convert() body now delegates to the new BeanReferenceHelper, which resolves #bean:id, #id, a plain bean id, #autowired and #type:fqn, and throws IllegalArgumentException naming the value, the target type and the configuration prefix when the value cannot be resolved. This also drops the per-type switch, which returned null for a target type it did not list. The generated customizers copied the whole configuration onto the target with CamelPropertiesHelper.copyProperties, which binds with failIfNotSet=false, so an option with no matching setter on the target was dropped. The same mojo emits failIfNotSet=true for camel.rest.*, so the two disagreed. The customizers now call the new CamelPropertiesHelper.copyConfigurationProperties, which: - removes the options owned by the auto configuration layer itself (enabled and customizer) before binding, as they are not options on the Camel target; - fails with IllegalArgumentException when an option the application configured itself cannot be set; - logs at DEBUG when an option that only carries its catalog default cannot be set, since the target keeps its own default and there is nothing the application can do about it. Telling the two apart uses the Spring ConfigurationPropertySources, so a catalog default that has never been bindable (camel.language.simple.trim, for example, which is an option of the expression model rather than of SimpleLanguage) does not turn into a startup failure for every application. Blanket strict binding needs the catalog defaults to stop being materialised as field initializers on the configuration classes first, which is left for a follow-up. camel.springboot.lenient-configuration-binding=true logs an explicitly configured option at WARN and continues, instead of failing. The language converter template also referenced an applicationContext field it did not declare; no starter currently generates a language converter, so this was latent. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> (cherry picked from commit b689587)
Regenerated the starters with the updated generator plugin. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> (cherry picked from commit 2e48974)
…ion binding Review follow-up on the previous commit. The generated converters are registered with @ConfigurationPropertiesBinding, so they take part in every @ConfigurationProperties binding in the application, not only in Camel's own. Failing closed unconditionally therefore turned an unrelated application property of a type a starter registers for, such as javax.net.ssl.HostnameVerifier, into a startup failure that talks about camel.component.*. BeanReferenceHelper now resolves the class being bound from TypeDescriptor.getSource(), which Spring Boot's binder fills with the setter's MethodParameter (or the Field for field access), and only applies the strict behaviour when that class is Camel's own: under org.apache.camel, or annotated with @ConfigurationProperties for a camel. prefix. Any other class keeps the behaviour it had before, and an unrecognised source counts as Camel's own so that Camel's own binding is never weakened. This is decided in convert() rather than in ConditionalGenericConverter.matches: GenericConversionService caches the converter it picked per source/target TypeDescriptor pair and TypeDescriptor.equals ignores the source, so matches is consulted once for the first class bound and the answer reused for every other class with a field of the same type. A conditional converter would therefore be order dependent, and in the bad order it would report a missing converter for a valid Camel property. Also from the review: - #type: now checks that the bean found by type is assignable to the option type, instead of leaving a ClassCastException for the binder to hit later. - camel.springboot.lenient-configuration-binding is read from the Spring Environment rather than from Camel's PropertiesComponent, and is declared in additional-spring-configuration-metadata.json so it shows up in IDE completion. - The mojo fails the build if a catalog option is ever named enabled or customizer, since the customizers strip those before binding. No component, data format or language declares one today. - The error message names the target class and points out that the option may be listed in the starter documentation, which is generated from the catalog rather than from that class, and so may never have taken effect. - isExplicitlyConfigured logs at WARN when it cannot decide, as returning false there downgrades a hard error to an ignored option. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> (cherry picked from commit e59029e)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Cherry-pick of #1935 onto
camel-spring-boot-4.22.x.Original PR: #1935 — CAMEL-24501: Report starter configuration options that cannot be bound
JIRA: CAMEL-24501
What it fixes
SpringBootAutoConfigurationMojogenerates two pieces of binding code into every starter, and both used to discard configuration silently instead of reporting it — so an option that never took effect looked identical to one that did.First, generated converters for complex (object) type options bound via a bean reference (e.g.
camel.component.http.ssl-context-parameters = #bean:mySslCtx) used to returnnullwhenever the reference couldn't be resolved — a typo'd bean id, or a target type the converter didn't know about. The generatedconvert()body now delegates to a newBeanReferenceHelper, which resolves#bean:myBean/#myBean/ plainmyBean/#autowired/#type:fqn, and throwsIllegalArgumentExceptionnaming the value, target type and configuration prefix when it can't resolve. This strict behaviour is scoped to classes that are Camel's own (underorg.apache.camel, or@ConfigurationPropertieswith acamel.prefix) — a third-party class such asjavax.net.ssl.HostnameVerifierthat happens to share a type with a starter keeps its pre-existing lenient behaviour, so an unrelated application property can't be made to fail binding by a starter on the classpath.Second, generated customizers no longer drop an option that can't be set on the target bean. They now call a new
CamelPropertiesHelper.copyConfigurationProperties, which stripsenabled/customizerbefore binding, fails withIllegalArgumentExceptionwhen an option the application explicitly configured can't be set, and only logs at DEBUG when the option that can't be set merely carries its catalog default (not a hard failure — catalog defaults are currently materialized as field initializers regardless of whether the application configured them, which is item 2 of the ticket and a separate, still-open follow-up, deliberately not included in this backport).Behavior change and opt-out
An application that configures an option which can never actually take effect now fails at startup, naming the option, instead of silently starting up with that option dropped.
camel.springboot.lenient-configuration-binding=truerestores the previous tolerant behaviour, logging at WARN instead of failing.Verification on this branch
b689587a76a,2e48974f6bf,e59029e0182) in order withgit cherry-pick -x. This did not apply cleanly against the currentcamel-spring-boot-4.22.xtip:camel-hivemq-starter,camel-python3-starter,camel-quickjs-starter,camel-rest-postman-starter,camel-toon-starter,camel-ubl-starter— added tomainafter this branch diverged). Resolved by removing those files, matching this branch's (correct) deletion of them.core/camel-spring-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json: the newcamel.springboot.lenient-configuration-bindingentry was inserted next to acamel.vault.ignore-resolution-failuresentry that exists onmain(from an unrelated, unbackported change) but not on this branch. Resolved by keeping only thelenient-configuration-bindingentry that belongs to this ticket.core/camel-spring-boottest suite:mvn verify— 164 unit tests + 2 integration tests, all passed, including the newBeanReferenceHelperTest(22 tests) andCamelPropertiesHelperLenientBindingTest.tooling/camel-spring-boot-generator-maven-plugintest suite:mvn verify— 9 tests, all passed, including the newSpringBootAutoConfigurationMojoTest.camel-http-starterbuilds cleanly against the regenerated code, and its newHttpComponentBeanReferenceBindingTest(6 tests) passes, exercising the fix end-to-end (unresolvable bean reference on a Camel property fails withIllegalArgumentException; an unrelated third-party property type keeps its lenient behaviour).camel-version/BOM entries to the released4.22.0(this branch's SNAPSHOT parent isn't resolvable from a fresh checkout); those edits were reverted before pushing — the pushed branch contains only the 3 cherry-picked commits' real changes.Claude Code on behalf of Federico Mariani