You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by mi...@apache.org on 2019/03/31 18:17:06 UTC

[maven-resolver] branch MRESOLVER-7 created (now 9d3b324)

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

michaelo pushed a change to branch MRESOLVER-7
in repository https://gitbox.apache.org/repos/asf/maven-resolver.git.


      at 9d3b324  [MRESOLVER-7] Download dependency POMs in parallel

This branch includes the following new commits:

     new 9d3b324  [MRESOLVER-7] Download dependency POMs in parallel

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: [MRESOLVER-7] Download dependency POMs in parallel

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

michaelo pushed a commit to branch MRESOLVER-7
in repository https://gitbox.apache.org/repos/asf/maven-resolver.git

commit 9d3b324e82d9b934bf51bb8818c83caf776e6faf
Author: Harald Wellmann <ha...@gmx.de>
AuthorDate: Sun Jul 1 01:28:33 2018 +0200

    [MRESOLVER-7] Download dependency POMs in parallel
    
    Dependencies are now processed asynchronously by an Executor
    
    This closes #30
---
 .../eclipse/aether/internal/impl/collect/Args.java |  69 ++
 .../aether/internal/impl/collect/DataPool.java     | 118 +--
 .../DefaultDependencyCollectionContext.java        | 178 ++++-
 .../impl/collect/DefaultDependencyCollector.java   | 869 ++++++++-------------
 .../impl/collect/DependencyCollectionUtils.java    | 171 ++++
 .../internal/impl/collect/DependencyContext.java   |  74 ++
 .../impl/collect/PremanagedDependency.java         | 148 ++++
 .../aether/internal/impl/collect/Results.java      |  83 ++
 .../aether/internal/impl/collect/DataPoolTest.java |  16 +-
 .../collect/DefaultDependencyCollectorTest.java    |  51 +-
 .../artifact-descriptions/gid3_aid1_1.ini          |   7 +
 .../artifact-descriptions/gid3_aid2_1.ini          |   2 +
 .../artifact-descriptions/gid3_aid3_1.ini          |   2 +
 .../artifact-descriptions/gid3_aid3_2.ini          |   2 +
 .../aether/util/concurrency/FutureResult.java      |  69 ++
 15 files changed, 1261 insertions(+), 598 deletions(-)

diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/Args.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/Args.java
new file mode 100644
index 0000000..7d5e96f
--- /dev/null
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/Args.java
@@ -0,0 +1,69 @@
+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 java.util.concurrent.ExecutorService;
+
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.RequestTrace;
+import org.eclipse.aether.collection.CollectRequest;
+import org.eclipse.aether.util.ConfigUtils;
+import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
+
+class Args
+{
+
+    final RepositorySystemSession session;
+
+    final boolean ignoreRepos;
+
+    final boolean premanagedState;
+
+    final RequestTrace trace;
+
+    final DataPool pool;
+
+    final NodeStack nodes;
+
+    final DefaultDependencyCollectionContext collectionContext;
+
+    final DefaultVersionFilterContext versionContext;
+
+    final CollectRequest request;
+
+    final ExecutorService executor;
+
+    Args( RepositorySystemSession session, RequestTrace trace, DataPool pool, NodeStack nodes,
+          DefaultDependencyCollectionContext collectionContext, DefaultVersionFilterContext versionContext,
+          CollectRequest request, ExecutorService executor )
+    {
+        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.nodes = nodes;
+        this.collectionContext = collectionContext;
+        this.versionContext = versionContext;
+        this.executor = executor;
+    }
+
+}
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 a8a8e9e..b2390bb 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
@@ -19,12 +19,15 @@ package org.eclipse.aether.internal.impl.collect;
  * under the License.
  */
 
-import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.WeakHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 import org.eclipse.aether.RepositoryCache;
 import org.eclipse.aether.RepositorySystemSession;
@@ -42,6 +45,7 @@ import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
 import org.eclipse.aether.resolution.ArtifactDescriptorResult;
 import org.eclipse.aether.resolution.VersionRangeRequest;
 import org.eclipse.aether.resolution.VersionRangeResult;
+import org.eclipse.aether.util.concurrency.FutureResult;
 import org.eclipse.aether.version.Version;
 import org.eclipse.aether.version.VersionConstraint;
 
@@ -56,8 +60,8 @@ final class DataPool
 
     private static final String DESCRIPTORS = DataPool.class.getName() + "$Descriptors";
 
-    public static final ArtifactDescriptorResult NO_DESCRIPTOR =
-        new ArtifactDescriptorResult( new ArtifactDescriptorRequest() );
+    public static final Future<ArtifactDescriptorResult> NO_DESCRIPTOR =
+        new FutureResult<>( new ArtifactDescriptorResult( new ArtifactDescriptorRequest() ) );
 
     private ObjectPool<Artifact> artifacts;
 
@@ -65,9 +69,9 @@ final class DataPool
 
     private Map<Object, Descriptor> descriptors;
 
-    private Map<Object, Constraint> constraints = new HashMap<Object, Constraint>();
+    private Map<Object, Constraint> constraints = new HashMap<>();
 
-    private Map<Object, List<DependencyNode>> nodes = new HashMap<Object, List<DependencyNode>>( 256 );
+    private Map<Object, List<DependencyNode>> nodes = new HashMap<>( 256 );
 
     @SuppressWarnings( "unchecked" )
     DataPool( RepositorySystemSession session )
@@ -83,7 +87,7 @@ final class DataPool
 
         if ( artifacts == null )
         {
-            artifacts = new ObjectPool<Artifact>();
+            artifacts = new ObjectPool<>();
             if ( cache != null )
             {
                 cache.put( session, ARTIFACT_POOL, artifacts );
@@ -92,7 +96,7 @@ final class DataPool
 
         if ( dependencies == null )
         {
-            dependencies = new ObjectPool<Dependency>();
+            dependencies = new ObjectPool<>();
             if ( cache != null )
             {
                 cache.put( session, DEPENDENCY_POOL, dependencies );
@@ -124,7 +128,7 @@ final class DataPool
         return request.getArtifact();
     }
 
-    public ArtifactDescriptorResult getDescriptor( Object key, ArtifactDescriptorRequest request )
+    public Future<ArtifactDescriptorResult> getDescriptor( Object key, ArtifactDescriptorRequest request )
     {
         Descriptor descriptor = descriptors.get( key );
         if ( descriptor != null )
@@ -134,12 +138,12 @@ final class DataPool
         return null;
     }
 
-    public void putDescriptor( Object key, ArtifactDescriptorResult result )
+    public void putDescriptor( Object key, Future<ArtifactDescriptorResult> futureResult )
     {
-        descriptors.put( key, new GoodDescriptor( result ) );
+        descriptors.put( key, new GoodDescriptor( futureResult ) );
     }
 
-    public void putDescriptor( Object key, ArtifactDescriptorException e )
+    public void putDescriptor( Object key, ArtifactDescriptorException exception )
     {
         descriptors.put( key, BadDescriptor.INSTANCE );
     }
@@ -164,10 +168,10 @@ final class DataPool
         constraints.put( key, new Constraint( result ) );
     }
 
-    public Object toKey( Artifact artifact, List<RemoteRepository> repositories, DependencySelector selector,
-                         DependencyManager manager, DependencyTraverser traverser, VersionFilter filter )
+    public Object toKey( Artifact artifact, DefaultDependencyCollectionContext context )
     {
-        return new GraphKey( artifact, repositories, selector, manager, traverser, filter );
+        return new GraphKey( artifact, context.getRepositories(), context.getDepSelector(),
+                             context.getDepManager(), context.getDepTraverser(), context.getVerFilter() );
     }
 
     public List<DependencyNode> getChildren( Object key )
@@ -182,67 +186,79 @@ final class DataPool
 
     abstract static class Descriptor
     {
-
-        public abstract ArtifactDescriptorResult toResult( ArtifactDescriptorRequest request );
-
+        public abstract Future<ArtifactDescriptorResult> toResult( ArtifactDescriptorRequest request );
     }
 
     static final class GoodDescriptor
         extends Descriptor
     {
+        Future<ArtifactDescriptorResult> futureResult;
 
-        final Artifact artifact;
-
-        final List<Artifact> relocations;
-
-        final Collection<Artifact> aliases;
+        GoodDescriptor( Future<ArtifactDescriptorResult> futureResult )
+        {
+            this.futureResult = futureResult;
+        }
 
-        final List<RemoteRepository> repositories;
+        public Future<ArtifactDescriptorResult> toResult( final ArtifactDescriptorRequest request )
+        {
+            return new Future<ArtifactDescriptorResult>()
+            {
+                public boolean cancel( boolean mayInterruptIfRunning )
+                {
+                    return futureResult.cancel( mayInterruptIfRunning );
+                }
 
-        final List<Dependency> dependencies;
+                public boolean isCancelled()
+                {
+                    return futureResult.isCancelled();
+                }
 
-        final List<Dependency> managedDependencies;
+                public boolean isDone()
+                {
+                    return futureResult.isDone();
+                }
 
-        GoodDescriptor( ArtifactDescriptorResult result )
-        {
-            artifact = result.getArtifact();
-            relocations = result.getRelocations();
-            aliases = result.getAliases();
-            dependencies = result.getDependencies();
-            managedDependencies = result.getManagedDependencies();
-            repositories = result.getRepositories();
+                public ArtifactDescriptorResult get()
+                    throws InterruptedException, ExecutionException
+                {
+                    ArtifactDescriptorResult result = futureResult.get();
+                    return wrap( request, result );
+                }
+                public ArtifactDescriptorResult get( long timeout, TimeUnit unit )
+                    throws InterruptedException, ExecutionException, TimeoutException
+                {
+                    ArtifactDescriptorResult result = futureResult.get( timeout, unit );
+                    return wrap( request, result );
+                }
+            };
         }
 
-        public ArtifactDescriptorResult toResult( ArtifactDescriptorRequest request )
+        private ArtifactDescriptorResult wrap( ArtifactDescriptorRequest request, ArtifactDescriptorResult result )
         {
-            ArtifactDescriptorResult result = new ArtifactDescriptorResult( request );
-            result.setArtifact( artifact );
-            result.setRelocations( relocations );
-            result.setAliases( aliases );
-            result.setDependencies( dependencies );
-            result.setManagedDependencies( managedDependencies );
-            result.setRepositories( repositories );
-            return result;
+            ArtifactDescriptorResult wrapped = new ArtifactDescriptorResult( request );
+            wrapped.setArtifact( result.getArtifact() );
+            wrapped.setRelocations( result.getRelocations() );
+            wrapped.setAliases( result.getAliases() );
+            wrapped.setDependencies( result.getDependencies() );
+            wrapped.setManagedDependencies( result.getManagedDependencies() );
+            wrapped.setRepositories( result.getRepositories() );
+            return wrapped;
         }
-
     }
 
     static final class BadDescriptor
         extends Descriptor
     {
-
         static final BadDescriptor INSTANCE = new BadDescriptor();
 
-        public ArtifactDescriptorResult toResult( ArtifactDescriptorRequest request )
+        public Future<ArtifactDescriptorResult> toResult( ArtifactDescriptorRequest request )
         {
             return NO_DESCRIPTOR;
         }
-
     }
 
     static final class Constraint
     {
-
         final VersionRepo[] repositories;
 
         final VersionConstraint versionConstraint;
@@ -273,7 +289,6 @@ final class DataPool
 
         static final class VersionRepo
         {
-
             final Version version;
 
             final ArtifactRepository repo;
@@ -283,14 +298,11 @@ final class DataPool
                 this.version = version;
                 this.repo = repo;
             }
-
         }
-
     }
 
     static final class ConstraintKey
     {
-
         private final Artifact artifact;
 
         private final List<RemoteRepository> repositories;
@@ -361,12 +373,10 @@ final class DataPool
         {
             return hashCode;
         }
-
     }
 
     static final class GraphKey
     {
-
         private final Artifact artifact;
 
         private final List<RemoteRepository> repositories;
@@ -382,7 +392,7 @@ final class DataPool
         private final int hashCode;
 
         GraphKey( Artifact artifact, List<RemoteRepository> repositories, DependencySelector selector,
-                         DependencyManager manager, DependencyTraverser traverser, VersionFilter filter )
+                  DependencyManager manager, DependencyTraverser traverser, VersionFilter filter )
         {
             this.artifact = artifact;
             this.repositories = repositories;
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..6ea858b 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
@@ -22,14 +22,22 @@ package org.eclipse.aether.internal.impl.collect;
 import java.util.List;
 
 import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.RequestTrace;
 import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.collection.CollectResult;
 import org.eclipse.aether.collection.DependencyCollectionContext;
+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.Dependency;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.version.Version;
 
 /**
  * @see DefaultDependencyCollector
  */
-final class DefaultDependencyCollectionContext
+class DefaultDependencyCollectionContext
     implements DependencyCollectionContext
 {
 
@@ -40,9 +48,31 @@ final class DefaultDependencyCollectionContext
     private Dependency dependency;
 
     private List<Dependency> managedDependencies;
+    private CollectResult collectResult;
+
+    private RequestTrace trace;
+
+    private Args args;
+
+    private Results results;
+
+    private List<Dependency> dependencies;
+
+    private List<RemoteRepository> repositories;
+
+    private DependencySelector depSelector;
+
+    private DependencyManager depManager;
+
+    private DependencyTraverser depTraverser;
+
+    private VersionFilter verFilter;
+
+    private Version version;
+
 
     DefaultDependencyCollectionContext( RepositorySystemSession session, Artifact artifact,
-                                               Dependency dependency, List<Dependency> managedDependencies )
+                                        Dependency dependency, List<Dependency> managedDependencies )
     {
         this.session = session;
         this.artifact = ( dependency != null ) ? dependency.getArtifact() : artifact;
@@ -50,6 +80,34 @@ final class DefaultDependencyCollectionContext
         this.managedDependencies = managedDependencies;
     }
 
+    public void prepareDescent()
+    {
+        DependencySelector dependencySelector = session.getDependencySelector();
+        DependencyManager dependencyManager = session.getDependencyManager();
+        VersionFilter versionFilter = session.getVersionFilter();
+        setDepSelector( dependencySelector != null ? dependencySelector.deriveChildSelector( this ) : null );
+        setDepManager( dependencyManager != null ? dependencyManager.deriveChildManager( this ) : null );
+        setDepTraverser( depTraverser != null ? depTraverser.deriveChildTraverser( this ) : null );
+        setVerFilter( versionFilter != null ? versionFilter.deriveChildFilter( this ) : null );
+
+    }
+
+    public DefaultDependencyCollectionContext createChildContext()
+    {
+        DefaultDependencyCollectionContext childContext =
+                        new DefaultDependencyCollectionContext( getSession(), getArtifact(),
+                                                                getDependency(), getManagedDependencies() );
+        childContext.depSelector =
+            getDepSelector() != null ? getDepSelector().deriveChildSelector( this ) : null;
+        childContext.depManager =
+            getDepManager() != null ? getDepManager().deriveChildManager( this ) : null;
+        childContext.depTraverser =
+            getDepTraverser() != null ? getDepTraverser().deriveChildTraverser( this ) : null;
+        childContext.verFilter =
+            getVerFilter() != null ? getVerFilter().deriveChildFilter( this ) : null;
+        return childContext;
+    }
+
     public RepositorySystemSession getSession()
     {
         return session;
@@ -70,13 +128,127 @@ final class DefaultDependencyCollectionContext
         return managedDependencies;
     }
 
-    public void set( Dependency dependency, List<Dependency> managedDependencies )
+    public CollectResult getCollectResult()
+    {
+        return collectResult;
+    }
+
+    public void setCollectResult( CollectResult collectResult )
+    {
+        this.collectResult = collectResult;
+    }
+
+    public Args getArgs()
+    {
+        return args;
+    }
+
+    public void setArgs( Args args )
+    {
+        this.args = args;
+    }
+
+    public Results getResults()
+    {
+        return results;
+    }
+
+    public void setResults( Results results )
+    {
+        this.results = results;
+    }
+
+    public List<Dependency> getDependencies()
+    {
+        return dependencies;
+    }
+
+    public void setDependencies( List<Dependency> dependencies )
+    {
+        this.dependencies = dependencies;
+    }
+
+    public List<RemoteRepository> getRepositories()
+    {
+        return repositories;
+    }
+
+    public void setRepositories( List<RemoteRepository> repositories )
+    {
+        this.repositories = repositories;
+    }
+
+    public DependencySelector getDepSelector()
+    {
+        return depSelector;
+    }
+
+    public void setDepSelector( DependencySelector depSelector )
+    {
+        this.depSelector = depSelector;
+    }
+
+    public DependencyManager getDepManager()
+    {
+        return depManager;
+    }
+
+    public void setDepManager( DependencyManager depManager )
+    {
+        this.depManager = depManager;
+    }
+
+    public DependencyTraverser getDepTraverser()
+    {
+        return depTraverser;
+    }
+
+    public void setDepTraverser( DependencyTraverser depTraverser )
+    {
+        this.depTraverser = depTraverser;
+    }
+
+    public VersionFilter getVerFilter()
+    {
+        return verFilter;
+    }
+
+    public void setVerFilter( VersionFilter verFilter )
+    {
+        this.verFilter = verFilter;
+    }
+
+    public void setDependency( Dependency dependency )
     {
         artifact = dependency.getArtifact();
         this.dependency = dependency;
+    }
+
+    public void setManagedDependencies( List<Dependency> managedDependencies )
+    {
         this.managedDependencies = managedDependencies;
     }
 
+    public RequestTrace getTrace()
+    {
+        return trace;
+    }
+
+    public void setTrace( RequestTrace trace )
+    {
+        this.trace = trace;
+    }
+
+    public Version getVersion()
+    {
+        return version;
+    }
+
+    public void setVersion( Version version )
+    {
+        this.version = version;
+    }
+
     @Override
     public String toString()
     {
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 d2ca7d4..e4fb051 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
@@ -8,9 +8,9 @@ package org.eclipse.aether.internal.impl.collect;
  * 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
@@ -19,20 +19,31 @@ package org.eclipse.aether.internal.impl.collect;
  * under the License.
  */
 
+import static org.eclipse.aether.internal.impl.collect.DependencyCollectionUtils.addDependencyNode;
+import static org.eclipse.aether.internal.impl.collect.DependencyCollectionUtils.createArtifactDescriptorRequest;
+import static org.eclipse.aether.internal.impl.collect.DependencyCollectionUtils.createDependencyNode;
+import static org.eclipse.aether.internal.impl.collect.DependencyCollectionUtils.createVersionRangeRequest;
+import static org.eclipse.aether.internal.impl.collect.DependencyCollectionUtils.filterVersions;
+
 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 static java.util.Objects.requireNonNull;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
 
 import javax.inject.Inject;
 import javax.inject.Named;
 
-import org.eclipse.aether.DefaultRepositorySystemSession;
 import org.eclipse.aether.RepositoryException;
 import org.eclipse.aether.RepositorySystemSession;
 import org.eclipse.aether.RequestTrace;
@@ -42,15 +53,10 @@ 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;
@@ -66,7 +72,8 @@ import org.eclipse.aether.resolution.VersionRangeResult;
 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.concurrency.FutureResult;
+import org.eclipse.aether.util.concurrency.WorkerThreadFactory;
 import org.eclipse.aether.util.graph.transformer.TransformationContextKeys;
 import org.eclipse.aether.version.Version;
 import org.slf4j.Logger;
@@ -79,9 +86,12 @@ public class DefaultDependencyCollector
     implements DependencyCollector, Service
 {
 
-    private static final String CONFIG_PROP_MAX_EXCEPTIONS = "aether.dependencyCollector.maxExceptions";
+    static final String CONFIG_PROP_MAX_EXCEPTIONS = "aether.dependencyCollector.maxExceptions";
+
+    static final String CONFIG_PROP_MAX_CYCLES = "aether.dependencyCollector.maxCycles";
 
-    private static final String CONFIG_PROP_MAX_CYCLES = "aether.dependencyCollector.maxCycles";
+    private static final String CONFIG_PROP_THREADS = "aether.artifactDescriptor.threads";
+    private static final int DEFAULT_THREADS = 5;
 
     private static final Logger LOGGER = LoggerFactory.getLogger( DefaultDependencyCollector.class );
 
@@ -134,71 +144,52 @@ public class DefaultDependencyCollector
     public CollectResult collectDependencies( RepositorySystemSession session, CollectRequest request )
         throws DependencyCollectionException
     {
-        session = optimizeSession( session );
+        int numThreads = ConfigUtils.getInteger( session, DEFAULT_THREADS, CONFIG_PROP_THREADS );
+        LOGGER.debug( "{} = {} ", CONFIG_PROP_THREADS, numThreads );
+        ThreadPoolExecutor executor = new ThreadPoolExecutor( numThreads, numThreads, 3L, TimeUnit.SECONDS,
+                                                              new LinkedBlockingQueue<Runnable>(),
+                                                              new WorkerThreadFactory( "artifact-descriptor-resolver" ) );
+        try
+        {
+            return collectDependenciesWithExecutor( session, request, executor );
+        }
+        finally
+        {
+            executor.shutdown();
+        }
+    }
+
+    private CollectResult collectDependenciesWithExecutor( RepositorySystemSession session, CollectRequest request, ExecutorService executor )
+        throws DependencyCollectionException
+    {
+        session = DependencyCollectionUtils.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();
 
+        DefaultDependencyCollectionContext context =
+                new DefaultDependencyCollectionContext( session, request.getRootArtifact(), root, managedDependencies );
+        context.setDependencies( request.getDependencies() );
+        context.setCollectResult( result );
+        context.setTrace( trace );
+
+        Args args = new Args( session, trace, null, null, context, null, request, executor );
+        context.setArgs( args );
+
         Map<String, Object> stats = LOGGER.isDebugEnabled() ? new LinkedHashMap<String, Object>() : null;
         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() );
-            }
 
+            VersionRangeResult rangeResult = resolveRootVersionRange( context );
+            ArtifactDescriptorResult descriptorResult = readRootArtifactDescriptor( context );
             root = root.setArtifact( descriptorResult.getArtifact() );
 
             if ( !session.isIgnoreArtifactDescriptorRepositories() )
@@ -207,14 +198,14 @@ public class DefaultDependencyCollector
                                                                               descriptorResult.getRepositories(),
                                                                               true );
             }
-            dependencies = mergeDeps( dependencies, descriptorResult.getDependencies() );
-            managedDependencies = mergeDeps( managedDependencies, descriptorResult.getManagedDependencies() );
+            context.setDependencies( mergeDeps( context.getDependencies(), descriptorResult.getDependencies() ) );
+            context.setManagedDependencies( mergeDeps( managedDependencies, descriptorResult.getManagedDependencies() ) );
 
             node = new DefaultDependencyNode( root );
             node.setRequestContext( request.getRequestContext() );
             node.setRelocations( descriptorResult.getRelocations() );
             node.setVersionConstraint( rangeResult.getVersionConstraint() );
-            node.setVersion( version );
+            node.setVersion( context.getVersion() );
             node.setAliases( descriptorResult.getAliases() );
             node.setRepositories( request.getRepositories() );
         }
@@ -227,56 +218,41 @@ public class DefaultDependencyCollector
 
         result.setRoot( node );
 
+        DependencyTraverser depTraverser = session.getDependencyTraverser();
         boolean traverse = root == null || depTraverser == null || depTraverser.traverseDependency( root );
         String errorPath = null;
-        if ( traverse && !dependencies.isEmpty() )
+        if ( traverse && !context.getDependencies().isEmpty() )
         {
             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, nodes, context, versionContext, request );
+            args = new Args( session, trace, pool, nodes, context, versionContext, request, executor );
             Results results = new Results( result, session );
+            context.setArgs( args );
+            context.setResults( results );
+            context.setRepositories( repositories );
+            context.setDepTraverser( depTraverser );
+            context.prepareDescent();
 
-            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 );
+            process( context );
 
             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 );
