Enh: Allow validator factory to abstain#2008
Conversation
Also, cache validators across single session. Fixes: apache#2007
Revert the managed dependency validation removal from DefaultRepositorySystemValidator. As cstamas noted, managed deps should remain validatable for non-Maven use cases — the proper separation between direct and managed dependency validation belongs in PR #2008 (validateManagedDependency method). This PR now focuses solely on session-scoped re-entrancy detection via an AtomicInteger depth counter in session data, which covers the broken trace chain scenario independently. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
elharo
left a comment
There was a problem hiding this comment.
Needs tests
Is there a better signal than "null" for abstention that doesn't risk NPEs? Abstention seems equivalent to approval unless something requires some validator to approve?
| * that are never actually used. If a managed dependency IS matched and its coordinates are invalid, the error | ||
| * will surface naturally during version resolution or artifact resolution. | ||
| * | ||
| * @param managedDependency The managed dependency to validate, never {@code null}. |
There was a problem hiding this comment.
nit: begin with lower case the
no periods at end
per Oracle guidelines
There was a problem hiding this comment.
While I agree with this sentiment, this javadoc method is not the single one in this PR not following that, and in this codebase this is not the single one either (in fact, whole codebase follows this style). I'd leave this change to some time, and IMO the change should be done consistently across whole codebase.
There was a problem hiding this comment.
Hm, was wrong: lowercase stands but not the "no period at end"?
gnodet
left a comment
There was a problem hiding this comment.
The overall design of the three features (abstain, managed-dependency differentiation, caching) is sound and addresses real needs. The separate validateManagedDependency path correctly avoids false positives from uninterpolated BOM property expressions — well-documented rationale.
Two implementation issues in the caching logic:
-
Null values not cached:
ConcurrentHashMap.computeIfAbsentdoes not store entries when the mapping function returns null. When a factory abstains (returns null), the factory is re-invoked on every subsequent validation call within the same session — contradicting the Javadoc claim that "even nulls are cached." Fix: use a sentinel object pattern (e.g.,private static final Validator ABSENT = new Validator() {};) and unwrap on retrieval. -
System.identityHashCodeas map key:System.identityHashCodeis not guaranteed unique across distinct object instances. Hash collisions cause the second factory'snewInstance()to never be called — it silently reuses the first factory's Validator. SinceInteger.equalscompares by value, two different factory instances with the same identityHashCode map to the same key. Fix: use the factory instance itself as the key (ConcurrentHashMap<ValidatorFactory, Validator>), which relies on reference equality viaObject.equals.
Missing test coverage: No tests cover the three new behaviors — factory returning null to abstain, per-session caching, or managed dependencies routed to validateManagedDependency.
This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.
Claude Code on behalf of gnodet
gnodet
left a comment
There was a problem hiding this comment.
The critical bugs from the previous review are properly fixed — the NOOP sentinel pattern and string-keyed Map are clean solutions. Two minor issues remain.
This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.
Claude Code on behalf of gnodet
…impl/DefaultRepositorySystemValidator.java Co-authored-by: Guillaume Nodet <gnodet@gmail.com>
|
|
||
| @Inject | ||
| public DefaultRepositorySystemValidator(List<ValidatorFactory> validatorFactories) { | ||
| public DefaultRepositorySystemValidator(Map<String, ValidatorFactory> validatorFactories) { |
There was a problem hiding this comment.
public API defined here https://maven.apache.org/resolver/api-compatibility.html
So is not. Moreover, client code should use DI, and DI protects from changes like these, and they should inject managed beans, not manually (re)construct them.
| for (RemoteRepository repository : request.getRepositories()) { | ||
| for (Map.Entry<String, ValidatorFactory> entry : validatorFactories.entrySet()) { | ||
| Validator validator = newInstance(session, entry.getKey(), entry.getValue()); | ||
| if (validator != ValidatorFactory.NOOP) { |
There was a problem hiding this comment.
do we really need all these if (validator != ValidatorFactory.NOOP) checks? If it's Validator.NOOP then the validator doesn't throw any exceptions, nothing is added to the exceptions list, and we get exactly the same result. It feels cleaner to just treat Validator.NOOP like every other one.
There was a problem hiding this comment.
Agreed, this is leftover from null being allowed.
There was a problem hiding this comment.
Well, am leaving it as it is not hurting. OTOH, with today's app frameworks we at least avoid busy-looping (and calling no-op method) over thousands of managed deps...
| public class DefaultRepositorySystemValidator implements RepositorySystemValidator { | ||
| private final List<ValidatorFactory> validatorFactories; | ||
| private static final Object SESSION_VALIDATORS = Keys.of(DefaultRepositorySystemValidator.class, "validators"); | ||
| private final Map<String, ValidatorFactory> validatorFactories; |
There was a problem hiding this comment.
I'm sure there's a reason but I don't immediately see what the String key is here or why we need it. A comment might be helpful.
There was a problem hiding this comment.
See "What is Sisu" here https://eclipse.dev/sisu/org.eclipse.sisu.inject/
This is most common technique to have injected "variations" (or qualified beans) for same (component) contract.
gnodet
left a comment
There was a problem hiding this comment.
All previously identified issues are properly fixed. The NOOP sentinel pattern is a clean solution for the abstain mechanism, the Javadoc is now consistent, and null enforcement is uniform across both code paths. Test coverage is solid.
This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.
Claude Code on behalf of gnodet
Changes:
ValidatorFactoryto "abstain" and returnNOOPFixes: #2007