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