You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by rf...@apache.org on 2021/05/22 11:32:26 UTC

[maven-dependency-tree] 01/04: Add functionality to collect raw dependencies in Maven 3+

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

rfscholte pushed a commit to branch MSHARED-788
in repository https://gitbox.apache.org/repos/asf/maven-dependency-tree.git

commit b55e83e3ae7a642187ff125abd796d044f648446
Author: Gabriel Belingueres <be...@gmail.com>
AuthorDate: Wed Dec 26 16:08:23 2018 -0300

    Add functionality to collect raw dependencies in Maven 3+
    
    Added the funcionality.
    No added ITs, instead tested the maven-enforcer-plugin with this version
    of m-dependency-tree. Tested and passed all tests with: Maven 3.0.4,
    3.0.5, 3.1.0, 3.5.4, 3.6.0. Didn't work with Maven 3.0.0 (sonatype
    aether 1.7). Other Maven versions not tested.
---
 .../graph/DependencyCollectorBuilder.java          |  50 ++++
 .../DefaultDependencyCollectorBuilder.java         | 116 +++++++++
 .../shared/dependency/graph/internal/Invoker.java  |  22 ++
 .../Maven31DependencyCollectorBuilder.java         | 282 +++++++++++++++++++++
 .../internal/Maven3DependencyCollectorBuilder.java | 282 +++++++++++++++++++++
 5 files changed, 752 insertions(+)

