Skip to content

Enh: Allow validator factory to abstain#2008

Open
cstamas wants to merge 4 commits into
apache:masterfrom
cstamas:issue-2007
Open

Enh: Allow validator factory to abstain#2008
cstamas wants to merge 4 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

@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

Comment on lines 37 to +39
/**
* Creates a new validator for the session.
* Creates a new validator for the session, or {@link #NOOP} to abstain from validation. Factory is consulted
* once per session (if cache present in session) and returned instances (even {@code null}s) are cached and reused

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.

Stale Javadoc: "returned instances (even {@code null}s) are cached and reused" contradicts the @return on line 44 which says "never {@code null}", and the implementation wraps the call in requireNonNull. The "even nulls" text looks like a leftover from when null was the abstention signal.

Suggested change
/**
* Creates a new validator for the session.
* Creates a new validator for the session, or {@link #NOOP} to abstain from validation. Factory is consulted
* once per session (if cache present in session) and returned instances (even {@code null}s) are cached and reused
* Creates a new validator for the session, or {@link #NOOP} to abstain from validation. Factory is consulted
* once per session (if cache present in session) and returned instances are cached and reused
* across single session.

Comment on lines +76 to +78
session.getCache().computeIfAbsent(session, SESSION_VALIDATORS, ConcurrentHashMap::new))
.computeIfAbsent(name, key -> requireNonNull(factory.newInstance(session)));
} else {

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: the caching path guards with requireNonNull but this non-caching fallback does not. If a factory violates the contract and returns null here, the error surfaces as a confusing NPE downstream rather than a clear fail-fast.

Suggested change
session.getCache().computeIfAbsent(session, SESSION_VALIDATORS, ConcurrentHashMap::new))
.computeIfAbsent(name, key -> requireNonNull(factory.newInstance(session)));
} else {
} else {
return requireNonNull(factory.newInstance(session));
}

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