You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by cs...@apache.org on 2026/06/19 11:57:32 UTC

(maven) branch maven-3.10.x updated: Feat: Align CP ordering with Maven 4 (#12327)

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

cstamas pushed a commit to branch maven-3.10.x
in repository https://gitbox.apache.org/repos/asf/maven.git


The following commit(s) were added to refs/heads/maven-3.10.x by this push:
     new c64fbe4aa7 Feat: Align CP ordering with Maven 4 (#12327)
c64fbe4aa7 is described below

commit c64fbe4aa7e348a30bfecf8c5118d6d399627301
Author: Tamas Cservenak <ta...@cservenak.net>
AuthorDate: Fri Jun 19 13:57:26 2026 +0200

    Feat: Align CP ordering with Maven 4 (#12327)
    
    This PR aligns classpath ordering with Maven 4 (level order). All Maven 2/3 versions released so far has "pre-order" flattening (historical reasons, Maven 2 did it like that). Maven 3.10.0 switches to Resolver 2, where resolver exposes flattening configuration too, and defaults to safer "level order", preventing issues like described here https://arxiv.org/abs/2407.18760v3
    
    For resolver configuration, see configuration key `aether.system.dependencyVisitor` in
    https://maven.apache.org/resolver/configuration.html If _full Maven 3 behaviour needed_, user can simply set `-Daether.system.dependencyVisitor=preOrder` and it restores CP ordering to that used in Maven 3.9 and older.
    
    This PR changes:
    * remove scattered "flattening" happening in multiple spots in Maven, it is (and in fact was, but result was thrown away) fully delegating to Resolver.
    * simplify spots that was doing repeated deprecated pre-order flattening (which already happened in plugin dependencies resolver)
---
 .../plugin/internal/DefaultMavenPluginManager.java | 38 +++++++------------
 .../DefaultPluginDependenciesResolver.java         | 43 ++++++++++++++++++----
 .../internal/PluginDependenciesResolver.java       | 41 +++++++++++++++++++++
 .../internal/BootstrapCoreExtensionManager.java    | 22 +++++------
 4 files changed, 101 insertions(+), 43 deletions(-)

diff --git a/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java b/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java
index edd9bb129b..44aac42459 100644
--- a/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java
+++ b/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java
@@ -32,9 +32,7 @@
 import java.io.Reader;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -107,10 +105,10 @@
 import org.codehaus.plexus.util.xml.Xpp3Dom;
 import org.eclipse.aether.RepositorySystemSession;
 import org.eclipse.aether.graph.DependencyFilter;
-import org.eclipse.aether.graph.DependencyNode;
 import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.resolution.ArtifactResult;
+import org.eclipse.aether.resolution.DependencyResult;
 import org.eclipse.aether.util.filter.AndDependencyFilter;
-import org.eclipse.aether.util.graph.visitor.PreorderNodeListGenerator;
 
 /**
  * Provides basic services to manage Maven plugins and their mojos. This component is kept general in its design such
@@ -389,17 +387,17 @@ private void createPluginRealm(
         DependencyFilter dependencyFilter = project.getExtensionDependencyFilter();
         dependencyFilter = AndDependencyFilter.newInstance(dependencyFilter, filter);
 
-        DependencyNode root = pluginDependenciesResolver.resolve(
+        DependencyResult result = pluginDependenciesResolver.resolvePluginAndFlatten(
                 plugin,
                 RepositoryUtils.toArtifact(pluginArtifact),
                 dependencyFilter,
                 project.getRemotePluginRepositories(),
                 repositorySession);
 
-        PreorderNodeListGenerator nlg = new PreorderNodeListGenerator();
-        root.accept(nlg);
-
-        pluginArtifacts = toMavenArtifacts(root, nlg);
+        pluginArtifacts = result.getArtifactResults().stream()
+                .filter(ArtifactResult::isResolved)
+                .map(r -> RepositoryUtils.toArtifact(r.getArtifact()))
+                .collect(Collectors.toList());
 
         pluginRealm = classRealmManager.createPluginRealm(
                 plugin, parent, null, foreignImports, toAetherArtifacts(pluginArtifacts));
@@ -437,18 +435,6 @@ private List<org.eclipse.aether.artifact.Artifact> toAetherArtifacts(final List<
         return new ArrayList<>(RepositoryUtils.toArtifacts(pluginArtifacts));
     }
 
-    private List<Artifact> toMavenArtifacts(DependencyNode root, PreorderNodeListGenerator nlg) {
-        List<Artifact> artifacts = new ArrayList<>(nlg.getNodes().size());
-        RepositoryUtils.toArtifacts(artifacts, Collections.singleton(root), Collections.<String>emptyList(), null);
-        for (Iterator<Artifact> it = artifacts.iterator(); it.hasNext(); ) {
-            Artifact artifact = it.next();
-            if (artifact.getFile() == null) {
-                it.remove();
-            }
-        }
-        return Collections.unmodifiableList(artifacts);
-    }
-
     private Map<String, ClassLoader> calcImports(MavenProject project, ClassLoader parent, List<String> imports) {
         Map<String, ClassLoader> foreignImports = new HashMap<>();
 
@@ -841,9 +827,11 @@ public ExtensionRealmCache.CacheRecord setupExtensionsRealm(
     private List<Artifact> resolveExtensionArtifacts(
             Plugin extensionPlugin, List<RemoteRepository> repositories, RepositorySystemSession session)
             throws PluginResolutionException {
-        DependencyNode root = pluginDependenciesResolver.resolve(extensionPlugin, null, null, repositories, session);
-        PreorderNodeListGenerator nlg = new PreorderNodeListGenerator();
-        root.accept(nlg);
-        return toMavenArtifacts(root, nlg);
+        DependencyResult result =
+                pluginDependenciesResolver.resolvePluginAndFlatten(extensionPlugin, null, null, repositories, session);
+        return result.getArtifactResults().stream()
+                .filter(ArtifactResult::isResolved)
+                .map(r -> RepositoryUtils.toArtifact(r.getArtifact()))
+                .collect(Collectors.toList());
     }
 }
diff --git a/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginDependenciesResolver.java b/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginDependenciesResolver.java
index b2284661d6..ca7c1fcf08 100644
--- a/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginDependenciesResolver.java
+++ b/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginDependenciesResolver.java
@@ -57,6 +57,7 @@
 import org.eclipse.aether.resolution.ArtifactResolutionException;
 import org.eclipse.aether.resolution.DependencyRequest;
 import org.eclipse.aether.resolution.DependencyResolutionException;
+import org.eclipse.aether.resolution.DependencyResult;
 import org.eclipse.aether.util.artifact.JavaScopes;
 import org.eclipse.aether.util.filter.AndDependencyFilter;
 import org.eclipse.aether.util.filter.ScopeDependencyFilter;
@@ -98,6 +99,7 @@ private Artifact toArtifact(Plugin plugin, RepositorySystemSession session) {
                 session.getArtifactTypeRegistry().get("maven-plugin"));
     }
 
+    @Override
     public Artifact resolve(Plugin plugin, List<RemoteRepository> repositories, RepositorySystemSession session)
             throws PluginResolutionException {
         RequestTrace trace = RequestTrace.newChild(null, plugin);
@@ -154,16 +156,33 @@ public Artifact resolve(Plugin plugin, List<RemoteRepository> repositories, Repo
 
     /**
      * @since 3.3.0
+     * @deprecated Is unused since 3.10.0
      */
+    @Deprecated
     public DependencyNode resolveCoreExtension(
             Plugin plugin,
             DependencyFilter dependencyFilter,
             List<RemoteRepository> repositories,
             RepositorySystemSession session)
             throws PluginResolutionException {
+        return resolveInternal(plugin, null /* pluginArtifact */, dependencyFilter, repositories, session)
+                .getRoot();
+    }
+
+    /**
+     * @since 3.10.0
+     */
+    @Override
+    public DependencyResult resolveCoreExtensionAndFlatten(
+            Plugin plugin,
+            DependencyFilter dependencyFilter,
+            List<RemoteRepository> repositories,
+            RepositorySystemSession session)
+            throws PluginResolutionException {
         return resolveInternal(plugin, null /* pluginArtifact */, dependencyFilter, repositories, session);
     }
 
+    @Override
     public DependencyNode resolve(
             Plugin plugin,
             Artifact pluginArtifact,
@@ -171,10 +190,22 @@ public DependencyNode resolve(
             List<RemoteRepository> repositories,
             RepositorySystemSession session)
             throws PluginResolutionException {
+        return resolveInternal(plugin, pluginArtifact, dependencyFilter, repositories, session)
+                .getRoot();
+    }
+
+    @Override
+    public DependencyResult resolvePluginAndFlatten(
+            Plugin plugin,
+            Artifact pluginArtifact,
+            DependencyFilter dependencyFilter,
+            List<RemoteRepository> repositories,
+            RepositorySystemSession session)
+            throws PluginResolutionException {
         return resolveInternal(plugin, pluginArtifact, dependencyFilter, repositories, session);
     }
 
-    private DependencyNode resolveInternal(
+    private DependencyResult resolveInternal(
             Plugin plugin,
             Artifact pluginArtifact,
             DependencyFilter dependencyFilter,
@@ -190,8 +221,6 @@ private DependencyNode resolveInternal(
         DependencyFilter collectionFilter = new ScopeDependencyFilter("provided", "test");
         DependencyFilter resolutionFilter = AndDependencyFilter.newInstance(collectionFilter, dependencyFilter);
 
-        DependencyNode node;
-
         try {
             DependencySelector selector =
                     AndDependencySelector.newInstance(session.getDependencySelector(), new WagonExcluder());
@@ -221,14 +250,16 @@ private DependencyNode resolveInternal(
 
             request.setTrace(RequestTrace.newChild(trace, depRequest));
 
-            node = repoSystem.collectDependencies(pluginSession, request).getRoot();
+            DependencyNode node =
+                    repoSystem.collectDependencies(pluginSession, request).getRoot();
 
             if (logger.isDebugEnabled()) {
                 node.accept(new GraphLogger());
             }
 
             depRequest.setRoot(node);
-            repoSystem.resolveDependencies(session, depRequest);
+            return repoSystem.resolveDependencies(session, depRequest);
+
         } catch (DependencyCollectionException e) {
             throw new PluginResolutionException(
                     plugin, e.getResult().getExceptions(), logger.isDebugEnabled() ? e : null);
@@ -241,8 +272,6 @@ private DependencyNode resolveInternal(
                     .collect(Collectors.toList());
             throw new PluginResolutionException(plugin, exceptions, logger.isDebugEnabled() ? e : null);
         }
-
-        return node;
     }
 
     // Keep this class in sync with org.apache.maven.project.DefaultProjectDependenciesResolver.GraphLogger
diff --git a/maven-core/src/main/java/org/apache/maven/plugin/internal/PluginDependenciesResolver.java b/maven-core/src/main/java/org/apache/maven/plugin/internal/PluginDependenciesResolver.java
index 0f865df277..957365a293 100644
--- a/maven-core/src/main/java/org/apache/maven/plugin/internal/PluginDependenciesResolver.java
+++ b/maven-core/src/main/java/org/apache/maven/plugin/internal/PluginDependenciesResolver.java
@@ -27,6 +27,7 @@
 import org.eclipse.aether.graph.DependencyFilter;
 import org.eclipse.aether.graph.DependencyNode;
 import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.resolution.DependencyResult;
 
 /**
  * Assists in resolving the dependencies of a plugin. <strong>Warning:</strong> This is an internal utility interface
@@ -61,7 +62,9 @@ Artifact resolve(Plugin plugin, List<RemoteRepository> repositories, RepositoryS
      * @param session The repository session to use for resolving the plugin artifacts, must not be {@code null}.
      * @return The dependency tree denoting the resolved plugin class path, never {@code null}.
      * @throws PluginResolutionException If any dependency could not be resolved.
+     * @deprecated This method should be avoided, as it requires manual flattening; use {@link #resolvePluginAndFlatten(Plugin, Artifact, DependencyFilter, List, RepositorySystemSession)} instead to let Resolver handle it.
      */
+    @Deprecated
     DependencyNode resolve(
             Plugin plugin,
             Artifact pluginArtifact,
@@ -69,4 +72,42 @@ DependencyNode resolve(
             List<RemoteRepository> repositories,
             RepositorySystemSession session)
             throws PluginResolutionException;
+
+    /**
+     * Resolves the runtime dependencies of the specified core extension (as {@link Plugin} as GAV carrier).
+     *
+     * @param plugin The plugin for which to resolve the dependencies, must not be {@code null}.
+     * @param dependencyFilter A filter to exclude artifacts from resolution (but not collection), may be {@code null}.
+     * @param repositories The plugin repositories to use for resolving the plugin artifacts, must not be {@code null}.
+     * @param session The repository session to use for resolving the plugin artifacts, must not be {@code null}.
+     * @return The dependency resolution result having the resolved extension class path but also the tree, never {@code null}.
+     * @throws PluginResolutionException If any dependency could not be resolved.
+     * @since 3.10.0
+     */
+    DependencyResult resolveCoreExtensionAndFlatten(
+            Plugin plugin,
+            DependencyFilter dependencyFilter,
+            List<RemoteRepository> repositories,
+            RepositorySystemSession session)
+            throws PluginResolutionException;
+
+    /**
+     * Resolves the runtime dependencies of the specified plugin.
+     *
+     * @param plugin The plugin for which to resolve the dependencies, must not be {@code null}.
+     * @param pluginArtifact The plugin's main artifact, may be {@code null}.
+     * @param dependencyFilter A filter to exclude artifacts from resolution (but not collection), may be {@code null}.
+     * @param repositories The plugin repositories to use for resolving the plugin artifacts, must not be {@code null}.
+     * @param session The repository session to use for resolving the plugin artifacts, must not be {@code null}.
+     * @return The dependency resolution result having the resolved plugin class path but also the tree, never {@code null}.
+     * @throws PluginResolutionException If any dependency could not be resolved.
+     * @since 3.10.0
+     */
+    DependencyResult resolvePluginAndFlatten(
+            Plugin plugin,
+            Artifact pluginArtifact,
+            DependencyFilter dependencyFilter,
+            List<RemoteRepository> repositories,
+            RepositorySystemSession session)
+            throws PluginResolutionException;
 }
diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/internal/BootstrapCoreExtensionManager.java b/maven-embedder/src/main/java/org/apache/maven/cli/internal/BootstrapCoreExtensionManager.java
index c11e6ce893..53e20302d8 100644
--- a/maven-embedder/src/main/java/org/apache/maven/cli/internal/BootstrapCoreExtensionManager.java
+++ b/maven-embedder/src/main/java/org/apache/maven/cli/internal/BootstrapCoreExtensionManager.java
@@ -26,6 +26,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 import org.apache.maven.RepositoryUtils;
 import org.apache.maven.cli.internal.extension.model.CoreExtension;
@@ -35,7 +36,7 @@
 import org.apache.maven.internal.aether.DefaultRepositorySystemSessionFactory;
 import org.apache.maven.model.Plugin;
 import org.apache.maven.plugin.PluginResolutionException;
-import org.apache.maven.plugin.internal.DefaultPluginDependenciesResolver;
+import org.apache.maven.plugin.internal.PluginDependenciesResolver;
 import org.codehaus.plexus.DefaultPlexusContainer;
 import org.codehaus.plexus.PlexusContainer;
 import org.codehaus.plexus.classworlds.ClassWorld;
@@ -48,10 +49,10 @@
 import org.eclipse.aether.RepositorySystemSession;
 import org.eclipse.aether.artifact.Artifact;
 import org.eclipse.aether.graph.DependencyFilter;
-import org.eclipse.aether.graph.DependencyNode;
 import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.resolution.ArtifactResult;
+import org.eclipse.aether.resolution.DependencyResult;
 import org.eclipse.aether.util.filter.ExclusionsDependencyFilter;
-import org.eclipse.aether.util.graph.visitor.PreorderNodeListGenerator;
 
 /**
  * BootstrapCoreExtensionManager
@@ -64,7 +65,7 @@ public class BootstrapCoreExtensionManager {
 
     private final Logger log;
 
-    private final DefaultPluginDependenciesResolver pluginDependenciesResolver;
+    private final PluginDependenciesResolver pluginDependenciesResolver;
 
     private final DefaultRepositorySystemSessionFactory repositorySystemSessionFactory;
 
@@ -77,7 +78,7 @@ public class BootstrapCoreExtensionManager {
     @Inject
     public BootstrapCoreExtensionManager(
             Logger log,
-            DefaultPluginDependenciesResolver pluginDependenciesResolver,
+            PluginDependenciesResolver pluginDependenciesResolver,
             DefaultRepositorySystemSessionFactory repositorySystemSessionFactory,
             CoreExports coreExports,
             PlexusContainer container) {
@@ -174,13 +175,12 @@ private List<Artifact> resolveExtension(
             plugin.setArtifactId(interpolator.interpolate(extension.getArtifactId()));
             plugin.setVersion(interpolator.interpolate(extension.getVersion()));
 
-            DependencyNode root = pluginDependenciesResolver.resolveCoreExtension(
+            DependencyResult result = pluginDependenciesResolver.resolveCoreExtensionAndFlatten(
                     plugin, dependencyFilter, repositories, repoSession);
-            PreorderNodeListGenerator nlg = new PreorderNodeListGenerator();
-            root.accept(nlg);
-            List<Artifact> artifacts = nlg.getArtifacts(false);
-
-            return artifacts;
+            return result.getArtifactResults().stream()
+                    .filter(ArtifactResult::isResolved)
+                    .map(ArtifactResult::getArtifact)
+                    .collect(Collectors.toList());
         } catch (PluginResolutionException | InterpolationException e) {
             throw new ExtensionResolutionException(extension, e);
         }