Salvatore Mongiardo created CAMEL-26782:
-------------------------------------------
Summary: camel-spring-boot: copyConfigurationProperties drops
values assigned in Java to a starter configuration bean
Key: CAMEL-26782
URL: https://issues.apache.org/jira/browse/CAMEL-26782
Project: Camel
Issue Type: Bug
Components: tooling
Affects Versions: 4.23.0
Reporter: Salvatore Mongiardo
Fix For: 4.24.0, 4.22.3
h3. Problem
CAMEL-24501 (apache/camel-spring-boot#1951, commit 72323d48) changed
{{CamelPropertiesHelper.copyConfigurationProperties}} to copy only the options
that Spring Boot recorded in {{BoundConfigurationProperties}}. That fixes
catalog defaults overwriting values on user supplied Camel beans, but
{{isConfigured()}} can only recognise values bound from properties. A value
assigned in Java to the generated starter configuration bean before the
customizer runs is no longer copied to the component.
{{camel.springboot.lenient-configuration-binding=true}} does not restore it.
The change is released: {{camel-spring-boot-4.23.0}} contains it.
h3. Reproduction
Using {{HttpComponentConfiguration}} and {{HttpComponent}}:
{code:java}
configuration.setSkipRequestHeaders(true); // before the customizer runs
{code}
{code}
camel.component.http.log-http-activity=true
{code}
Before #1951 both values reach the component. After it, {{logHttpActivity}} is
{{true}} but {{skipRequestHeaders}} stays {{false}}.
h3. In-tree cases of the same regression
* {{SpringBootPlatformWebMvcConfiguration}} (main) writes
{{spring.mvc.async.request-timeout}} into
{{PlatformHttpComponentConfiguration.requestTimeout}} from code, so it no
longer reaches {{PlatformHttpComponent}} (small impact, Spring MVC still
enforces the timeout). Good test for the fix.
* {{SpringKafkaPropertiesAutoConfiguration}} (camel-spring-boot-4.22.x only)
writes the {{spring.kafka.*}} values into {{KafkaComponentConfiguration}} from
a {{@PostConstruct}}. The filter drops them. On main the bridge is a
{{ComponentCustomizer}} (e7850714524, precedence fixed in 115f626f4e7 /
CAMEL-22760), so 4.22.x must not take #1951 as is.
h3. Related effect: defaults moved from catalog to the component's own
Any option the application does not configure now keeps the component's Java
default instead of the catalog default, where the two differ. Example:
{{XsltComponent.xpathTotalOpLimit}} is an {{int}} with {{defaultValue =
"10000"}} in its metadata, the field is 0, the starter initializes 10000.
Before #1951 the starter copied 10000. Also,
{{BaseOptionModel.resolveDefaultValue()}} turns a missing default into
{{false}} for {{java.lang.Boolean}}, so many nullable Booleans go from
{{false}} to {{null}}. Only 8 of the 607 {{java.lang.Boolean}} options in the
4.22.2 component catalogs have no default.
h3. Why it was noticed
Red Hat CSB-13052: with {{camel-ai-tool-starter}} +
{{camel-mcp-server-starter}} on 4.22.1, the catalog {{defaultValue: false}} for
the nullable MCP hints ({{readOnlyHint}}, {{destructiveHint}},
{{idempotentHint}}, {{openWorldHint}}) is materialised as {{= false}} in
{{AiToolComponentConfiguration}}, copied onto {{AiToolComponent}}, and
published in MCP {{tools/list}} as explicit {{false}} annotations although the
route sets none. #1951 resolves this on main, so the fix for the regression
must keep the hints unset.
h3. Proposed fix
*main (4.24.0), option A:* in {{copyConfigurationProperties}} copy an option if
it was bound or if its value differs from the value on a freshly constructed
instance of the same configuration class (computed once per class). A catalog
default equals the fresh instance's value, so it is not copied; a Java
assignment differs, so it is copied.
* Collections: the only object valued initializer in the generated classes is
{{serverConfig = new HashMap<>()}} (Jolokia), and two empty maps compare equal;
collections without initializer are {{null}} on a fresh instance.
* Fallback when the instance cannot be created (should not happen, generated
classes have a no-arg constructor): copy everything, as {{boundProperties()}}
does, but fail only on options known to be configured. #1951 removed the
DEBUG-only branch for options that carry just their default, so copying
everything would otherwise fail startup.
* Known gap, to document, also in the paragraph #1951 added to
{{starter-configuration.adoc}}: a Java assignment equal to the catalog default
(e.g. {{setSkipRequestHeaders(false)}} onto a user bean holding {{true}}) is
not copied.
* Smaller points: copy {{BoundConfigurationProperties.getAll()}} (live view)
inside the {{try}}; check {{BoundConfigurationProperties.get()}} for {{null}}
instead of catching the NPE; log skipped options at DEBUG; restore coverage in
{{testCopyConfigurationPropertiesIgnoresDefaultThatCannotBeSet}} (uses
{{configWithoutDrift()}}) and
{{testCopyConfigurationPropertiesIgnoresAutoConfigurationOptions}} (does not
bind {{enabled}}/{{customizer}}).
*camel-spring-boot-4.22.x (4.22.3), option C:* do not backport #1951 (it
changes every starter on a patch branch). Do not initialize the four nullable
MCP hint fields of {{camel-ai-tool}} in {{SpringBootAutoConfigurationMojo}};
{{returnDirect}} keeps its declared {{false}} default. Only the ai-tool starter
changes.
h3. Tests
* {{HttpComponentConfiguration}} scenario above (Java assignment plus a bound
value).
* ai-tool hints stay unset on {{AiToolComponent}} and in the MCP {{tools/list}}
output.
* A bound value equal to the catalog default is still copied onto a user bean.
* platform-http {{requestTimeout}} from {{spring.mvc.async.request-timeout}}.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)