Skip to content

CAMEL-22640: Add JSpecify null safety annotations to camel-api#22313

Draft
gnodet wants to merge 3 commits intoapache:mainfrom
gnodet:CAMEL-22640-jspecify-null-annotations
Draft

CAMEL-22640: Add JSpecify null safety annotations to camel-api#22313
gnodet wants to merge 3 commits intoapache:mainfrom
gnodet:CAMEL-22640-jspecify-null-annotations

Conversation

@gnodet
Copy link
Copy Markdown
Contributor

@gnodet gnodet commented Mar 29, 2026

Summary

Add JSpecify null safety annotations (@NullMarked / @Nullable) to the camel-api module, aligning with Spring Framework 7 / Spring Boot 4's adoption of JSpecify as the standard null safety annotation library.

Changes

  • JSpecify dependency: Added org.jspecify:jspecify:1.0.0 to parent/pom.xml (dependencyManagement) and core/camel-api/pom.xml
  • @NullMarked on all packages: Added package-info.java with @NullMarked to all 16 packages in camel-api, making non-null the default
  • @Nullable annotations: Comprehensive audit of all ~200+ Java files, marking parameters, return types, and fields that can be null. Annotations were verified by:
    1. Scanning implementations for return null
    2. Checking callers for != null / == null guards
    3. Checking field initializers for null defaults
  • Runtime enforcement: Added Objects.requireNonNull() checks on non-null reference parameters in all concrete classes, providing fail-fast behavior at API boundaries
  • Fixed false non-null promises: The runtime enforcement revealed several parameters that were missing @Nullable — these were fixed:
    • CamelExchangeException constructors: exchange param made @Nullable (field already nullable)
    • CamelExecutionException: exchange param made @Nullable (called with null from ExchangeHelper)
    • ExchangeTimedOutException: exchange param made @Nullable (consistent with parent)
    • RuntimeExchangeException: exchange param already @Nullable (verified)
    • ValidationException: exchange param already @Nullable (verified)
    • ResolveEndpointFailedException: uri param made @Nullable
    • ExpressionEvaluationException: expression/exchange params made @Nullable
    • Transformer.setName(): scheme/name params made @Nullable
    • Transformer.setFrom()/setTo(): params made @Nullable (fields already nullable)
    • JsseParameters.parsePropertyValue(): value param made @Nullable
    • EventClock.add() / ContextClock.add(): clock param made @Nullable

Approach

  1. Added class-level @NullMarked during review, then moved to package-level at the end
  2. Two-pass audit: Javadoc-based first pass, then implementation-verified second pass
  3. Runtime Objects.requireNonNull() enforcement on concrete classes caught 10+ missed @Nullable annotations

JIRA: CAMEL-22640

Add @NullMarked (JSpecify 1.0.0) to all packages in camel-api,
making non-null the default. Annotate ~200 API interfaces and
classes with @nullable where parameters or return values can
legitimately be null, covering the core API surface including
CamelContext, Exchange, Message, Component, Endpoint, TypeConverter,
and all SPI interfaces.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gnodet gnodet requested review from davsclaus and oscerd March 29, 2026 21:03
@github-actions
Copy link
Copy Markdown
Contributor

🌟 Thank you for your contribution to the Apache Camel project! 🌟
🤖 CI automation will test this PR automatically.

🐫 Apache Camel Committers, please review the following items:

  • First-time contributors require MANUAL approval for the GitHub Actions to run
  • You can use the command /component-test (camel-)component-name1 (camel-)component-name2.. to request a test from the test bot although they are normally detected and executed by CI.
  • You can label PRs using build-all, build-dependents, skip-tests and test-dependents to fine-tune the checks executed by this PR.
  • Build and test logs are available in the summary page. Only Apache Camel committers have access to the summary.

⚠️ Be careful when sharing logs. Review their contents before sharing them publicly.

gnodet and others added 2 commits March 30, 2026 07:53
Scan actual implementations (not just Javadoc) for return null,
null field values, and caller null-checks. Adds missing @nullable
annotations and moves @NullMarked to package-level only.

Key additions found by implementation scanning:
- Exchange.getPattern() can be null (field uninitialized)
- Message.getExchange() can be null (field starts null)
- Route.getOnException/navigate() can return null
- DelegateProcessor.getProcessor() can be null (LazyStartProducer)
- Various transient exception fields nullable after deserialization
- ConsumerTemplate.receiveBody() without timeout can return null
- CamelContext.getComponent() overloads can return null
- ExtendedCamelContext.getErrorHandlerFactory() can be null
- Multiple SPI methods found nullable through impl analysis

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- CamelExchangeException: accept @nullable exchange (field already nullable)
- CamelExecutionException: accept @nullable exchange (called with null from ExchangeHelper)
- ExchangeTimedOutException: accept @nullable exchange (consistent with parent)
- Transformer: setFrom/setTo accept @nullable (fields already nullable, called with null from reifiers)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant