Fix #12531: filter NO_REPOSITORY sentinel from mapped exceptions in ArtifactResolverResult#12533
Fix #12531: filter NO_REPOSITORY sentinel from mapped exceptions in ArtifactResolverResult#12533gnodet wants to merge 3 commits into
Conversation
…rtifactResolverResult Plugins that resolve dependencies (cyclonedx, camel-spring-boot-generator) threw IllegalArgumentException when ArtifactResult.NO_REPOSITORY leaked into AbstractSession.getRepository() via the mapped exceptions map. While commit a2aed72 added NO_REPOSITORY handling to AbstractSession.getRepository() (returning Optional.empty()), the DefaultArtifactResolver.toResult() method still converted NO_REPOSITORY entries to null keys in the Map<Repository, List<Exception>> exposed through the public API. This could cause NullPointerException in downstream code iterating over the exceptions map keys. This fix: - Filters out NO_REPOSITORY entries when building the mapped exceptions map, so no null keys leak into the Maven API - Preserves all exceptions (including NO_REPOSITORY ones) in a separate flat list used by isMissing(), ensuring correct missing-artifact detection - Uses merge() when building the map to handle potential duplicate keys robustly Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
gnodet
left a comment
There was a problem hiding this comment.
Code Review
Clean, well-scoped fix for issue #12531. The PR correctly filters the NO_REPOSITORY sentinel from the public API's exceptions map while preserving all exceptions in an internal list for isMissing() semantics. The use of merge() when building the map is a robustness improvement over the old Collectors.toMap() (gracefully merges exception lists instead of throwing on duplicate keys). Three new unit tests adequately cover the fix scenarios.
Two minor observations (non-blocking):
- The
allExceptionsArrayList could be wrapped withList.copyOf()for defensive immutability, consistent with record semantics. - Pre-existing:
isMissing()returnstrueon an emptyallExceptionslist due toallMatch()on empty stream (vacuous truth) — consider guarding with!allExceptions.isEmpty()if the intent requires at least oneArtifactNotFoundException.
LGTM.
This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.
Claude Code on behalf of gnodet
… against vacuous truth Address review feedback: - Wrap allExceptions with List.copyOf() for defensive immutability, consistent with record semantics - Guard isMissing() with !allExceptions.isEmpty() to prevent vacuous truth on empty stream (allMatch on empty returns true) - Add test for the vacuous truth edge case Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Thanks for the review! Both observations addressed in 27ee62b:
|
gnodet
left a comment
There was a problem hiding this comment.
Code Review (Re-review)
Both observations from the previous review have been properly addressed in commit 27ee62b:
-
List.copyOf()for allExceptions — ✅ TheallExceptionsArrayList is now wrapped withList.copyOf()for defensive immutability before being passed to the record constructor. -
Vacuous truth guard in
isMissing()— ✅ The method now checks!allExceptions.isEmpty()before theallMatch()call, preventing an empty exception list from falsely reporting the artifact as missing. A dedicated test (isMissingReturnsFalseWhenNoExceptions) was added to cover this edge case.
One minor note (non-blocking):
- The
mappedExceptionsHashMap is not wrapped for immutability (e.g.,Map.copyOf()), whileallExceptionson the same line is wrapped withList.copyOf(). This is a minor inconsistency — the pre-existing code also used a mutable map, so not a regression.
LGTM.
This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.
Claude Code on behalf of gnodet
…ability Address review feedback: wrap mappedExceptions HashMap with Map.copyOf() for consistency with allExceptions wrapping. Update test assertions to use keySet stream instead of containsKey(null) since immutable maps throw NPE on null key lookups. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Addressed the additional observation in 3446694: wrapped |
Summary
Fixes #12531 — Plugins that resolve dependencies (cyclonedx-maven-plugin, camel-spring-boot-generator) throw
IllegalArgumentException: Unsupported repository type: class org.eclipse.aether.resolution.ArtifactResult$NoRepositorywhen building the dependency tree under Maven 4.Root cause
ArtifactResult.NO_REPOSITORYis a sentinel used in the resolver's mapped exceptions for errors with no associated repository. WhenDefaultArtifactResolver.toResult()converts these to the Maven API, theNO_REPOSITORYkey was mapped to anullkey in theMap<Repository, List<Exception>>exposed throughResultItem.getExceptions(). This null key could causeNullPointerExceptionin downstream code iterating over the exceptions map.Changes
NO_REPOSITORYentries from the mapped exceptions map intoResult(), ensuring no null keys leak into the public APINO_REPOSITORYones) in a separate flat list (allExceptions) so thatisMissing()still correctly considers all exceptionsmerge()when building the exceptions map for robustness against duplicate keysNO_REPOSITORYentries don't produce null keys in the exceptions mapisMissing()correctly considersNO_REPOSITORYexceptions (including non-ArtifactNotFoundExceptiontypes)NO_REPOSITORYexceptions are handled properlyThis builds on commit a2aed72 which added
NO_REPOSITORYhandling toAbstractSession.getRepository().Test plan
DefaultArtifactResolverTest(3 tests)mvn verifyinimpl/maven-impl(515 tests pass)mvn clean install -DskipTestspasses🤖 Generated with Claude Code