Following https://github.com/hyperledger/besu/pull/9913
When upgrading to Guava 33.5.0-jre, we discovered it no longer pulls in jsr305
(com.google.code.findbugs:jsr305) or checker-qual (org.checkerframework:checker-qual)
as transitive dependencies (Guava changelog).
We added them as explicit compileOnly dependencies as a short-term fix, but should migrate to better-maintained alternatives.
Both of these annotation libraries have known issues:
- JSR305 (javax.annotation.*): The JSR-305 spec has been dormant since 2012. Using the javax.annotation package causes split-package conflicts in Java 9+ modular builds.
It has no active maintainership and the Guava team has explicitly said they are moving away from it.
- checker-qual (org.checkerframework.*): Maintained but heavy — the Checker Framework is primarily a full static analysis suite. Adding it purely for @Nullable or @Unsigned annotations is excessive and brings in dependencies not used for their core purpose.
Current usage in this codebase
JSR305 (javax.annotation.*) — 18 files, added as compileOnly to 8 modules:
- @Nullable — 17 files (API parameters, EVM worldstate, reference tests, ethstats, consensus)
- @CheckForNull — 1 file (evm/.../UndoTable.java)
checker-qual (org.checkerframework.*) — 1 file:
- @Unsigned — datatypes/.../UnsignedLongParameter.java
Options
Option A — Migrate to JSpecify (recommended)
JSpecify 1.0.0 (released 2024) is the new standard for Java nullness annotations, backed by Google, JetBrains, Oracle, Meta, and others. Guava 33.x has already adopted it internally (using @NullMarked). It is supported by ErrorProne, NullAway, IntelliJ IDEA, The Checker Framework, and SonarQube.
- Replace javax.annotation.@Nullable → org.jspecify.annotations.@Nullable
- Replace javax.annotation.@CheckForNull → org.jspecify.annotations.@Nullable
(semantically equivalent)
- Dependency: org.jspecify:jspecify:1.0.0 (tiny library, annotations only)
- Keep checker-qual for @Unsigned (no JSpecify equivalent exists for signedness)
- Optional follow-up: add NullAway (ErrorProne plugin) for compile-time null checking
Option B — Migrate to jakarta.annotation.@Nullable
jakarta.annotation-api is already a widely-used annotation library in Jakarta EE projects. However, it is more focused on runtime/injection concerns than static analysis, and the major tooling (NullAway, ErrorProne) has better support for JSpecify.
Option C — Keep current explicit compileOnly dependencies
Minimal effort — no code changes needed. The current jsr305 and checker-qual constraints in platform/build.gradle work fine as explicit dependencies. The main downside is carrying two unmaintained/heavy annotation libraries indefinitely.
Recommended approach
Migrate javax.annotation.* usages to JSpecify (Option A). The migration is small in scope (18 files) and straightforward — @Nullable maps 1:1, and @CheckForNull also maps to @Nullable. Keep checker-qual for the single @Unsigned usage in UnsignedLongParameter.java, which has no equivalent elsewhere.
This positions the codebase in line with the direction Guava and the broader Java
ecosystem are heading.
Files to change (if Option A is chosen)
- platform/build.gradle — replace jsr305 constraint with org.jspecify:jspecify:1.0.0
- Remove compileOnly 'com.google.code.findbugs:jsr305' from 8 build.gradle files and add compileOnly 'org.jspecify:jspecify' where needed
- 18 Java source files — replace import javax.annotation.Nullable/CheckForNull
with import org.jspecify.annotations.Nullable
Notes for the issue author
- The three options give readers a clear choice with tradeoffs
- Option A is the path that aligns with Guava's own migration
- The scope is small (18 files) so this is achievable in one PR
- Could optionally add a note about NullAway as a future enhancement (separate issue)
- The @Unsigned situation (no modern equivalent) is worth calling out explicitly so it doesn't get forgotten