Skip to content

refactor (jspecify): roll @NullMarked out to all sub-packages#133

Open
adityamparikh wants to merge 1 commit into
apache:mainfrom
adityamparikh:jspecify-everywhere
Open

refactor (jspecify): roll @NullMarked out to all sub-packages#133
adityamparikh wants to merge 1 commit into
apache:mainfrom
adityamparikh:jspecify-everywhere

Conversation

@adityamparikh
Copy link
Copy Markdown
Contributor

Summary

Extends JSpecify null-safety enforcement from the single root package org.apache.solr.mcp.server to every sub-package in the codebase — main and test alike.

Before this change, NullAway:OnlyNullMarked=true was silently skipping all production code except the (mostly empty) root package. So the project advertised null-safety but only enforced it on Main.java.

Changes

  1. package-info.java with @NullMarked added to 10 packages (8 main + 2 test-only):

    • main: .collection, .config, .indexing, .indexing.documentcreator, .metadata, .search, .security, .util
    • test-only: .containerization, .observability
  2. Production code annotated with @Nullable where appropriate, derived from running NullAway:

    • CollectionUtils.getLong/getFloat/getInteger — return null on missing/unparseable
    • CollectionService cache/handler metrics methods — return null when the Solr endpoint is unavailable (graceful degradation by design)
    • Many Dtos.java record components: SolrMetrics.{cacheStats,handlerStats}, all of CacheStats/CacheInfo/HandlerStats/HandlerInfo, SolrHealthStatus.{errorMessage,responseTime,totalDocuments,solrVersion,status}, IndexStats.{numDocs,segmentCount}
    • @McpToolParam(required = false) parameters on CollectionService.createCollection and SearchService.search
    • JsonResponseParser.convertValue — JSON null inputs
    • CollectionService.isCacheStatsEmpty — accepts @nullable
  3. HttpSecurityConfiguration Spring @Value fields initialised with defaults ("" for issuerUrl matching the property default, List.of() for allowedOrigins) so the analyser can see they are always non-null after construction.

  4. NullAway is explicitly disabled on compileTestJava for this PR (one-line in build.gradle.kts with explanatory comment).

    • The rollout exposes ~30 test sites that unbox or dereference values now declared @Nullable in production (Map.get returns, optional metric fields, etc.).
    • Each can be tightened by extracting to a local + assertNotNull, but the volume makes test-side cleanup a deliberate follow-up.
    • Production code is fully enforced.

Why draft

Draft because the test-side cleanup is intentionally deferred. Two reasonable paths forward:

  • Merge as-is, then a follow-up PR fixes each test site (@Nullable flow analysis requires extracting to locals + assertNotNull). Roughly 30 sites across CollectionServiceTest, CollectionServiceIntegrationTest, CollectionUtilsTest, SearchServiceIntegrationTest, ConferenceEndToEndIntegrationTest, IndexingServiceIntegrationTest, XmlIndexingTest, SchemaServiceTest, SchemaServiceIntegrationTest, TraceAssertions, DistributedTracingTest.
  • Land both at once, by extending this PR with the test-side cleanups before un-drafting.

Happy to do either.

Test plan

  • ./gradlew build — full build green (compile + unit + integration)
  • NullAway enforces null safety on all production code
  • Pre-existing tests still pass at runtime

🤖 Generated with Claude Code

Background: prior to this change only the root package
`org.apache.solr.mcp.server` was @NullMarked via package-info.java. All
sub-packages — `.collection`, `.config`, `.indexing`,
`.indexing.documentcreator`, `.metadata`, `.search`, `.security`,
`.util`, plus the test-only `.containerization` and `.observability` —
were running unannotated, so NullAway (`OnlyNullMarked=true`) was
silently skipping them.

Changes:

1. Adds `package-info.java` with `@NullMarked` to every sub-package (10
   files: 8 main + 2 test-only).

2. Annotates production code with `@Nullable` where return types,
   parameters, or record components legitimately admit null. The
   non-obvious nullable surfaces:
   - `CollectionUtils.getLong/getFloat/getInteger` return null on missing
     or unparseable keys.
   - `CollectionService` cache/handler metrics methods return null when
     the underlying Solr endpoint is unavailable (graceful degradation
     by design).
   - `SolrMetrics.cacheStats` / `handlerStats`, `CacheStats.*`,
     `CacheInfo.*`, `HandlerStats.*`, `HandlerInfo.*`,
     `SolrHealthStatus.errorMessage` / `responseTime` / `totalDocuments`
     / `solrVersion` / `status` — all documented as nullable, now
     annotated.
   - `IndexStats.numDocs` / `segmentCount` — Luke can return either
     field as null.
   - Optional `@McpToolParam(required = false)` parameters on
     `CollectionService.createCollection` and `SearchService.search`.
   - `JsonResponseParser.convertValue` — JSON null inputs.

3. `HttpSecurityConfiguration` Spring `@Value` fields initialised with
   defaults (`""` for `issuerUrl` matching the property default,
   `List.of()` for `allowedOrigins`) so the analyser can see they are
   always non-null after construction.

4. NullAway is explicitly disabled on `compileTestJava` (with an
   explanatory comment). The @NullMarked rollout exposes ~30 test sites
   that unbox or dereference values now declared @nullable in
   production (Map.get returns, metric fields that are null when an
   endpoint is unavailable, etc.). Each can be tightened by extracting
   to a local + `assertNotNull`, but the volume makes it a deliberate
   follow-up. Production is fully enforced.

Build verified green (compile + unit + integration).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
@adityamparikh adityamparikh changed the title chore(jspecify): roll @NullMarked out to all sub-packages refactor (jspecify): roll @NullMarked out to all sub-packages May 19, 2026
@adityamparikh adityamparikh marked this pull request as ready for review May 19, 2026 17:04
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.

1 participant