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();
+ }
+
+}