-            }
-        }
+        transformDependencyGraph( context, stats );
 
         if ( stats != null )
         {
             long time3 = System.nanoTime();
             stats.put( "DefaultDependencyCollector.collectTime", time2 - time1 );
             stats.put( "DefaultDependencyCollector.transformTime", time3 - time2 );
-            LOGGER.debug( "Dependency collection stats: " + stats );
+            LOGGER.debug( "Dependency collection stats: {}", stats );
         }
 
         if ( errorPath != null )
@@ -291,11 +267,80 @@ public class DefaultDependencyCollector
         return result;
     }
 
-    private static RepositorySystemSession optimizeSession( RepositorySystemSession session )
+    private VersionRangeResult resolveRootVersionRange( DefaultDependencyCollectionContext context )
+        throws DependencyCollectionException
     {
-        DefaultRepositorySystemSession optimized = new DefaultRepositorySystemSession( session );
-        optimized.setArtifactTypeRegistry( CachingArtifactTypeRegistry.newInstance( session ) );
-        return optimized;
+        CollectRequest request = context.getArgs().request;
+        RepositorySystemSession session = context.getSession();
+        List<? extends Version> versions;
+        VersionRangeResult rangeResult;
+        Artifact artifact = request.getRoot().getArtifact();
+        try
+        {
+            VersionRangeRequest rangeRequest =
+                    new VersionRangeRequest( artifact, request.getRepositories(), request.getRequestContext() );
+            rangeRequest.setTrace( context.getTrace() );
+            rangeResult = versionRangeResolver.resolveVersionRange( session, rangeRequest );
+            versions = filterVersions( context.getDependency(), rangeResult, context.getVerFilter(),
+                    new DefaultVersionFilterContext( session ) );
+        }
+        catch ( VersionRangeResolutionException e )
+        {
+            context.getCollectResult().addException( e );
+            throw new DependencyCollectionException( context.getCollectResult(), e.getMessage() );
+        }
+
+        Version version = versions.get( versions.size() - 1 );
+        context.setVersion( version );
+        context.setDependency( request.getRoot().setArtifact( artifact.setVersion( version.toString() ) ) );
+        return rangeResult;
+    }
+
+    private ArtifactDescriptorResult readRootArtifactDescriptor( DefaultDependencyCollectionContext context )
+        throws DependencyCollectionException
+    {
+        CollectRequest request = context.getArgs().request;
+        Artifact artifact = request.getRoot().getArtifact();
+        try
+        {
+            ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest();
+            descriptorRequest.setArtifact( artifact );
+            descriptorRequest.setRepositories( request.getRepositories() );
+            descriptorRequest.setRequestContext( request.getRequestContext() );
+            descriptorRequest.setTrace( context.getTrace() );
+
+            ArtifactDescriptorResult descriptorResult =
+                    isLackingDescriptor( artifact ) ? new ArtifactDescriptorResult( descriptorRequest )
+                            : descriptorReader.readArtifactDescriptor( context.getSession(), descriptorRequest );
+            context.setDependency( request.getRoot().setArtifact( descriptorResult.getArtifact() ) );
+            return descriptorResult;
+        }
+        catch ( ArtifactDescriptorException e )
+        {
+            context.getCollectResult().addException( e );
+            throw new DependencyCollectionException( context.getCollectResult(), e.getMessage() );
+        }
+    }
+
+    private void transformDependencyGraph( DefaultDependencyCollectionContext context, Map<String, Object> stats )
+    {
+        RepositorySystemSession session = context.getSession();
+        DependencyGraphTransformer transformer = session.getDependencyGraphTransformer();
+        if ( transformer != null )
+        {
+            try
+            {
+                DefaultDependencyGraphTransformationContext tfContext =
+                        new DefaultDependencyGraphTransformationContext( session );
+                tfContext.put( TransformationContextKeys.STATS, stats );
+                context.getCollectResult().setRoot( transformer.transformGraph( context.getCollectResult().getRoot(),
+                        tfContext ) );
+            }
+            catch ( RepositoryException e )
+            {
+                context.getCollectResult().addException( e );
+            }
+        }
     }
 
     private List<Dependency> mergeDeps( List<Dependency> dominant, List<Dependency> recessive )
@@ -312,8 +357,8 @@ public class DefaultDependencyCollector
         else
         {
             int initialCapacity = dominant.size() + recessive.size();
-            result = new ArrayList<Dependency>( initialCapacity );
-            Collection<String> ids = new HashSet<String>( initialCapacity, 1.0f );
+            result = new ArrayList<>( initialCapacity );
+            Collection<String> ids = new HashSet<>( initialCapacity, 1.0f );
             for ( Dependency dependency : dominant )
             {
                 ids.add( getId( dependency.getArtifact() ) );
@@ -335,158 +380,213 @@ public class DefaultDependencyCollector
         return a.getGroupId() + ':' + a.getArtifactId() + ':' + a.getClassifier() + ':' + a.getExtension();
     }
 
-    private void process( final Args args, Results results, List<Dependency> dependencies,
-                          List<RemoteRepository> repositories, DependencySelector depSelector,
-                          DependencyManager depManager, DependencyTraverser depTraverser, VersionFilter verFilter )
+    private void process( DefaultDependencyCollectionContext context )
     {
-        for ( Dependency dependency : dependencies )
+        List<DependencyContext> depContexts = new ArrayList<>();
+        for ( Dependency d : context.getDependencies() )
         {
-            processDependency( args, results, repositories, depSelector, depManager, depTraverser, verFilter,
-                               dependency );
+            depContexts.add( new DependencyContext( context, d ) );
+        }
+
+        List<Future<DependencyContext>> futures = new ArrayList<>();
+        for ( DependencyContext dc : depContexts )
+        {
+            futures.add( asyncProcessDependency( dc ) );
+        }
+        int pos = 0;
+        for ( Future<DependencyContext> future : futures )
+        {
+            try
+            {
+                processDependencyNode( future.get() );
+            }
+            catch ( ExecutionException e )
+            {
+                context.getResults().addException( context.getDependencies().get( pos ), (Exception) e.getCause(),
+                        context.getArgs().nodes );
+            }
+            catch ( InterruptedException e )
+            {
+                context.getResults().addException( context.getDependencies().get( pos ), e, context.getArgs().nodes );
+            }
+            pos++;
         }
     }
 
-    private void processDependency( Args args, Results results, List<RemoteRepository> repositories,
-                                    DependencySelector depSelector, DependencyManager depManager,
-                                    DependencyTraverser depTraverser, VersionFilter verFilter, Dependency dependency )
+    private Future<DependencyContext> asyncProcessDependency( final DependencyContext dc )
     {
+        return dc.args.executor.submit( new Callable<DependencyContext>()
+        {
 
-        List<Artifact> relocations = Collections.emptyList();
-        boolean disableVersionManagement = false;
-        processDependency( args, results, repositories, depSelector, depManager, depTraverser, verFilter, dependency,
-                           relocations, disableVersionManagement );
+            public DependencyContext call()
+                throws Exception
+            {
+                return processDependency( dc );
+            }
+        } );
     }
 
-    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 )
+    private DependencyContext processDependency( DependencyContext dc )
     {
+        DefaultDependencyCollectionContext context = dc.context;
+        Args args = context.getArgs();
+        Results results = context.getResults();
 
-        if ( depSelector != null && !depSelector.selectDependency( dependency ) )
+        if ( context.getDepSelector() != null && !context.getDepSelector().selectDependency( dc.origDependency ) )
         {
-            return;
+            return null;
         }
 
         PremanagedDependency preManaged =
-            PremanagedDependency.create( depManager, dependency, disableVersionManagement, args.premanagedState );
-        dependency = preManaged.managedDependency;
+                PremanagedDependency.create( context.getDepManager(), dc.origDependency, dc.disableVersionManagement,
+                                             args.premanagedState );
+        Dependency dependency = preManaged.managedDependency;
 
         boolean noDescriptor = isLackingDescriptor( dependency.getArtifact() );
 
-        boolean traverse = !noDescriptor && ( depTraverser == null || depTraverser.traverseDependency( dependency ) );
+        boolean traverse = !noDescriptor
+                && ( context.getDepTraverser() == null || context.getDepTraverser().traverseDependency( dependency ) );
 
-        List<? extends Version> versions;
-        VersionRangeResult rangeResult;
         try
         {
-            VersionRangeRequest rangeRequest = createVersionRangeRequest( args, repositories, dependency );
-
-            rangeResult = cachedResolveRangeResult( rangeRequest, args.pool, args.session );
+            VersionRangeRequest rangeRequest = createVersionRangeRequest( args, context.getRepositories(), dependency );
+            VersionRangeResult rangeResult = cachedResolveRangeResult( rangeRequest, args.pool, args.session );
+            for ( Version version : filterVersions( dependency, rangeResult, context.getVerFilter(),
+                                                    args.versionContext ) )
+            {
 
-            versions = filterVersions( dependency, rangeResult, verFilter, args.versionContext );
+                Artifact originalArtifact = dependency.getArtifact().setVersion( version.toString() );
+                Dependency d = dependency.setArtifact( originalArtifact );
+
+                ArtifactDescriptorRequest descriptorRequest =
+                        createArtifactDescriptorRequest( args, context.getRepositories(), d );
+
+                dc.args = args;
+                dc.preManaged = preManaged;
+                dc.traverse = traverse;
+                dc.rangeResult = rangeResult;
+                dc.version = version;
+                dc.originalArtifact = originalArtifact;
+                dc.managedDependency = d;
+                dc.futureDescriptorResult =
+                        getArtifactDescriptorResult( args, results, noDescriptor, d, descriptorRequest );
+            }
         }
         catch ( VersionRangeResolutionException e )
         {
             results.addException( dependency, e, args.nodes );
-            return;
         }
+        return dc;
+    }
 
