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:58 UTC

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

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;