Skip to content

CAMEL-23999/CAMEL-24000: camel-spring-boot - Spring Boot 4 idiom alignment and configuration metadata regression fix#1843

Merged
Croway merged 8 commits into
apache:mainfrom
Croway:core-spring-boot-4-modernization
Jul 10, 2026
Merged

CAMEL-23999/CAMEL-24000: camel-spring-boot - Spring Boot 4 idiom alignment and configuration metadata regression fix#1843
Croway merged 8 commits into
apache:mainfrom
Croway:core-spring-boot-4-modernization

Conversation

@Croway

@Croway Croway commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Claude Code on behalf of Federico Mariani (@Croway)

Resolves CAMEL-23999 and CAMEL-24000.

Modernization of core/camel-spring-boot following a review against Spring Boot 4.1.x best practices, plus one build regression fix found along the way.

CAMEL-23999 — restore spring-configuration-metadata.json (regression)

The maven-compiler-plugin 3.15.0 upgrade (#1648) silently stopped running annotation processors discovered on the compile classpath (behaviour change in maven-compiler-plugin 3.13+). As a result the core and all starter jars were built without META-INF/spring-configuration-metadata.json — breaking IDE completion for every camel.* property — and the generated src/main/docs/*.json files could no longer be regenerated. Fixed with <proc>full</proc> in the root POM; verified the metadata is back in both the core jar and a starter jar.

CAMEL-24000 — Spring Boot 4 idiom alignment

  • MainListener beans notified exactly once with camel.main.run-controller=true. They previously received beforeStart/afterStart (and the stop callbacks) twice: once from the controller created by CamelAutoConfiguration (the complete callback stream added in CAMEL-21359) and once more from the run controller's internal Main. The run controller no longer registers the listener beans. A new test (CamelMainListenerRunControllerTest) asserts exactly-once delivery for all seven callbacks — it was written first and demonstrated the duplication (beforeStart=2, afterStart=2) before the fix.
  • Dead code removed: HierarchicalCondition and CompositeConversionService (referenced by nothing), ClusteredRouteControllerConfiguration.clusterServiceRef (no accessors, never bindable), unused import in CamelDevConsoleAutoConfiguration.
  • No more mutation of shared @ConfigurationProperties beans: the trace, error registry and security policy auto-configurations applied dev/prod profile defaults by mutating the injected singleton, so other beans reading the same properties observed altered values. Effective values are now computed locally.
  • @ConditionalOnBooleanProperty (Spring Boot 3.4+) replaces @ConditionalOnProperty(name = "enabled") on the supervising and clustered route controller auto-configurations; the redundant in-method isEnabled() re-check and null @Bean return are gone.
  • Vault auto-configurations consolidated: the eight hand-written per-field copy chains are replaced by BeanUtils.copyProperties (all properties mirror the Camel core vault configurations by name and type), guarded by a new reflection test that fails when Camel core gains a vault option the starter does not expose. The test immediately found real drift: camel.vault.ibm.refresh-enabled and camel.vault.ibm.secrets existed in Camel core but could not be configured from Spring Boot — both added and spring-boot.json regenerated.
  • Duration properties bound as java.time.Duration with @DurationUnit(MILLIS): supervising route controller initial-delay / back-off-delay / back-off-max-delay / back-off-max-elapsed-time and startup condition interval / timeout. Plain numeric values keep meaning milliseconds (existing tests binding plain numbers pass unchanged); readable values like 5s now work too.

Compatibility notes

  • The getters of the two converted properties classes change type (long/intDuration); these classes are starter-internal but technically public API.
  • The clustered route controller initial-delay deliberately keeps its String type + TimePatternConverter: it accepts Camel time patterns (e.g. 10 seconds) that Duration binding does not support, so converting it would break existing configurations.
  • Null @Bean returns in the trace/errorregistry/threadpool/routetemplate auto-configurations were kept: their conditions depend on the runtime Camel profile or on "any property under the prefix" semantics, which cannot be expressed declaratively.

Testing

  • mvn verify on core/camel-spring-boot passes (full suite + ITs).
  • New tests: CamelMainListenerRunControllerTest (exactly-once listener callbacks), VaultConfigurationPropertiesMappingTest (vault option drift guard).

🤖 Generated with Claude Code

Review follow-ups

Croway and others added 7 commits July 10, 2026 18:13
…ce with run-controller enabled

With camel.main.run-controller=true, MainListener beans were notified twice
for beforeStart/afterStart (and beforeStop/afterStop): once via the
controller created by CamelAutoConfiguration (the complete callback stream
added in CAMEL-21359) and once more via the run controller's internal Main,
which registered the same listener beans and fires the start/stop callbacks
from MainSupport.run().

The run controller's Main is only used to keep the JVM running, so it no
longer registers the MainListener beans.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…iguration

- HierarchicalCondition and CompositeConversionService are referenced by
  nothing in the repository
- ClusteredRouteControllerConfiguration.clusterServiceRef has no accessors,
  so it can never be bound nor read
- unused ConditionalOnAvailableEndpoint import in
  CamelDevConsoleAutoConfiguration

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…roperties beans

The trace, error registry and security policy auto-configurations applied
dev/prod profile defaults by mutating the injected @ConfigurationProperties
singleton, so any other bean reading the same properties observed the
mutated values instead of what the user configured. Compute the effective
values locally instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…r route controller auto-configurations

Replace @ConditionalOnProperty(name = "enabled") with the clearer
@ConditionalOnBooleanProperty (Spring Boot 3.4+) on the supervising and
clustered route controller auto-configurations, and drop the redundant
in-method isEnabled() re-check and the discouraged null @bean return in
SupervisingRouteControllerAutoConfiguration (the class-level condition
already guarantees the property is true).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ocessor execution

The maven-compiler-plugin 3.15.0 upgrade (apache#1648) silently stopped running
annotation processors discovered on the compile classpath (behaviour change
in maven-compiler-plugin 3.13+). As a result the camel-spring-boot core and
all starter jars no longer contained META-INF/spring-configuration-metadata.json,
breaking IDE completion for camel.* properties and the generated
src/main/docs/*.json property documentation. Configure <proc>full</proc> to
restore processor execution.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s and guard against option drift

Replace the eight hand-written per-field copy chains in the vault
auto-configurations with BeanUtils.copyProperties (all properties mirror the
Camel core vault configurations by name and type), and add a reflection test
that fails when Camel core gains a vault option the starter properties class
does not expose.

The new test immediately found real drift: camel.vault.ibm.refresh-enabled
and camel.vault.ibm.secrets existed in Camel core but could not be configured
from Spring Boot. Add both properties and regenerate spring-boot.json.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e.Duration

Convert the raw millisecond long/int properties of the supervising route
controller (initial-delay, back-off-delay, back-off-max-delay,
back-off-max-elapsed-time) and the startup conditions (interval, timeout)
to java.time.Duration with @DurationUnit(MILLIS). Existing plain-number
values keep meaning milliseconds; users can now also write readable values
such as 5s or 2m.

The clustered route controller initial-delay keeps its String type on
purpose: it accepts Camel time patterns (via TimePatternConverter) that
Duration binding does not support, so converting it would break existing
configurations.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Croway
Croway requested review from davsclaus, gzurowski and oscerd July 10, 2026 16:43

@davsclaus davsclaus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code on behalf of Claus Ibsen (@davsclaus)

Nice work, Federico — this is a well-structured PR with clear atomic commits and good test coverage. The git history confirms all changes are consistent with prior intent (none revert intentional decisions), and the MainListener exactly-once fix addresses a genuine bug from CAMEL-21359.

Confirmed findings

1. Narrowing cast (int) Duration.toMillis() — potential overflow (Low)

In CamelAutoConfiguration.java, Duration.toMillis() returns long; casting to int silently wraps for values > ~24.8 days. The defaults (500ms, 20s) make this safe in practice, but Math.toIntExact() would turn a misconfiguration into a clear ArithmeticException instead of silent wraparound.

2. Missing upgrade guide entry for property type changes (Medium)

The getter/setter types on SupervisingRouteControllerConfiguration (longDuration) and CamelStartupConditionConfigurationProperties (intDuration) are public API changes. @DurationUnit(MILLIS) ensures backward compatibility for plain numeric values in application.properties, but code calling e.g. config.getBackOffDelay() and expecting a long will fail to compile. An upgrade guide entry documenting the type changes would be appropriate.

Observations (non-blocking)

  • @ConditionalOnBooleanProperty semantic tightening: the old @ConditionalOnProperty(name = "enabled") matched any value except "false"; the new annotation only matches "true" (case-insensitive). Correct behavior for a boolean flag, but technically a subtle change.
  • BeanUtils.copyProperties for vault configs: clean simplification, and the new VaultConfigurationPropertiesMappingTest is an excellent drift guard.
  • Dead code removals (CompositeConversionService, HierarchicalCondition, clusterServiceRef): verified — no references anywhere in the repo.
  • IBM vault drift fix (refreshEnabled, secrets): confirmed genuine missing configuration.

This review does not replace specialized tools like CodeRabbit, Sourcery, or SonarCloud.

This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.

Comment on lines +485 to +486
scs.setInterval((int) config.getInterval().toMillis());
scs.setTimeout((int) config.getTimeout().toMillis());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: Duration.toMillis() returns long; the (int) cast silently wraps for large values. Consider using Math.toIntExact() to fail fast on misconfiguration instead of silent overflow:

Suggested change
scs.setInterval((int) config.getInterval().toMillis());
scs.setTimeout((int) config.getTimeout().toMillis());
scs.setInterval(Math.toIntExact(config.getInterval().toMillis()));
scs.setTimeout(Math.toIntExact(config.getTimeout().toMillis()));

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied in 25b33d8 — switched to Math.toIntExact() for both interval and timeout. Thanks!

Claude Code on behalf of Federico Mariani.

…ad of silent int wraparound

Address review feedback: use Math.toIntExact instead of a narrowing cast
when converting the startup condition interval/timeout Durations to millis.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Croway

Croway commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

Both review findings addressed:

  1. Overflow on (int) Duration.toMillis() — fixed in 25b33d8 using Math.toIntExact(), so a misconfigured interval/timeout now fails fast with ArithmeticException instead of silently wrapping.
  2. Upgrade guide entry — the 4.x upgrade guides live in the main repo, so this is covered by CAMEL-24000: camel-spring-boot - upgrade guide entry for Duration configuration properties camel#24593 (documents the Duration type changes, the plain-numbers-stay-millis compatibility, and why camel.clustered.controller.initial-delay keeps its String type).

Claude Code on behalf of Federico Mariani.

@Croway
Croway requested a review from davsclaus July 10, 2026 17:11
@Croway
Croway merged commit 21283c3 into apache:main Jul 10, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants