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

[maven-resolver] branch master updated: [MRESOLVER-262] Provide contextual data in trace during collect (#182)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 4702bcf2 [MRESOLVER-262] Provide contextual data in trace during collect (#182)
4702bcf2 is described below

commit 4702bcf246f018ec8e9d1d3dd3f3dfe9a86b8da0
Author: Tamas Cservenak <ta...@cservenak.net>
AuthorDate: Thu Jun 9 21:26:01 2022 +0200

    [MRESOLVER-262] Provide contextual data in trace during collect (#182)
    
    Pass contextual data in RequestTrace data object.
---
 .../eclipse/aether/collection/CollectStepData.java |  46 +++++----
 .../maven/resolver/examples/AllResolverDemos.java  |   1 +
 .../resolver/examples/ReverseDependencyTree.java   |  83 +++++++++++++++
 .../util/ReverseTreeRepositoryListener.java        | 115 +++++++++++++++++++++
 .../internal/impl/collect/CollectStepDataImpl.java |  67 ++++++++++++
 .../impl/collect/DependencyCollectorDelegate.java  |  27 +++++
 .../impl/collect/bf/BfDependencyCollector.java     |  25 +++--
 .../impl/collect/df/DfDependencyCollector.java     |  46 ++++-----
 8 files changed, 352 insertions(+), 58 deletions(-)

diff --git a/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/AllResolverDemos.java b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/CollectStepData.java
similarity index 53%
copy from maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/AllResolverDemos.java
copy to maven-resolver-api/src/main/java/org/eclipse/aether/collection/CollectStepData.java
index 74bb3d48..28036e9d 100644
--- a/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/AllResolverDemos.java
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/CollectStepData.java
@@ -1,4 +1,4 @@
-package org.apache.maven.resolver.examples;
+package org.eclipse.aether.collection;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -8,9 +8,9 @@ package org.apache.maven.resolver.examples;
  * 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,28 +19,32 @@ package org.apache.maven.resolver.examples;
  * under the License.
  */
 
+import java.util.List;
+
+import org.eclipse.aether.graph.Dependency;
+import org.eclipse.aether.graph.DependencyNode;
+
 /**
- * Runs all demos at once.
+ * A trace data object revealing collect step (while executing {@link CollectRequest}.
+ *
+ * @see org.eclipse.aether.RequestTrace
+ * @since 1.8.1
  */
-public class AllResolverDemos
+public interface CollectStepData
 {
     /**
-     * Main 
-     * @param args
-     * @throws Exception
+     * Returns the context of collection. Never {@code null}.
      */
-    public static void main( String[] args )
-        throws Exception
-    {
-        FindAvailableVersions.main( args );
-        FindNewestVersion.main( args );
-        GetDirectDependencies.main( args );
-        GetDependencyTree.main( args );
-        GetDependencyHierarchy.main( args );
-        ResolveArtifact.main( args );
-        ResolveTransitiveDependencies.main( args );
-        InstallArtifacts.main( args );
-        DeployArtifacts.main( args );
-    }
+    String getContext();
 
+    /**
+     * Returns the path of dependency nodes that led collector to current node returned by {@link #getNode()}.
+     * Never {@code null}.
+     */
+    List<DependencyNode> getPath();
+
+    /**
+     * Returns the current node being collected. Never {@code null}.
+     */
+    Dependency getNode();
 }
diff --git a/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/AllResolverDemos.java b/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/AllResolverDemos.java
index 74bb3d48..bb260aaf 100644
--- a/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/AllResolverDemos.java
+++ b/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/AllResolverDemos.java
@@ -39,6 +39,7 @@ public class AllResolverDemos
         GetDependencyHierarchy.main( args );
         ResolveArtifact.main( args );
         ResolveTransitiveDependencies.main( args );
+        ReverseDependencyTree.main( args );
         InstallArtifacts.main( args );
         DeployArtifacts.main( args );
     }
diff --git a/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/ReverseDependencyTree.java b/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/ReverseDependencyTree.java
new file mode 100644
index 00000000..97b0b4af
--- /dev/null
+++ b/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/ReverseDependencyTree.java
@@ -0,0 +1,83 @@
+package org.apache.maven.resolver.examples;
+
+/*
+ * 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.apache.maven.resolver.examples.util.Booter;
+import org.apache.maven.resolver.examples.util.ReverseTreeRepositoryListener;
+import org.eclipse.aether.DefaultRepositorySystemSession;
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.artifact.DefaultArtifact;
+import org.eclipse.aether.collection.CollectRequest;
+import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
+import org.eclipse.aether.resolution.ArtifactDescriptorResult;
+import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
+import org.eclipse.aether.util.graph.transformer.ConflictResolver;
+import org.eclipse.aether.util.listener.ChainedRepositoryListener;
+
+/**
+ * Example of building reverse dependency tree using custom {@link ReverseTreeRepositoryListener}.
+ */
+public class ReverseDependencyTree
+{
+
+    /**
+     * Main.
+     * @param args
+     * @throws Exception
+     */
+    public static void main( String[] args )
+        throws Exception
+    {
+        System.out.println( "------------------------------------------------------------" );
+        System.out.println( ReverseDependencyTree.class.getSimpleName() );
+
+        RepositorySystem system = Booter.newRepositorySystem( Booter.selectFactory( args ) );
+
+        DefaultRepositorySystemSession session = Booter.newRepositorySystemSession( system );
+
+        // install the listener into session
+        session.setRepositoryListener( new ChainedRepositoryListener( session.getRepositoryListener(),
+                new ReverseTreeRepositoryListener() ) );
+
+        session.setConfigProperty( ConflictResolver.CONFIG_PROP_VERBOSE, true );
+        session.setConfigProperty( DependencyManagerUtils.CONFIG_PROP_VERBOSE, true );
+
+        Artifact artifact = new DefaultArtifact( "org.apache.maven:maven-resolver-provider:3.6.1" );
+
+        ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest();
+        descriptorRequest.setArtifact( artifact );
+        descriptorRequest.setRepositories( Booter.newRepositories( system, session ) );
+        ArtifactDescriptorResult descriptorResult = system.readArtifactDescriptor( session, descriptorRequest );
+
+        CollectRequest collectRequest = new CollectRequest();
+        collectRequest.setRequestContext( "demo" );
+        collectRequest.setRootArtifact( descriptorResult.getArtifact() );
+        collectRequest.setDependencies( descriptorResult.getDependencies() );
+        collectRequest.setManagedDependencies( descriptorResult.getManagedDependencies() );
+        collectRequest.setRepositories( descriptorRequest.getRepositories() );
+
+        system.collectDependencies( session, collectRequest );
+
+        // in this demo we are not interested in collect result,
+        // as all the "demo work" is done by installed ReverseTreeRepositoryListener
+    }
+
+}
diff --git a/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/util/ReverseTreeRepositoryListener.java b/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/util/ReverseTreeRepositoryListener.java
new file mode 100644
index 00000000..a13a768a
--- /dev/null
+++ b/maven-resolver-demos/maven-resolver-demo-snippets/src/main/java/org/apache/maven/resolver/examples/util/ReverseTreeRepositoryListener.java
@@ -0,0 +1,115 @@
+package org.apache.maven.resolver.examples.util;
+
+/*
+ * 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.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ListIterator;
+import java.util.Objects;
+
+import org.eclipse.aether.AbstractRepositoryListener;
+import org.eclipse.aether.RepositoryEvent;
+import org.eclipse.aether.RequestTrace;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.collection.CollectStepData;
+import org.eclipse.aether.graph.Dependency;
+import org.eclipse.aether.graph.DependencyNode;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * A demo class building reverse tree using {@link CollectStepData} trace data provided in {@link RepositoryEvent}
+ * events fired during collection.
+ */
+public class ReverseTreeRepositoryListener
+        extends AbstractRepositoryListener
+{
+    private static final String EOL = System.lineSeparator();
+
+    @Override
+    public void artifactResolved( RepositoryEvent event )
+    {
+        requireNonNull( event, "event cannot be null" );
+
+        RequestTrace trace = event.getTrace();
+        CollectStepData collectStepTrace = null;
+        while ( trace != null )
+        {
+            if ( trace.getData() instanceof CollectStepData )
+            {
+                collectStepTrace = (CollectStepData) trace.getData();
+                break;
+            }
+            trace = trace.getParent();
+        }
+
+        if ( collectStepTrace == null )
+        {
+            return;
+        }
+
+        Artifact resolvedArtifact = event.getArtifact();
+        Artifact nodeArtifact = collectStepTrace.getNode().getArtifact();
+
+        if ( isInScope( resolvedArtifact, nodeArtifact ) )
+        {
+            Dependency node = collectStepTrace.getNode();
+            String trackingData = node + " (" + collectStepTrace.getContext() + ")" + EOL;
+            String indent = "";
+            ListIterator<DependencyNode> iter = collectStepTrace.getPath()
+                    .listIterator( collectStepTrace.getPath().size() );
+            while ( iter.hasPrevious() )
+            {
+                DependencyNode curr = iter.previous();
+                indent += "  ";
+                trackingData += indent + curr + " (" + collectStepTrace.getContext() + ")" + EOL;
+            }
+            try
+            {
+                Path trackingDir = resolvedArtifact.getFile().getParentFile().toPath().resolve( ".tracking" );
+                Files.createDirectories( trackingDir );
+                Path trackingFile = trackingDir.resolve( collectStepTrace.getPath().get( 0 )
+                        .getArtifact().toString().replace( ":", "_" ) );
+                Files.write( trackingFile, trackingData.getBytes( StandardCharsets.UTF_8 ) );
+                System.out.println( trackingData );
+            }
+            catch ( IOException e )
+            {
+                throw new UncheckedIOException( e );
+            }
+        }
+    }
+
+    /**
+     * The event "artifact resolved" if fired WHENEVER an artifact is resolved, BUT it happens also when an artifact
+     * descriptor (model, the POM) is being built, and parent (and parent of parent...) is being asked for. Hence, this
+     * method "filters" out in WHICH artifact are we interested in, but it intentionally neglects extension as
+     * ArtifactDescriptorReader modifies extension to "pom" during collect. So all we have to rely on is GAV only.
+     */
+    private boolean isInScope( Artifact artifact, Artifact nodeArtifact )
+    {
+        return Objects.equals( artifact.getGroupId(), nodeArtifact.getGroupId() )
+                && Objects.equals( artifact.getArtifactId(), nodeArtifact.getArtifactId() )
+                && Objects.equals( artifact.getVersion(), nodeArtifact.getVersion() );
+    }
+}
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/CollectStepDataImpl.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/CollectStepDataImpl.java
new file mode 100644
index 00000000..eb1df324
--- /dev/null
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/CollectStepDataImpl.java
@@ -0,0 +1,67 @@
+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.List;
+
+import org.eclipse.aether.collection.CollectStepData;
+import org.eclipse.aether.graph.Dependency;
+import org.eclipse.aether.graph.DependencyNode;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Trace objects for dependency collection.
+ *
+ * @since 1.8.1
+ */
+public final class CollectStepDataImpl implements CollectStepData
+{
+    private final String context;
+
+    private final List<DependencyNode> path;
+
+    private final Dependency node;
+
+    public CollectStepDataImpl( final String context, final List<DependencyNode> path, final Dependency node )
+    {
+        this.context = requireNonNull( context );
+        this.path = requireNonNull( path );
+        this.node = requireNonNull( node );
+    }
+
+    @Override
+    public String getContext()
+    {
+        return context;
+    }
+
+    @Override
+    public List<DependencyNode> getPath()
+    {
+        return path;
+    }
+
+    @Override
+    public Dependency getNode()
+    {
+        return node;
+    }
+}
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java
index 0a54f1b7..0897cc68 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java
@@ -290,6 +290,33 @@ public abstract class DependencyCollectorDelegate implements DependencyCollector
         return result;
     }
 
+    /**
+     * Creates child {@link RequestTrace} instance from passed in {@link RequestTrace} and parameters by creating
+     * {@link CollectStepDataImpl} instance out of passed in data. Caller must ensure that passed in parameters are
+     * NOT affected by threading (or that there is no multi threading involved). In other words, the passed in values
+     * should be immutable.
+     *
+     * @param trace   The current trace instance.
+     * @param context The context from {@link CollectRequest#getRequestContext()}, never {@code null}.
+     * @param path    List representing the path of dependency nodes, never {@code null}. Caller must ensure, that this
+     *                list does not change during the lifetime of the requested {@link RequestTrace} instance. If it may
+     *                change, simplest is to pass here a copy of used list.
+     * @param node    Currently collected node, that collector came by following the passed in path.
+     * @return A child request trance instance, never {@code null}.
+     */
+    protected RequestTrace collectStepTrace( RequestTrace trace, String context, List<DependencyNode> path,
+                                             Dependency node )
+    {
+        return RequestTrace.newChild(
+                trace,
+                new CollectStepDataImpl(
+                        context,
+                        path,
+                        node
+                )
+        );
+    }
+
     @SuppressWarnings( "checkstyle:parameternumber" )
     protected abstract void doCollectDependencies( RepositorySystemSession session, RequestTrace trace, DataPool pool,
                                                    DefaultDependencyCollectionContext context,
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java
index 8542dc7c..c82eed94 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java
@@ -126,7 +126,7 @@ public class BfDependencyCollector
         }
 
         Args args =
