Skip to content

Enh: Allow validator factory to abstain#2008

Merged
cstamas merged 8 commits into
apache:masterfrom
cstamas:issue-2007
Jul 24, 2026
Merged

Enh: Allow validator factory to abstain#2008
cstamas merged 8 commits into
apache:masterfrom
cstamas:issue-2007

Conversation

@cstamas

@cstamas cstamas commented Jul 22, 2026

Copy link
Copy Markdown
Member

Changes:

  • allow ValidatorFactory to "abstain" and return NOOP
  • introduce alt execution path for managed dependencies (use cases varies)
  • cache validators per session

Fixes: #2007

Also, cache validators across single session.

Fixes: apache#2007
@cstamas cstamas added this to the 2.0.22 milestone Jul 22, 2026
@cstamas cstamas self-assigned this Jul 22, 2026
@cstamas cstamas added the enhancement New feature or request label Jul 22, 2026
@cstamas
cstamas requested a review from kwin July 22, 2026 13:12
@cstamas
cstamas marked this pull request as ready for review July 22, 2026 13:12
gnodet added a commit that referenced this pull request Jul 23, 2026
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 elharo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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}.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: begin with lower case the
no periods at end

per Oracle guidelines

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, was wrong: lowercase stands but not the "no period at end"?

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

  1. Null values not cached: ConcurrentHashMap.computeIfAbsent does 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.

  2. System.identityHashCode as map key: System.identityHashCode is not guaranteed unique across distinct object instances. Hash collisions cause the second factory's newInstance() to never be called — it silently reuses the first factory's Validator. Since Integer.equals compares 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 via Object.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

@cstamas
cstamas requested review from elharo and gnodet July 23, 2026 21:16

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

cstamas and others added 4 commits July 24, 2026 10:33
…impl/DefaultRepositorySystemValidator.java

Co-authored-by: Guillaume Nodet <gnodet@gmail.com>
@cstamas
cstamas requested a review from gnodet July 24, 2026 09:05

@Inject
public DefaultRepositorySystemValidator(List<ValidatorFactory> validatorFactories) {
public DefaultRepositorySystemValidator(Map<String, ValidatorFactory> validatorFactories) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public API change

@cstamas cstamas Jul 24, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, this is leftover from null being allowed.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@cstamas cstamas Jul 24, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@cstamas
cstamas merged commit f164aa9 into apache:master Jul 24, 2026
20 checks passed
@cstamas
cstamas deleted the issue-2007 branch July 24, 2026 12:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ValidatorFactory should be allowed to return null

3 participants