You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by sl...@apache.org on 2026/10/05 21:22:57 UTC

(maven-project-info-reports-plugin) branch master updated (4e84f50 -> b4c2b30)

This is an automated email from the ASF dual-hosted git repository.

slachiewicz pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/maven-project-info-reports-plugin.git


    from 4e84f50  Simplify GH PR template boilerplate
     new 73ded7e  [MPIR-458] Replace maven-dependency-tree with Maven Resolver
     new b4c2b30  Pass the reactor project straight to the convergence tree builder

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 pom.xml                                            |  10 +-
 .../report/projectinfo/DependenciesReport.java     |  28 ++-
 .../projectinfo/DependencyConvergenceReport.java   |  99 ++-------
 .../projectinfo/dependencies/Dependencies.java     |   1 -
 .../projectinfo/dependencies/DependencyNode.java   | 110 ++++++++++
 ...nfiguration.java => DependencyNodeVisitor.java} |  26 +--
 .../dependencies/DependencyTreeBuilder.java        | 224 +++++++++++++++++++++
 .../dependencies/DependencyTreeException.java}     |  17 +-
 .../dependencies/DependencyVersionMap.java         |   2 -
 .../SinkSerializingDependencyNodeVisitor.java      |   2 -
 .../renderer/DependenciesRenderer.java             |   2 +-
 11 files changed, 398 insertions(+), 123 deletions(-)
 create mode 100644 src/main/java/org/apache/maven/report/projectinfo/dependencies/DependencyNode.java
 copy src/main/java/org/apache/maven/report/projectinfo/dependencies/{DependenciesReportConfiguration.java => DependencyNodeVisitor.java} (64%)
 create mode 100644 src/main/java/org/apache/maven/report/projectinfo/dependencies/DependencyTreeBuilder.java
 copy src/{it/multi-module/dep/src/main/java/org/apache/maven/its/dep/Dep.java => main/java/org/apache/maven/report/projectinfo/dependencies/DependencyTreeException.java} (68%)


(maven-project-info-reports-plugin) 01/02: [MPIR-458] Replace maven-dependency-tree with Maven Resolver

Posted by sl...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

slachiewicz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-project-info-reports-plugin.git

commit 73ded7ecbd63652ef3ce0779791a76df080420f5
Author: Sylwester Lachiewicz <sl...@apache.org>
AuthorDate: Wed Sep 30 18:29:32 2026 +0200

    [MPIR-458] Replace maven-dependency-tree with Maven Resolver
    
    The reports only need the artifact tree, so it is built from ProjectDependenciesResolver and RepositorySystem
    directly; the verbose collection must keep omitted nodes, which convergence relies on.
---
 pom.xml                                            |  10 +-
 .../report/projectinfo/DependenciesReport.java     |  28 ++-
 .../projectinfo/DependencyConvergenceReport.java   |  72 ++-----
 .../projectinfo/dependencies/Dependencies.java     |   1 -
 .../projectinfo/dependencies/DependencyNode.java   | 110 ++++++++++
 .../dependencies/DependencyNodeVisitor.java        |  42 ++++
 .../dependencies/DependencyTreeBuilder.java        | 224 +++++++++++++++++++++
 .../dependencies/DependencyTreeException.java      |  32 +++
 .../dependencies/DependencyVersionMap.java         |   2 -
 .../SinkSerializingDependencyNodeVisitor.java      |   2 -
 .../renderer/DependenciesRenderer.java             |   2 +-
 11 files changed, 440 insertions(+), 85 deletions(-)

diff --git a/pom.xml b/pom.xml
index bd834ff..d36e200 100644
--- a/pom.xml
+++ b/pom.xml
@@ -178,17 +178,17 @@ under the License.
       <version>${mavenVersion}</version>
       <scope>provided</scope>
     </dependency>
-    <dependency>
-      <groupId>org.apache.maven.shared</groupId>
-      <artifactId>maven-dependency-tree</artifactId>
-      <version>3.3.0</version>
-    </dependency>
     <dependency>
       <groupId>org.apache.maven.resolver</groupId>
       <artifactId>maven-resolver-api</artifactId>
       <version>${resolverVersion}</version>
       <scope>provided</scope>
     </dependency>
+    <dependency>
+      <groupId>org.apache.maven.resolver</groupId>
+      <artifactId>maven-resolver-util</artifactId>
+      <version>${resolverVersion}</version>
+    </dependency>
 
     <dependency>
       <groupId>org.apache.maven.plugin-tools</groupId>
diff --git a/src/main/java/org/apache/maven/report/projectinfo/DependenciesReport.java b/src/main/java/org/apache/maven/report/projectinfo/DependenciesReport.java
index 61c571c..13be4de 100644
--- a/src/main/java/org/apache/maven/report/projectinfo/DependenciesReport.java
+++ b/src/main/java/org/apache/maven/report/projectinfo/DependenciesReport.java
@@ -19,7 +19,6 @@
 package org.apache.maven.report.projectinfo;
 
 import javax.inject.Inject;
-import javax.inject.Named;
 
 import java.io.BufferedReader;
 import java.io.File;
@@ -38,17 +37,16 @@ import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
 import org.apache.maven.plugins.annotations.Mojo;
 import org.apache.maven.plugins.annotations.Parameter;
 import org.apache.maven.plugins.annotations.ResolutionScope;
-import org.apache.maven.project.DefaultProjectBuildingRequest;
 import org.apache.maven.project.ProjectBuilder;
-import org.apache.maven.project.ProjectBuildingRequest;
+import org.apache.maven.project.ProjectDependenciesResolver;
 import org.apache.maven.report.projectinfo.dependencies.Dependencies;
 import org.apache.maven.report.projectinfo.dependencies.DependenciesReportConfiguration;
+import org.apache.maven.report.projectinfo.dependencies.DependencyNode;
+import org.apache.maven.report.projectinfo.dependencies.DependencyTreeBuilder;
+import org.apache.maven.report.projectinfo.dependencies.DependencyTreeException;
 import org.apache.maven.report.projectinfo.dependencies.RepositoryUtils;
 import org.apache.maven.report.projectinfo.dependencies.renderer.DependenciesRenderer;
 import org.apache.maven.reporting.MavenReportException;
-import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder;
-import org.apache.maven.shared.dependency.graph.DependencyGraphBuilderException;
-import org.apache.maven.shared.dependency.graph.DependencyNode;
 import org.apache.maven.shared.jar.classes.JarClassesAnalysis;
 import org.codehaus.plexus.i18n.I18N;
 import org.codehaus.plexus.util.IOUtil;
@@ -86,11 +84,11 @@ public class DependenciesReport extends AbstractProjectInfoReport {
     // ----------------------------------------------------------------------
 
     /**
-     * Dependency graph builder component.
+     * Project dependencies resolver component.
      *
-     * @since 2.5
+     * @since 3.9.1
      */
-    private final DependencyGraphBuilder dependencyGraphBuilder;
+    private final ProjectDependenciesResolver dependenciesResolver;
 
     /**
      * Jar classes analyzer component.
@@ -106,11 +104,11 @@ public class DependenciesReport extends AbstractProjectInfoReport {
             RepositorySystem repositorySystem,
             I18N i18n,
             ProjectBuilder projectBuilder,
-            @Named("default") DependencyGraphBuilder dependencyGraphBuilder,
+            ProjectDependenciesResolver dependenciesResolver,
             JarClassesAnalysis classesAnalyzer,
             RepositoryUtils repoUtils) {
         super(repositorySystem, i18n, projectBuilder);
-        this.dependencyGraphBuilder = dependencyGraphBuilder;
+        this.dependenciesResolver = dependenciesResolver;
         this.classesAnalyzer = classesAnalyzer;
         this.repoUtils = repoUtils;
     }
@@ -191,11 +189,9 @@ public class DependenciesReport extends AbstractProjectInfoReport {
     private DependencyNode resolveProject() {
         try {
             ArtifactFilter artifactFilter = new ScopeArtifactFilter(Artifact.SCOPE_TEST);
-            ProjectBuildingRequest buildingRequest =
-                    new DefaultProjectBuildingRequest(getSession().getProjectBuildingRequest());
-            buildingRequest.setProject(project);
-            return dependencyGraphBuilder.buildDependencyGraph(buildingRequest, artifactFilter);
-        } catch (DependencyGraphBuilderException e) {
+            return DependencyTreeBuilder.resolve(
+                    dependenciesResolver, project, getSession().getRepositorySession(), artifactFilter);
+        } catch (DependencyTreeException e) {
             getLog().error("Unable to build dependency tree.", e);
             return null;
         }
diff --git a/src/main/java/org/apache/maven/report/projectinfo/DependencyConvergenceReport.java b/src/main/java/org/apache/maven/report/projectinfo/DependencyConvergenceReport.java
index d81e498..5811553 100644
--- a/src/main/java/org/apache/maven/report/projectinfo/DependencyConvergenceReport.java
+++ b/src/main/java/org/apache/maven/report/projectinfo/DependencyConvergenceReport.java
@@ -44,21 +44,14 @@ import org.apache.maven.project.DefaultProjectBuildingRequest;
 import org.apache.maven.project.MavenProject;
 import org.apache.maven.project.ProjectBuilder;
 import org.apache.maven.project.ProjectBuildingRequest;
+import org.apache.maven.report.projectinfo.dependencies.DependencyNode;
+import org.apache.maven.report.projectinfo.dependencies.DependencyNodeVisitor;
+import org.apache.maven.report.projectinfo.dependencies.DependencyTreeBuilder;
+import org.apache.maven.report.projectinfo.dependencies.DependencyTreeException;
 import org.apache.maven.report.projectinfo.dependencies.DependencyVersionMap;
 import org.apache.maven.report.projectinfo.dependencies.SinkSerializingDependencyNodeVisitor;
 import org.apache.maven.reporting.MavenReportException;
 import org.apache.maven.shared.artifact.filter.StrictPatternIncludesArtifactFilter;
-import org.apache.maven.shared.dependency.graph.DependencyCollectorBuilder;
-import org.apache.maven.shared.dependency.graph.DependencyCollectorBuilderException;
-import org.apache.maven.shared.dependency.graph.DependencyNode;
-import org.apache.maven.shared.dependency.graph.filter.AncestorOrSelfDependencyNodeFilter;
-import org.apache.maven.shared.dependency.graph.filter.AndDependencyNodeFilter;
-import org.apache.maven.shared.dependency.graph.filter.ArtifactDependencyNodeFilter;
-import org.apache.maven.shared.dependency.graph.filter.DependencyNodeFilter;
-import org.apache.maven.shared.dependency.graph.traversal.BuildingDependencyNodeVisitor;
-import org.apache.maven.shared.dependency.graph.traversal.CollectingDependencyNodeVisitor;
-import org.apache.maven.shared.dependency.graph.traversal.DependencyNodeVisitor;
-import org.apache.maven.shared.dependency.graph.traversal.FilteringDependencyNodeVisitor;
 import org.codehaus.plexus.i18n.I18N;
 import org.eclipse.aether.RepositorySystem;
 
@@ -92,19 +85,9 @@ public class DependencyConvergenceReport extends AbstractProjectInfoReport {
     // Mojo parameters
     // ----------------------------------------------------------------------
 
-    /**
-     * Raw dependency collector builder, will use it to build dependency tree.
-     */
-    private final DependencyCollectorBuilder dependencyCollectorBuilder;
-
     @Inject
-    protected DependencyConvergenceReport(
-            RepositorySystem repositorySystem,
-            I18N i18n,
-            ProjectBuilder projectBuilder,
-            DependencyCollectorBuilder dependencyCollectorBuilder) {
+    protected DependencyConvergenceReport(RepositorySystem repositorySystem, I18N i18n, ProjectBuilder projectBuilder) {
         super(repositorySystem, i18n, projectBuilder);
-        this.dependencyCollectorBuilder = dependencyCollectorBuilder;
     }
 
     // ----------------------------------------------------------------------
@@ -405,44 +388,16 @@ public class DependencyConvergenceReport extends AbstractProjectInfoReport {
      * @return the serialized dependency tree
      */
     private void serializeDependencyTree(DependencyNode rootNode, String key, Sink sink) {
-        DependencyNodeVisitor visitor = getSerializingDependencyNodeVisitor(sink);
-
-        visitor = new BuildingDependencyNodeVisitor(visitor);
+        List<String> patterns = Arrays.asList(key.split(","));
 
-        DependencyNodeFilter nodeFilter = createDependencyNodeFilter(key);
+        getLog().debug("+ Filtering dependency tree by artifact include patterns: " + patterns);
 
-        if (nodeFilter != null) {
-            CollectingDependencyNodeVisitor collectingVisitor = new CollectingDependencyNodeVisitor();
-            DependencyNodeVisitor firstPassVisitor = new FilteringDependencyNodeVisitor(collectingVisitor, nodeFilter);
-            rootNode.accept(firstPassVisitor);
+        // keep only the nodes matching the key and their ancestors
+        DependencyNode tree = rootNode.prune(new StrictPatternIncludesArtifactFilter(patterns));
 
-            DependencyNodeFilter secondPassFilter =
-                    new AncestorOrSelfDependencyNodeFilter(collectingVisitor.getNodes());
-            visitor = new FilteringDependencyNodeVisitor(visitor, secondPassFilter);
+        if (tree != null) {
+            tree.accept(getSerializingDependencyNodeVisitor(sink));
         }
-
-        rootNode.accept(visitor);
-    }
-
-    /**
-     * Gets the dependency node filter to use when serializing the dependency graph.
-     *
-     * @return the dependency node filter, or <code>null</code> if none required
-     */
-    private DependencyNodeFilter createDependencyNodeFilter(String includes) {
-        List<DependencyNodeFilter> filters = new ArrayList<>();
-
-        // filter includes
-        if (includes != null) {
-            List<String> patterns = Arrays.asList(includes.split(","));
-
-            getLog().debug("+ Filtering dependency tree by artifact include patterns: " + patterns);
-
-            ArtifactFilter artifactFilter = new StrictPatternIncludesArtifactFilter(patterns);
-            filters.add(new ArtifactDependencyNodeFilter(artifactFilter));
-        }
-
-        return filters.isEmpty() ? null : new AndDependencyNodeFilter(filters);
     }
 
     /**
@@ -851,8 +806,9 @@ public class DependencyConvergenceReport extends AbstractProjectInfoReport {
      */
     private DependencyNode getNode(ProjectBuildingRequest buildingRequest) throws MavenReportException {
         try {
-            return dependencyCollectorBuilder.collectDependencyGraph(buildingRequest, filter);
-        } catch (DependencyCollectorBuilderException e) {
+            return DependencyTreeBuilder.collectVerbose(
+                    repositorySystem, buildingRequest.getProject(), buildingRequest.getRepositorySession(), filter);
+        } catch (DependencyTreeException e) {
             throw new MavenReportException("Could not build dependency tree: " + e.getMessage(), e);
         }
     }
diff --git a/src/main/java/org/apache/maven/report/projectinfo/dependencies/Dependencies.java b/src/main/java/org/apache/maven/report/projectinfo/dependencies/Dependencies.java
index 150afc1..29a913a 100644
--- a/src/main/java/org/apache/maven/report/projectinfo/dependencies/Dependencies.java
+++ b/src/main/java/org/apache/maven/report/projectinfo/dependencies/Dependencies.java
@@ -28,7 +28,6 @@ import java.util.jar.JarEntry;
 
 import org.apache.maven.artifact.Artifact;
 import org.apache.maven.project.MavenProject;
-import org.apache.maven.shared.dependency.graph.DependencyNode;
 import org.apache.maven.shared.jar.JarAnalyzer;
 import org.apache.maven.shared.jar.JarData;
 import org.apache.maven.shared.jar.classes.JarClasses;
diff --git a/src/main/java/org/apache/maven/report/projectinfo/dependencies/DependencyNode.java b/src/main/java/org/apache/maven/report/projectinfo/dependencies/DependencyNode.java
new file mode 100644
index 0000000..0cfd7a8
--- /dev/null
+++ b/src/main/java/org/apache/maven/report/projectinfo/dependencies/DependencyNode.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.report.projectinfo.dependencies;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
+
+/**
+ * A node of the dependency tree shown by the reports: an artifact and the nodes it brings in.
+ *
+ * @since 3.9.1
+ */
+public class DependencyNode {
+    private final DependencyNode parent;
+
+    private final Artifact artifact;
+
+    private final boolean optional;
+
+    private List<DependencyNode> children = Collections.emptyList();
+
+    DependencyNode(DependencyNode parent, Artifact artifact, boolean optional) {
+        this.parent = parent;
+        this.artifact = artifact;
+        this.optional = optional;
+    }
+
+    public Artifact getArtifact() {
+        return artifact;
+    }
+
+    public DependencyNode getParent() {
+        return parent;
+    }
+
+    public List<DependencyNode> getChildren() {
+        return children;
+    }
+
+    void setChildren(List<DependencyNode> children) {
+        this.children = Collections.unmodifiableList(children);
+    }
+
+    public String toNodeString() {
+        return artifact + (optional ? " (optional)" : "");
+    }
+
+    /**
+     * Visits this node and its descendants depth-first.
+     *
+     * @param visitor the visitor
+     * @return the result of {@link DependencyNodeVisitor#endVisit(DependencyNode)} for this node
+     */
+    public boolean accept(DependencyNodeVisitor visitor) {
+        if (visitor.visit(this)) {
+            for (DependencyNode child : children) {
+                if (!child.accept(visitor)) {
+                    break;
+                }
+            }
+        }
+        return visitor.endVisit(this);
+    }
+
+    /**
+     * Copies this tree keeping only the nodes that match the filter and their ancestors.
+     *
+     * @param filter the filter
+     * @return the pruned copy, or <code>null</code> if no node matches
+     */
+    public DependencyNode prune(ArtifactFilter filter) {
+        return prune(null, filter);
+    }
+
+    private DependencyNode prune(DependencyNode newParent, ArtifactFilter filter) {
+        DependencyNode copy = new DependencyNode(newParent, artifact, optional);
+        List<DependencyNode> copiedChildren = new ArrayList<>();
+        for (DependencyNode child : children) {
+            DependencyNode copiedChild = child.prune(copy, filter);
+            if (copiedChild != null) {
+                copiedChildren.add(copiedChild);
+            }
+        }
+        if (copiedChildren.isEmpty() && !filter.include(artifact)) {
+            return null;
+        }
+        copy.setChildren(copiedChildren);
+        return copy;
+    }
+}
diff --git a/src/main/java/org/apache/maven/report/projectinfo/dependencies/DependencyNodeVisitor.java b/src/main/java/org/apache/maven/report/projectinfo/dependencies/DependencyNodeVisitor.java
new file mode 100644
index 0000000..257f225
--- /dev/null
+++ b/src/main/java/org/apache/maven/report/projectinfo/dependencies/DependencyNodeVisitor.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.report.projectinfo.dependencies;
+
+/**
+ * Visitor of a {@link DependencyNode} tree.
+ *
+ * @since 3.9.1
+ */
+public interface DependencyNodeVisitor {
+    /**
+     * Starts visiting a node.
+     *
+     * @param node the node
+     * @return <code>true</code> to visit the children of the node
+     */
+    boolean visit(DependencyNode node);
+
+    /**
+     * Ends visiting a node.
+     *
+     * @param node the node
+     * @return <code>true</code> to visit the following siblings of the node
+     */
+    boolean endVisit(DependencyNode node);
+}
diff --git a/src/main/java/org/apache/maven/report/projectinfo/dependencies/DependencyTreeBuilder.java b/src/main/java/org/apache/maven/report/projectinfo/dependencies/DependencyTreeBuilder.java
new file mode 100644
index 0000000..8fdd3e3
--- /dev/null
+++ b/src/main/java/org/apache/maven/report/projectinfo/dependencies/DependencyTreeBuilder.java
@@ -0,0 +1,224 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.report.projectinfo.dependencies;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.maven.RepositoryUtils;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.project.DefaultDependencyResolutionRequest;
+import org.apache.maven.project.DependencyResolutionException;
+import org.apache.maven.project.DependencyResolutionRequest;
+import org.apache.maven.project.DependencyResolutionResult;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.project.ProjectDependenciesResolver;
+import org.eclipse.aether.DefaultRepositorySystemSession;
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.ArtifactTypeRegistry;
+import org.eclipse.aether.collection.CollectRequest;
+import org.eclipse.aether.collection.DependencyCollectionException;
+import org.eclipse.aether.collection.DependencySelector;
+import org.eclipse.aether.graph.DependencyFilter;
+import org.eclipse.aether.util.artifact.JavaScopes;
+import org.eclipse.aether.util.graph.selector.AndDependencySelector;
+import org.eclipse.aether.util.graph.selector.ExclusionDependencySelector;
+import org.eclipse.aether.util.graph.selector.OptionalDependencySelector;
+import org.eclipse.aether.util.graph.transformer.ConflictResolver;
+import org.eclipse.aether.util.graph.transformer.JavaScopeDeriver;
+import org.eclipse.aether.util.graph.transformer.JavaScopeSelector;
+import org.eclipse.aether.util.graph.transformer.NearestVersionSelector;
+import org.eclipse.aether.util.graph.transformer.SimpleOptionalitySelector;
+
+/**
+ * Builds the {@link DependencyNode} trees of a project directly from Maven Resolver.
+ *
+ * @since 3.9.1
+ */
+public final class DependencyTreeBuilder {
+    private DependencyTreeBuilder() {}
+
+    /**
+     * Builds the tree Maven resolves for the project, without downloading any artifact but the POMs.
+     *
+     * @param resolver the project dependencies resolver
+     * @param project the project
+     * @param session the repository session
+     * @param filter filters the artifacts of the nodes below the root, may be <code>null</code>
+     * @return the root node
+     * @throws DependencyTreeException if the dependencies cannot be resolved
+     */
+    public static DependencyNode resolve(
+            ProjectDependenciesResolver resolver,
+            MavenProject project,
+            RepositorySystemSession session,
+            ArtifactFilter filter)
+            throws DependencyTreeException {
+        DependencyResolutionRequest request = new DefaultDependencyResolutionRequest();
+        request.setMavenProject(project);
+        request.setRepositorySession(session);
+        // only download the poms, not the artifacts
+        DependencyFilter noArtifacts = (node, parents) -> false;
+        request.setResolutionFilter(noArtifacts);
+
+        DependencyResolutionResult result;
+        try {
+            result = resolver.resolve(request);
+        } catch (DependencyResolutionException e) {
+            throw new DependencyTreeException(
+                    "Could not resolve following dependencies: " + e.getResult().getUnresolvedDependencies(), e);
+        }
+
+        return toNode(null, result.getDependencyGraph(), project.getArtifact(), filter);
+    }
+
+    /**
+     * Collects the tree of the project including the nodes omitted for a conflict or a duplicate, which
+     * {@link #resolve} leaves out.
+     *
+     * @param repositorySystem the repository system
+     * @param project the project
+     * @param session the repository session
+     * @param filter filters the artifacts of the nodes below the root, may be <code>null</code>
+     * @return the root node
+     * @throws DependencyTreeException if the dependencies cannot be collected
+     */
+    public static DependencyNode collectVerbose(
+            RepositorySystem repositorySystem,
+            MavenProject project,
+            RepositorySystemSession session,
+            ArtifactFilter filter)
+            throws DependencyTreeException {
+        DefaultRepositorySystemSession verboseSession = new DefaultRepositorySystemSession(session);
+        verboseSession.setConfigProperty(ConflictResolver.CONFIG_PROP_VERBOSE, true);
+        verboseSession.setDependencyGraphTransformer(new ConflictResolver(
+                new NearestVersionSelector(),
+                new JavaScopeSelector(),
+                new SimpleOptionalitySelector(),
+                new JavaScopeDeriver()));
+        verboseSession.setDependencySelector(new AndDependencySelector(
+                new DirectScopeDependencySelector(JavaScopes.TEST),
+                new DirectScopeDependencySelector(JavaScopes.PROVIDED),
+                new OptionalDependencySelector(),
+                new ExclusionDependencySelector()));
+
+        Artifact projectArtifact = project.getArtifact();
+
+        CollectRequest collectRequest = new CollectRequest();
+        collectRequest.setRootArtifact(RepositoryUtils.toArtifact(projectArtifact));
+        collectRequest.setRepositories(RepositoryUtils.toRepos(project.getRemoteArtifactRepositories()));
+
+        ArtifactTypeRegistry stereotypes = verboseSession.getArtifactTypeRegistry();
+        for (Dependency dependency : project.getDependencies()) {
+            collectRequest.addDependency(RepositoryUtils.toDependency(dependency, stereotypes));
+        }
+        if (project.getDependencyManagement() != null) {
+            for (Dependency dependency : project.getDependencyManagement().getDependencies()) {
+                collectRequest.addManagedDependency(RepositoryUtils.toDependency(dependency, stereotypes));
+            }
+        }
+
+        try {
+            return toNode(
+                    null,
+                    repositorySystem
+                            .collectDependencies(verboseSession, collectRequest)
+                            .getRoot(),
+                    projectArtifact,
+                    filter);
+        } catch (DependencyCollectionException e) {
+            throw new DependencyTreeException("Could not collect dependencies: " + e.getResult(), e);
+        }
+    }
+
+    private static DependencyNode toNode(
+            DependencyNode parent,
+            org.eclipse.aether.graph.DependencyNode node,
+            Artifact artifact,
+            ArtifactFilter filter) {
+        DependencyNode current = new DependencyNode(parent, artifact, artifact.isOptional());
+
+        List<DependencyNode> children = new ArrayList<>(node.getChildren().size());
+        for (org.eclipse.aether.graph.DependencyNode child : node.getChildren()) {
+            Artifact childArtifact = toArtifact(child.getDependency());
+            if (filter == null || filter.include(childArtifact)) {
+                children.add(toNode(current, child, childArtifact, filter));
+            }
+        }
+        current.setChildren(children);
+
+        return current;
+    }
+
+    private static Artifact toArtifact(org.eclipse.aether.graph.Dependency dependency) {
+        Artifact artifact = RepositoryUtils.toArtifact(dependency.getArtifact());
+        artifact.setScope(dependency.getScope());
+        artifact.setOptional(dependency.isOptional());
+        return artifact;
+    }
+
+    /**
+     * Excludes a scope from the direct dependencies' own dependencies (depth 2 and deeper) only.
+     */
+    private static final class DirectScopeDependencySelector implements DependencySelector {
+        private final String scope;
+
+        private final int depth;
+
+        DirectScopeDependencySelector(String scope) {
+            this(scope, 0);
+        }
+
+        private DirectScopeDependencySelector(String scope, int depth) {
+            this.scope = scope;
+            this.depth = depth;
+        }
+
+        @Override
+        public boolean selectDependency(org.eclipse.aether.graph.Dependency dependency) {
+            return depth < 2 || !scope.equals(dependency.getScope());
+        }
+
+        @Override
+        public DependencySelector deriveChildSelector(
+                org.eclipse.aether.collection.DependencyCollectionContext context) {
+            return depth >= 2 ? this : new DirectScopeDependencySelector(scope, depth + 1);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj == null || getClass() != obj.getClass()) {
+                return false;
+            }
+            DirectScopeDependencySelector other = (DirectScopeDependencySelector) obj;
+            return depth == other.depth && scope.equals(other.scope);
+        }
+
+        @Override
+        public int hashCode() {
+            return 31 * (31 + depth) + scope.hashCode();
+        }
+    }
+}
diff --git a/src/main/java/org/apache/maven/report/projectinfo/dependencies/DependencyTreeException.java b/src/main/java/org/apache/maven/report/projectinfo/dependencies/DependencyTreeException.java
new file mode 100644
index 0000000..97c4210
--- /dev/null
+++ b/src/main/java/org/apache/maven/report/projectinfo/dependencies/DependencyTreeException.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.report.projectinfo.dependencies;
+
+/**
+ * Signals that the dependency tree of a project could not be built.
+ *
+ * @since 3.9.1
+ */
+public class DependencyTreeException extends Exception {
+    private static final long serialVersionUID = 1L;
+
+    public DependencyTreeException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/src/main/java/org/apache/maven/report/projectinfo/dependencies/DependencyVersionMap.java b/src/main/java/org/apache/maven/report/projectinfo/dependencies/DependencyVersionMap.java
index 3cfe705..f463332 100644
--- a/src/main/java/org/apache/maven/report/projectinfo/dependencies/DependencyVersionMap.java
+++ b/src/main/java/org/apache/maven/report/projectinfo/dependencies/DependencyVersionMap.java
@@ -24,8 +24,6 @@ import java.util.List;
 import java.util.Map;
 
 import org.apache.maven.artifact.Artifact;
-import org.apache.maven.shared.dependency.graph.DependencyNode;
-import org.apache.maven.shared.dependency.graph.traversal.DependencyNodeVisitor;
 
 /**
  * @author Simon Wang
diff --git a/src/main/java/org/apache/maven/report/projectinfo/dependencies/SinkSerializingDependencyNodeVisitor.java b/src/main/java/org/apache/maven/report/projectinfo/dependencies/SinkSerializingDependencyNodeVisitor.java
index 6d6c879..3b9532e 100644
--- a/src/main/java/org/apache/maven/report/projectinfo/dependencies/SinkSerializingDependencyNodeVisitor.java
+++ b/src/main/java/org/apache/maven/report/projectinfo/dependencies/SinkSerializingDependencyNodeVisitor.java
@@ -21,8 +21,6 @@ package org.apache.maven.report.projectinfo.dependencies;
 import java.util.List;
 
 import org.apache.maven.doxia.sink.Sink;
-import org.apache.maven.shared.dependency.graph.DependencyNode;
-import org.apache.maven.shared.dependency.graph.traversal.DependencyNodeVisitor;
 
 /**
  * A dependency node visitor that serializes visited nodes to a sink writer. It's used to serialize tree in project
diff --git a/src/main/java/org/apache/maven/report/projectinfo/dependencies/renderer/DependenciesRenderer.java b/src/main/java/org/apache/maven/report/projectinfo/dependencies/renderer/DependenciesRenderer.java
index ce48a98..ec50f4d 100644
--- a/src/main/java/org/apache/maven/report/projectinfo/dependencies/renderer/DependenciesRenderer.java
+++ b/src/main/java/org/apache/maven/report/projectinfo/dependencies/renderer/DependenciesRenderer.java
@@ -55,9 +55,9 @@ import org.apache.maven.report.projectinfo.LicenseMapping;
 import org.apache.maven.report.projectinfo.ProjectInfoReportUtils;
 import org.apache.maven.report.projectinfo.dependencies.Dependencies;
 import org.apache.maven.report.projectinfo.dependencies.DependenciesReportConfiguration;
+import org.apache.maven.report.projectinfo.dependencies.DependencyNode;
 import org.apache.maven.report.projectinfo.dependencies.RepositoryUtils;
 import org.apache.maven.report.projectinfo.dependencies.renderer.DependenciesRenderer.TotalCell.SummaryTableRowOrder;
-import org.apache.maven.shared.dependency.graph.DependencyNode;
 import org.apache.maven.shared.jar.JarData;
 import org.apache.maven.shared.jar.classes.JarClasses;
 import org.apache.maven.shared.jar.classes.JarVersionedRuntime;


(maven-project-info-reports-plugin) 02/02: Pass the reactor project straight to the convergence tree builder

Posted by sl...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

slachiewicz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-project-info-reports-plugin.git

commit b4c2b303316fdd4d664155f31fb49f3442bb2a88
Author: Sylwester Lachiewicz <sl...@apache.org>
AuthorDate: Wed Sep 30 20:15:01 2026 +0200

    Pass the reactor project straight to the convergence tree builder
    
    The ProjectBuildingRequest only existed to carry the project into
    maven-dependency-tree; the new node model never has null children.
---
 .../projectinfo/DependencyConvergenceReport.java   | 29 ++++++----------------
 1 file changed, 8 insertions(+), 21 deletions(-)

diff --git a/src/main/java/org/apache/maven/report/projectinfo/DependencyConvergenceReport.java b/src/main/java/org/apache/maven/report/projectinfo/DependencyConvergenceReport.java
index 5811553..dd1c5fb 100644
--- a/src/main/java/org/apache/maven/report/projectinfo/DependencyConvergenceReport.java
+++ b/src/main/java/org/apache/maven/report/projectinfo/DependencyConvergenceReport.java
@@ -40,10 +40,8 @@ import org.apache.maven.doxia.sink.SinkEventAttributes;
 import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet;
 import org.apache.maven.model.Dependency;
 import org.apache.maven.plugins.annotations.Mojo;
-import org.apache.maven.project.DefaultProjectBuildingRequest;
 import org.apache.maven.project.MavenProject;
 import org.apache.maven.project.ProjectBuilder;
-import org.apache.maven.project.ProjectBuildingRequest;
 import org.apache.maven.report.projectinfo.dependencies.DependencyNode;
 import org.apache.maven.report.projectinfo.dependencies.DependencyNodeVisitor;
 import org.apache.maven.report.projectinfo.dependencies.DependencyTreeBuilder;
@@ -654,13 +652,8 @@ public class DependencyConvergenceReport extends AbstractProjectInfoReport {
         Map<String, List<ReverseDependencyLink>> conflictingDependencyMap = new TreeMap<>();
         Map<String, List<ReverseDependencyLink>> allDependencies = new TreeMap<>();
 
-        ProjectBuildingRequest buildingRequest =
-                new DefaultProjectBuildingRequest(getSession().getProjectBuildingRequest());
-
         for (MavenProject reactorProject : reactorProjects) {
-            buildingRequest.setProject(reactorProject);
-
-            DependencyNode node = getNode(buildingRequest);
+            DependencyNode node = getNode(reactorProject);
 
             this.projectMap.put(reactorProject, node);
 
@@ -800,14 +793,14 @@ public class DependencyConvergenceReport extends AbstractProjectInfoReport {
     /**
      * Get root node of dependency tree for a given project
      *
-     * @param buildingRequest
+     * @param project the project
      * @return root node of dependency tree
      * @throws MavenReportException
      */
-    private DependencyNode getNode(ProjectBuildingRequest buildingRequest) throws MavenReportException {
+    private DependencyNode getNode(MavenProject project) throws MavenReportException {
         try {
             return DependencyTreeBuilder.collectVerbose(
-                    repositorySystem, buildingRequest.getProject(), buildingRequest.getRepositorySession(), filter);
+                    repositorySystem, project, getSession().getRepositorySession(), filter);
         } catch (DependencyTreeException e) {
             throw new MavenReportException("Could not build dependency tree: " + e.getMessage(), e);
         }
@@ -820,16 +813,10 @@ public class DependencyConvergenceReport extends AbstractProjectInfoReport {
      * @return set of descendants artifacts.
      */
     private Set<Artifact> getAllDescendants(DependencyNode node) {
-        Set<Artifact> children = null;
-        if (node.getChildren() != null) {
-            children = new HashSet<>();
-            for (DependencyNode depNode : node.getChildren()) {
-                children.add(depNode.getArtifact());
-                Set<Artifact> subNodes = getAllDescendants(depNode);
-                if (subNodes != null) {
-                    children.addAll(subNodes);
-                }
-            }
+        Set<Artifact> children = new HashSet<>();
+        for (DependencyNode depNode : node.getChildren()) {
+            children.add(depNode.getArtifact());
+            children.addAll(getAllDescendants(depNode));
         }
         return children;
     }