-        for ( Version version : versions )
+    private void processDependencyNode( DependencyContext dc )
+    {
+        if ( dc == null )
         {
-            Artifact originalArtifact = dependency.getArtifact().setVersion( version.toString() );
-            Dependency d = dependency.setArtifact( originalArtifact );
-
-            ArtifactDescriptorRequest descriptorRequest = createArtifactDescriptorRequest( args, repositories, d );
-
-            final ArtifactDescriptorResult descriptorResult =
-                getArtifactDescriptorResult( args, results, noDescriptor, d, descriptorRequest );
-            if ( descriptorResult != null )
+            return;
+        }
+        try
+        {
+            boolean noResult = dc.futureDescriptorResult == null;
+            if ( !noResult )
+            {
+                dc.descriptorResult = dc.futureDescriptorResult.get();
+                noResult = dc.descriptorResult == null;
+            }
+            if ( noResult )
             {
-                d = d.setArtifact( descriptorResult.getArtifact() );
+                List<RemoteRepository> repos =
+                        getRemoteRepositories( dc.rangeResult.getRepository( dc.version ), dc.context.getRepositories() );
+                addDependencyNode( dc.args.nodes.top(), dc.relocations, dc.preManaged, dc.rangeResult, dc.version,
+                                   dc.managedDependency, null, repos, dc.args.request.getRequestContext() );
+            }
+            else
+            {
+                processDependencyVersion( dc );
+            }
+        }
+        catch ( InterruptedException e )
+        {
+            dc.context.getResults().addException( dc.preManaged.managedDependency, e, dc.args.nodes );
+        }
+        catch ( ExecutionException e )
+        {
+            dc.context.getResults().addException( dc.preManaged.managedDependency, (Exception) e.getCause(),
+                                                  dc.args.nodes );
+        }
+    }
 
-                DependencyNode node = args.nodes.top();
+    private boolean processDependencyVersion( DependencyContext dc )
+    {
+        Args args = dc.context.getArgs();
+        Results results = dc.context.getResults();
+        Dependency d = dc.managedDependency.setArtifact( dc.descriptorResult.getArtifact() );
+        dc.managedDependency = d;
 
-                int cycleEntry = args.nodes.find( d.getArtifact() );
-                if ( cycleEntry >= 0 )
-                {
-                    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 );
-                        node.getChildren().add( child );
-                        continue;
-                    }
-                }
+        DependencyNode node = args.nodes.top();
 
-                if ( !descriptorResult.getRelocations().isEmpty() )
-                {
-                    boolean disableVersionManagementSubsequently =
-                        originalArtifact.getGroupId().equals( d.getArtifact().getGroupId() )
-                            && originalArtifact.getArtifactId().equals( d.getArtifact().getArtifactId() );
+        int cycleEntry = args.nodes.find( d.getArtifact() );
+        if ( cycleEntry >= 0 )
+        {
+            results.addCycle( args.nodes, cycleEntry, d );
+            DependencyNode cycleNode = args.nodes.get( cycleEntry );
+            if ( cycleNode.getDependency() != null )
+            {
+                createDependencyNode( node, dc.relocations, dc.preManaged, dc.rangeResult, dc.version, d,
+                                      dc.descriptorResult, cycleNode );
+                return true;
+            }
+        }
 
-                    processDependency( args, results, repositories, depSelector, depManager, depTraverser, verFilter, d,
-                                       descriptorResult.getRelocations(), disableVersionManagementSubsequently );
-                    return;
-                }
-                else
-                {
-                    d = args.pool.intern( d.setArtifact( args.pool.intern( d.getArtifact() ) ) );
+        if ( !dc.descriptorResult.getRelocations().isEmpty() )
+        {
+            boolean disableVersionManagementSubsequently =
+                    dc.originalArtifact.getGroupId().equals( d.getArtifact().getGroupId() )
+                            && dc.originalArtifact.getArtifactId().equals( d.getArtifact().getArtifactId() );
 
-                    List<RemoteRepository> repos =
-                        getRemoteRepositories( rangeResult.getRepository( version ), repositories );
+            DependencyContext dc2 = new DependencyContext();
+            dc2.context = dc.context;
+            dc2.origDependency = d;
+            dc2.relocations = dc.descriptorResult.getRelocations();
+            dc2.disableVersionManagement = disableVersionManagementSubsequently;
+            dc2 = processDependency( dc2 );
+            processDependencyNode( dc2 );
+            return true;
+        }
+        else
+        {
+            d = args.pool.intern( d );
 
-                    DefaultDependencyNode child =
-                        createDependencyNode( relocations, preManaged, rangeResult, version, d,
-                                              descriptorResult.getAliases(), repos, args.request.getRequestContext() );
+            List<RemoteRepository> repos =
+                    getRemoteRepositories( dc.rangeResult.getRepository( dc.version ), dc.context.getRepositories() );
 
-                    node.getChildren().add( child );
+            DefaultDependencyNode child =
+                    addDependencyNode( node, dc.relocations, dc.preManaged, dc.rangeResult, dc.version, d,
+                                       dc.descriptorResult.getAliases(), repos, args.request.getRequestContext() );
 
-                    boolean recurse = traverse && !descriptorResult.getDependencies().isEmpty();
-                    if ( recurse )
-                    {
-                        doRecurse( args, results, repositories, depSelector, depManager, depTraverser, verFilter, d,
-                                   descriptorResult, child );
-                    }
-                }
-            }
-            else
+            if ( dc.traverse && !dc.descriptorResult.getDependencies().isEmpty() )
             {
-                DependencyNode node = args.nodes.top();
-                List<RemoteRepository> repos =
-                    getRemoteRepositories( rangeResult.getRepository( version ), repositories );
-                DefaultDependencyNode child =
-                    createDependencyNode( relocations, preManaged, rangeResult, version, d, null, repos,
-                                          args.request.getRequestContext() );
-                node.getChildren().add( child );
+                doRecurse( dc.context, d, dc.descriptorResult, child );
             }
+            return false;
         }
     }
 
-    private void doRecurse( Args args, Results results, List<RemoteRepository> repositories,
-                            DependencySelector depSelector, DependencyManager depManager,
-                            DependencyTraverser depTraverser, VersionFilter verFilter, Dependency d,
+    private void doRecurse( DefaultDependencyCollectionContext context, Dependency d,
                             ArtifactDescriptorResult descriptorResult, DefaultDependencyNode child )
     {
-        DefaultDependencyCollectionContext context = args.collectionContext;
-        context.set( d, descriptorResult.getManagedDependencies() );
+        context.setDependency( d );
+        context.setManagedDependencies( descriptorResult.getManagedDependencies() );
 
-        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;
+        DefaultDependencyCollectionContext childContext = context.createChildContext();
+        Args args = context.getArgs();
 
-        final List<RemoteRepository> childRepos =
-            args.ignoreRepos
-                ? repositories
-                : remoteRepositoryManager.aggregateRepositories( args.session, repositories,
-                                                                 descriptorResult.getRepositories(), true );
+        final List<RemoteRepository> childRepos = args.ignoreRepos ? context.getRepositories()
+                : remoteRepositoryManager.aggregateRepositories( args.session, context.getRepositories(),
+                descriptorResult.getRepositories(), true );
+        childContext.setRepositories( childRepos );
 
-        Object key =
-            args.pool.toKey( d.getArtifact(), childRepos, childSelector, childManager, childTraverser, childFilter );
+        Object key = args.pool.toKey( d.getArtifact(), childContext );
 
         List<DependencyNode> children = args.pool.getChildren( key );
         if ( children == null )
@@ -495,8 +595,11 @@ public class DefaultDependencyCollector
 
             args.nodes.push( child );
 
-            process( args, results, descriptorResult.getDependencies(), childRepos, childSelector, childManager,
-                     childTraverser, childFilter );
+            childContext.setArgs( args );
+            childContext.setResults( context.getResults() );
+            childContext.setDependencies( descriptorResult.getDependencies() );
+
+            process( childContext );
 
             args.nodes.pop();
         }
@@ -506,99 +609,54 @@ public class DefaultDependencyCollector
         }
     }
 
-    private ArtifactDescriptorResult getArtifactDescriptorResult( Args args, Results results, boolean noDescriptor,
-                                                                  Dependency d,
-                                                                  ArtifactDescriptorRequest descriptorRequest )
+    private Future<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 );
-
+                ? new FutureResult<ArtifactDescriptorResult>( new ArtifactDescriptorResult( descriptorRequest ) )
+                : resolveCachedArtifactDescriptor( args.pool, descriptorRequest, args.session, d, results,
+                args );
     }
 
-    private ArtifactDescriptorResult resolveCachedArtifactDescriptor( DataPool pool,
-                                                                      ArtifactDescriptorRequest descriptorRequest,
-                                                                      RepositorySystemSession session, Dependency d,
-                                                                      Results results, Args args )
+    private Future<ArtifactDescriptorResult> resolveCachedArtifactDescriptor( final DataPool pool,
+                                                                              final ArtifactDescriptorRequest descriptorRequest,
+                                                                              final RepositorySystemSession session,
+                                                                              final Dependency d, final Results results,
+                                                                              final Args args )
     {
-        Object key = pool.toKey( descriptorRequest );
-        ArtifactDescriptorResult descriptorResult = pool.getDescriptor( key, descriptorRequest );
+        final Object key = pool.toKey( descriptorRequest );
+        Future<ArtifactDescriptorResult> descriptorResult = pool.getDescriptor( key, descriptorRequest );
         if ( descriptorResult == null )
         {
-            try
-            {
-                descriptorResult = descriptorReader.readArtifactDescriptor( session, descriptorRequest );
-                pool.putDescriptor( key, descriptorResult );
-            }
-            catch ( ArtifactDescriptorException e )
+            descriptorResult = args.executor.submit( new Callable<ArtifactDescriptorResult>()
             {
-                results.addException( d, e, args.nodes );
-                pool.putDescriptor( key, e );
-                return null;
-            }
 
+                public ArtifactDescriptorResult call()
+                        throws Exception
+                {
+                    try
+                    {
+                        return descriptorReader.readArtifactDescriptor( session, descriptorRequest );
+                    }
+                    catch ( ArtifactDescriptorException e )
+                    {
+                        results.addException( d, e, args.nodes );
+                        pool.putDescriptor( key, e );
+                        return null;
+                    }
+                }
+            } );
+
+            pool.putDescriptor( key, descriptorResult );
         }
         else if ( descriptorResult == DataPool.NO_DESCRIPTOR )
         {
-            return null;
+            return new FutureResult<>( null );
         }
 
         return descriptorResult;
     }
-
-    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
@@ -631,257 +689,4 @@ public class DefaultDependencyCollector
         }
         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 NodeStack nodes;
