Skip to content

Node API: expose dependency resolution metadata (winner, pre-managed version/scope, original scope) #13151

Description

@gnodet

Summary

The Maven 4 Node API (org.apache.maven.api.Node) does not expose dependency resolution metadata that Aether stores in DependencyNode.getData() and via DependencyManagerUtils. This information is essential for tools that display verbose dependency trees (conflict indicators, version management overrides, scope changes).

Missing accessors on Node

The following four pieces of metadata are currently inaccessible through the public Maven 4 API:

Information Aether source Used for
Winner node (conflict loser's winning sibling) ConflictResolver.NODE_DATA_WINNER "omitted for conflict" display
Pre-managed version (original version before DM override) DependencyManagerUtils.getPremanagedVersion() "4.4.3 ⚠ 3.25.1" conflict indicator
Pre-managed scope (original scope before DM override) DependencyManagerUtils.getPremanagedScope() Scope management indicator
Original scope (scope before conflict resolution) ConflictResolver.NODE_DATA_ORIGINAL_SCOPE Scope conflict display

Current workaround

Tools like pilot must use reflection to call AbstractNode.getDependencyNode() (package-private) to reach the Aether DependencyNode and access this data:

// TODO: remove reflection once this issue is resolved
Method m = node.getClass().getDeclaredMethod("getDependencyNode");
m.setAccessible(true);
DependencyNode aetherNode = (DependencyNode) m.invoke(node);
String premanagedVersion = DependencyManagerUtils.getPremanagedVersion(aetherNode);
DependencyNode winner = (DependencyNode) aetherNode.getData().get(ConflictResolver.NODE_DATA_WINNER);

Note: DefaultNode.asString() already reads all this data internally (it's how mvn dependency:tree -Dverbose works), so the underlying plumbing is solid — it just isn't exposed through the API.

Proposed API additions on Node

Following the noun-style accessor convention for immutable value types established in #13035 / #13036:

/**
 * Returns the node that won the dependency conflict resolution for this node,
 * or empty if this node was not omitted due to a conflict.
 * Only available when the resolver is run in verbose mode.
 */
Optional<Node> winner();

/**
 * Returns the version of this dependency as originally declared (before
 * dependency management overrode it), or empty if the version was not managed.
 * Only available when the resolver is run in verbose mode.
 */
Optional<String> premanagedVersion();

/**
 * Returns the scope of this dependency as originally declared (before
 * dependency management overrode it), or empty if the scope was not managed.
 * Only available when the resolver is run in verbose mode.
 */
Optional<String> premanagedScope();

/**
 * Returns the scope of this dependency before conflict resolution,
 * or empty if the scope was not changed during conflict resolution.
 * Only available when the resolver is run in verbose mode.
 */
Optional<String> originalScope();

Note: Node is already annotated @Immutable, so these follow the same noun-style accessor pattern being applied to Artifact, Dependency, Project, etc. in #13036. No getX() compat bridge is needed since these are new methods with no prior getX() form.

Also needed: API constant for verbose mode

To populate the above metadata, the resolver session must be configured with verbose mode. Currently this requires importing DependencyManagerUtils.CONFIG_PROP_VERBOSE from maven-resolver-util (an Aether internal), which leaks an implementation dependency into client code:

// Requires import of org.eclipse.aether.util.graph.manager.DependencyManagerUtils — an Aether internal
mutableSession.setConfigProperty(DependencyManagerUtils.CONFIG_PROP_VERBOSE, Boolean.TRUE);

DefaultDependencyResolver already handles this internally when DependencyResolverRequest.verbose() is set — but that path performs full resolution. Tools that call RepositorySystem.collectDependencies() directly (e.g. to collect without resolving artifacts) have no Maven API way to request verbose mode.

Proposed addition: expose the config property key as a constant on a Maven API type (e.g. DependencyResolverRequest or a new ResolverConfig class in maven-api-core), so tools don't need to depend on maven-resolver-util just to enable verbose mode.

Affected components

  • maven-api-core: org.apache.maven.api.Node (new accessors)
  • maven-impl: org.apache.maven.impl.AbstractNode / DefaultNode (implementations)
  • maven-api-core: new constant for the verbose config property key (optional, separate)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions