[MRESOLVER-7] Download dependency POMs in parallel

Dependencies are now processed asynchronously by an Executor

This closes #30
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 @@
  * 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.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 @@
 
     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 @@
 
     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 @@
 
         if ( artifacts == null )
         {
-            artifacts = new ObjectPool<Artifact>();
+            artifacts = new ObjectPool<>();
             if ( cache != null )
             {
                 cache.put( session, ARTIFACT_POOL, artifacts );
@@ -92,7 +96,7 @@
 
         if ( dependencies == null )
         {
-            dependencies = new ObjectPool<Dependency>();
+            dependencies = new ObjectPool<>();
             if ( cache != null )
             {
                 cache.put( session, DEPENDENCY_POOL, dependencies );
@@ -124,7 +128,7 @@
         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 @@
         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 @@
         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 @@
 
     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;
-
-        final List<RemoteRepository> repositories;
-
-        final List<Dependency> dependencies;
-
-        final List<Dependency> managedDependencies;
-
-        GoodDescriptor( ArtifactDescriptorResult result )
+        GoodDescriptor( Future<ArtifactDescriptorResult> futureResult )
         {
-            artifact = result.getArtifact();
-            relocations = result.getRelocations();
-            aliases = result.getAliases();
-            dependencies = result.getDependencies();
-            managedDependencies = result.getManagedDependencies();
-            repositories = result.getRepositories();
+            this.futureResult = futureResult;
         }
 
-        public ArtifactDescriptorResult toResult( ArtifactDescriptorRequest request )
+        public Future<ArtifactDescriptorResult> toResult( final ArtifactDescriptorRequest request )
         {
-            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;
+            return new Future<ArtifactDescriptorResult>()
+            {
+                public boolean cancel( boolean mayInterruptIfRunning )
+                {
+                    return futureResult.cancel( mayInterruptIfRunning );
+                }
+
+                public boolean isCancelled()
+                {
+                    return futureResult.isCancelled();
+                }
+
+                public boolean isDone()
+                {
+                    return futureResult.isDone();
+                }
+
+                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 );
+                }
+            };
         }
 
+        private ArtifactDescriptorResult wrap( ArtifactDescriptorRequest request, ArtifactDescriptorResult 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 @@
 
         static final class VersionRepo
         {
-
             final Version version;
 
             final ArtifactRepository repo;
@@ -283,14 +298,11 @@
                 this.version = version;
                 this.repo = repo;
             }
-
         }
-
     }
 
     static final class ConstraintKey
     {
-
         private final Artifact artifact;
 
         private final List<RemoteRepository> repositories;
@@ -361,12 +373,10 @@
         {
             return hashCode;
         }
-
     }
 
     static final class GraphKey
     {
-
         private final Artifact artifact;
 
         private final List<RemoteRepository> repositories;
@@ -382,7 +392,7 @@
         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 @@
 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 @@
     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 @@
         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 @@
         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 @@
  * 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 @@
  * 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.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.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 @@
     implements DependencyCollector, Service
 {
 
-    private static final String CONFIG_PROP_MAX_EXCEPTIONS = "aether.dependencyCollector.maxExceptions";
+    static final String CONFIG_PROP_MAX_EXCEPTIONS = "aether.dependencyCollector.maxExceptions";
 
-    private static final String CONFIG_PROP_MAX_CYCLES = "aether.dependencyCollector.maxCycles";
+    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 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 @@
                                                                               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 @@
 
         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 @@
         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 @@
         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 @@
         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 );
+            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 ) )
+            {
 
-            rangeResult = cachedResolveRangeResult( rangeRequest, args.pool, args.session );
+                Artifact originalArtifact = dependency.getArtifact().setVersion( version.toString() );
+                Dependency d = dependency.setArtifact( originalArtifact );
 
-            versions = filterVersions( dependency, rangeResult, verFilter, args.versionContext );
+                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 dc;
+    }
+
+    private void processDependencyNode( DependencyContext dc )
+    {
+        if ( dc == null )
+        {
             return;
         }
-
-        for ( Version version : versions )
+        try
         {
-            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 )
+            boolean noResult = dc.futureDescriptorResult == null;
+            if ( !noResult )
             {
-                d = d.setArtifact( descriptorResult.getArtifact() );
-
-                DependencyNode node = args.nodes.top();
-
-                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;
-                    }
-                }
-
-                if ( !descriptorResult.getRelocations().isEmpty() )
-                {
-                    boolean disableVersionManagementSubsequently =
-                        originalArtifact.getGroupId().equals( d.getArtifact().getGroupId() )
-                            && originalArtifact.getArtifactId().equals( d.getArtifact().getArtifactId() );
-
-                    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() ) ) );
-
-                    List<RemoteRepository> repos =
-                        getRemoteRepositories( rangeResult.getRepository( version ), repositories );
-
-                    DefaultDependencyNode child =
-                        createDependencyNode( relocations, preManaged, rangeResult, version, d,
-                                              descriptorResult.getAliases(), repos, args.request.getRequestContext() );
-
-                    node.getChildren().add( child );
-
-                    boolean recurse = traverse && !descriptorResult.getDependencies().isEmpty();
-                    if ( recurse )
-                    {
-                        doRecurse( args, results, repositories, depSelector, depManager, depTraverser, verFilter, d,
-                                   descriptorResult, child );
-                    }
-                }
+                dc.descriptorResult = dc.futureDescriptorResult.get();
+                noResult = dc.descriptorResult == null;
+            }
+            if ( noResult )
+            {
+                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
             {
-                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 );
+                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 );
+        }
     }
 
-    private void doRecurse( Args args, Results results, List<RemoteRepository> repositories,
-                            DependencySelector depSelector, DependencyManager depManager,
-                            DependencyTraverser depTraverser, VersionFilter verFilter, Dependency d,
+    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;
+
+        DependencyNode node = args.nodes.top();
+
+        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;
+            }
+        }
+
+        if ( !dc.descriptorResult.getRelocations().isEmpty() )
+        {
+            boolean disableVersionManagementSubsequently =
+                    dc.originalArtifact.getGroupId().equals( d.getArtifact().getGroupId() )
+                            && dc.originalArtifact.getArtifactId().equals( d.getArtifact().getArtifactId() );
+
+            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 );
+
+            List<RemoteRepository> repos =
+                    getRemoteRepositories( dc.rangeResult.getRepository( dc.version ), dc.context.getRepositories() );
+
+            DefaultDependencyNode child =
+                    addDependencyNode( node, dc.relocations, dc.preManaged, dc.rangeResult, dc.version, d,
+                                       dc.descriptorResult.getAliases(), repos, args.request.getRequestContext() );
+
+            if ( dc.traverse && !dc.descriptorResult.getDependencies().isEmpty() )
+            {
+                doRecurse( dc.context, d, dc.descriptorResult, child );
+            }
+            return false;
+        }
+    }
+
+    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 @@
 
             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 @@
         }
     }
 
-    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 = args.executor.submit( new Callable<ArtifactDescriptorResult>()
             {
-                descriptorResult = descriptorReader.readArtifactDescriptor( session, descriptorRequest );
-                pool.putDescriptor( key, descriptorResult );
-            }
-            catch ( ArtifactDescriptorException e )
-            {
-                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 @@
         }
         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 @@
  * 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.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 @@
     }
 
     @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 @@
         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 @@
  * 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 @@
  * 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.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 @@
         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;
+    }
+}