-
-        final DefaultDependencyCollectionContext collectionContext;
-
-        final DefaultVersionFilterContext versionContext;
-
-        final CollectRequest request;
-
-        Args( RepositorySystemSession session, RequestTrace trace, DataPool pool, NodeStack nodes,
-                     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.nodes = nodes;
-            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;
-            this.maxExceptions = ConfigUtils.getInteger( session, 50, CONFIG_PROP_MAX_EXCEPTIONS );
-            this.maxCycles = ConfigUtils.getInteger( session, 10, CONFIG_PROP_MAX_CYCLES );
-        }
-
-        public void addException( Dependency dependency, Exception e, NodeStack nodes )
-        {
-            if ( maxExceptions < 0 || result.getExceptions().size() < maxExceptions )
-            {
-                result.addException( e );
-                if ( errorPath == null )
-                {
-                    StringBuilder buffer = new StringBuilder( 256 );
-                    for ( int i = 0; i < nodes.size(); i++ )
-                    {
-                        if ( buffer.length() > 0 )
-                        {
-                            buffer.append( " -> " );
-                        }
-                        Dependency dep = nodes.get( i ).getDependency();
-                        if ( dep != null )
-                        {
-                            buffer.append( dep.getArtifact() );
-                        }
-                    }
-                    if ( buffer.length() > 0 )
-                    {
-                        buffer.append( " -> " );
-                    }
-                    buffer.append( dependency.getArtifact() );
-                    errorPath = buffer.toString();
-                }
-            }
-        }
-
-        public void addCycle( NodeStack 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;
-
-        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<Exclusion>( premanagedExclusions ) )
-                    : null;
-
-            this.premanagedProperties =
-                premanagedProperties != null
-                    ? Collections.unmodifiableMap( new HashMap<String, String>( 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/DependencyCollectionUtils.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectionUtils.java
new file mode 100644
index 0000000..e5d777d
--- /dev/null
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectionUtils.java
@@ -0,0 +1,171 @@
+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 java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.aether.DefaultRepositorySystemSession;
+import org.eclipse.aether.RepositoryException;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.artifact.ArtifactProperties;
+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.repository.ArtifactRepository;
+import org.eclipse.aether.repository.RemoteRepository;
+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.version.Version;
+
+/**
+ * Utility methods for dependency collection.
+ *
+ */
+public class DependencyCollectionUtils
+{
+
+    static DefaultDependencyNode addDependencyNode( DependencyNode parent, 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 );
+        parent.getChildren().add( child );
+        return child;
+    }
+
+    static DefaultDependencyNode createDependencyNode( DependencyNode parent, List<Artifact> relocations,
+                                                       PremanagedDependency preManaged, VersionRangeResult rangeResult,
+                                                       Version version, Dependency d,
+                                                       ArtifactDescriptorResult descriptorResult,
+                                                       DependencyNode cycleNode )
+    {
+        DefaultDependencyNode child =
+            addDependencyNode( parent, relocations, preManaged, rangeResult, version, d, descriptorResult.getAliases(),
+                               cycleNode.getRepositories(), cycleNode.getRequestContext() );
+        child.setChildren( cycleNode.getChildren() );
+        return child;
+    }
+
+    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;
+    }
+
+    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;
+    }
+
+    static boolean isLackingDescriptor( Artifact artifact )
+    {
+        return artifact.getProperty( ArtifactProperties.LOCAL_PATH, null ) != null;
+    }
+
+    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;
+    }
+
+    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 RepositorySystemSession optimizeSession( RepositorySystemSession session )
+    {
+        DefaultRepositorySystemSession optimized = new DefaultRepositorySystemSession( session );
+        optimized.setArtifactTypeRegistry( CachingArtifactTypeRegistry.newInstance( session ) );
+        return optimized;
+    }
+
+    static String getId( Dependency d )
+    {
+        Artifact a = d.getArtifact();
+        return a.getGroupId() + ':' + a.getArtifactId() + ':' + a.getClassifier() + ':' + a.getExtension();
+    }
+}
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyContext.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyContext.java
new file mode 100644
index 0000000..48af70f
--- /dev/null
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyContext.java
@@ -0,0 +1,74 @@
+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 java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Future;
+
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.graph.Dependency;
+import org.eclipse.aether.resolution.ArtifactDescriptorResult;
+import org.eclipse.aether.resolution.VersionRangeResult;
+import org.eclipse.aether.version.Version;
+
+class DependencyContext
+{
+    Dependency origDependency;
+
+    DefaultDependencyCollectionContext context;
+
+    List<Artifact> relocations;
+
+    boolean disableVersionManagement;
+
+    Args args;
+
+    PremanagedDependency preManaged;
+
+    boolean traverse;
+
+    VersionRangeResult rangeResult;
+
+    Version version;
+
+    Artifact originalArtifact;
+
+    Dependency managedDependency;
+
+    Future<ArtifactDescriptorResult> futureDescriptorResult;
+
+    ArtifactDescriptorResult descriptorResult;
+
+    DependencyContext()
+    {
+        // empty
+    }
+
+    DependencyContext( DefaultDependencyCollectionContext context, Dependency dependency )
+    {
+        this.context = context;
+        this.origDependency = dependency;
+        this.relocations = Collections.emptyList();
+        this.disableVersionManagement = false;
+        this.args = context.getArgs();
+    }
+
+}
\ No newline at end of file
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/PremanagedDependency.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/PremanagedDependency.java
new file mode 100644
index 0000000..222cfe3
--- /dev/null
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/PremanagedDependency.java
@@ -0,0 +1,148 @@
+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 java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.collection.DependencyManagement;
+import org.eclipse.aether.collection.DependencyManager;
+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.util.graph.manager.DependencyManagerUtils;
+
+class PremanagedDependency
+{
+    final String premanagedVersion;
+
+    final String premanagedScope;
+
+    final Boolean premanagedOptional;
+
+    /**
+     * @since 1.4.0
+     */
+    final Collection<Exclusion> premanagedExclusions;
+
+    /**
+     * @since 1.4.0
+     */
+    final Map<String, String> premanagedProperties;
+
+    final int managedBits;
+
+    final Dependency managedDependency;
+
+    final boolean premanagedState;
+
+    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/Results.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/Results.java
new file mode 100644
index 0000000..2d27c9d
--- /dev/null
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/Results.java
@@ -0,0 +1,83 @@
+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.RepositorySystemSession;
+import org.eclipse.aether.collection.CollectResult;
+import org.eclipse.aether.graph.Dependency;
+import org.eclipse.aether.util.ConfigUtils;
+
+class Results
+{
+
+    private final CollectResult result;
+
+    private final int maxExceptions;
+
+    private final int maxCycles;
+
+    String errorPath;
+
+    Results( CollectResult result, RepositorySystemSession session )
+    {
+        this.result = result;
+        maxExceptions = ConfigUtils.getInteger( session, 50, DefaultDependencyCollector.CONFIG_PROP_MAX_EXCEPTIONS );
+        maxCycles = ConfigUtils.getInteger( session, 10, DefaultDependencyCollector.CONFIG_PROP_MAX_CYCLES );
+    }
+
+    public void addException( Dependency dependency, Exception e, NodeStack nodes )
+    {
+        if ( maxExceptions < 0 || result.getExceptions().size() < maxExceptions )
+        {
+            result.addException( e );
+            if ( errorPath == null )
+            {
+                StringBuilder buffer = new StringBuilder( 256 );
+                for ( int i = 0; i < nodes.size(); i++ )
+                {
+                    if ( buffer.length() > 0 )
+                    {
+                        buffer.append( " -> " );
+                    }
+                    Dependency dep = nodes.get( i ).getDependency();
+                    if ( dep != null )
+                    {
+                        buffer.append( dep.getArtifact() );
+                    }
+                }
+                if ( buffer.length() > 0 )
+                {
+                    buffer.append( " -> " );
+                }
+                buffer.append( dependency.getArtifact() );
+                errorPath = buffer.toString();
+            }
+        }
+    }
+
+    public void addCycle( NodeStack nodes, int cycleEntry, Dependency dependency )
+    {
+        if ( maxCycles < 0 || result.getCycles().size() < maxCycles )
+        {
+            result.addCycle( new DefaultDependencyCycle( nodes, cycleEntry, dependency ) );
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DataPoolTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DataPoolTest.java
index 6b26f33..2ac1855 100644
--- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DataPoolTest.java
+++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DataPoolTest.java
@@ -18,8 +18,11 @@ package org.eclipse.aether.internal.impl.collect;
  * specific language governing permissions and limitations
  * under the License.
  */
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 
-import static org.junit.Assert.*;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
 
 import org.eclipse.aether.DefaultRepositorySystemSession;
 import org.eclipse.aether.artifact.DefaultArtifact;
@@ -27,6 +30,7 @@ import org.eclipse.aether.graph.Dependency;
 import org.eclipse.aether.repository.RemoteRepository;
 import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
 import org.eclipse.aether.resolution.ArtifactDescriptorResult;
+import org.eclipse.aether.util.concurrency.FutureResult;
 import org.junit.Test;
 
 public class DataPoolTest
@@ -38,7 +42,7 @@ public class DataPoolTest
     }
 
     @Test
-    public void testArtifactDescriptorCaching()
+    public void testArtifactDescriptorCaching() throws InterruptedException, ExecutionException
     {
         ArtifactDescriptorRequest request = new ArtifactDescriptorRequest();
         request.setArtifact( new DefaultArtifact( "gid:aid:1" ) );
@@ -49,12 +53,14 @@ public class DataPoolTest
         result.addManagedDependency( new Dependency( new DefaultArtifact( "gid:mdep:3" ), "runtime" ) );
         result.addRepository( new RemoteRepository.Builder( "test", "default", "http://localhost" ).build() );
         result.addAlias( new DefaultArtifact( "gid:alias:4" ) );
+        Future<ArtifactDescriptorResult> futureResult = new FutureResult<>( result );
 
         DataPool pool = newDataPool();
         Object key = pool.toKey( request );
-        pool.putDescriptor( key, result );
-        ArtifactDescriptorResult cached = pool.getDescriptor( key, request );
-        assertNotNull( cached );
+        pool.putDescriptor( key, futureResult );
+        Future<ArtifactDescriptorResult> futureCached = pool.getDescriptor( key, request );
+        assertNotNull( futureCached );
+        ArtifactDescriptorResult cached = futureCached.get();
         assertEquals( result.getArtifact(), cached.getArtifact() );
         assertEquals( result.getRelocations(), cached.getRelocations() );
         assertEquals( result.getDependencies(), cached.getDependencies() );
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/DefaultDependencyCollectorTest.java
index 0e2d644..94c504e 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/DefaultDependencyCollectorTest.java
@@ -8,9 +8,9 @@ package org.eclipse.aether.internal.impl.collect;
  * 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
@@ -18,8 +18,12 @@ package org.eclipse.aether.internal.impl.collect;
  * specific language governing permissions and limitations
  * under the License.
  */
-
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+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;
@@ -60,6 +64,7 @@ import org.eclipse.aether.resolution.ArtifactDescriptorResult;
 import org.eclipse.aether.util.artifact.ArtifactIdUtils;
 import org.eclipse.aether.util.graph.manager.ClassicDependencyManager;
 import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
+import org.eclipse.aether.util.graph.selector.ScopeDependencySelector;
 import org.eclipse.aether.util.graph.version.HighestVersionFilter;
 import org.junit.Before;
 import org.junit.Test;
@@ -492,6 +497,44 @@ public class DefaultDependencyCollectorTest
         assertEquals( 1, result.getRoot().getChildren().size() );
     }
 
+    /**
+     * Tests that scope based dependency selection happens before dependency management.
+     * <p>
+     * This is not really correct (see MRESOLVER-9), but there are a number of tests
+     * in the Maven and Maven Integration Testing projects that currently rely on this
+     * behaviour.
+     */
+    @Test
+    public void testSelectionBeforeManagement()
+        throws IOException, DependencyCollectionException
+    {
+        session.setDependencySelector( new ScopeDependencySelector( "provided", "test" ) );
+        session.setDependencyManager( new ClassicDependencyManager() );
+
+        Dependency dependency = newDep( "gid3:aid1:ext:1", "compile" );
+        CollectRequest request = new CollectRequest( dependency, Arrays.asList( repository ) );
+        CollectResult result = collector.collectDependencies( session, request );
+
+        assertEquals( 0, result.getExceptions().size() );
+
+        DependencyNode root = result.getRoot();
+        Dependency newDependency = root.getDependency();
+
+        assertEquals( dependency, newDependency );
+        assertEquals( dependency.getArtifact(), newDependency.getArtifact() );
+
+        assertEquals( 1, root.getChildren().size() );
+
+        Dependency expect = newDep( "gid3:aid2:ext:1", "compile" );
+        DependencyNode childLevel1 = root.getChildren().get( 0 );
+        assertEquals( expect, childLevel1.getDependency() );
+
+        // With proper dependency management, the test scope of aid3 would
+        // be managed to compile, and we would get another child.
+        // Currently, the dependency gets filtered by ScopeDependencyManager.
+        assertEquals( 0, childLevel1.getChildren().size() );
+    }
+
     static class TestDependencyManager
         implements DependencyManager
     {
diff --git a/maven-resolver-impl/src/test/resources/artifact-descriptions/gid3_aid1_1.ini b/maven-resolver-impl/src/test/resources/artifact-descriptions/gid3_aid1_1.ini
new file mode 100644
index 0000000..ffc6dbd
--- /dev/null
+++ b/maven-resolver-impl/src/test/resources/artifact-descriptions/gid3_aid1_1.ini
@@ -0,0 +1,7 @@
+[dependencies]
+gid3:aid2:ext:1:compile
+
+[managed-dependencies]
+gid3:aid3:ext:1:compile:!optional
+
+
diff --git a/maven-resolver-impl/src/test/resources/artifact-descriptions/gid3_aid2_1.ini b/maven-resolver-impl/src/test/resources/artifact-descriptions/gid3_aid2_1.ini
new file mode 100644
index 0000000..8d64166
--- /dev/null
+++ b/maven-resolver-impl/src/test/resources/artifact-descriptions/gid3_aid2_1.ini
@@ -0,0 +1,2 @@
+[dependencies]
+gid3:aid3:ext:2:test:optional
diff --git a/maven-resolver-impl/src/test/resources/artifact-descriptions/gid3_aid3_1.ini b/maven-resolver-impl/src/test/resources/artifact-descriptions/gid3_aid3_1.ini
new file mode 100644
index 0000000..4322fc6
--- /dev/null
+++ b/maven-resolver-impl/src/test/resources/artifact-descriptions/gid3_aid3_1.ini
@@ -0,0 +1,2 @@
+[dependencies]
+
diff --git a/maven-resolver-impl/src/test/resources/artifact-descriptions/gid3_aid3_2.ini b/maven-resolver-impl/src/test/resources/artifact-descriptions/gid3_aid3_2.ini
new file mode 100644
index 0000000..4322fc6
--- /dev/null
+++ b/maven-resolver-impl/src/test/resources/artifact-descriptions/gid3_aid3_2.ini
@@ -0,0 +1,2 @@
+[dependencies]
+
diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/concurrency/FutureResult.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/concurrency/FutureResult.java
new file mode 100644
index 0000000..dbbe1df
--- /dev/null
+++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/concurrency/FutureResult.java
@@ -0,0 +1,69 @@
+package org.eclipse.aether.util.concurrency;
+
+/*
+ * 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.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Wraps a result value as a Future.
+ *
+ * @param <T> result type
+ */
+public class FutureResult<T>
+    implements Future<T>
+{
+    private final T value;
+
+    public FutureResult( T value )
+    {
+        this.value = value;
+    }
+
+    @Override
+    public boolean cancel( boolean mayInterruptIfRunning )
+    {
+        return false;
+    }
+
+    @Override
+    public boolean isCancelled()
+    {
+        return false;
+    }
+
+    @Override
+    public boolean isDone()
+    {
+        return true;
+    }
+
+    @Override
+    public T get()
+    {
+        return value;
+    }
+
+    @Override
+    public T get( long timeout, TimeUnit unit )
+    {
+        return value;
+    }
+}