-                new Args( session, trace, pool, context, versionContext, request,
+                new Args( session, pool, context, versionContext, request,
                         useSkip ? DependencyResolutionSkipper.defaultSkipper()
                                 : DependencyResolutionSkipper.neverSkipper() );
 
@@ -150,7 +150,7 @@ public class BfDependencyCollector
 
         while ( !args.dependencyProcessingQueue.isEmpty() )
         {
-            processDependency( args, results, args.dependencyProcessingQueue.remove(), Collections.emptyList(),
+            processDependency( args, trace, results, args.dependencyProcessingQueue.remove(), Collections.emptyList(),
                     false );
         }
 
@@ -158,15 +158,17 @@ public class BfDependencyCollector
     }
 
     @SuppressWarnings( "checkstyle:parameternumber" )
-    private void processDependency( Args args, Results results, DependencyProcessingContext context,
-                                    List<Artifact> relocations, boolean disableVersionManagement )
+    private void processDependency( Args args, RequestTrace parent, Results results,
+                                    DependencyProcessingContext context, List<Artifact> relocations,
+                                    boolean disableVersionManagement )
     {
-
         if ( context.depSelector != null && !context.depSelector.selectDependency( context.dependency ) )
         {
             return;
         }
 
+        RequestTrace trace = collectStepTrace( parent, args.request.getRequestContext(), context.parents,
+                context.dependency );
         PremanagedDependency preManaged =
                 PremanagedDependency.create( context.depManager, context.dependency, disableVersionManagement,
                         args.premanagedState );
@@ -182,7 +184,7 @@ public class BfDependencyCollector
         VersionRangeResult rangeResult;
         try
         {
-            VersionRangeRequest rangeRequest = createVersionRangeRequest( args.request.getRequestContext(), args.trace,
+            VersionRangeRequest rangeRequest = createVersionRangeRequest( args.request.getRequestContext(), trace,
                     context.repositories, dependency );
 
             rangeResult = cachedResolveRangeResult( rangeRequest, args.pool, args.session );
@@ -203,7 +205,7 @@ public class BfDependencyCollector
             Dependency d = dependency.setArtifact( originalArtifact );
 
             ArtifactDescriptorRequest descriptorRequest = createArtifactDescriptorRequest(
-                    args.request.getRequestContext(), args.trace, context.repositories, d );
+                    args.request.getRequestContext(), trace, context.repositories, d );
 
             final ArtifactDescriptorResult descriptorResult =
                     noDescriptor
@@ -236,8 +238,8 @@ public class BfDependencyCollector
                         originalArtifact.getGroupId().equals( d.getArtifact().getGroupId() )
                             && originalArtifact.getArtifactId().equals( d.getArtifact().getArtifactId() );
 
-                    processDependency( args, results, context.withDependency( d ), descriptorResult.getRelocations(),
-                            disableVersionManagementSubsequently );
+                    processDependency( args, parent, results, context.withDependency( d ),
+                            descriptorResult.getRelocations(), disableVersionManagementSubsequently );
                     return;
                 }
                 else
@@ -372,8 +374,6 @@ public class BfDependencyCollector
 
         final boolean premanagedState;
 
-        final RequestTrace trace;
-
         final DataPool pool;
 
         final Queue<DependencyProcessingContext> dependencyProcessingQueue = new ArrayDeque<>( 128 );
@@ -386,7 +386,7 @@ public class BfDependencyCollector
 
         final DependencyResolutionSkipper skipper;
 
-        Args( RepositorySystemSession session, RequestTrace trace, DataPool pool,
+        Args( RepositorySystemSession session, DataPool pool,
                      DefaultDependencyCollectionContext collectionContext, DefaultVersionFilterContext versionContext,
                      CollectRequest request, DependencyResolutionSkipper skipper )
         {
@@ -394,7 +394,6 @@ public class BfDependencyCollector
             this.request = request;
             this.ignoreRepos = session.isIgnoreArtifactDescriptorRepositories();
             this.premanagedState = ConfigUtils.getBoolean( session, false, DependencyManagerUtils.CONFIG_PROP_VERBOSE );
-            this.trace = trace;
             this.pool = pool;
             this.collectionContext = collectionContext;
             this.versionContext = versionContext;
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java
index 4bfaf872..268100f0 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java
@@ -102,9 +102,9 @@ public class DfDependencyCollector
         NodeStack nodes = new NodeStack();
         nodes.push( node );
 
-        Args args = new Args( session, trace, pool, nodes, context, versionContext, request );
+        Args args = new Args( session, pool, nodes, context, versionContext, request );
 
-        process( args, results, dependencies, repositories,
+        process( args, trace, results, dependencies, repositories,
                 session.getDependencySelector() != null
                         ? session.getDependencySelector().deriveChildSelector( context ) : null,
                 session.getDependencyManager() != null
@@ -116,40 +116,41 @@ public class DfDependencyCollector
     }
 
         @SuppressWarnings( "checkstyle:parameternumber" )
-    private void process( final Args args, Results results, List<Dependency> dependencies,
+    private void process( final Args args, RequestTrace trace, Results results, List<Dependency> dependencies,
                           List<RemoteRepository> repositories, DependencySelector depSelector,
                           DependencyManager depManager, DependencyTraverser depTraverser, VersionFilter verFilter )
     {
         for ( Dependency dependency : dependencies )
         {
-            processDependency( args, results, repositories, depSelector, depManager, depTraverser, verFilter,
+            processDependency( args, trace, results, repositories, depSelector, depManager, depTraverser, verFilter,
                                dependency );
         }
     }
 
     @SuppressWarnings( "checkstyle:parameternumber" )
-    private void processDependency( Args args, Results results, List<RemoteRepository> repositories,
+    private void processDependency( Args args, RequestTrace trace, Results results, List<RemoteRepository> repositories,
                                     DependencySelector depSelector, DependencyManager depManager,
                                     DependencyTraverser depTraverser, VersionFilter verFilter, Dependency dependency )
     {
 
         List<Artifact> relocations = Collections.emptyList();
-        processDependency( args, results, repositories, depSelector, depManager, depTraverser, verFilter, dependency,
-                           relocations, false );
+        processDependency( args, trace, results, repositories, depSelector, depManager, depTraverser, verFilter,
+                dependency, relocations, false );
     }
 
     @SuppressWarnings( "checkstyle:parameternumber" )
-    private void processDependency( Args args, Results results, List<RemoteRepository> repositories,
-                                    DependencySelector depSelector, DependencyManager depManager,
-                                    DependencyTraverser depTraverser, VersionFilter verFilter, Dependency dependency,
-                                    List<Artifact> relocations, boolean disableVersionManagement )
+    private void processDependency( Args args, RequestTrace parent, Results results,
+                                    List<RemoteRepository> repositories, DependencySelector depSelector,
+                                    DependencyManager depManager, DependencyTraverser depTraverser,
+                                    VersionFilter verFilter, Dependency dependency, List<Artifact> relocations,
+                                    boolean disableVersionManagement )
     {
-
         if ( depSelector != null && !depSelector.selectDependency( dependency ) )
         {
             return;
         }
 
+        RequestTrace trace = collectStepTrace( parent, args.request.getRequestContext(), args.nodes.nodes, dependency );
         PremanagedDependency preManaged =
             PremanagedDependency.create( depManager, dependency, disableVersionManagement, args.premanagedState );
         dependency = preManaged.getManagedDependency();
@@ -162,7 +163,7 @@ public class DfDependencyCollector
         VersionRangeResult rangeResult;
         try
         {
-            VersionRangeRequest rangeRequest = createVersionRangeRequest( args.request.getRequestContext(), args.trace,
+            VersionRangeRequest rangeRequest = createVersionRangeRequest( args.request.getRequestContext(), trace,
                     repositories, dependency );
 
             rangeResult = cachedResolveRangeResult( rangeRequest, args.pool, args.session );
@@ -181,7 +182,7 @@ public class DfDependencyCollector
             Dependency d = dependency.setArtifact( originalArtifact );
 
             ArtifactDescriptorRequest descriptorRequest = createArtifactDescriptorRequest(
-                    args.request.getRequestContext(), args.trace, repositories, d );
+                    args.request.getRequestContext(), trace, repositories, d );
 
             final ArtifactDescriptorResult descriptorResult =
                 getArtifactDescriptorResult( args, results, noDescriptor, d, descriptorRequest );
@@ -212,8 +213,8 @@ public class DfDependencyCollector
                         originalArtifact.getGroupId().equals( d.getArtifact().getGroupId() )
                             && originalArtifact.getArtifactId().equals( d.getArtifact().getArtifactId() );
 
-                    processDependency( args, results, repositories, depSelector, depManager, depTraverser, verFilter, d,
-                                       descriptorResult.getRelocations(), disableVersionManagementSubsequently );
+                    processDependency( args, parent, results, repositories, depSelector, depManager, depTraverser,
+                            verFilter, d, descriptorResult.getRelocations(), disableVersionManagementSubsequently );
                     return;
                 }
                 else
@@ -232,8 +233,8 @@ public class DfDependencyCollector
                     boolean recurse = traverse && !descriptorResult.getDependencies().isEmpty();
                     if ( recurse )
                     {
-                        doRecurse( args, results, repositories, depSelector, depManager, depTraverser, verFilter, d,
-                                   descriptorResult, child );
+                        doRecurse( args, parent, results, repositories, depSelector, depManager, depTraverser,
+                                verFilter, d, descriptorResult, child );
                     }
                 }
             }
@@ -251,7 +252,7 @@ public class DfDependencyCollector
     }
 
     @SuppressWarnings( "checkstyle:parameternumber" )
-    private void doRecurse( Args args, Results results, List<RemoteRepository> repositories,
+    private void doRecurse( Args args, RequestTrace trace, Results results, List<RemoteRepository> repositories,
                             DependencySelector depSelector, DependencyManager depManager,
                             DependencyTraverser depTraverser, VersionFilter verFilter, Dependency d,
                             ArtifactDescriptorResult descriptorResult, DefaultDependencyNode child )
@@ -280,7 +281,7 @@ public class DfDependencyCollector
 
             args.nodes.push( child );
 
-            process( args, results, descriptorResult.getDependencies(), childRepos, childSelector, childManager,
+            process( args, trace, results, descriptorResult.getDependencies(), childRepos, childSelector, childManager,
                      childTraverser, childFilter );
 
             args.nodes.pop();
@@ -340,8 +341,6 @@ public class DfDependencyCollector
 
         final boolean premanagedState;
 
-        final RequestTrace trace;
-
         final DataPool pool;
 
         final NodeStack nodes;
@@ -352,7 +351,7 @@ public class DfDependencyCollector
 
         final CollectRequest request;
 
-        Args( RepositorySystemSession session, RequestTrace trace, DataPool pool, NodeStack nodes,
+        Args( RepositorySystemSession session, DataPool pool, NodeStack nodes,
                      DefaultDependencyCollectionContext collectionContext, DefaultVersionFilterContext versionContext,
                      CollectRequest request )
         {
@@ -360,7 +359,6 @@ public class DfDependencyCollector
             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;