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 2022/03/17 13:27:25 UTC

[maven-resolver] branch bf-df-coexitsts created (now ac046e0)

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

cstamas pushed a change to branch bf-df-coexitsts
in repository https://gitbox.apache.org/repos/asf/maven-resolver.git.


      at ac046e0  DependencyCollector: DF vs BF coexists

This branch includes the following new commits:

     new ac046e0  DependencyCollector: DF vs BF coexists

The 1 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.


[maven-resolver] 01/01: DependencyCollector: DF vs BF coexists

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

cstamas pushed a commit to branch bf-df-coexitsts
in repository https://gitbox.apache.org/repos/asf/maven-resolver.git

commit ac046e09093824fcb426d4b09db99e4f8c6412fb
Author: Tamas Cservenak <ta...@cservenak.net>
AuthorDate: Thu Mar 17 14:25:28 2022 +0100

    DependencyCollector: DF vs BF coexists
---
 .../eclipse/aether/impl/guice/AetherModule.java    |  22 +
 .../impl/collect/CachingArtifactTypeRegistry.java  |   2 +-
 .../aether/internal/impl/collect/DataPool.java     |  52 +-
 .../DefaultDependencyCollectionContext.java        |   4 +-
 .../impl/collect/DefaultDependencyCollector.java   | 868 +--------------------
 ...efaultDependencyGraphTransformationContext.java |   4 +-
 .../impl/collect/DefaultVersionFilterContext.java  |   4 +-
 .../impl/collect/DependencyCollectorDelegate.java  |  29 +
 .../BfDependencyCollector.java}                    |  57 +-
 .../BfDependencyCycle.java}                        |   8 +-
 .../BfProcessingContext.java}                      |  10 +-
 .../DfDependencyCollector.java}                    | 237 +++---
 .../DfDependencyCycle.java}                        |  61 +-
 .../aether/internal/impl/collect/df/NodeStack.java | 127 +++
 .../BfDependencyCollectorTest.java}                |  25 +-
 .../BfDependencyCycleTest.java}                    |  12 +-
 .../DfDependencyCollectorTest.java}                |  26 +-
 .../DfDependencyCycleTest.java}                    |  15 +-
 18 files changed, 467 insertions(+), 1096 deletions(-)

diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java
index 9629dc9..2082422 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java
@@ -48,6 +48,9 @@ import org.eclipse.aether.internal.impl.checksum.Sha1ChecksumAlgorithmFactory;
 import org.eclipse.aether.internal.impl.checksum.Sha256ChecksumAlgorithmFactory;
 import org.eclipse.aether.internal.impl.checksum.Sha512ChecksumAlgorithmFactory;
 import org.eclipse.aether.internal.impl.checksum.DefaultChecksumAlgorithmFactorySelector;
+import org.eclipse.aether.internal.impl.collect.DependencyCollectorDelegate;
+import org.eclipse.aether.internal.impl.collect.bf.BfDependencyCollector;
+import org.eclipse.aether.internal.impl.collect.df.DfDependencyCollector;
 import org.eclipse.aether.internal.impl.synccontext.DefaultSyncContextFactory;
 import org.eclipse.aether.internal.impl.synccontext.named.NamedLockFactorySelector;
 import org.eclipse.aether.internal.impl.synccontext.named.SimpleNamedLockFactorySelector;
@@ -132,8 +135,14 @@ public class AetherModule
                 .to( DefaultRepositorySystem.class ).in( Singleton.class );
         bind( ArtifactResolver.class ) //
                 .to( DefaultArtifactResolver.class ).in( Singleton.class );
+
         bind( DependencyCollector.class ) //
                 .to( DefaultDependencyCollector.class ).in( Singleton.class );
+        bind( DependencyCollectorDelegate.class ).annotatedWith( Names.named( BfDependencyCollector.NAME ) ) //
+                .to( BfDependencyCollector.class ).in( Singleton.class );
+        bind( DependencyCollectorDelegate.class ).annotatedWith( Names.named( DfDependencyCollector.NAME ) ) //
+                .to( DfDependencyCollector.class ).in( Singleton.class );
+
         bind( Deployer.class ) //
                 .to( DefaultDeployer.class ).in( Singleton.class );
         bind( Installer.class ) //
@@ -214,6 +223,19 @@ public class AetherModule
 
     @Provides
     @Singleton
+    Map<String, DependencyCollectorDelegate> provideDependencyCollectorDelegates(
+            @Named( BfDependencyCollector.NAME ) DependencyCollectorDelegate bf,
+            @Named( DfDependencyCollector.NAME ) DependencyCollectorDelegate df
+    )
+    {
+        Map<String, DependencyCollectorDelegate> providedDependencyCollectorDelegates = new HashMap<>();
+        providedDependencyCollectorDelegates.put( BfDependencyCollector.NAME, bf );
+        providedDependencyCollectorDelegates.put( DfDependencyCollector.NAME, df );
+        return providedDependencyCollectorDelegates;
+    }
+
+    @Provides
+    @Singleton
     Map<String, ProvidedChecksumsSource> provideChecksumSources(
         @Named( FileProvidedChecksumsSource.NAME ) ProvidedChecksumsSource fileProvidedChecksumSource
     )
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/CachingArtifactTypeRegistry.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/CachingArtifactTypeRegistry.java
index a260234..bb03b14 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/CachingArtifactTypeRegistry.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/CachingArtifactTypeRegistry.java
@@ -29,7 +29,7 @@ import org.eclipse.aether.artifact.ArtifactTypeRegistry;
 /**
  * A short-lived artifact type registry that caches results from a presumedly slower type registry.
  */
