View Javadoc
1   package org.apache.maven.shared.dependency.graph.internal;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.ArrayList;
23  import java.util.Collections;
24  import java.util.List;
25  
26  import javax.inject.Inject;
27  import javax.inject.Named;
28  
29  import org.apache.maven.RepositoryUtils;
30  import org.apache.maven.artifact.Artifact;
31  import org.apache.maven.artifact.repository.ArtifactRepository;
32  import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
33  import org.apache.maven.model.Dependency;
34  import org.apache.maven.project.MavenProject;
35  import org.apache.maven.project.ProjectBuildingRequest;
36  import org.apache.maven.shared.dependency.graph.DependencyCollectorBuilder;
37  import org.apache.maven.shared.dependency.graph.DependencyCollectorBuilderException;
38  import org.apache.maven.shared.dependency.graph.DependencyNode;
39  import org.eclipse.aether.DefaultRepositorySystemSession;
40  import org.eclipse.aether.RepositorySystem;
41  import org.eclipse.aether.RepositorySystemSession;
42  import org.eclipse.aether.artifact.ArtifactTypeRegistry;
43  import org.eclipse.aether.collection.CollectRequest;
44  import org.eclipse.aether.collection.CollectResult;
45  import org.eclipse.aether.collection.DependencyCollectionException;
46  import org.eclipse.aether.collection.DependencyGraphTransformer;
47  import org.eclipse.aether.collection.DependencySelector;
48  import org.eclipse.aether.graph.DependencyVisitor;
49  import org.eclipse.aether.graph.Exclusion;
50  import org.eclipse.aether.util.artifact.JavaScopes;
51  import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
52  import org.eclipse.aether.util.graph.selector.AndDependencySelector;
53  import org.eclipse.aether.util.graph.selector.ExclusionDependencySelector;
54  import org.eclipse.aether.util.graph.selector.OptionalDependencySelector;
55  import org.eclipse.aether.util.graph.transformer.ConflictResolver;
56  import org.eclipse.aether.util.graph.transformer.JavaScopeDeriver;
57  import org.eclipse.aether.util.graph.transformer.NearestVersionSelector;
58  import org.eclipse.aether.util.graph.transformer.SimpleOptionalitySelector;
59  import org.eclipse.aether.util.graph.visitor.TreeDependencyVisitor;
60  import org.eclipse.aether.version.VersionConstraint;
61  import org.slf4j.Logger;
62  import org.slf4j.LoggerFactory;
63  
64  /**
65   * Project dependency raw dependency collector API, abstracting Maven 3.1+'s Aether implementation.
66   * 
67   * @author Gabriel Belingueres
68   * @since 3.1.0
69   */
70  @Named
71  public class DefaultDependencyCollectorBuilder
72      implements DependencyCollectorBuilder
73  {
74      private static final Logger LOGGER = LoggerFactory.getLogger( DefaultDependencyCollectorBuilder.class );
75  
76      private final RepositorySystem repositorySystem;
77  
78      @Inject
79      public DefaultDependencyCollectorBuilder( RepositorySystem repositorySystem )
80      {
81          this.repositorySystem = repositorySystem;
82      }
83  
84      @Override
85      public DependencyNode collectDependencyGraph( ProjectBuildingRequest buildingRequest, ArtifactFilter filter )
86          throws DependencyCollectorBuilderException
87      {
88          DefaultRepositorySystemSession session = null;
89          try
90          {
91              MavenProject project = buildingRequest.getProject();
92  
93              Artifact projectArtifact = project.getArtifact();
94              List<ArtifactRepository> remoteArtifactRepositories = project.getRemoteArtifactRepositories();
95  
96              RepositorySystemSession repositorySession = buildingRequest.getRepositorySession();
97  
98              session = new DefaultRepositorySystemSession( repositorySession );
99  
100             DependencyGraphTransformer transformer =
101                 new ConflictResolver( new NearestVersionSelector(), new VerboseJavaScopeSelector(),
102                                       new SimpleOptionalitySelector(), new JavaScopeDeriver() );
103             session.setDependencyGraphTransformer( transformer );
104 
105             DependencySelector depFilter =
106                 new AndDependencySelector( new DirectScopeDependencySelector( JavaScopes.TEST ),
107                                            new DirectScopeDependencySelector( JavaScopes.PROVIDED ),
108                                            new OptionalDependencySelector(),
109                                            new ExclusionDependencySelector() );
110             session.setDependencySelector( depFilter );
111 
112             session.setConfigProperty( ConflictResolver.CONFIG_PROP_VERBOSE, true );
113             session.setConfigProperty( DependencyManagerUtils.CONFIG_PROP_VERBOSE, true );
114 
115             org.eclipse.aether.artifact.Artifact aetherArtifact = RepositoryUtils.toArtifact( projectArtifact );
116 
117             List<org.eclipse.aether.repository.RemoteRepository> aetherRepos =
118                 RepositoryUtils.toRepos( remoteArtifactRepositories );
119 
120             CollectRequest collectRequest = new CollectRequest();
121             collectRequest.setRootArtifact( aetherArtifact );
122             collectRequest.setRepositories( aetherRepos );
123 
124             org.eclipse.aether.artifact.ArtifactTypeRegistry stereotypes = session.getArtifactTypeRegistry();
125             collectDependencyList( collectRequest, project, stereotypes );
126             collectManagedDependencyList( collectRequest, project, stereotypes );
127 
128             CollectResult collectResult = repositorySystem.collectDependencies( session, collectRequest );
129 
130             org.eclipse.aether.graph.DependencyNode rootNode = collectResult.getRoot();
131 
132             if ( LOGGER.isDebugEnabled() )
133             {
134                 logTree( rootNode );
135             }
136 
137             return buildDependencyNode( null, rootNode, projectArtifact, filter );
138         }
139         catch ( DependencyCollectionException e )
140         {
141             throw new DependencyCollectorBuilderException( "Could not collect dependencies: " + e.getResult(), e );
142         }
143         finally
144         {
145             if ( session != null )
146             {
147                 session.setReadOnly();
148             }
149         }
150     }
151 
152     private void logTree( org.eclipse.aether.graph.DependencyNode rootNode )
153     {
154         // print the node tree with its associated data Map
155         rootNode.accept( new TreeDependencyVisitor( new DependencyVisitor()
156         {
157             String indent = "";
158 
159             @Override
160             public boolean visitEnter( org.eclipse.aether.graph.DependencyNode dependencyNode )
161             {
162                 LOGGER.debug( "{}Aether node: {} data map: {}", indent, dependencyNode, dependencyNode.getData() );
163                 indent += "    ";
164                 return true;
165             }
166 
167             @Override
168             public boolean visitLeave( org.eclipse.aether.graph.DependencyNode dependencyNode )
169             {
170                 indent = indent.substring( 0, indent.length() - 4 );
171                 return true;
172             }
173         } ) );
174     }
175 
176     private void collectManagedDependencyList( CollectRequest collectRequest, MavenProject project,
177                                                ArtifactTypeRegistry stereotypes )
178     {
179         if ( project.getDependencyManagement() != null )
180         {
181             for ( Dependency dependency : project.getDependencyManagement().getDependencies() )
182             {
183                 org.eclipse.aether.graph.Dependency aetherDep = RepositoryUtils.toDependency( dependency, stereotypes );
184                 collectRequest.addManagedDependency( aetherDep );
185             }
186         }
187     }
188 
189     private void collectDependencyList( CollectRequest collectRequest, MavenProject project,
190                                         org.eclipse.aether.artifact.ArtifactTypeRegistry stereotypes )
191     {
192         for ( Dependency dependency : project.getDependencies() )
193         {
194             org.eclipse.aether.graph.Dependency aetherDep = RepositoryUtils.toDependency( dependency, stereotypes );
195             collectRequest.addDependency( aetherDep );
196         }
197     }
198 
199     private Artifact getDependencyArtifact( org.eclipse.aether.graph.Dependency dep )
200     {
201         org.eclipse.aether.artifact.Artifact artifact = dep.getArtifact();
202 
203         Artifact mavenArtifact = RepositoryUtils.toArtifact( artifact );
204         mavenArtifact.setScope( dep.getScope() );
205         mavenArtifact.setOptional( dep.isOptional() );
206 
207         return mavenArtifact;
208     }
209 
210     private DependencyNode buildDependencyNode( DependencyNode parent, org.eclipse.aether.graph.DependencyNode node,
211                                                 Artifact artifact, ArtifactFilter filter )
212     {
213         String premanagedVersion = DependencyManagerUtils.getPremanagedVersion( node );
214         String premanagedScope = DependencyManagerUtils.getPremanagedScope( node );
215 
216         Boolean optional = null;
217         if ( node.getDependency() != null )
218         {
219             optional = node.getDependency().isOptional();
220         }
221         
222         List<org.apache.maven.model.Exclusion> exclusions = null;
223         if ( node.getDependency() != null )
224         {
225             exclusions = new ArrayList<>( node.getDependency().getExclusions().size() );
226             for ( Exclusion exclusion : node.getDependency().getExclusions() )
227             {
228                 org.apache.maven.model.Exclusion modelExclusion = new org.apache.maven.model.Exclusion();
229                 modelExclusion.setGroupId( exclusion.getGroupId() );
230                 modelExclusion.setArtifactId( exclusion.getArtifactId() );
231                 exclusions.add( modelExclusion );
232             }
233         }
234 
235         org.eclipse.aether.graph.DependencyNode winner =
236             (org.eclipse.aether.graph.DependencyNode) node.getData().get( ConflictResolver.NODE_DATA_WINNER );
237         String winnerVersion = null;
238         String ignoredScope = null;
239         if ( winner != null )
240         {
241             winnerVersion = winner.getArtifact().getBaseVersion();
242         }
243         else
244         {
245             ignoredScope = (String) node.getData().get( VerboseJavaScopeSelector.REDUCED_SCOPE );
246         }
247         
248         ConflictData data = new ConflictData( winnerVersion, ignoredScope );
249         
250         VerboseDependencyNode current =
251             new VerboseDependencyNode( parent, artifact, premanagedVersion, premanagedScope,
252                                        getVersionSelectedFromRange( node.getVersionConstraint() ), optional,
253                                        exclusions, data );
254 
255         List<DependencyNode> nodes = new ArrayList<>( node.getChildren().size() );
256         for ( org.eclipse.aether.graph.DependencyNode child : node.getChildren() )
257         {
258             Artifact childArtifact = getDependencyArtifact( child.getDependency() );
259 
260             if ( ( filter == null ) || filter.include( childArtifact ) )
261             {
262                 nodes.add( buildDependencyNode( current, child, childArtifact, filter ) );
263             }
264         }
265 
266         current.setChildren( Collections.unmodifiableList( nodes ) );
267 
268         return current;
269     }
270 
271     private String getVersionSelectedFromRange( VersionConstraint constraint )
272     {
273         if ( ( constraint == null ) || ( constraint.getVersion() != null ) )
274         {
275             return null;
276         }
277 
278         return constraint.getRange().toString();
279     }
280 }