diff --git a/src/main/java/org/apache/maven/shared/dependency/graph/DependencyCollectorBuilder.java b/src/main/java/org/apache/maven/shared/dependency/graph/DependencyCollectorBuilder.java
new file mode 100644
index 0000000..8865fad
--- /dev/null
+++ b/src/main/java/org/apache/maven/shared/dependency/graph/DependencyCollectorBuilder.java
@@ -0,0 +1,50 @@
+package org.apache.maven.shared.dependency.graph;
+
+/*
+ * 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.artifact.repository.ArtifactRepository;
+import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
+import org.apache.maven.project.ProjectBuildingRequest;
+
+/**
+ * Maven project dependency raw dependency collector API, providing an abstraction layer against Maven 3 and Maven 3.1+
+ * particular Aether implementations.
+ * 
+ * @author Gabriel Belingueres
+ * @since 3.0.2
+ */
+public interface DependencyCollectorBuilder
+{
+
+    /**
+     * collect the project's raw dependency graph, with information to allow the API client to reason on its own about
+     * dependencies.
+     * 
+     * @param localRepository the local repository.
+     * @param buildingRequest the request with the project to process its dependencies.
+     * @param filter an artifact filter if not all dependencies are required (can be <code>null</code>)
+     * @return the raw dependency tree
+     * @throws DependencyGraphBuilderException if some of the dependencies could not be collected.
+     */
+    DependencyNode collectDependencyGraph( ArtifactRepository localRepository, ProjectBuildingRequest buildingRequest,
+                                         ArtifactFilter filter )
+        throws DependencyGraphBuilderException;
+
+}
diff --git a/src/main/java/org/apache/maven/shared/dependency/graph/internal/DefaultDependencyCollectorBuilder.java b/src/main/java/org/apache/maven/shared/dependency/graph/internal/DefaultDependencyCollectorBuilder.java
new file mode 100644
index 0000000..ff420b9
--- /dev/null
+++ b/src/main/java/org/apache/maven/shared/dependency/graph/internal/DefaultDependencyCollectorBuilder.java
@@ -0,0 +1,116 @@
+package org.apache.maven.shared.dependency.graph.internal;
+
+/*
+ * 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.artifact.repository.ArtifactRepository;
+import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.project.ProjectBuildingRequest;
+import org.apache.maven.shared.dependency.graph.DependencyCollectorBuilder;
+import org.apache.maven.shared.dependency.graph.DependencyGraphBuilderException;
+import org.apache.maven.shared.dependency.graph.DependencyNode;
+import org.codehaus.plexus.PlexusConstants;
+import org.codehaus.plexus.PlexusContainer;
+import org.codehaus.plexus.component.annotations.Component;
+import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
+import org.codehaus.plexus.context.Context;
+import org.codehaus.plexus.context.ContextException;
+import org.codehaus.plexus.logging.AbstractLogEnabled;
+import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
+
+/**
+ * Default project dependency raw dependency collector API, providing an abstraction layer against Maven 3 and Maven
+ * 3.1+ particular Aether implementations.
+ * 
+ * @author Gabriel Belingueres
+ * @since 3.0.2
+ */
+@Component( role = DependencyCollectorBuilder.class )
+public class DefaultDependencyCollectorBuilder
+    extends AbstractLogEnabled
+    implements DependencyCollectorBuilder, Contextualizable
+{
+    protected PlexusContainer container;
+
+    @Override
+    public DependencyNode collectDependencyGraph( ArtifactRepository localRepository,
+                                                ProjectBuildingRequest buildingRequest, ArtifactFilter filter )
+        throws DependencyGraphBuilderException
+    {
+        try
+        {
+            String hint = isMaven31() ? "maven31" : "maven3";
+
+            DependencyCollectorBuilder effectiveGraphBuilder =
+                (DependencyCollectorBuilder) container.lookup( DependencyCollectorBuilder.class.getCanonicalName(),
+                                                               hint );
+
+            if ( getLogger().isDebugEnabled() )
+            {
+                MavenProject project = buildingRequest.getProject();
+
+                getLogger().debug( "building " + hint + " RAW dependency tree for " + project.getId() + " with "
+                    + effectiveGraphBuilder.getClass().getSimpleName() );
+            }
+
+            return effectiveGraphBuilder.collectDependencyGraph( localRepository, buildingRequest, filter );
+        }
+        catch ( ComponentLookupException e )
+        {
+            throw new DependencyGraphBuilderException( e.getMessage(), e );
+        }
+    }
+
+    /**
+     * @return true if the current Maven version is Maven 3.1.
+     */
+    protected static boolean isMaven31()
+    {
+        return canFindCoreClass( "org.eclipse.aether.artifact.Artifact" ); // Maven 3.1 specific
+    }
+
+    private static boolean canFindCoreClass( String className )
+    {
+        try
+        {
+            Thread.currentThread().getContextClassLoader().loadClass( className );
+
+            return true;
+        }
+        catch ( ClassNotFoundException e )
+        {
+            return false;
+        }
+    }
+
+    /**
+     * Injects the Plexus content.
+     *
+     * @param context Plexus context to inject.
+     * @throws ContextException if the PlexusContainer could not be located.
+     */
+    @Override
+    public void contextualize( Context context )
+        throws ContextException
+    {
+        container = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );
+    }
+
+}
diff --git a/src/main/java/org/apache/maven/shared/dependency/graph/internal/Invoker.java b/src/main/java/org/apache/maven/shared/dependency/graph/internal/Invoker.java
index f591973..cabfe37 100644
--- a/src/main/java/org/apache/maven/shared/dependency/graph/internal/Invoker.java
+++ b/src/main/java/org/apache/maven/shared/dependency/graph/internal/Invoker.java
@@ -102,4 +102,26 @@ final class Invoker
             throw new DependencyGraphBuilderException( e.getMessage(), e );
         }
     }
+
+    public static Object invoke( Class<?> objectClazz, String staticMethod, Class<?> argClazz1, Class<?> argClazz2,
+                                 Object arg1, Object arg2 )
+        throws DependencyGraphBuilderException
+    {
+        try
+        {
+            return objectClazz.getMethod( staticMethod, argClazz1, argClazz2 ).invoke( null, arg1, arg2 );
+        }
+        catch ( IllegalAccessException e )
+        {
+            throw new DependencyGraphBuilderException( e.getMessage(), e );
+        }
+        catch ( InvocationTargetException e )
+        {
+            throw new DependencyGraphBuilderException( e.getMessage(), e );
+        }
+        catch ( NoSuchMethodException e )
+        {
+            throw new DependencyGraphBuilderException( e.getMessage(), e );
+        }
+    }
 }