-class CachingArtifactTypeRegistry
+public class CachingArtifactTypeRegistry
     implements ArtifactTypeRegistry
 {
 
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DataPool.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DataPool.java
index 4a14555..6190c76 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DataPool.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DataPool.java
@@ -48,8 +48,9 @@ import org.eclipse.aether.version.Version;
 import org.eclipse.aether.version.VersionConstraint;
 
 /**
+ * DataPool.
  */
-final class DataPool
+public final class DataPool
 {
 
     private static final String ARTIFACT_POOL = DataPool.class.getName() + "$Artifact";
@@ -58,7 +59,7 @@ final class DataPool
 
     private static final String DESCRIPTORS = DataPool.class.getName() + "$Descriptors";
 
-    static final ArtifactDescriptorResult NO_DESCRIPTOR =
+    public static final ArtifactDescriptorResult NO_DESCRIPTOR =
         new ArtifactDescriptorResult( new ArtifactDescriptorRequest() );
 
     private ObjectPool<Artifact> artifacts;
@@ -72,7 +73,7 @@ final class DataPool
     private final Map<Object, List<DependencyNode>> nodes = new HashMap<>( 256 );
 
     @SuppressWarnings( "unchecked" )
-    DataPool( RepositorySystemSession session )
+    public DataPool( RepositorySystemSession session )
     {
         RepositoryCache cache = session.getCache();
 
@@ -121,12 +122,12 @@ final class DataPool
         return dependencies.intern( dependency );
     }
 
-    Object toKey( ArtifactDescriptorRequest request )
+    public Object toKey( ArtifactDescriptorRequest request )
     {
         return request.getArtifact();
     }
 
-    ArtifactDescriptorResult getDescriptor( Object key, ArtifactDescriptorRequest request )
+    public ArtifactDescriptorResult getDescriptor( Object key, ArtifactDescriptorRequest request )
     {
         Descriptor descriptor = descriptors.get( key );
         if ( descriptor != null )
@@ -136,22 +137,22 @@ final class DataPool
         return null;
     }
 
-    void putDescriptor( Object key, ArtifactDescriptorResult result )
+    public void putDescriptor( Object key, ArtifactDescriptorResult result )
     {
         descriptors.put( key, new GoodDescriptor( result ) );
     }
 
-    void putDescriptor( Object key, ArtifactDescriptorException e )
+    public void putDescriptor( Object key, ArtifactDescriptorException e )
     {
         descriptors.put( key, BadDescriptor.INSTANCE );
     }
 
-    Object toKey( VersionRangeRequest request )
+    public Object toKey( VersionRangeRequest request )
     {
         return new ConstraintKey( request );
     }
 
-    VersionRangeResult getConstraint( Object key, VersionRangeRequest request )
+    public VersionRangeResult getConstraint( Object key, VersionRangeRequest request )
     {
         Constraint constraint = constraints.get( key );
         if ( constraint != null )
@@ -161,7 +162,7 @@ final class DataPool
         return null;
     }
 
-    void putConstraint( Object key, VersionRangeResult result )
+    public void putConstraint( Object key, VersionRangeResult result )
     {
         constraints.put( key, new Constraint( result ) );
     }
@@ -182,14 +183,20 @@ final class DataPool
         nodes.put( key, children );
     }
 
-    abstract static class Descriptor
+    /**
+     * Descriptor
+     */
+    public abstract static class Descriptor
     {
 
         public abstract ArtifactDescriptorResult toResult( ArtifactDescriptorRequest request );
 
     }
 
-    static final class GoodDescriptor
+    /**
+     * GoodDescriptor
+     */
+    public static final class GoodDescriptor
         extends Descriptor
     {
 
@@ -205,7 +212,7 @@ final class DataPool
 
         final List<Dependency> managedDependencies;
 
-        GoodDescriptor( ArtifactDescriptorResult result )
+        public GoodDescriptor( ArtifactDescriptorResult result )
         {
             artifact = result.getArtifact();
             relocations = result.getRelocations();
@@ -229,7 +236,10 @@ final class DataPool
 
     }
 
-    static final class BadDescriptor
+    /**
+     * BadDescriptor
+     */
+    public static final class BadDescriptor
         extends Descriptor
     {
 
@@ -285,7 +295,10 @@ final class DataPool
         }
     }
 
-    static final class ConstraintKey
+    /**
+     * ConstraintKey
+     */
+    public static final class ConstraintKey
     {
         private final Artifact artifact;
 
@@ -293,7 +306,7 @@ final class DataPool
 
         private final int hashCode;
 
-        ConstraintKey( VersionRangeRequest request )
+        public ConstraintKey( VersionRangeRequest request )
         {
             artifact = request.getArtifact();
             repositories = request.getRepositories();
@@ -360,7 +373,10 @@ final class DataPool
         }
     }
 
-    static final class GraphKey
+    /**
+     * GraphKey
+     */
+    public static final class GraphKey
     {
         private final Artifact artifact;
 
@@ -376,7 +392,7 @@ final class DataPool
 
         private final int hashCode;
 
-        GraphKey( Artifact artifact, List<RemoteRepository> repositories, DependencySelector selector,
+        public GraphKey( Artifact artifact, List<RemoteRepository> repositories, DependencySelector selector,
                   DependencyManager manager, DependencyTraverser traverser, VersionFilter filter )
         {
             this.artifact = artifact;
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectionContext.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectionContext.java
index 3bf4fe1..0c66fa7 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectionContext.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectionContext.java
@@ -29,7 +29,7 @@ import org.eclipse.aether.graph.Dependency;
 /**
  * @see DefaultDependencyCollector
  */
-final class DefaultDependencyCollectionContext
+public final class DefaultDependencyCollectionContext
     implements DependencyCollectionContext
 {
 
@@ -41,7 +41,7 @@ final class DefaultDependencyCollectionContext
 
     private List<Dependency> managedDependencies;
 
-    DefaultDependencyCollectionContext( RepositorySystemSession session, Artifact artifact,
+    public DefaultDependencyCollectionContext( RepositorySystemSession session, Artifact artifact,
                                                Dependency dependency, List<Dependency> managedDependencies )
     {
         this.session = session;
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java
index a773367..0dba4e4 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java
@@ -19,875 +19,79 @@ package org.eclipse.aether.internal.impl.collect;
  * under the License.
  */
 
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Queue;
-
-import static java.util.Objects.requireNonNull;
-import static org.eclipse.aether.internal.impl.collect.DefaultDependencyCycle.find;
-
 import javax.inject.Inject;
 import javax.inject.Named;
 import javax.inject.Singleton;
 
-import org.eclipse.aether.DefaultRepositorySystemSession;
-import org.eclipse.aether.RepositoryException;
+import java.util.HashMap;
+import java.util.Map;
+
 import org.eclipse.aether.RepositorySystemSession;
-import org.eclipse.aether.RequestTrace;
-import org.eclipse.aether.artifact.Artifact;
-import org.eclipse.aether.artifact.ArtifactProperties;
 import org.eclipse.aether.collection.CollectRequest;
 import org.eclipse.aether.collection.CollectResult;
 import org.eclipse.aether.collection.DependencyCollectionException;
-import org.eclipse.aether.collection.DependencyGraphTransformer;
-import org.eclipse.aether.collection.DependencyManagement;
-import org.eclipse.aether.collection.DependencyManager;
-import org.eclipse.aether.collection.DependencySelector;
-import org.eclipse.aether.collection.DependencyTraverser;
-import org.eclipse.aether.collection.VersionFilter;
-import org.eclipse.aether.graph.DefaultDependencyNode;
-import org.eclipse.aether.graph.Dependency;
-import org.eclipse.aether.graph.DependencyNode;
-import org.eclipse.aether.graph.Exclusion;
-import org.eclipse.aether.impl.ArtifactDescriptorReader;
 import org.eclipse.aether.impl.DependencyCollector;
-import org.eclipse.aether.impl.RemoteRepositoryManager;
-import org.eclipse.aether.impl.VersionRangeResolver;
-import org.eclipse.aether.repository.ArtifactRepository;
-import org.eclipse.aether.repository.RemoteRepository;
-import org.eclipse.aether.resolution.ArtifactDescriptorException;
-import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
-import org.eclipse.aether.resolution.ArtifactDescriptorResult;
-import org.eclipse.aether.resolution.VersionRangeRequest;
-import org.eclipse.aether.resolution.VersionRangeResolutionException;
-import org.eclipse.aether.resolution.VersionRangeResult;
+import org.eclipse.aether.internal.impl.collect.bf.BfDependencyCollector;
+import org.eclipse.aether.internal.impl.collect.df.DfDependencyCollector;
 import org.eclipse.aether.spi.locator.Service;
 import org.eclipse.aether.spi.locator.ServiceLocator;
 import org.eclipse.aether.util.ConfigUtils;
-import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
-import org.eclipse.aether.util.graph.transformer.TransformationContextKeys;
-import org.eclipse.aether.version.Version;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+
+import static java.util.Objects.requireNonNull;
 
 /**
+ *
  */
 @Singleton
 @Named
 public class DefaultDependencyCollector
-    implements DependencyCollector, Service
+        implements DependencyCollector, Service
 {
+    private static final String CONFIG_PROP_COLLECTOR_IMPL = "aether.collector.impl";
 
-    private static final String CONFIG_PROP_MAX_EXCEPTIONS = "aether.dependencyCollector.maxExceptions";
-
-    private static final int CONFIG_PROP_MAX_EXCEPTIONS_DEFAULT = 50;
-
-    private static final String CONFIG_PROP_MAX_CYCLES = "aether.dependencyCollector.maxCycles";
-
-    private static final int CONFIG_PROP_MAX_CYCLES_DEFAULT = 10;
-
-    private static final Logger LOGGER = LoggerFactory.getLogger( DefaultDependencyCollector.class );
+    private static final String DEFAULT_COLLECTOR_IMPL = DfDependencyCollector.NAME;
 
-    private RemoteRepositoryManager remoteRepositoryManager;
-
-    private ArtifactDescriptorReader descriptorReader;
-
-    private VersionRangeResolver versionRangeResolver;
+    private final Map<String, DependencyCollectorDelegate> delegates;
 
+    /**
+     * Default ctor for SL.
+     *
+     * @deprecated SL is to be removed.
+     */
+    @Deprecated
     public DefaultDependencyCollector()
     {
-        // enables default constructor
+       this.delegates = new HashMap<>();
     }
 
     @Inject
-    DefaultDependencyCollector( RemoteRepositoryManager remoteRepositoryManager,
-                                ArtifactDescriptorReader artifactDescriptorReader,
-                                VersionRangeResolver versionRangeResolver )
+    public DefaultDependencyCollector( Map<String, DependencyCollectorDelegate> delegates )
     {
-        setRemoteRepositoryManager( remoteRepositoryManager );
-        setArtifactDescriptorReader( artifactDescriptorReader );
-        setVersionRangeResolver( versionRangeResolver );
+        this.delegates = requireNonNull( delegates );
     }
 
+    @Override
     public void initService( ServiceLocator locator )
     {
-        setRemoteRepositoryManager( locator.getService( RemoteRepositoryManager.class ) );
-        setArtifactDescriptorReader( locator.getService( ArtifactDescriptorReader.class ) );
-        setVersionRangeResolver( locator.getService( VersionRangeResolver.class ) );
-    }
-
-    public DefaultDependencyCollector setRemoteRepositoryManager( RemoteRepositoryManager remoteRepositoryManager )
-    {
-        this.remoteRepositoryManager =
-                requireNonNull( remoteRepositoryManager, "remote repository provider cannot be null" );
-        return this;
-    }
-
-    public DefaultDependencyCollector setArtifactDescriptorReader( ArtifactDescriptorReader artifactDescriptorReader )
-    {
-        descriptorReader = requireNonNull( artifactDescriptorReader, "artifact descriptor reader cannot be null" );
-        return this;
-    }
-
-    public DefaultDependencyCollector setVersionRangeResolver( VersionRangeResolver versionRangeResolver )
-    {
-        this.versionRangeResolver =
-                requireNonNull( versionRangeResolver, "version range resolver cannot be null" );
-        return this;
+        BfDependencyCollector bf = new BfDependencyCollector();
+        bf.initService( locator );
+        DfDependencyCollector df = new DfDependencyCollector();
+        df.initService( locator );
+        this.delegates.put( BfDependencyCollector.NAME, bf );
+        this.delegates.put( DfDependencyCollector.NAME, df );
     }
 
-    @SuppressWarnings( "checkstyle:methodlength" )
+    @Override
     public CollectResult collectDependencies( RepositorySystemSession session, CollectRequest request )
-        throws DependencyCollectionException
+            throws DependencyCollectionException
     {
-        requireNonNull( session, "session cannot be null" );
-        requireNonNull( request, "request cannot be null" );
-        session = optimizeSession( session );
-
-        RequestTrace trace = RequestTrace.newChild( request.getTrace(), request );
-
-        CollectResult result = new CollectResult( request );
-
-        DependencySelector depSelector = session.getDependencySelector();
-        DependencyManager depManager = session.getDependencyManager();
-        DependencyTraverser depTraverser = session.getDependencyTraverser();
-        VersionFilter verFilter = session.getVersionFilter();
-
-        Dependency root = request.getRoot();
-        List<RemoteRepository> repositories = request.getRepositories();
-        List<Dependency> dependencies = request.getDependencies();
-        List<Dependency> managedDependencies = request.getManagedDependencies();
-
-        Map<String, Object> stats = new LinkedHashMap<>();
-        long time1 = System.nanoTime();
-
-        DefaultDependencyNode node;
-        if ( root != null )
-        {
-            List<? extends Version> versions;
-            VersionRangeResult rangeResult;
-            try
-            {
-                VersionRangeRequest rangeRequest =
-                    new VersionRangeRequest( root.getArtifact(), request.getRepositories(),
-                                             request.getRequestContext() );
-                rangeRequest.setTrace( trace );
-                rangeResult = versionRangeResolver.resolveVersionRange( session, rangeRequest );
-                versions = filterVersions( root, rangeResult, verFilter, new DefaultVersionFilterContext( session ) );
-            }
-            catch ( VersionRangeResolutionException e )
-            {
-                result.addException( e );
-                throw new DependencyCollectionException( result, e.getMessage() );
-            }
-
-            Version version = versions.get( versions.size() - 1 );
-            root = root.setArtifact( root.getArtifact().setVersion( version.toString() ) );
-
-            ArtifactDescriptorResult descriptorResult;
-            try
-            {
-                ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest();
-                descriptorRequest.setArtifact( root.getArtifact() );
-                descriptorRequest.setRepositories( request.getRepositories() );
-                descriptorRequest.setRequestContext( request.getRequestContext() );
-                descriptorRequest.setTrace( trace );
-                if ( isLackingDescriptor( root.getArtifact() ) )
-                {
-                    descriptorResult = new ArtifactDescriptorResult( descriptorRequest );
-                }
-                else
-                {
-                    descriptorResult = descriptorReader.readArtifactDescriptor( session, descriptorRequest );
-                }
-            }
-            catch ( ArtifactDescriptorException e )
-            {
-                result.addException( e );
-                throw new DependencyCollectionException( result, e.getMessage() );
-            }
-
-            root = root.setArtifact( descriptorResult.getArtifact() );
-
-            if ( !session.isIgnoreArtifactDescriptorRepositories() )
-            {
-                repositories = remoteRepositoryManager.aggregateRepositories( session, repositories,
-                                                                              descriptorResult.getRepositories(),
-                                                                              true );
-            }
-            dependencies = mergeDeps( dependencies, descriptorResult.getDependencies() );
-            managedDependencies = mergeDeps( managedDependencies, descriptorResult.getManagedDependencies() );
-
-            node = new DefaultDependencyNode( root );
-            node.setRequestContext( request.getRequestContext() );
-            node.setRelocations( descriptorResult.getRelocations() );
-            node.setVersionConstraint( rangeResult.getVersionConstraint() );
-            node.setVersion( version );
-            node.setAliases( descriptorResult.getAliases() );
-            node.setRepositories( request.getRepositories() );
-        }
-        else
-        {
-            node = new DefaultDependencyNode( request.getRootArtifact() );
-            node.setRequestContext( request.getRequestContext() );
-            node.setRepositories( request.getRepositories() );
-        }
-
-        result.setRoot( node );
-
-        boolean traverse = root == null || depTraverser == null || depTraverser.traverseDependency( root );
-        String errorPath = null;
-        if ( traverse && !dependencies.isEmpty() )
+        String delegateName = ConfigUtils.getString( session, DEFAULT_COLLECTOR_IMPL, CONFIG_PROP_COLLECTOR_IMPL );
+        DependencyCollectorDelegate delegate = delegates.get( delegateName );
+        if ( delegate == null )
         {
-            DataPool pool = new DataPool( session );
-
-            DefaultDependencyCollectionContext context =
-                new DefaultDependencyCollectionContext( session, request.getRootArtifact(), root, managedDependencies );
-
-            DefaultVersionFilterContext versionContext = new DefaultVersionFilterContext( session );
-
-            Args args = new Args( session, trace, pool, context, versionContext, request );
-            Results results = new Results( result, session );
-
-            DependencySelector rootDepSelector =
-                    depSelector != null ? depSelector.deriveChildSelector( context ) : null;
-            DependencyManager rootDepManager = depManager != null ? depManager.deriveChildManager( context ) : null;
-            DependencyTraverser rootDepTraverser =
-                    depTraverser != null ? depTraverser.deriveChildTraverser( context ) : null;
-            VersionFilter rootVerFilter = verFilter != null ? verFilter.deriveChildFilter( context ) : null;
-
-            for ( Dependency dependency : dependencies )
-            {
-                args.dependencyProcessingQueue.add(
-                        new DependencyProcessingContext( rootDepSelector, rootDepManager, rootDepTraverser,
-                                rootVerFilter, repositories, managedDependencies, Collections.singletonList( node ),
-                                dependency ) );
-            }
-
-            while ( !args.dependencyProcessingQueue.isEmpty() )
-            {
-                processDependency( args, results, args.dependencyProcessingQueue.remove(), Collections.emptyList(),
-                        false );
-            }
-
-            errorPath = results.errorPath;
-        }
-
-        long time2 = System.nanoTime();
-
-        DependencyGraphTransformer transformer = session.getDependencyGraphTransformer();
-        if ( transformer != null )
-        {
-            try
-            {
-                DefaultDependencyGraphTransformationContext context =
-                    new DefaultDependencyGraphTransformationContext( session );
-                context.put( TransformationContextKeys.STATS, stats );
-                result.setRoot( transformer.transformGraph( node, context ) );
-            }
-            catch ( RepositoryException e )
-            {
-                result.addException( e );
-            }
-        }
-
-        long time3 = System.nanoTime();
-        stats.put( "DefaultDependencyCollector.collectTime", time2 - time1 );
-        stats.put( "DefaultDependencyCollector.transformTime", time3 - time2 );
-        LOGGER.debug( "Dependency collection stats {}", stats );
-
-        if ( errorPath != null )
-        {
-            throw new DependencyCollectionException( result, "Failed to collect dependencies at " + errorPath );
-        }
-        if ( !result.getExceptions().isEmpty() )
-        {
-            throw new DependencyCollectionException( result );
-        }
-
-        return result;
-    }
-
-    private static RepositorySystemSession optimizeSession( RepositorySystemSession session )
-    {
-        DefaultRepositorySystemSession optimized = new DefaultRepositorySystemSession( session );
-        optimized.setArtifactTypeRegistry( CachingArtifactTypeRegistry.newInstance( session ) );
-        return optimized;
-    }
-
-    private List<Dependency> mergeDeps( List<Dependency> dominant, List<Dependency> recessive )
-    {
-        List<Dependency> result;
-        if ( dominant == null || dominant.isEmpty() )
-        {
-            result = recessive;
-        }
-        else if ( recessive == null || recessive.isEmpty() )
-        {
-            result = dominant;
-        }
-        else
-        {
-            int initialCapacity = dominant.size() + recessive.size();
-            result = new ArrayList<>( initialCapacity );
-            Collection<String> ids = new HashSet<>( initialCapacity, 1.0f );
-            for ( Dependency dependency : dominant )
-            {
-                ids.add( getId( dependency.getArtifact() ) );
-                result.add( dependency );
-            }
-            for ( Dependency dependency : recessive )
-            {
-                if ( !ids.contains( getId( dependency.getArtifact() ) ) )
-                {
-                    result.add( dependency );
-                }
-            }
-        }
-        return result;
-    }
-
-    private static String getId( Artifact a )
-    {
-        return a.getGroupId() + ':' + a.getArtifactId() + ':' + a.getClassifier() + ':' + a.getExtension();
-    }
-
-    @SuppressWarnings( "checkstyle:parameternumber" )
-    private void processDependency( Args args, Results results, DependencyProcessingContext context,
-                                    List<Artifact> relocations, boolean disableVersionManagement )
-    {
-
-        if ( context.depSelector != null && !context.depSelector.selectDependency( context.dependency ) )
-        {
-            return;
-        }
-
-        PremanagedDependency preManaged =
-                PremanagedDependency.create( context.depManager, context.dependency, disableVersionManagement,
-                        args.premanagedState );
-        Dependency dependency = preManaged.managedDependency;
-
-        boolean noDescriptor = isLackingDescriptor( dependency.getArtifact() );
-
-        boolean traverse =
-                !noDescriptor && ( context.depTraverser == null || context.depTraverser.traverseDependency(
-                        dependency ) );
-
-        List<? extends Version> versions;
-        VersionRangeResult rangeResult;
-        try
-        {
-            VersionRangeRequest rangeRequest = createVersionRangeRequest( args, context.repositories, dependency );
-
-            rangeResult = cachedResolveRangeResult( rangeRequest, args.pool, args.session );
-
-            versions = filterVersions( dependency, rangeResult, context.verFilter, args.versionContext );
-        }
-        catch ( VersionRangeResolutionException e )
-        {
-            results.addException( dependency, e, context.parents );
-            return;
-        }
-
-        for ( Version version : versions )
-        {
-            Artifact originalArtifact = dependency.getArtifact().setVersion( version.toString() );
-            Dependency d = dependency.setArtifact( originalArtifact );
-
-            ArtifactDescriptorRequest descriptorRequest =
-                    createArtifactDescriptorRequest( args, context.repositories, d );
-
-            final ArtifactDescriptorResult descriptorResult =
-                    noDescriptor
-                            ? new ArtifactDescriptorResult( descriptorRequest )
-                            : resolveCachedArtifactDescriptor( args.pool, descriptorRequest, args.session,
-                                    context.withDependency( d ), results );
-
-            if ( descriptorResult != null )
-            {
-                d = d.setArtifact( descriptorResult.getArtifact() );
-
-                int cycleEntry = find( context.parents, d.getArtifact() );
-                if ( cycleEntry >= 0 )
-                {
-                    results.addCycle( context.parents, cycleEntry, d );
-                    DependencyNode cycleNode = context.parents.get( cycleEntry );
-                    if ( cycleNode.getDependency() != null )
-                    {
-                        DefaultDependencyNode child =
-                                createDependencyNode( relocations, preManaged, rangeResult, version, d,
-                                        descriptorResult, cycleNode );
-                        context.getParent().getChildren().add( child );
-                        continue;
-                    }
-                }
-
-                if ( !descriptorResult.getRelocations().isEmpty() )
-                {
-                    boolean disableVersionManagementSubsequently =
-                        originalArtifact.getGroupId().equals( d.getArtifact().getGroupId() )
-                            && originalArtifact.getArtifactId().equals( d.getArtifact().getArtifactId() );
-
-                    processDependency( args, results, context.withDependency( d ), descriptorResult.getRelocations(),
-                            disableVersionManagementSubsequently );
-                    return;
-                }
-                else
-                {
-                    d = args.pool.intern( d.setArtifact( args.pool.intern( d.getArtifact() ) ) );
-
-                    List<RemoteRepository> repos =
-                        getRemoteRepositories( rangeResult.getRepository( version ), context.repositories );
-
-                    DefaultDependencyNode child =
-                        createDependencyNode( relocations, preManaged, rangeResult, version, d,
-                                              descriptorResult.getAliases(), repos, args.request.getRequestContext() );
-
-                    context.getParent().getChildren().add( child );
-
-                    boolean recurse = traverse && !descriptorResult.getDependencies().isEmpty();
-                    if ( recurse )
-                    {
-                        doRecurse( args, context.withDependency( d ), descriptorResult, child );
-                    }
-                }
-            }
-            else
-            {
-                List<RemoteRepository> repos =
-                    getRemoteRepositories( rangeResult.getRepository( version ), context.repositories );
-                DefaultDependencyNode child =
-                    createDependencyNode( relocations, preManaged, rangeResult, version, d, null, repos,
-                                          args.request.getRequestContext() );
-                context.getParent().getChildren().add( child );
-            }
+            throw new IllegalArgumentException( "Unknown collector impl: '" + delegateName
+                    + "', known implementations are " + delegates.keySet() );
         }
+        return delegate.collectDependencies( session, request );
     }
-
-    @SuppressWarnings( "checkstyle:parameternumber" )
-    private void doRecurse( Args args, DependencyProcessingContext parentContext,
-                            ArtifactDescriptorResult descriptorResult, DefaultDependencyNode child )
-    {
-        DefaultDependencyCollectionContext context = args.collectionContext;
-        context.set( parentContext.dependency, descriptorResult.getManagedDependencies() );
-
-        DependencySelector childSelector =
-                parentContext.depSelector != null ? parentContext.depSelector.deriveChildSelector( context ) : null;
-        DependencyManager childManager =
-                parentContext.depManager != null ? parentContext.depManager.deriveChildManager( context ) : null;
-        DependencyTraverser childTraverser =
-                parentContext.depTraverser != null ? parentContext.depTraverser.deriveChildTraverser( context ) : null;
-        VersionFilter childFilter =
-                parentContext.verFilter != null ? parentContext.verFilter.deriveChildFilter( context ) : null;
-
-        final List<RemoteRepository> childRepos =
-                args.ignoreRepos
-                        ? parentContext.repositories
-                        : remoteRepositoryManager.aggregateRepositories( args.session, parentContext.repositories,
-                        descriptorResult.getRepositories(), true );
-
-        Object key =
-                args.pool.toKey( parentContext.dependency.getArtifact(), childRepos, childSelector, childManager,
-                        childTraverser, childFilter );
-
-        List<DependencyNode> children = args.pool.getChildren( key );
-        if ( children == null )
-        {
-            args.pool.putChildren( key, child.getChildren() );
-
-            List<DependencyNode> parents = new ArrayList<>( parentContext.parents );
-            parents.add( child );
-            for ( Dependency dependency : descriptorResult.getDependencies() )
-            {
-                args.dependencyProcessingQueue.add(
-                        new DependencyProcessingContext( childSelector, childManager, childTraverser, childFilter,
-                                childRepos, descriptorResult.getManagedDependencies(), parents, dependency ) );
-            }
-        }
-        else
-        {
-            child.setChildren( children );
-        }
-    }
-
-    private ArtifactDescriptorResult resolveCachedArtifactDescriptor( DataPool pool,
-                                                                      ArtifactDescriptorRequest descriptorRequest,
-                                                                      RepositorySystemSession session,
-                                                                      DependencyProcessingContext context,
-                                                                      Results results )
-    {
-        Object key = pool.toKey( descriptorRequest );
-        ArtifactDescriptorResult descriptorResult = pool.getDescriptor( key, descriptorRequest );
-        if ( descriptorResult == null )
-        {
-            try
-            {
-                descriptorResult = descriptorReader.readArtifactDescriptor( session, descriptorRequest );
-                pool.putDescriptor( key, descriptorResult );
-            }
-            catch ( ArtifactDescriptorException e )
-            {
-                results.addException( context.dependency, e, context.parents );
-                pool.putDescriptor( key, e );
-                return null;
-            }
-
-        }
-        else if ( descriptorResult == DataPool.NO_DESCRIPTOR )
-        {
-            return null;
-        }
-
-        return descriptorResult;
-    }
-
-    @SuppressWarnings( "checkstyle:parameternumber" )
-    private static DefaultDependencyNode createDependencyNode( List<Artifact> relocations,
-                                                               PremanagedDependency preManaged,
-                                                               VersionRangeResult rangeResult, Version version,
-                                                               Dependency d, Collection<Artifact> aliases,
-                                                               List<RemoteRepository> repos, String requestContext )
-    {
-        DefaultDependencyNode child = new DefaultDependencyNode( d );
-        preManaged.applyTo( child );
-        child.setRelocations( relocations );
-        child.setVersionConstraint( rangeResult.getVersionConstraint() );
-        child.setVersion( version );
-        child.setAliases( aliases );
-        child.setRepositories( repos );
-        child.setRequestContext( requestContext );
-        return child;
-    }
-
-    private static DefaultDependencyNode createDependencyNode( List<Artifact> relocations,
-                                                               PremanagedDependency preManaged,
-                                                               VersionRangeResult rangeResult, Version version,
-                                                               Dependency d, ArtifactDescriptorResult descriptorResult,
-                                                               DependencyNode cycleNode )
-    {
-        DefaultDependencyNode child =
-            createDependencyNode( relocations, preManaged, rangeResult, version, d, descriptorResult.getAliases(),
-                                  cycleNode.getRepositories(), cycleNode.getRequestContext() );
-        child.setChildren( cycleNode.getChildren() );
-        return child;
-    }
-
-    private static ArtifactDescriptorRequest createArtifactDescriptorRequest( Args args,
-                                                                              List<RemoteRepository> repositories,
-                                                                              Dependency d )
-    {
-        ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest();
-        descriptorRequest.setArtifact( d.getArtifact() );
-        descriptorRequest.setRepositories( repositories );
-        descriptorRequest.setRequestContext( args.request.getRequestContext() );
-        descriptorRequest.setTrace( args.trace );
-        return descriptorRequest;
-    }
-
-    private static VersionRangeRequest createVersionRangeRequest( Args args, List<RemoteRepository> repositories,
-                                                                  Dependency dependency )
-    {
-        VersionRangeRequest rangeRequest = new VersionRangeRequest();
-        rangeRequest.setArtifact( dependency.getArtifact() );
-        rangeRequest.setRepositories( repositories );
-        rangeRequest.setRequestContext( args.request.getRequestContext() );
-        rangeRequest.setTrace( args.trace );
-        return rangeRequest;
-    }
-
-    private VersionRangeResult cachedResolveRangeResult( VersionRangeRequest rangeRequest, DataPool pool,
-                                                         RepositorySystemSession session )
-        throws VersionRangeResolutionException
-    {
-        Object key = pool.toKey( rangeRequest );
-        VersionRangeResult rangeResult = pool.getConstraint( key, rangeRequest );
-        if ( rangeResult == null )
-        {
-            rangeResult = versionRangeResolver.resolveVersionRange( session, rangeRequest );
-            pool.putConstraint( key, rangeResult );
-        }
-        return rangeResult;
-    }
-
-    private static boolean isLackingDescriptor( Artifact artifact )
-    {
-        return artifact.getProperty( ArtifactProperties.LOCAL_PATH, null ) != null;
-    }
-
-    private static List<RemoteRepository> getRemoteRepositories( ArtifactRepository repository,
-                                                                 List<RemoteRepository> repositories )
-    {
-        if ( repository instanceof RemoteRepository )
-        {
-            return Collections.singletonList( (RemoteRepository) repository );
-        }
-        if ( repository != null )
-        {
-            return Collections.emptyList();
-        }
-        return repositories;
-    }
-
-    private static List<? extends Version> filterVersions( Dependency dependency, VersionRangeResult rangeResult,
-                                                           VersionFilter verFilter,
-                                                           DefaultVersionFilterContext verContext )
-        throws VersionRangeResolutionException
-    {
-        if ( rangeResult.getVersions().isEmpty() )
-        {
-            throw new VersionRangeResolutionException( rangeResult,
-                                                       "No versions available for " + dependency.getArtifact()
-                                                           + " within specified range" );
-        }
-
-        List<? extends Version> versions;
-        if ( verFilter != null && rangeResult.getVersionConstraint().getRange() != null )
-        {
-            verContext.set( dependency, rangeResult );
-            try
-            {
-                verFilter.filterVersions( verContext );
-            }
-            catch ( RepositoryException e )
-            {
-                throw new VersionRangeResolutionException( rangeResult,
-                                                           "Failed to filter versions for " + dependency.getArtifact()
-                                                               + ": " + e.getMessage(), e );
-            }
-            versions = verContext.get();
-            if ( versions.isEmpty() )
-            {
-                throw new VersionRangeResolutionException( rangeResult,
-                                                           "No acceptable versions for " + dependency.getArtifact()
-                                                               + ": " + rangeResult.getVersions() );
-            }
-        }
-        else
-        {
-            versions = rangeResult.getVersions();
-        }
-        return versions;
-    }
-
-    static class Args
-    {
-
-        final RepositorySystemSession session;
-
-        final boolean ignoreRepos;
-
-        final boolean premanagedState;
-
-        final RequestTrace trace;
-
-        final DataPool pool;
-
-        final Queue<DependencyProcessingContext> dependencyProcessingQueue = new ArrayDeque<>();
-
-        final DefaultDependencyCollectionContext collectionContext;
-
-        final DefaultVersionFilterContext versionContext;
-
-        final CollectRequest request;
-
-        Args( RepositorySystemSession session, RequestTrace trace, DataPool pool,
-                     DefaultDependencyCollectionContext collectionContext, DefaultVersionFilterContext versionContext,
-                     CollectRequest request )
-        {
-            this.session = session;
-            this.request = request;
-            this.ignoreRepos = session.isIgnoreArtifactDescriptorRepositories();
-            this.premanagedState = ConfigUtils.getBoolean( session, false, DependencyManagerUtils.CONFIG_PROP_VERBOSE );
-            this.trace = trace;
-            this.pool = pool;
-            this.collectionContext = collectionContext;
-            this.versionContext = versionContext;
-        }
-
-    }
-
-    static class Results
-    {
-
-        private final CollectResult result;
-
-        final int maxExceptions;
-
-        final int maxCycles;
-
-        String errorPath;
-
-        Results( CollectResult result, RepositorySystemSession session )
-        {
-            this.result = result;
-
-            maxExceptions =
-                    ConfigUtils.getInteger( session, CONFIG_PROP_MAX_EXCEPTIONS_DEFAULT, CONFIG_PROP_MAX_EXCEPTIONS );
-
-            maxCycles = ConfigUtils.getInteger( session, CONFIG_PROP_MAX_CYCLES_DEFAULT, CONFIG_PROP_MAX_CYCLES );
-        }
-
-        public void addException( Dependency dependency, Exception e, List<DependencyNode> nodes )
-        {
-            if ( maxExceptions < 0 || result.getExceptions().size() < maxExceptions )
-            {
-                result.addException( e );
-                if ( errorPath == null )
-                {
-                    StringBuilder buffer = new StringBuilder( 256 );
-                    for ( DependencyNode node : nodes )
-                    {
-                        if ( buffer.length() > 0 )
-                        {
-                            buffer.append( " -> " );
-                        }
-                        Dependency dep = node.getDependency();
-                        if ( dep != null )
-                        {
-                            buffer.append( dep.getArtifact() );
-                        }
-                    }
-                    if ( buffer.length() > 0 )
-                    {
-                        buffer.append( " -> " );
-                    }
-                    buffer.append( dependency.getArtifact() );
-                    errorPath = buffer.toString();
-                }
-            }
-        }
-
-        public void addCycle( List<DependencyNode> nodes, int cycleEntry, Dependency dependency )
-        {
-            if ( maxCycles < 0 || result.getCycles().size() < maxCycles )
-            {
-                result.addCycle( new DefaultDependencyCycle( nodes, cycleEntry, dependency ) );
-            }
-        }
-
-    }
-
-    static class PremanagedDependency
-    {
-
-        final String premanagedVersion;
-
-        final String premanagedScope;
-
-        final Boolean premanagedOptional;
-
-        /**
-         * @since 1.1.0
-         */
-        final Collection<Exclusion> premanagedExclusions;
-
-        /**
-         * @since 1.1.0
-         */
-        final Map<String, String> premanagedProperties;
-
-        final int managedBits;
-
-        final Dependency managedDependency;
-
-        final boolean premanagedState;
-
-        @SuppressWarnings( "checkstyle:parameternumber" )
-        PremanagedDependency( String premanagedVersion, String premanagedScope, Boolean premanagedOptional,
-                              Collection<Exclusion> premanagedExclusions, Map<String, String> premanagedProperties,
-                              int managedBits, Dependency managedDependency, boolean premanagedState )
-        {
-            this.premanagedVersion = premanagedVersion;
-            this.premanagedScope = premanagedScope;
-            this.premanagedOptional = premanagedOptional;
-            this.premanagedExclusions =
-                premanagedExclusions != null
-                    ? Collections.unmodifiableCollection( new ArrayList<>( premanagedExclusions ) )
-                    : null;
-
-            this.premanagedProperties =
-                premanagedProperties != null
-                    ? Collections.unmodifiableMap( new HashMap<>( premanagedProperties ) )
-                    : null;
-
-            this.managedBits = managedBits;
-            this.managedDependency = managedDependency;
-            this.premanagedState = premanagedState;
-        }
-
-        static PremanagedDependency create( DependencyManager depManager, Dependency dependency,
-                                            boolean disableVersionManagement, boolean premanagedState )
-        {
-            DependencyManagement depMngt = depManager != null ? depManager.manageDependency( dependency ) : null;
-
-            int managedBits = 0;
-            String premanagedVersion = null;
-            String premanagedScope = null;
-            Boolean premanagedOptional = null;
-            Collection<Exclusion> premanagedExclusions = null;
-            Map<String, String> premanagedProperties = null;
-
-            if ( depMngt != null )
-            {
-                if ( depMngt.getVersion() != null && !disableVersionManagement )
-                {
-                    Artifact artifact = dependency.getArtifact();
-                    premanagedVersion = artifact.getVersion();
-                    dependency = dependency.setArtifact( artifact.setVersion( depMngt.getVersion() ) );
-                    managedBits |= DependencyNode.MANAGED_VERSION;
-                }
-                if ( depMngt.getProperties() != null )
-                {
-                    Artifact artifact = dependency.getArtifact();
-                    premanagedProperties = artifact.getProperties();
-                    dependency = dependency.setArtifact( artifact.setProperties( depMngt.getProperties() ) );
-                    managedBits |= DependencyNode.MANAGED_PROPERTIES;
-                }
-                if ( depMngt.getScope() != null )
-                {
-                    premanagedScope = dependency.getScope();
-                    dependency = dependency.setScope( depMngt.getScope() );
-                    managedBits |= DependencyNode.MANAGED_SCOPE;
-                }
-                if ( depMngt.getOptional() != null )
-                {
-                    premanagedOptional = dependency.isOptional();
-                    dependency = dependency.setOptional( depMngt.getOptional() );
-                    managedBits |= DependencyNode.MANAGED_OPTIONAL;
-                }
-                if ( depMngt.getExclusions() != null )
-                {
-                    premanagedExclusions = dependency.getExclusions();
-                    dependency = dependency.setExclusions( depMngt.getExclusions() );
-                    managedBits |= DependencyNode.MANAGED_EXCLUSIONS;
-                }
-            }
-            return new PremanagedDependency( premanagedVersion, premanagedScope, premanagedOptional,
-                                             premanagedExclusions, premanagedProperties, managedBits, dependency,
-                                             premanagedState );
-
-        }
-
-        public void applyTo( DefaultDependencyNode child )
-        {
-            child.setManagedBits( managedBits );
-            if ( premanagedState )
-            {
-                child.setData( DependencyManagerUtils.NODE_DATA_PREMANAGED_VERSION, premanagedVersion );
-                child.setData( DependencyManagerUtils.NODE_DATA_PREMANAGED_SCOPE, premanagedScope );
-                child.setData( DependencyManagerUtils.NODE_DATA_PREMANAGED_OPTIONAL, premanagedOptional );
-                child.setData( DependencyManagerUtils.NODE_DATA_PREMANAGED_EXCLUSIONS, premanagedExclusions );
-                child.setData( DependencyManagerUtils.NODE_DATA_PREMANAGED_PROPERTIES, premanagedProperties );
-            }
-        }
-
-    }
-
 }
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyGraphTransformationContext.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyGraphTransformationContext.java
index 41c0126..28ea747 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyGraphTransformationContext.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyGraphTransformationContext.java
@@ -28,7 +28,7 @@ import org.eclipse.aether.collection.DependencyGraphTransformationContext;
 
 /**
  */
-class DefaultDependencyGraphTransformationContext
+public class DefaultDependencyGraphTransformationContext
     implements DependencyGraphTransformationContext
 {
 
@@ -36,7 +36,7 @@ class DefaultDependencyGraphTransformationContext
 
     private final Map<Object, Object> map;
 
-    DefaultDependencyGraphTransformationContext( RepositorySystemSession session )
+    public DefaultDependencyGraphTransformationContext( RepositorySystemSession session )
     {
         this.session = session;
         this.map = new HashMap<>();
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultVersionFilterContext.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultVersionFilterContext.java
index bfea062..55b6fba 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultVersionFilterContext.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultVersionFilterContext.java
@@ -36,7 +36,7 @@ import org.eclipse.aether.version.VersionConstraint;
 /**
  * @see DefaultDependencyCollector
  */
-final class DefaultVersionFilterContext
+public final class DefaultVersionFilterContext
     implements VersionFilter.VersionFilterContext
 {
     private final RepositorySystemSession session;
@@ -47,7 +47,7 @@ final class DefaultVersionFilterContext
 
     private List<Version> versions;
 
-    DefaultVersionFilterContext( RepositorySystemSession session )
+    public DefaultVersionFilterContext( RepositorySystemSession session )
     {
         this.session = session;
     }
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java
new file mode 100644
index 0000000..d47a2b9
--- /dev/null
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java
@@ -0,0 +1,29 @@
+package org.eclipse.aether.internal.impl.collect;
+
+/*
+ * 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.
+ */
+
+import org.eclipse.aether.impl.DependencyCollector;
+
+/**
+ * Sub-type for actual implementations.
+ */
+public interface DependencyCollectorDelegate extends DependencyCollector
+{
+}
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java
similarity index 95%
copy from maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java
copy to maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java
index a773367..ad87c22 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java
@@ -1,4 +1,4 @@
-package org.eclipse.aether.internal.impl.collect;
+package org.eclipse.aether.internal.impl.collect.bf;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,6 +19,10 @@ package org.eclipse.aether.internal.impl.collect;
  * under the License.
  */
 
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -30,13 +34,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Queue;
 
-import static java.util.Objects.requireNonNull;
-import static org.eclipse.aether.internal.impl.collect.DefaultDependencyCycle.find;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.inject.Singleton;
-
 import org.eclipse.aether.DefaultRepositorySystemSession;
 import org.eclipse.aether.RepositoryException;
 import org.eclipse.aether.RepositorySystemSession;
@@ -57,9 +54,14 @@ import org.eclipse.aether.graph.Dependency;
 import org.eclipse.aether.graph.DependencyNode;
 import org.eclipse.aether.graph.Exclusion;
 import org.eclipse.aether.impl.ArtifactDescriptorReader;
-import org.eclipse.aether.impl.DependencyCollector;
 import org.eclipse.aether.impl.RemoteRepositoryManager;
 import org.eclipse.aether.impl.VersionRangeResolver;
+import org.eclipse.aether.internal.impl.collect.CachingArtifactTypeRegistry;
+import org.eclipse.aether.internal.impl.collect.DataPool;
+import org.eclipse.aether.internal.impl.collect.DefaultDependencyCollectionContext;
+import org.eclipse.aether.internal.impl.collect.DefaultDependencyGraphTransformationContext;
+import org.eclipse.aether.internal.impl.collect.DefaultVersionFilterContext;
+import org.eclipse.aether.internal.impl.collect.DependencyCollectorDelegate;
 import org.eclipse.aether.repository.ArtifactRepository;
 import org.eclipse.aether.repository.RemoteRepository;
 import org.eclipse.aether.resolution.ArtifactDescriptorException;
@@ -77,13 +79,18 @@ import org.eclipse.aether.version.Version;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static java.util.Objects.requireNonNull;
+import static org.eclipse.aether.internal.impl.collect.bf.BfDependencyCycle.find;
+
 /**
+ * The breadth-first implementation of {@link org.eclipse.aether.impl.DependencyCollector}.
  */
 @Singleton
-@Named
-public class DefaultDependencyCollector
-    implements DependencyCollector, Service
+@Named( BfDependencyCollector.NAME )
+public class BfDependencyCollector
+        implements DependencyCollectorDelegate, Service
 {
+    public static final String NAME = "bf";
 
     private static final String CONFIG_PROP_MAX_EXCEPTIONS = "aether.dependencyCollector.maxExceptions";
 
@@ -93,7 +100,7 @@ public class DefaultDependencyCollector
 
     private static final int CONFIG_PROP_MAX_CYCLES_DEFAULT = 10;
 
-    private static final Logger LOGGER = LoggerFactory.getLogger( DefaultDependencyCollector.class );
+    private static final Logger LOGGER = LoggerFactory.getLogger( BfDependencyCollector.class );
 
     private RemoteRepositoryManager remoteRepositoryManager;
 
@@ -101,13 +108,13 @@ public class DefaultDependencyCollector
 
     private VersionRangeResolver versionRangeResolver;
 
-    public DefaultDependencyCollector()
+    public BfDependencyCollector()
     {
         // enables default constructor
     }
 
     @Inject
-    DefaultDependencyCollector( RemoteRepositoryManager remoteRepositoryManager,
+    BfDependencyCollector( RemoteRepositoryManager remoteRepositoryManager,
                                 ArtifactDescriptorReader artifactDescriptorReader,
                                 VersionRangeResolver versionRangeResolver )
     {
@@ -123,20 +130,20 @@ public class DefaultDependencyCollector
         setVersionRangeResolver( locator.getService( VersionRangeResolver.class ) );
     }
 
-    public DefaultDependencyCollector setRemoteRepositoryManager( RemoteRepositoryManager remoteRepositoryManager )
+    public BfDependencyCollector setRemoteRepositoryManager( RemoteRepositoryManager remoteRepositoryManager )
     {
         this.remoteRepositoryManager =
                 requireNonNull( remoteRepositoryManager, "remote repository provider cannot be null" );
         return this;
     }
 
-    public DefaultDependencyCollector setArtifactDescriptorReader( ArtifactDescriptorReader artifactDescriptorReader )
+    public BfDependencyCollector setArtifactDescriptorReader( ArtifactDescriptorReader artifactDescriptorReader )
     {
         descriptorReader = requireNonNull( artifactDescriptorReader, "artifact descriptor reader cannot be null" );
         return this;
     }
 
-    public DefaultDependencyCollector setVersionRangeResolver( VersionRangeResolver versionRangeResolver )
+    public BfDependencyCollector setVersionRangeResolver( VersionRangeResolver versionRangeResolver )
     {
         this.versionRangeResolver =
                 requireNonNull( versionRangeResolver, "version range resolver cannot be null" );
@@ -266,7 +273,7 @@ public class DefaultDependencyCollector
             for ( Dependency dependency : dependencies )
             {
                 args.dependencyProcessingQueue.add(
-                        new DependencyProcessingContext( rootDepSelector, rootDepManager, rootDepTraverser,
+                        new BfProcessingContext( rootDepSelector, rootDepManager, rootDepTraverser,
                                 rootVerFilter, repositories, managedDependencies, Collections.singletonList( node ),
                                 dependency ) );
             }
@@ -360,7 +367,7 @@ public class DefaultDependencyCollector
     }
 
     @SuppressWarnings( "checkstyle:parameternumber" )
-    private void processDependency( Args args, Results results, DependencyProcessingContext context,
+    private void processDependency( Args args, Results results, BfProcessingContext context,
                                     List<Artifact> relocations, boolean disableVersionManagement )
     {
 
@@ -472,7 +479,7 @@ public class DefaultDependencyCollector
     }
 
     @SuppressWarnings( "checkstyle:parameternumber" )
-    private void doRecurse( Args args, DependencyProcessingContext parentContext,
+    private void doRecurse( Args args, BfProcessingContext parentContext,
                             ArtifactDescriptorResult descriptorResult, DefaultDependencyNode child )
     {
         DefaultDependencyCollectionContext context = args.collectionContext;
@@ -507,7 +514,7 @@ public class DefaultDependencyCollector
             for ( Dependency dependency : descriptorResult.getDependencies() )
             {
                 args.dependencyProcessingQueue.add(
-                        new DependencyProcessingContext( childSelector, childManager, childTraverser, childFilter,
+                        new BfProcessingContext( childSelector, childManager, childTraverser, childFilter,
                                 childRepos, descriptorResult.getManagedDependencies(), parents, dependency ) );
             }
         }
@@ -520,7 +527,7 @@ public class DefaultDependencyCollector
     private ArtifactDescriptorResult resolveCachedArtifactDescriptor( DataPool pool,
                                                                       ArtifactDescriptorRequest descriptorRequest,
                                                                       RepositorySystemSession session,
-                                                                      DependencyProcessingContext context,
+                                                                      BfProcessingContext context,
                                                                       Results results )
     {
         Object key = pool.toKey( descriptorRequest );
@@ -689,7 +696,7 @@ public class DefaultDependencyCollector
 
         final DataPool pool;
 
-        final Queue<DependencyProcessingContext> dependencyProcessingQueue = new ArrayDeque<>();
+        final Queue<BfProcessingContext> dependencyProcessingQueue = new ArrayDeque<>();
 
         final DefaultDependencyCollectionContext collectionContext;
 
@@ -768,7 +775,7 @@ public class DefaultDependencyCollector
         {
             if ( maxCycles < 0 || result.getCycles().size() < maxCycles )
             {
-                result.addCycle( new DefaultDependencyCycle( nodes, cycleEntry, dependency ) );
+                result.addCycle( new BfDependencyCycle( nodes, cycleEntry, dependency ) );
             }
         }
 
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycle.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCycle.java
similarity index 95%
copy from maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycle.java
copy to maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCycle.java
index dd1565c..fb370a6 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycle.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCycle.java
@@ -1,4 +1,4 @@
-package org.eclipse.aether.internal.impl.collect;
+package org.eclipse.aether.internal.impl.collect.bf;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -30,9 +30,9 @@ import org.eclipse.aether.graph.DependencyNode;
 import org.eclipse.aether.util.artifact.ArtifactIdUtils;
 
 /**
- * @see DefaultDependencyCollector
+ * @see BfDependencyCollector
  */
-final class DefaultDependencyCycle
+final class BfDependencyCycle
     implements DependencyCycle
 {
 
@@ -40,7 +40,7 @@ final class DefaultDependencyCycle
 
     private final int cycleEntry;
 
-    DefaultDependencyCycle( List<DependencyNode> nodes, int cycleEntry, Dependency dependency )
+    BfDependencyCycle( List<DependencyNode> nodes, int cycleEntry, Dependency dependency )
     {
         // skip root node unless it actually has a dependency or is considered the cycle entry (due to its label)
         int offset = ( cycleEntry > 0 && nodes.get( 0 ).getDependency() == null ) ? 1 : 0;
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyProcessingContext.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfProcessingContext.java
similarity index 88%
rename from maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyProcessingContext.java
rename to maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfProcessingContext.java
index 71815d0..2cca571 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyProcessingContext.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfProcessingContext.java
@@ -1,4 +1,4 @@
-package org.eclipse.aether.internal.impl.collect;
+package org.eclipse.aether.internal.impl.collect.bf;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -29,7 +29,7 @@ import org.eclipse.aether.graph.Dependency;
 import org.eclipse.aether.graph.DependencyNode;
 import org.eclipse.aether.repository.RemoteRepository;
 
-final class DependencyProcessingContext
+final class BfProcessingContext
 {
     final DependencySelector depSelector;
     final DependencyManager depManager;
@@ -45,7 +45,7 @@ final class DependencyProcessingContext
     final Dependency dependency;
 
     @SuppressWarnings( "checkstyle:parameternumber" )
-    DependencyProcessingContext( DependencySelector depSelector,
+    BfProcessingContext( DependencySelector depSelector,
                                  DependencyManager depManager,
                                  DependencyTraverser depTraverser,
                                  VersionFilter verFilter,
@@ -64,9 +64,9 @@ final class DependencyProcessingContext
         this.parents = parents;
     }
 
-    DependencyProcessingContext withDependency( Dependency dependency )
+    BfProcessingContext withDependency( Dependency dependency )
     {
-        return new DependencyProcessingContext( depSelector, depManager, depTraverser, verFilter, repositories,
+        return new BfProcessingContext( depSelector, depManager, depTraverser, verFilter, repositories,
                 managedDependencies, parents, dependency );
     }
 
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java
similarity index 80%
copy from maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java
copy to maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java
index a773367..1fb00f4 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java
@@ -1,4 +1,4 @@
-package org.eclipse.aether.internal.impl.collect;
+package org.eclipse.aether.internal.impl.collect.df;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,7 +19,10 @@ package org.eclipse.aether.internal.impl.collect;
  * under the License.
  */
 
-import java.util.ArrayDeque;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -28,14 +31,6 @@ import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Queue;
-
-import static java.util.Objects.requireNonNull;
-import static org.eclipse.aether.internal.impl.collect.DefaultDependencyCycle.find;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.inject.Singleton;
 
 import org.eclipse.aether.DefaultRepositorySystemSession;
 import org.eclipse.aether.RepositoryException;
@@ -57,9 +52,14 @@ import org.eclipse.aether.graph.Dependency;
 import org.eclipse.aether.graph.DependencyNode;
 import org.eclipse.aether.graph.Exclusion;
 import org.eclipse.aether.impl.ArtifactDescriptorReader;
-import org.eclipse.aether.impl.DependencyCollector;
 import org.eclipse.aether.impl.RemoteRepositoryManager;
 import org.eclipse.aether.impl.VersionRangeResolver;
+import org.eclipse.aether.internal.impl.collect.CachingArtifactTypeRegistry;
+import org.eclipse.aether.internal.impl.collect.DataPool;
+import org.eclipse.aether.internal.impl.collect.DefaultDependencyCollectionContext;
+import org.eclipse.aether.internal.impl.collect.DefaultDependencyGraphTransformationContext;
+import org.eclipse.aether.internal.impl.collect.DefaultVersionFilterContext;
+import org.eclipse.aether.internal.impl.collect.DependencyCollectorDelegate;
 import org.eclipse.aether.repository.ArtifactRepository;
 import org.eclipse.aether.repository.RemoteRepository;
 import org.eclipse.aether.resolution.ArtifactDescriptorException;
@@ -77,13 +77,17 @@ import org.eclipse.aether.version.Version;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static java.util.Objects.requireNonNull;
+
 /**
+ * The depth-first implementation of {@link org.eclipse.aether.impl.DependencyCollector}.
  */
 @Singleton
-@Named
-public class DefaultDependencyCollector
-    implements DependencyCollector, Service
+@Named( DfDependencyCollector.NAME )
+public class DfDependencyCollector
+        implements DependencyCollectorDelegate, Service
 {
+    public static final String NAME = "df";
 
     private static final String CONFIG_PROP_MAX_EXCEPTIONS = "aether.dependencyCollector.maxExceptions";
 
@@ -93,7 +97,7 @@ public class DefaultDependencyCollector
 
     private static final int CONFIG_PROP_MAX_CYCLES_DEFAULT = 10;
 
-    private static final Logger LOGGER = LoggerFactory.getLogger( DefaultDependencyCollector.class );
+    private static final Logger LOGGER = LoggerFactory.getLogger( DfDependencyCollector.class );
 
     private RemoteRepositoryManager remoteRepositoryManager;
 
@@ -101,15 +105,15 @@ public class DefaultDependencyCollector
 
     private VersionRangeResolver versionRangeResolver;
 
-    public DefaultDependencyCollector()
+    public DfDependencyCollector()
     {
         // enables default constructor
     }
 
     @Inject
-    DefaultDependencyCollector( RemoteRepositoryManager remoteRepositoryManager,
-                                ArtifactDescriptorReader artifactDescriptorReader,
-                                VersionRangeResolver versionRangeResolver )
+    DfDependencyCollector( RemoteRepositoryManager remoteRepositoryManager,
+                           ArtifactDescriptorReader artifactDescriptorReader,
+                           VersionRangeResolver versionRangeResolver )
     {
         setRemoteRepositoryManager( remoteRepositoryManager );
         setArtifactDescriptorReader( artifactDescriptorReader );
@@ -123,20 +127,20 @@ public class DefaultDependencyCollector
         setVersionRangeResolver( locator.getService( VersionRangeResolver.class ) );
     }
 
-    public DefaultDependencyCollector setRemoteRepositoryManager( RemoteRepositoryManager remoteRepositoryManager )
+    public DfDependencyCollector setRemoteRepositoryManager( RemoteRepositoryManager remoteRepositoryManager )
     {
         this.remoteRepositoryManager =
                 requireNonNull( remoteRepositoryManager, "remote repository provider cannot be null" );
         return this;
     }
 
-    public DefaultDependencyCollector setArtifactDescriptorReader( ArtifactDescriptorReader artifactDescriptorReader )
+    public DfDependencyCollector setArtifactDescriptorReader( ArtifactDescriptorReader artifactDescriptorReader )
     {
         descriptorReader = requireNonNull( artifactDescriptorReader, "artifact descriptor reader cannot be null" );
         return this;
     }
 
-    public DefaultDependencyCollector setVersionRangeResolver( VersionRangeResolver versionRangeResolver )
+    public DfDependencyCollector setVersionRangeResolver( VersionRangeResolver versionRangeResolver )
     {
         this.versionRangeResolver =
                 requireNonNull( versionRangeResolver, "version range resolver cannot be null" );
@@ -248,34 +252,23 @@ public class DefaultDependencyCollector
         {
             DataPool pool = new DataPool( session );
 
+            NodeStack
+                    nodes = new NodeStack();
+            nodes.push( node );
+
             DefaultDependencyCollectionContext context =
                 new DefaultDependencyCollectionContext( session, request.getRootArtifact(), root, managedDependencies );
 
             DefaultVersionFilterContext versionContext = new DefaultVersionFilterContext( session );
 
-            Args args = new Args( session, trace, pool, context, versionContext, request );
+            Args args = new Args( session, trace, pool, nodes, context, versionContext, request );
             Results results = new Results( result, session );
 
-            DependencySelector rootDepSelector =
-                    depSelector != null ? depSelector.deriveChildSelector( context ) : null;
-            DependencyManager rootDepManager = depManager != null ? depManager.deriveChildManager( context ) : null;
-            DependencyTraverser rootDepTraverser =
-                    depTraverser != null ? depTraverser.deriveChildTraverser( context ) : null;
-            VersionFilter rootVerFilter = verFilter != null ? verFilter.deriveChildFilter( context ) : null;
-
-            for ( Dependency dependency : dependencies )
-            {
-                args.dependencyProcessingQueue.add(
-                        new DependencyProcessingContext( rootDepSelector, rootDepManager, rootDepTraverser,
-                                rootVerFilter, repositories, managedDependencies, Collections.singletonList( node ),
-                                dependency ) );
-            }
-
-            while ( !args.dependencyProcessingQueue.isEmpty() )
-            {
-                processDependency( args, results, args.dependencyProcessingQueue.remove(), Collections.emptyList(),
-                        false );
-            }
+            process( args, results, dependencies, repositories,
+                     depSelector != null ? depSelector.deriveChildSelector( context ) : null,
+                     depManager != null ? depManager.deriveChildManager( context ) : null,
+                     depTraverser != null ? depTraverser.deriveChildTraverser( context ) : null,
+                     verFilter != null ? verFilter.deriveChildFilter( context ) : null );
 
             errorPath = results.errorPath;
         }
@@ -360,39 +353,61 @@ public class DefaultDependencyCollector
     }
 
     @SuppressWarnings( "checkstyle:parameternumber" )
-    private void processDependency( Args args, Results results, DependencyProcessingContext context,
+    private void process( final Args args, Results results, List<Dependency> dependencies,
+                          List<RemoteRepository> repositories, DependencySelector depSelector,
+                          DependencyManager depManager, DependencyTraverser depTraverser, VersionFilter verFilter )
+    {
+        for ( Dependency dependency : dependencies )
+        {
+            processDependency( args, results, repositories, depSelector, depManager, depTraverser, verFilter,
+                               dependency );
+        }
+    }
+
+    @SuppressWarnings( "checkstyle:parameternumber" )
+    private void processDependency( Args args, Results results, List<RemoteRepository> repositories,
+                                    DependencySelector depSelector, DependencyManager depManager,
+                                    DependencyTraverser depTraverser, VersionFilter verFilter, Dependency dependency )
+    {
+
+        List<Artifact> relocations = Collections.emptyList();
+        processDependency( args, results, repositories, depSelector, depManager, depTraverser, verFilter, dependency,
+                           relocations, false );
+    }
+
+    @SuppressWarnings( "checkstyle:parameternumber" )
+    private void processDependency( Args args, Results results, List<RemoteRepository> repositories,
+                                    DependencySelector depSelector, DependencyManager depManager,
+                                    DependencyTraverser depTraverser, VersionFilter verFilter, Dependency dependency,
                                     List<Artifact> relocations, boolean disableVersionManagement )
     {
 
-        if ( context.depSelector != null && !context.depSelector.selectDependency( context.dependency ) )
+        if ( depSelector != null && !depSelector.selectDependency( dependency ) )
         {
             return;
         }
 
         PremanagedDependency preManaged =
-                PremanagedDependency.create( context.depManager, context.dependency, disableVersionManagement,
-                        args.premanagedState );
-        Dependency dependency = preManaged.managedDependency;
+            PremanagedDependency.create( depManager, dependency, disableVersionManagement, args.premanagedState );
+        dependency = preManaged.managedDependency;
 
         boolean noDescriptor = isLackingDescriptor( dependency.getArtifact() );
 
-        boolean traverse =
-                !noDescriptor && ( context.depTraverser == null || context.depTraverser.traverseDependency(
-                        dependency ) );
+        boolean traverse = !noDescriptor && ( depTraverser == null || depTraverser.traverseDependency( dependency ) );
 
         List<? extends Version> versions;
         VersionRangeResult rangeResult;
         try
         {
-            VersionRangeRequest rangeRequest = createVersionRangeRequest( args, context.repositories, dependency );
+            VersionRangeRequest rangeRequest = createVersionRangeRequest( args, repositories, dependency );
 
             rangeResult = cachedResolveRangeResult( rangeRequest, args.pool, args.session );
 
-            versions = filterVersions( dependency, rangeResult, context.verFilter, args.versionContext );
+            versions = filterVersions( dependency, rangeResult, verFilter, args.versionContext );
         }
         catch ( VersionRangeResolutionException e )
         {
-            results.addException( dependency, e, context.parents );
+            results.addException( dependency, e, args.nodes );
             return;
         }
 
@@ -401,30 +416,27 @@ public class DefaultDependencyCollector
             Artifact originalArtifact = dependency.getArtifact().setVersion( version.toString() );
             Dependency d = dependency.setArtifact( originalArtifact );
 
-            ArtifactDescriptorRequest descriptorRequest =
-                    createArtifactDescriptorRequest( args, context.repositories, d );
+            ArtifactDescriptorRequest descriptorRequest = createArtifactDescriptorRequest( args, repositories, d );
 
             final ArtifactDescriptorResult descriptorResult =
-                    noDescriptor
-                            ? new ArtifactDescriptorResult( descriptorRequest )
-                            : resolveCachedArtifactDescriptor( args.pool, descriptorRequest, args.session,
-                                    context.withDependency( d ), results );
-
+                getArtifactDescriptorResult( args, results, noDescriptor, d, descriptorRequest );
             if ( descriptorResult != null )
             {
                 d = d.setArtifact( descriptorResult.getArtifact() );
 
-                int cycleEntry = find( context.parents, d.getArtifact() );
+                DependencyNode node = args.nodes.top();
+
+                int cycleEntry = args.nodes.find( d.getArtifact() );
                 if ( cycleEntry >= 0 )
                 {
-                    results.addCycle( context.parents, cycleEntry, d );
-                    DependencyNode cycleNode = context.parents.get( cycleEntry );
+                    results.addCycle( args.nodes, cycleEntry, d );
+                    DependencyNode cycleNode = args.nodes.get( cycleEntry );
                     if ( cycleNode.getDependency() != null )
                     {
                         DefaultDependencyNode child =
-                                createDependencyNode( relocations, preManaged, rangeResult, version, d,
-                                        descriptorResult, cycleNode );
-                        context.getParent().getChildren().add( child );
+                            createDependencyNode( relocations, preManaged, rangeResult, version, d, descriptorResult,
+                                                  cycleNode );
+                        node.getChildren().add( child );
                         continue;
                     }
                 }
@@ -435,8 +447,8 @@ public class DefaultDependencyCollector
                         originalArtifact.getGroupId().equals( d.getArtifact().getGroupId() )
                             && originalArtifact.getArtifactId().equals( d.getArtifact().getArtifactId() );
 
-                    processDependency( args, results, context.withDependency( d ), descriptorResult.getRelocations(),
-                            disableVersionManagementSubsequently );
+                    processDependency( args, results, repositories, depSelector, depManager, depTraverser, verFilter, d,
+                                       descriptorResult.getRelocations(), disableVersionManagementSubsequently );
                     return;
                 }
                 else
@@ -444,72 +456,69 @@ public class DefaultDependencyCollector
                     d = args.pool.intern( d.setArtifact( args.pool.intern( d.getArtifact() ) ) );
 
                     List<RemoteRepository> repos =
-                        getRemoteRepositories( rangeResult.getRepository( version ), context.repositories );
+                        getRemoteRepositories( rangeResult.getRepository( version ), repositories );
 
                     DefaultDependencyNode child =
                         createDependencyNode( relocations, preManaged, rangeResult, version, d,
                                               descriptorResult.getAliases(), repos, args.request.getRequestContext() );
 
-                    context.getParent().getChildren().add( child );
+                    node.getChildren().add( child );
 
                     boolean recurse = traverse && !descriptorResult.getDependencies().isEmpty();
                     if ( recurse )
                     {
-                        doRecurse( args, context.withDependency( d ), descriptorResult, child );
+                        doRecurse( args, results, repositories, depSelector, depManager, depTraverser, verFilter, d,
+                                   descriptorResult, child );
                     }
                 }
             }
             else
             {
+                DependencyNode node = args.nodes.top();
                 List<RemoteRepository> repos =
-                    getRemoteRepositories( rangeResult.getRepository( version ), context.repositories );
+                    getRemoteRepositories( rangeResult.getRepository( version ), repositories );
                 DefaultDependencyNode child =
                     createDependencyNode( relocations, preManaged, rangeResult, version, d, null, repos,
                                           args.request.getRequestContext() );
-                context.getParent().getChildren().add( child );
+                node.getChildren().add( child );
             }
         }
     }
 
     @SuppressWarnings( "checkstyle:parameternumber" )
-    private void doRecurse( Args args, DependencyProcessingContext parentContext,
+    private void doRecurse( Args args, Results results, List<RemoteRepository> repositories,
+                            DependencySelector depSelector, DependencyManager depManager,
+                            DependencyTraverser depTraverser, VersionFilter verFilter, Dependency d,
                             ArtifactDescriptorResult descriptorResult, DefaultDependencyNode child )
     {
         DefaultDependencyCollectionContext context = args.collectionContext;
-        context.set( parentContext.dependency, descriptorResult.getManagedDependencies() );
+        context.set( d, descriptorResult.getManagedDependencies() );
 
-        DependencySelector childSelector =
-                parentContext.depSelector != null ? parentContext.depSelector.deriveChildSelector( context ) : null;
-        DependencyManager childManager =
-                parentContext.depManager != null ? parentContext.depManager.deriveChildManager( context ) : null;
-        DependencyTraverser childTraverser =
-                parentContext.depTraverser != null ? parentContext.depTraverser.deriveChildTraverser( context ) : null;
-        VersionFilter childFilter =
-                parentContext.verFilter != null ? parentContext.verFilter.deriveChildFilter( context ) : null;
+        DependencySelector childSelector = depSelector != null ? depSelector.deriveChildSelector( context ) : null;
+        DependencyManager childManager = depManager != null ? depManager.deriveChildManager( context ) : null;
+        DependencyTraverser childTraverser = depTraverser != null ? depTraverser.deriveChildTraverser( context ) : null;
+        VersionFilter childFilter = verFilter != null ? verFilter.deriveChildFilter( context ) : null;
 
         final List<RemoteRepository> childRepos =
-                args.ignoreRepos
-                        ? parentContext.repositories
-                        : remoteRepositoryManager.aggregateRepositories( args.session, parentContext.repositories,
-                        descriptorResult.getRepositories(), true );
+            args.ignoreRepos
+                ? repositories
+                : remoteRepositoryManager.aggregateRepositories( args.session, repositories,
+                                                                 descriptorResult.getRepositories(), true );
 
         Object key =
-                args.pool.toKey( parentContext.dependency.getArtifact(), childRepos, childSelector, childManager,
-                        childTraverser, childFilter );
+            args.pool.toKey( d.getArtifact(), childRepos, childSelector, childManager, childTraverser, childFilter );
 
         List<DependencyNode> children = args.pool.getChildren( key );
         if ( children == null )
         {
             args.pool.putChildren( key, child.getChildren() );
 
-            List<DependencyNode> parents = new ArrayList<>( parentContext.parents );
-            parents.add( child );
-            for ( Dependency dependency : descriptorResult.getDependencies() )
-            {
-                args.dependencyProcessingQueue.add(
-                        new DependencyProcessingContext( childSelector, childManager, childTraverser, childFilter,
-                                childRepos, descriptorResult.getManagedDependencies(), parents, dependency ) );
-            }
+            args.nodes.push( child );
+
+            process( args, results, descriptorResult.getDependencies(), childRepos, childSelector, childManager,
+                     childTraverser, childFilter );
+
+            args.nodes.pop();
         }
         else
         {
@@ -517,11 +526,20 @@ public class DefaultDependencyCollector
         }
     }
 
+    private ArtifactDescriptorResult getArtifactDescriptorResult( Args args, Results results, boolean noDescriptor,
+                                                                  Dependency d,
+                                                                  ArtifactDescriptorRequest descriptorRequest )
+    {
+        return noDescriptor
+                   ? new ArtifactDescriptorResult( descriptorRequest )
+                   : resolveCachedArtifactDescriptor( args.pool, descriptorRequest, args.session, d, results, args );
+
+    }
+
     private ArtifactDescriptorResult resolveCachedArtifactDescriptor( DataPool pool,
                                                                       ArtifactDescriptorRequest descriptorRequest,
-                                                                      RepositorySystemSession session,
-                                                                      DependencyProcessingContext context,
-                                                                      Results results )
+                                                                      RepositorySystemSession session, Dependency d,
+                                                                      Results results, Args args )
     {
         Object key = pool.toKey( descriptorRequest );
         ArtifactDescriptorResult descriptorResult = pool.getDescriptor( key, descriptorRequest );
@@ -534,7 +552,7 @@ public class DefaultDependencyCollector
             }
             catch ( ArtifactDescriptorException e )
             {
-                results.addException( context.dependency, e, context.parents );
+                results.addException( d, e, args.nodes );
                 pool.putDescriptor( key, e );
                 return null;
             }
@@ -689,7 +707,7 @@ public class DefaultDependencyCollector
 
         final DataPool pool;
 
-        final Queue<DependencyProcessingContext> dependencyProcessingQueue = new ArrayDeque<>();
+        final NodeStack nodes;
 
         final DefaultDependencyCollectionContext collectionContext;
 
@@ -697,7 +715,7 @@ public class DefaultDependencyCollector
 
         final CollectRequest request;
 
-        Args( RepositorySystemSession session, RequestTrace trace, DataPool pool,
+        Args( RepositorySystemSession session, RequestTrace trace, DataPool pool, NodeStack nodes,
                      DefaultDependencyCollectionContext collectionContext, DefaultVersionFilterContext versionContext,
                      CollectRequest request )
         {
@@ -707,6 +725,7 @@ public class DefaultDependencyCollector
             this.premanagedState = ConfigUtils.getBoolean( session, false, DependencyManagerUtils.CONFIG_PROP_VERBOSE );
             this.trace = trace;
             this.pool = pool;
+            this.nodes = nodes;
             this.collectionContext = collectionContext;
             this.versionContext = versionContext;
         }
@@ -734,7 +753,7 @@ public class DefaultDependencyCollector
             maxCycles = ConfigUtils.getInteger( session, CONFIG_PROP_MAX_CYCLES_DEFAULT, CONFIG_PROP_MAX_CYCLES );
         }
 
-        public void addException( Dependency dependency, Exception e, List<DependencyNode> nodes )
+        public void addException( Dependency dependency, Exception e, NodeStack nodes )
         {
             if ( maxExceptions < 0 || result.getExceptions().size() < maxExceptions )
             {
@@ -742,13 +761,13 @@ public class DefaultDependencyCollector
                 if ( errorPath == null )
                 {
                     StringBuilder buffer = new StringBuilder( 256 );
-                    for ( DependencyNode node : nodes )
+                    for ( int i = 0; i < nodes.size(); i++ )
                     {
                         if ( buffer.length() > 0 )
                         {
                             buffer.append( " -> " );
                         }
-                        Dependency dep = node.getDependency();
+                        Dependency dep = nodes.get( i ).getDependency();
                         if ( dep != null )
                         {
                             buffer.append( dep.getArtifact() );
@@ -764,11 +783,11 @@ public class DefaultDependencyCollector
             }
         }
 
-        public void addCycle( List<DependencyNode> nodes, int cycleEntry, Dependency dependency )
+        public void addCycle( NodeStack nodes, int cycleEntry, Dependency dependency )
         {
             if ( maxCycles < 0 || result.getCycles().size() < maxCycles )
             {
-                result.addCycle( new DefaultDependencyCycle( nodes, cycleEntry, dependency ) );
+                result.addCycle( new DfDependencyCycle( nodes, cycleEntry, dependency ) );
             }
         }
 
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycle.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCycle.java
similarity index 57%
rename from maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycle.java
rename to maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCycle.java
index dd1565c..b0ff519 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycle.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCycle.java
@@ -1,4 +1,4 @@
-package org.eclipse.aether.internal.impl.collect;
+package org.eclipse.aether.internal.impl.collect.df;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -23,16 +23,15 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
-import org.eclipse.aether.artifact.Artifact;
 import org.eclipse.aether.graph.Dependency;
 import org.eclipse.aether.graph.DependencyCycle;
 import org.eclipse.aether.graph.DependencyNode;
 import org.eclipse.aether.util.artifact.ArtifactIdUtils;
 
 /**
- * @see DefaultDependencyCollector
+ * @see DfDependencyCollector
  */
-final class DefaultDependencyCycle
+final class DfDependencyCycle
     implements DependencyCycle
 {
 
@@ -40,7 +39,7 @@ final class DefaultDependencyCycle
 
     private final int cycleEntry;
 
-    DefaultDependencyCycle( List<DependencyNode> nodes, int cycleEntry, Dependency dependency )
+    DfDependencyCycle( NodeStack nodes, int cycleEntry, Dependency dependency )
     {
         // skip root node unless it actually has a dependency or is considered the cycle entry (due to its label)
         int offset = ( cycleEntry > 0 && nodes.get( 0 ).getDependency() == null ) ? 1 : 0;
@@ -70,58 +69,6 @@ final class DefaultDependencyCycle
         return dependencies.subList( cycleEntry, dependencies.size() );
     }
 
-    /**
-     * Searches for a node associated with the given artifact. A version of the artifact is not considered during the
-     * search.
-     *
-     * @param nodes a list representing single path in the dependency graph. First element is the root.
-     * @param artifact to find among the parent nodes.
-     * @return the index of the node furthest from the root and associated with the given artifact, or {@literal -1} if
-     * there is no such node.
-     */
-    public static int find( List<DependencyNode> nodes, Artifact artifact )
-    {
-
-        for ( int i = nodes.size() - 1; i >= 0; i-- )
-        {
-            DependencyNode node = nodes.get( i );
-
-            Artifact a = node.getArtifact();
-            if ( a == null )
-            {
-                break;
-            }
-
-            if ( !a.getArtifactId().equals( artifact.getArtifactId() ) )
-            {
-                continue;
-            }
-            if ( !a.getGroupId().equals( artifact.getGroupId() ) )
-            {
-                continue;
-            }
-            if ( !a.getExtension().equals( artifact.getExtension() ) )
-            {
-                continue;
-            }
-            if ( !a.getClassifier().equals( artifact.getClassifier() ) )
-            {
-                continue;
-            }
-            /*
-             * NOTE: While a:1 and a:2 are technically different artifacts, we want to consider the path a:2 -> b:2 ->
-             * a:1 a cycle in the current context. The artifacts themselves might not form a cycle but their producing
-             * projects surely do. Furthermore, conflict resolution will always have to consider a:1 a loser (otherwise
-             * its ancestor a:2 would get pruned and so would a:1) so there is no point in building the sub graph of
-             * a:1.
-             */
-
-            return i;
-        }
-
-        return -1;
-    }
-
     @Override
     public String toString()
     {
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/NodeStack.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/NodeStack.java
new file mode 100644
index 0000000..81123c1
--- /dev/null
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/NodeStack.java
@@ -0,0 +1,127 @@
+package org.eclipse.aether.internal.impl.collect.df;
+
+/*
+ * 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.
+ */
+
+import java.util.Arrays;
+
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.graph.DependencyNode;
+
+/**
+ * @see DfDependencyCollector
+ */
+final class NodeStack
+{
+
+    @SuppressWarnings( {"unchecked", "checkstyle:magicnumber" } )
+    // CHECKSTYLE_OFF: MagicNumber
+    private DependencyNode[] nodes = new DependencyNode[96];
+    // CHECKSTYLE_ON: MagicNumber
+
+    private int size;
+
+    public DependencyNode top()
+    {
+        if ( size <= 0 )
+        {
+            throw new IllegalStateException( "stack empty" );
+        }
+        return nodes[size - 1];
+    }
+
+    public void push( DependencyNode node )
+    {
+        if ( size >= nodes.length )
+        {
+            DependencyNode[] tmp = new DependencyNode[size + 64];
+            System.arraycopy( nodes, 0, tmp, 0, nodes.length );
+            nodes = tmp;
+        }
+        nodes[size++] = node;
+    }
+
+    public void pop()
+    {
+        if ( size <= 0 )
+        {
+            throw new IllegalStateException( "stack empty" );
+        }
+        size--;
+    }
+
+    public int find( Artifact artifact )
+    {
+        for ( int i = size - 1; i >= 0; i-- )
+        {
+            DependencyNode node = nodes[i];
+
+            Artifact a = node.getArtifact();
+            if ( a == null )
+            {
+                break;
+            }
+
+            if ( !a.getArtifactId().equals( artifact.getArtifactId() ) )
+            {
+                continue;
+            }
+            if ( !a.getGroupId().equals( artifact.getGroupId() ) )
+            {
+                continue;
+            }
+            if ( !a.getExtension().equals( artifact.getExtension() ) )
+            {
+                continue;
+            }
+            if ( !a.getClassifier().equals( artifact.getClassifier() ) )
+            {
+                continue;
+            }
+            /*
+             * NOTE: While a:1 and a:2 are technically different artifacts, we want to consider the path a:2 -> b:2 ->
+             * a:1 a cycle in the current context. The artifacts themselves might not form a cycle but their producing
+             * projects surely do. Furthermore, conflict resolution will always have to consider a:1 a loser (otherwise
+             * its ancestor a:2 would get pruned and so would a:1) so there is no point in building the sub graph of
+             * a:1.
+             */
+
+            return i;
+        }
+
+        return -1;
+    }
+
+    public int size()
+    {
+        return size;
+    }
+
+    public DependencyNode get( int index )
+    {
+        return nodes[index];
+    }
+
+    @Override
+    public String toString()
+    {
+        return Arrays.toString( nodes );
+    }
+
+}
diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectorTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollectorTest.java
similarity index 99%
copy from maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectorTest.java
copy to maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollectorTest.java
index 5d4c2aa..fcae807 100644
--- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectorTest.java
+++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollectorTest.java
@@ -1,4 +1,4 @@
-package org.eclipse.aether.internal.impl.collect;
+package org.eclipse.aether.internal.impl.collect.bf;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -18,14 +18,6 @@ package org.eclipse.aether.internal.impl.collect;
  * specific language governing permissions and limitations
  * under the License.
  */
-import static java.util.Objects.requireNonNull;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -73,12 +65,21 @@ import org.eclipse.aether.util.graph.version.HighestVersionFilter;
 import org.junit.Before;
 import org.junit.Test;
 
+import static java.util.Objects.requireNonNull;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 /**
  */
-public class DefaultDependencyCollectorTest
+public class BfDependencyCollectorTest
 {
 
-    private DefaultDependencyCollector collector;
+    private BfDependencyCollector collector;
 
     private DefaultRepositorySystemSession session;
 
@@ -106,7 +107,7 @@ public class DefaultDependencyCollectorTest
     {
         session = TestUtils.newSession();
 
-        collector = new DefaultDependencyCollector();
+        collector = new BfDependencyCollector();
         collector.setArtifactDescriptorReader( newReader( "" ) );
         collector.setVersionRangeResolver( new StubVersionRangeResolver() );
         collector.setRemoteRepositoryManager( new StubRemoteRepositoryManager() );
diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycleTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCycleTest.java
similarity index 90%
copy from maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycleTest.java
copy to maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCycleTest.java
index 0a382f8..a1be7c9 100644
--- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycleTest.java
+++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCycleTest.java
@@ -1,4 +1,4 @@
-package org.eclipse.aether.internal.impl.collect;
+package org.eclipse.aether.internal.impl.collect.bf;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,6 +19,9 @@ package org.eclipse.aether.internal.impl.collect;
  * under the License.
  */
 
+import java.util.ArrayList;
+import java.util.List;
+
 import org.eclipse.aether.artifact.DefaultArtifact;
 import org.eclipse.aether.graph.DefaultDependencyNode;
 import org.eclipse.aether.graph.Dependency;
@@ -26,12 +29,9 @@ import org.eclipse.aether.graph.DependencyCycle;
 import org.eclipse.aether.graph.DependencyNode;
 import org.junit.Test;
 
-import java.util.ArrayList;
-import java.util.List;
-
 import static org.junit.Assert.assertEquals;
 
-public class DefaultDependencyCycleTest
+public class BfDependencyCycleTest
 {
     private static final Dependency FOO_DEPENDENCY = new Dependency( new DefaultArtifact( "group-id:foo:1.0" ), "test" );
     private static final Dependency BAR_DEPENDENCY = new Dependency( new DefaultArtifact( "group-id:bar:1.0" ), "test" );
@@ -41,7 +41,7 @@ public class DefaultDependencyCycleTest
     {
         List<DependencyNode> nodes = new ArrayList<>();
         nodes.add( new DefaultDependencyNode( FOO_DEPENDENCY ) );
-        DependencyCycle cycle = new DefaultDependencyCycle( nodes, 1, BAR_DEPENDENCY );
+        DependencyCycle cycle = new BfDependencyCycle( nodes, 1, BAR_DEPENDENCY );
 
         assertEquals( "group-id:foo:jar -> group-id:bar:jar", cycle.toString() );
     }
diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectorTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollectorTest.java
similarity index 99%
rename from maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectorTest.java
rename to maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollectorTest.java
index 5d4c2aa..61d6e09 100644
--- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectorTest.java
+++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollectorTest.java
@@ -1,4 +1,4 @@
-package org.eclipse.aether.internal.impl.collect;
+package org.eclipse.aether.internal.impl.collect.df;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -18,14 +18,6 @@ package org.eclipse.aether.internal.impl.collect;
  * specific language governing permissions and limitations
  * under the License.
  */
-import static java.util.Objects.requireNonNull;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -58,6 +50,7 @@ import org.eclipse.aether.impl.ArtifactDescriptorReader;
 import org.eclipse.aether.internal.impl.IniArtifactDescriptorReader;
 import org.eclipse.aether.internal.impl.StubRemoteRepositoryManager;
 import org.eclipse.aether.internal.impl.StubVersionRangeResolver;
+import org.eclipse.aether.internal.impl.collect.DefaultDependencyCollector;
 import org.eclipse.aether.internal.test.util.DependencyGraphParser;
 import org.eclipse.aether.internal.test.util.TestUtils;
 import org.eclipse.aether.repository.RemoteRepository;
@@ -73,12 +66,21 @@ import org.eclipse.aether.util.graph.version.HighestVersionFilter;
 import org.junit.Before;
 import org.junit.Test;
 
+import static java.util.Objects.requireNonNull;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 /**
  */
-public class DefaultDependencyCollectorTest
+public class DfDependencyCollectorTest
 {
 
-    private DefaultDependencyCollector collector;
+    private DfDependencyCollector collector;
 
     private DefaultRepositorySystemSession session;
 
@@ -106,7 +108,7 @@ public class DefaultDependencyCollectorTest
     {
         session = TestUtils.newSession();
 
-        collector = new DefaultDependencyCollector();
+        collector = new DfDependencyCollector();
         collector.setArtifactDescriptorReader( newReader( "" ) );
         collector.setVersionRangeResolver( new StubVersionRangeResolver() );
         collector.setRemoteRepositoryManager( new StubRemoteRepositoryManager() );
diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycleTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCycleTest.java
similarity index 78%
rename from maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycleTest.java
rename to maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCycleTest.java
index 0a382f8..0cc8a7d 100644
--- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycleTest.java
+++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCycleTest.java
@@ -1,4 +1,4 @@
-package org.eclipse.aether.internal.impl.collect;
+package org.eclipse.aether.internal.impl.collect.df;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -23,15 +23,11 @@ import org.eclipse.aether.artifact.DefaultArtifact;
 import org.eclipse.aether.graph.DefaultDependencyNode;
 import org.eclipse.aether.graph.Dependency;
 import org.eclipse.aether.graph.DependencyCycle;
-import org.eclipse.aether.graph.DependencyNode;
 import org.junit.Test;
 
-import java.util.ArrayList;
-import java.util.List;
-
 import static org.junit.Assert.assertEquals;
 
-public class DefaultDependencyCycleTest
+public class DfDependencyCycleTest
 {
     private static final Dependency FOO_DEPENDENCY = new Dependency( new DefaultArtifact( "group-id:foo:1.0" ), "test" );
     private static final Dependency BAR_DEPENDENCY = new Dependency( new DefaultArtifact( "group-id:bar:1.0" ), "test" );
@@ -39,9 +35,10 @@ public class DefaultDependencyCycleTest
     @Test
     public void testToString()
     {
-        List<DependencyNode> nodes = new ArrayList<>();
-        nodes.add( new DefaultDependencyNode( FOO_DEPENDENCY ) );
-        DependencyCycle cycle = new DefaultDependencyCycle( nodes, 1, BAR_DEPENDENCY );
+        org.eclipse.aether.internal.impl.collect.df.NodeStack
+                nodeStack = new org.eclipse.aether.internal.impl.collect.df.NodeStack();
+        nodeStack.push( new DefaultDependencyNode( FOO_DEPENDENCY ) );
+        DependencyCycle cycle = new DfDependencyCycle( nodeStack, 1, BAR_DEPENDENCY );
 
         assertEquals( "group-id:foo:jar -> group-id:bar:jar", cycle.toString() );
     }