refactor (jspecify): roll @NullMarked out to all sub-packages - #133
refactor (jspecify): roll @NullMarked out to all sub-packages#133adityamparikh wants to merge 3 commits into
Conversation
…safety README: - Add `add-fields` and `add-field-types` to the Tools table (PR apache#131). - Add a sentence on MCP behavior hints (`readOnlyHint`, `destructiveHint`, `idempotentHint`) so client integrators know to read them (PR apache#134). - Add an "MCP Prompts" section listing the six workflow prompts introduced in the prompts PR. - Clarify that `solr://{collection}/schema` autocompletion is prefix-filtered, case-insensitive, capped at 100 — describing the behavior shipped with the completion PR. CONTRIBUTING: - Add a "Null safety" subsection covering the project-wide `@NullMarked` contract and NullAway enforcement (PR apache#133). These docs land alongside the corresponding feature PRs at apache/main. Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
699cbfc to
411ba9d
Compare
|
Follow-up stacked on this PR: #179, which marks the six test packages this No action needed here — #179 is based on this branch, so its diff currently One correction worth folding in here rather than there, since the line belongs |
@NullMarked on a package-info.java applies to that package only - Java packages are not hierarchical for annotation purposes. Combined with NullAway:OnlyNullMarked=true, that means an unmarked package is exempt from nullness checking even though NullAway is configured at error level. apache#133 marked every main package and two test packages (containerization, observability). This adds the six that were still unmarked, so every main and test package is now null-marked and the opt-in is uniform. NullAway remains disabled on compileTestJava, so this is declarative for now. Enabling it surfaces 48 test sites (the note in build.gradle.kts estimates ~30); that is left as a follow-up rather than folded in here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Aditya Parikh <aditya.m.parikh@gmail.com>
411ba9d to
ce5a5ab
Compare
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`, `.schema`, `.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, and always the case on Solr 10 where /admin/mbeans was removed). - `SolrMetrics.cacheStats` / `handlerStats`, `CacheStats.*`, `CacheInfo.*`, `HandlerStats.*`, `HandlerInfo.*`, `SolrHealthStatus.errorMessage` / `responseTime` / `totalDocuments` — 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> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
The method unconditionally returns `new CacheStats(...)`; null-wrapping happens one level up in `fetchCacheMetrics` via the `isCacheStatsEmpty` check. The @nullable was misleading — readers expect a method marked @nullable to actually have a `return null` path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
ce5a5ab to
b20bae2
Compare
* chore(jspecify): roll @NullMarked out to all sub-packages 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> * refactor(jspecify): drop unnecessary @nullable on extractCacheStats The method unconditionally returns `new CacheStats(...)`; null-wrapping happens one level up in `fetchCacheMetrics` via the `isCacheStatsEmpty` check. The @nullable was misleading — readers expect a method marked @nullable to actually have a `return null` path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com> * chore(jspecify): complete the @NullMarked opt-in for test packages @NullMarked on a package-info.java applies to that package only - Java packages are not hierarchical for annotation purposes. Combined with NullAway:OnlyNullMarked=true, that means an unmarked package is exempt from nullness checking even though NullAway is configured at error level. #133 marked every main package and two test packages (containerization, observability). This adds the six that were still unmarked, so every main and test package is now null-marked and the opt-in is uniform. NullAway remains disabled on compileTestJava, so this is declarative for now. Enabling it surfaces 48 test sites (the note in build.gradle.kts estimates ~30); that is left as a follow-up rather than folded in here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Aditya Parikh <aditya.m.parikh@gmail.com> --------- Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com> Signed-off-by: Aditya Parikh <aditya.m.parikh@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Eric Pugh <epugh@opensourceconnections.com>
|
Closing as superseded — every part of this PR is already on I checked each piece rather than assuming: 1. All 10 2. The option("NullAway:HandleTestAssertionLibraries", "true") // Teach NullAway that JUnit assertNotNull narrows nullnessand the 3. The 4. Nothing is left over. Diffing this branch against The one thing here that has not landed and is worth its own issue if anyone wants it: NullAway is still disabled on No action needed — closing. |
Summary
Extends JSpecify null-safety enforcement from the single root package
org.apache.solr.mcp.serverto every sub-package in the codebase — main and test alike.Before this change,
NullAway:OnlyNullMarked=truewas silently skipping all production code except the (mostly empty) root package. So the project advertised null-safety but only enforced it onMain.java.Supersedes #109 and #115 (both now closed) — each was a subset of this rollout:
search/package-info.javaandSearchService.searchparameter annotations are included here.Dtos.java,CollectionUtils.get{Long,Float,Integer},CollectionServicecache/handler method returns, andJsonResponseParser.convertValueannotations are included here. (TheSolrConfig#useHttp1_1(true)change that rode along with fix: add JSpecify @Nullable annotations for accurate null contracts #115 has already landed separately via fix(solr): force HTTP/1.1 in SolrJ client to avoid flaky H2 EOF #130.)Changes
package-info.javawith@NullMarkedadded to 10 packages (8 main + 2 test-only):.collection,.config,.indexing,.indexing.documentcreator,.schema,.search,.security,.util.containerization,.observabilityProduction code annotated with
@Nullablewhere appropriate, derived from running NullAway:CollectionUtils.getLong/getFloat/getInteger— return null on missing/unparseable keysCollectionServicecache/handler metrics methods — return null when the Solr endpoint is unavailable (graceful degradation by design; always the case on Solr 10, where/admin/mbeanswas removed)Dtos.javarecord components:SolrMetrics.{cacheStats,handlerStats}, all ofCacheStats/CacheInfo/HandlerStats/HandlerInfo,SolrHealthStatus.{errorMessage,responseTime,totalDocuments},IndexStats.{numDocs,segmentCount}@McpToolParam(required = false)parameters onCollectionService.createCollectionandSearchService.searchJsonResponseParser.convertValue— JSON null inputsCollectionService.isCacheStatsEmpty— accepts @nullableHttpSecurityConfigurationSpring@Valuefields initialised with defaults (""forissuerUrlmatching the property default,List.of()forallowedOrigins) so the analyser can see they are always non-null after construction.NullAway is explicitly disabled on
compileTestJava(one-line inbuild.gradle.ktswith explanatory comment).@Nullablein production (Map.get returns, optional metric fields, etc.).assertNotNull, but the volume makes test-side cleanup a deliberate follow-up.Rebase note
Rebased onto
main@be84c90. Two conflicts arose from upstream changes landing on the exact lines this PR annotates, both resolved in favour of upstream:collection/Dtos.java— refactor: remove dead fields and unused FieldStats type #106 removed the deadSolrHealthStatus.solrVersion/statuscomponents (so they are no longer annotated here) and refactor: replace mutable Date with Instant in DTO records #113 replacedjava.util.Datewithjava.time.Instant.search/SearchService.java— feat(search): document eDisMax local params in search tool description #161 rewrote theq/fqtool-parameter descriptions; those descriptions are kept verbatim, with@Nullablelayered on top.Test plan
./gradlew build— full build green after rebase (compile + spotless + unit + integration): 359 tests, 0 failures@NullMarkedpackage-info.java; new files carry the ASF license header required by the RAT check added in feat(build): RAT license-header enforcement as a buildSrc convention plugin (stacked on #138) #150🤖 Generated with Claude Code