diff --git a/src/main/java/org/apache/maven/shared/dependency/graph/internal/Maven31DependencyCollectorBuilder.java b/src/main/java/org/apache/maven/shared/dependency/graph/internal/Maven31DependencyCollectorBuilder.java
new file mode 100644
index 0000000..f4fd955
--- /dev/null
+++ b/src/main/java/org/apache/maven/shared/dependency/graph/internal/Maven31DependencyCollectorBuilder.java
@@ -0,0 +1,282 @@
+package org.apache.maven.shared.dependency.graph.internal;
+
+/*
+ * 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.Collections;
+import java.util.List;
+
+import org.apache.maven.RepositoryUtils;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.project.ProjectBuildingRequest;
+import org.apache.maven.shared.dependency.graph.DependencyCollectorBuilder;
+import org.apache.maven.shared.dependency.graph.DependencyGraphBuilderException;
+import org.apache.maven.shared.dependency.graph.DependencyNode;
+import org.codehaus.plexus.component.annotations.Component;
+import org.codehaus.plexus.component.annotations.Requirement;
+import org.codehaus.plexus.logging.AbstractLogEnabled;
+import org.eclipse.aether.DefaultRepositorySystemSession;
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.artifact.ArtifactTypeRegistry;
+import org.eclipse.aether.collection.CollectRequest;
+import org.eclipse.aether.collection.CollectResult;
+import org.eclipse.aether.collection.DependencyCollectionException;
+import org.eclipse.aether.collection.DependencyGraphTransformer;
+import org.eclipse.aether.collection.DependencySelector;
+import org.eclipse.aether.graph.DependencyVisitor;
+import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
+import org.eclipse.aether.util.graph.selector.AndDependencySelector;
+import org.eclipse.aether.util.graph.selector.ExclusionDependencySelector;
+import org.eclipse.aether.util.graph.selector.OptionalDependencySelector;
+import org.eclipse.aether.util.graph.selector.ScopeDependencySelector;
+import org.eclipse.aether.util.graph.transformer.ConflictResolver;
+import org.eclipse.aether.util.graph.transformer.JavaScopeDeriver;
+import org.eclipse.aether.util.graph.transformer.JavaScopeSelector;
+import org.eclipse.aether.util.graph.transformer.NearestVersionSelector;
+import org.eclipse.aether.util.graph.transformer.SimpleOptionalitySelector;
+import org.eclipse.aether.util.graph.visitor.CloningDependencyVisitor;
+import org.eclipse.aether.util.graph.visitor.TreeDependencyVisitor;
+import org.eclipse.aether.version.VersionConstraint;
+
+/**
+ * Project dependency raw dependency collector API, abstracting Maven 3.1+'s Aether implementation.
+ * 
+ * @author Gabriel Belingueres
+ * @since 3.0.2
+ */
+@Component( role = DependencyCollectorBuilder.class, hint = "maven31" )
+public class Maven31DependencyCollectorBuilder
+    extends AbstractLogEnabled
+    implements DependencyCollectorBuilder
+{
+    @Requirement
+    private RepositorySystem repositorySystem;
+
+    @Override
+    public DependencyNode collectDependencyGraph( ArtifactRepository localRepository,
+                                                ProjectBuildingRequest buildingRequest, ArtifactFilter filter )
+        throws DependencyGraphBuilderException
+    {
+        DefaultRepositorySystemSession session = null;
+        try
+        {
+            MavenProject project = buildingRequest.getProject();
+            Artifact projectArtifact = project.getArtifact();
+            List<ArtifactRepository> remoteArtifactRepositories = project.getRemoteArtifactRepositories();
+
+            DefaultRepositorySystemSession repositorySession =
+                (DefaultRepositorySystemSession) Invoker.invoke( buildingRequest, "getRepositorySession" );
+            
+            session = new DefaultRepositorySystemSession( repositorySession );
+
+            DependencyGraphTransformer transformer =
+                new ConflictResolver( new NearestVersionSelector(), new JavaScopeSelector(),
+                                      new SimpleOptionalitySelector(), new JavaScopeDeriver() );
+
+            session.setDependencyGraphTransformer( transformer );
+
+            session.setConfigProperty( ConflictResolver.CONFIG_PROP_VERBOSE, true );
+            session.setConfigProperty( DependencyManagerUtils.CONFIG_PROP_VERBOSE, true );
+            
+            org.eclipse.aether.artifact.Artifact aetherArtifact =
+                (org.eclipse.aether.artifact.Artifact) Invoker.invoke( RepositoryUtils.class, "toArtifact",
+                                                                       Artifact.class, projectArtifact );
+
+            @SuppressWarnings( "unchecked" )
+            List<org.eclipse.aether.repository.RemoteRepository> aetherRepos =
+                (List<org.eclipse.aether.repository.RemoteRepository>) Invoker.invoke( RepositoryUtils.class, "toRepos",
+                                                                                       List.class,
+                                                                                       remoteArtifactRepositories );
+
+            CollectRequest collectRequest = new CollectRequest();
+            collectRequest.setRoot( new org.eclipse.aether.graph.Dependency( aetherArtifact, "" ) );
+            collectRequest.setRepositories( aetherRepos );
+
+            DependencySelector depFilter =
+                new AndDependencySelector( new ScopeDependencySelector( "provided" ), new OptionalDependencySelector(),
+                                           new ExclusionDependencySelector() );
+            session.setDependencySelector( depFilter );
+            
+            org.eclipse.aether.artifact.ArtifactTypeRegistry stereotypes = session.getArtifactTypeRegistry();
+            collectDependencyList( collectRequest, project, stereotypes );
+            collectManagedDependencyList( collectRequest, project, stereotypes );
+
+            CollectResult collectResult = repositorySystem.collectDependencies( session, collectRequest );
+
+            org.eclipse.aether.graph.DependencyNode rootNode = collectResult.getRoot();
+
+            CloningDependencyVisitor cloner = new CloningDependencyVisitor();
+            TreeDependencyVisitor treeVisitor = new TreeDependencyVisitor( cloner );
+            rootNode.accept( treeVisitor );
+
+            rootNode = cloner.getRootNode();
+
+            if ( getLogger().isDebugEnabled() )
+            {
+                logTree( rootNode );
+            }
+
+            return buildDependencyNode( null, rootNode, projectArtifact, filter );
+        }
+        catch ( DependencyCollectionException e )
+        {
+            throw new DependencyGraphBuilderException( "Could not collect dependencies: " + e.getResult(), e );
+        }
+        finally
+        {
+            if ( session != null )
+            {
+                session.setReadOnly();
+            }
+        }
+    }
+
+    private void logTree( org.eclipse.aether.graph.DependencyNode rootNode )
+    {
+        // print the node tree with its associated data Map
+        rootNode.accept( new TreeDependencyVisitor( new DependencyVisitor()
+        {
+            String indent = "";
+
+            @Override
+            public boolean visitEnter( org.eclipse.aether.graph.DependencyNode dependencyNode )
+            {
+                getLogger().debug( indent + "Aether node: " + dependencyNode + " data map: "
+                    + dependencyNode.getData() );
+                indent += "    ";
+                return true;
+            }
+
+            @Override
+            public boolean visitLeave( org.eclipse.aether.graph.DependencyNode dependencyNode )
+            {
+                indent = indent.substring( 0, indent.length() - 4 );
+                return true;
+            }
+        } ) );
+    }
+
+    private void collectManagedDependencyList( CollectRequest collectRequest, MavenProject project,
+                                               ArtifactTypeRegistry stereotypes )
+        throws DependencyGraphBuilderException
+    {
+        if ( project.getDependencyManagement() != null )
+        {
+            for ( Dependency dependency : project.getDependencyManagement().getDependencies() )
+            {
+                org.eclipse.aether.graph.Dependency aetherDep = toAetherDependency( stereotypes, dependency );
+                collectRequest.addManagedDependency( aetherDep );
+            }
+        }
+    }
+
+    private void collectDependencyList( CollectRequest collectRequest, MavenProject project,
+                                         org.eclipse.aether.artifact.ArtifactTypeRegistry stereotypes )
+        throws DependencyGraphBuilderException
+    {
+        for ( Dependency dependency : project.getDependencies() )
+        {
+            org.eclipse.aether.graph.Dependency aetherDep = toAetherDependency( stereotypes, dependency );
+            collectRequest.addDependency( aetherDep );
+        }
+    }
+
+    // CHECKSTYLE_OFF: LineLength
+    private org.eclipse.aether.graph.Dependency toAetherDependency( org.eclipse.aether.artifact.ArtifactTypeRegistry stereotypes,
+                                                                    Dependency dependency )
+        throws DependencyGraphBuilderException
+    {
+        org.eclipse.aether.graph.Dependency aetherDep =
+            (org.eclipse.aether.graph.Dependency) Invoker.invoke( RepositoryUtils.class, "toDependency",
+                                                                  Dependency.class,
+                                                                  org.eclipse.aether.artifact.ArtifactTypeRegistry.class,
+                                                                  dependency, stereotypes );
+        return aetherDep;
+    }
+    // CHECKSTYLE_ON: LineLength
+
+    private Artifact getDependencyArtifact( org.eclipse.aether.graph.Dependency dep )
+    {
+        org.eclipse.aether.artifact.Artifact artifact = dep.getArtifact();
+
+        try
+        {
+            Artifact mavenArtifact = (Artifact) Invoker.invoke( RepositoryUtils.class, "toArtifact",
+                                                                org.eclipse.aether.artifact.Artifact.class, artifact );
+
+            mavenArtifact.setScope( dep.getScope() );
+            mavenArtifact.setOptional( dep.isOptional() );
+
+            return mavenArtifact;
+        }
+        catch ( DependencyGraphBuilderException e )
+        {
+            // ReflectionException should not happen
+            throw new RuntimeException( e.getMessage(), e );
+        }
+    }
+
+    private DependencyNode buildDependencyNode( DependencyNode parent, org.eclipse.aether.graph.DependencyNode node,
+                                                Artifact artifact, ArtifactFilter filter )
+    {
+        String premanagedVersion = DependencyManagerUtils.getPremanagedVersion( node );
+        String premanagedScope = DependencyManagerUtils.getPremanagedScope( node );
+        
+        Boolean optional = null;
+        if ( node.getDependency() != null )
+        {
+            optional = node.getDependency().isOptional();
+        }
+
+        DefaultDependencyNode current =
+            new DefaultDependencyNode( parent, artifact, premanagedVersion, premanagedScope,
+                                       getVersionSelectedFromRange( node.getVersionConstraint() ), optional );
+
+        List<DependencyNode> nodes = new ArrayList<DependencyNode>( node.getChildren().size() );
+        for ( org.eclipse.aether.graph.DependencyNode child : node.getChildren() )
+        {
+            Artifact childArtifact = getDependencyArtifact( child.getDependency() );
+
+            if ( ( filter == null ) || filter.include( childArtifact ) )
+            {
+                nodes.add( buildDependencyNode( current, child, childArtifact, filter ) );
+            }
+        }
+
+        current.setChildren( Collections.unmodifiableList( nodes ) );
+
+        return current;
+    }
+
+    private String getVersionSelectedFromRange( VersionConstraint constraint )
+    {
+        if ( ( constraint == null ) || ( constraint.getVersion() != null ) )
+        {
+            return null;
+        }
+
+        return constraint.getRange().toString();
+    }
+
+}
diff --git a/src/main/java/org/apache/maven/shared/dependency/graph/internal/Maven3DependencyCollectorBuilder.java b/src/main/java/org/apache/maven/shared/dependency/graph/internal/Maven3DependencyCollectorBuilder.java
new file mode 100644
index 0000000..d6b6d49
--- /dev/null
+++ b/src/main/java/org/apache/maven/shared/dependency/graph/internal/Maven3DependencyCollectorBuilder.java
@@ -0,0 +1,282 @@
+package org.apache.maven.shared.dependency.graph.internal;
+
+/*
+ * 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.Collections;
+import java.util.List;
+
+import org.apache.maven.RepositoryUtils;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.project.ProjectBuildingRequest;
+import org.apache.maven.shared.dependency.graph.DependencyCollectorBuilder;
+import org.apache.maven.shared.dependency.graph.DependencyGraphBuilderException;
+import org.apache.maven.shared.dependency.graph.DependencyNode;
+import org.codehaus.plexus.component.annotations.Component;
+import org.codehaus.plexus.component.annotations.Requirement;
+import org.codehaus.plexus.logging.AbstractLogEnabled;
+import org.sonatype.aether.RepositorySystem;
+import org.sonatype.aether.RepositorySystemSession;
+import org.sonatype.aether.artifact.ArtifactTypeRegistry;
+import org.sonatype.aether.collection.CollectRequest;
+import org.sonatype.aether.collection.CollectResult;
+import org.sonatype.aether.collection.DependencyCollectionException;
+import org.sonatype.aether.collection.DependencySelector;
+import org.sonatype.aether.graph.DependencyVisitor;
+import org.sonatype.aether.util.DefaultRepositorySystemSession;
+import org.sonatype.aether.util.graph.CloningDependencyVisitor;
+import org.sonatype.aether.util.graph.TreeDependencyVisitor;
+import org.sonatype.aether.util.graph.selector.AndDependencySelector;
+import org.sonatype.aether.util.graph.selector.ExclusionDependencySelector;
+import org.sonatype.aether.util.graph.selector.OptionalDependencySelector;
+import org.sonatype.aether.util.graph.selector.ScopeDependencySelector;
+import org.sonatype.aether.version.VersionConstraint;
+
+/**
+ * Project dependency raw dependency collector API, abstracting Maven 3's Aether implementation.
+ * 
+ * @author Gabriel Belingueres
+ * @since 3.0.2
+ */
+@Component( role = DependencyCollectorBuilder.class, hint = "maven3" )
+public class Maven3DependencyCollectorBuilder
+    extends AbstractLogEnabled
+    implements DependencyCollectorBuilder
+{
+    @Requirement
+    private RepositorySystem repositorySystem;
+
+    @Override
+    public DependencyNode collectDependencyGraph( ArtifactRepository localRepository,
+                                                ProjectBuildingRequest buildingRequest, ArtifactFilter filter )
+        throws DependencyGraphBuilderException
+    {
+        ClassLoader prevClassLoader = Thread.currentThread().getContextClassLoader();
+        try
+        {
+            MavenProject project = buildingRequest.getProject();
+
+            Artifact projectArtifact = project.getArtifact();
+            List<ArtifactRepository> remoteArtifactRepositories = project.getRemoteArtifactRepositories();
+
+            // throws ClassCastException (classloading issues?)
+            // DefaultRepositorySystemSession repositorySystemSession =
+            // (DefaultRepositorySystemSession) Invoker.invoke( buildingRequest, "getRepositorySession" );
+            RepositorySystemSession repositorySystemSession = buildingRequest.getRepositorySession();
+
+            DefaultRepositorySystemSession session = new DefaultRepositorySystemSession( repositorySystemSession );
+
+            session.setDependencyGraphTransformer( null );
+
+            DependencySelector depFilter =
+                new AndDependencySelector( new ScopeDependencySelector( "provided" ), new OptionalDependencySelector(),
+                                           new ExclusionDependencySelector() );
+            session.setDependencySelector( depFilter );
+
+            org.sonatype.aether.artifact.Artifact aetherArtifact =
+                (org.sonatype.aether.artifact.Artifact) Invoker.invoke( RepositoryUtils.class, "toArtifact",
+                                                                       Artifact.class, projectArtifact );
+
+            @SuppressWarnings( "unchecked" )
+            List<org.sonatype.aether.repository.RemoteRepository> aetherRepos =
+                (List<org.sonatype.aether.repository.RemoteRepository>) Invoker.invoke( RepositoryUtils.class,
+                                                                                        "toRepos", List.class,
+                                                                                        remoteArtifactRepositories );
+
+            CollectRequest collectRequest = new CollectRequest();
+            collectRequest.setRoot( new org.sonatype.aether.graph.Dependency( aetherArtifact, "" ) );
+            collectRequest.setRepositories( aetherRepos );
+
+            org.sonatype.aether.artifact.ArtifactTypeRegistry stereotypes = session.getArtifactTypeRegistry();
+            collectDependencyList( collectRequest, project, stereotypes );
+            collectManagedDependencyList( collectRequest, project, stereotypes );
+
+            CollectResult collectResult = repositorySystem.collectDependencies( session, collectRequest );
+
+            org.sonatype.aether.graph.DependencyNode rootNode = collectResult.getRoot();
+
+            CloningDependencyVisitor cloner = new CloningDependencyVisitor();
+            TreeDependencyVisitor treeVisitor = new TreeDependencyVisitor( cloner );
+            rootNode.accept( treeVisitor );
+
+            rootNode = cloner.getRootNode();
+
+            if ( getLogger().isDebugEnabled() )
+            {
+                logTree( rootNode );
+            }
+
+            return buildDependencyNode( null, rootNode, projectArtifact, filter );
+        }
+        catch ( DependencyCollectionException e )
+        {
+            throw new DependencyGraphBuilderException( "Could not collect dependencies: " + e.getResult(), e );
+        }
+        finally
+        {
+            Thread.currentThread().setContextClassLoader( prevClassLoader );
+        }
+    }
+
+    private void logTree( org.sonatype.aether.graph.DependencyNode rootNode )
+    {
+        // print the node tree with its associated data Map
+        rootNode.accept( new TreeDependencyVisitor( new DependencyVisitor()
+        {
+            String indent = "";
+
+            @Override
+            public boolean visitEnter( org.sonatype.aether.graph.DependencyNode dependencyNode )
+            {
+                StringBuilder sb = new StringBuilder();
+                sb.append( indent ).append( "Aether node: " ).append( dependencyNode );
+                if ( !dependencyNode.getData().isEmpty() )
+                {
+                    sb.append( "data map: " ).append( dependencyNode.getData() );
+                }
+                if ( dependencyNode.getPremanagedVersion() != null && !dependencyNode.getPremanagedVersion().isEmpty() )
+                {
+                    sb.append( "Premanaged.version: " ).append( dependencyNode.getPremanagedVersion() );
+                }
+                if ( dependencyNode.getPremanagedScope() != null && !dependencyNode.getPremanagedScope().isEmpty() )
+                {
+                    sb.append( "Premanaged.scope: " ).append( dependencyNode.getPremanagedScope() );
+                }
+                getLogger().debug( sb.toString() );
+                indent += "    ";
+                return true;
+            }
+
+            @Override
+            public boolean visitLeave( org.sonatype.aether.graph.DependencyNode dependencyNode )
+            {
+                indent = indent.substring( 0, indent.length() - 4 );
+                return true;
+            }
+        } ) );
+    }
+
+    private void collectManagedDependencyList( CollectRequest collectRequest, MavenProject project,
+                                               ArtifactTypeRegistry stereotypes )
+        throws DependencyGraphBuilderException
+    {
+        if ( project.getDependencyManagement() != null )
+        {
+            for ( Dependency dependency : project.getDependencyManagement().getDependencies() )
+            {
+                org.sonatype.aether.graph.Dependency aetherDep = toAetherDependency( stereotypes, dependency );
+                collectRequest.addManagedDependency( aetherDep );
+            }
+        }
+    }
+
+    private void collectDependencyList( CollectRequest collectRequest, MavenProject project,
+                                        org.sonatype.aether.artifact.ArtifactTypeRegistry stereotypes )
+        throws DependencyGraphBuilderException
+    {
+        for ( Dependency dependency : project.getDependencies() )
+        {
+            org.sonatype.aether.graph.Dependency aetherDep = toAetherDependency( stereotypes, dependency );
+            collectRequest.addDependency( aetherDep );
+        }
+    }
+
+    // CHECKSTYLE_OFF: LineLength
+    private org.sonatype.aether.graph.Dependency toAetherDependency( org.sonatype.aether.artifact.ArtifactTypeRegistry stereotypes,
+                                                                    Dependency dependency )
+        throws DependencyGraphBuilderException
+    {
+        org.sonatype.aether.graph.Dependency aetherDep =
+            (org.sonatype.aether.graph.Dependency) Invoker.invoke( RepositoryUtils.class, "toDependency",
+                                                                  Dependency.class,
+                                                                   org.sonatype.aether.artifact.ArtifactTypeRegistry.class,
+                                                                  dependency, stereotypes );
+        return aetherDep;
+    }
+    // CHECKSTYLE_ON: LineLength
+
+    private Artifact getDependencyArtifact( org.sonatype.aether.graph.Dependency dep )
+    {
+        org.sonatype.aether.artifact.Artifact artifact = dep.getArtifact();
+
+        try
+        {
+            Artifact mavenArtifact = (Artifact) Invoker.invoke( RepositoryUtils.class, "toArtifact",
+                                                                org.sonatype.aether.artifact.Artifact.class, artifact );
+
+            mavenArtifact.setScope( dep.getScope() );
+            mavenArtifact.setOptional( dep.isOptional() );
+
+            return mavenArtifact;
+        }
+        catch ( DependencyGraphBuilderException e )
+        {
+            // ReflectionException should not happen
+            throw new RuntimeException( e.getMessage(), e );
+        }
+    }
+
+    private DependencyNode buildDependencyNode( DependencyNode parent, org.sonatype.aether.graph.DependencyNode node,
+                                                Artifact artifact, ArtifactFilter filter )
+    {
+        String premanagedVersion = node.getPremanagedVersion();
+        String premanagedScope = node.getPremanagedScope();
+
+        Boolean optional = null;
+        if ( node.getDependency() != null )
+        {
+            optional = node.getDependency().isOptional();
+        }
+
+        DefaultDependencyNode current =
+            new DefaultDependencyNode( parent, artifact, premanagedVersion, premanagedScope,
+                                       getVersionSelectedFromRange( node.getVersionConstraint() ), optional );
+
+        List<DependencyNode> nodes = new ArrayList<DependencyNode>( node.getChildren().size() );
+        for ( org.sonatype.aether.graph.DependencyNode child : node.getChildren() )
+        {
+            Artifact childArtifact = getDependencyArtifact( child.getDependency() );
+
+            if ( ( filter == null ) || filter.include( childArtifact ) )
+            {
+                nodes.add( buildDependencyNode( current, child, childArtifact, filter ) );
+            }
+        }
+
+        current.setChildren( Collections.unmodifiableList( nodes ) );
+
+        return current;
+    }
+
+    private String getVersionSelectedFromRange( VersionConstraint constraint )
+    {
+        if ( ( constraint == null ) || ( constraint.getVersion() != null ) || ( constraint.getRanges().isEmpty() ) )
+        {
+            return null;
+        }
+
+        return constraint.getRanges().iterator().next().toString();
+    }
+
+}