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 org.apache.maven.RepositoryUtils;
27  import org.apache.maven.artifact.Artifact;
28  import org.apache.maven.artifact.repository.ArtifactRepository;
29  import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
30  import org.apache.maven.model.Dependency;
31  import org.apache.maven.project.MavenProject;
32  import org.apache.maven.project.ProjectBuildingRequest;
33  import org.apache.maven.shared.dependency.graph.DependencyCollectorBuilder;
34  import org.apache.maven.shared.dependency.graph.DependencyCollectorBuilderException;
35  import org.apache.maven.shared.dependency.graph.DependencyNode;
36  import org.apache.maven.shared.dependency.graph.internal.maven31.Maven31DirectScopeDependencySelector;
37  import org.apache.maven.shared.dependency.graph.internal.maven31.VerboseJavaScopeSelector;
38  import org.codehaus.plexus.component.annotations.Component;
39  import org.codehaus.plexus.component.annotations.Requirement;
40  import org.codehaus.plexus.logging.AbstractLogEnabled;
41  import org.eclipse.aether.DefaultRepositorySystemSession;
42  import org.eclipse.aether.RepositorySystem;
43  import org.eclipse.aether.artifact.ArtifactTypeRegistry;
44  import org.eclipse.aether.collection.CollectRequest;
45  import org.eclipse.aether.collection.CollectResult;
46  import org.eclipse.aether.collection.DependencyCollectionException;
47  import org.eclipse.aether.collection.DependencyGraphTransformer;
48  import org.eclipse.aether.collection.DependencySelector;
49  import org.eclipse.aether.graph.DependencyVisitor;
50  import org.eclipse.aether.graph.Exclusion;
51  import org.eclipse.aether.util.artifact.JavaScopes;
52  import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
53  import org.eclipse.aether.util.graph.selector.AndDependencySelector;
54  import org.eclipse.aether.util.graph.selector.ExclusionDependencySelector;
55  import org.eclipse.aether.util.graph.selector.OptionalDependencySelector;
56  import org.eclipse.aether.util.graph.transformer.ConflictResolver;
57  import org.eclipse.aether.util.graph.transformer.JavaScopeDeriver;
58  import org.eclipse.aether.util.graph.transformer.NearestVersionSelector;
59  import org.eclipse.aether.util.graph.transformer.SimpleOptionalitySelector;
60  import org.eclipse.aether.util.graph.visitor.TreeDependencyVisitor;
61  import org.eclipse.aether.version.VersionConstraint;
62  
63  /**
64   * Project dependency raw dependency collector API, abstracting Maven 3.1+'s Aether implementation.
65   * 
66   * @author Gabriel Belingueres
67   * @since 3.1.0
68   */
69  @Component( role = DependencyCollectorBuilder.class, hint = "maven31" )
70  public class Maven31DependencyCollectorBuilder
71      extends AbstractLogEnabled
72      implements DependencyCollectorBuilder
73  {
74      @Requirement
75      private RepositorySystem repositorySystem;
76      
77      private final ExceptionHandler<DependencyCollectorBuilderException> exceptionHandler;
78      
79      public Maven31DependencyCollectorBuilder()
80      {
81          this.exceptionHandler = new ExceptionHandler<DependencyCollectorBuilderException>()
82          {
83              @Override
84              public DependencyCollectorBuilderException create( String message, Exception exception )
85              {
86                  return new DependencyCollectorBuilderException( message, exception );
87              }
88          };
89      }
90  
91      @Override
92      public DependencyNode collectDependencyGraph( ProjectBuildingRequest buildingRequest, ArtifactFilter filter )
93          throws DependencyCollectorBuilderException
94      {
95          DefaultRepositorySystemSession session = null;
96          try
97          {
98              MavenProject project = buildingRequest.getProject();
99  
100             Artifact projectArtifact = project.getArtifact();
101             List<ArtifactRepository> remoteArtifactRepositories = project.getRemoteArtifactRepositories();
102 
103             DefaultRepositorySystemSession repositorySession =
104                 (DefaultRepositorySystemSession) Invoker.invoke( buildingRequest, "getRepositorySession",
105                                                                  exceptionHandler );
106 
107             session = new DefaultRepositorySystemSession( repositorySession );
108 
109             DependencyGraphTransformer transformer =
110                 new ConflictResolver( new NearestVersionSelector(), new VerboseJavaScopeSelector(),
111                                       new SimpleOptionalitySelector(), new JavaScopeDeriver() );
112             session.setDependencyGraphTransformer( transformer );
113 
114             DependencySelector depFilter =
115                 new AndDependencySelector( new Maven31DirectScopeDependencySelector( JavaScopes.TEST ),
116                                            new OptionalDependencySelector(),
117                                            new ExclusionDependencySelector() );
118             session.setDependencySelector( depFilter );
119 
120             session.setConfigProperty( ConflictResolver.CONFIG_PROP_VERBOSE, true );
121             session.setConfigProperty( DependencyManagerUtils.CONFIG_PROP_VERBOSE, true );
122 
123             org.eclipse.aether.artifact.Artifact aetherArtifact =
124                 (org.eclipse.aether.artifact.Artifact) Invoker.invoke( RepositoryUtils.class, "toArtifact",
125                                                                        Artifact.class, projectArtifact,
126                                                                        exceptionHandler );
127 
128             @SuppressWarnings( "unchecked" )
129             List<org.eclipse.aether.repository.RemoteRepository> aetherRepos =
130                 (List<org.eclipse.aether.repository.RemoteRepository>) Invoker.invoke( RepositoryUtils.class, "toRepos",
131                                                                                        List.class,
132                                                                                        remoteArtifactRepositories,
133                                                                                        exceptionHandler );
134 
135             CollectRequest collectRequest = new CollectRequest();
136             collectRequest.setRoot( new org.eclipse.aether.graph.Dependency( aetherArtifact, "" ) );
137             collectRequest.setRepositories( aetherRepos );
138 
139             org.eclipse.aether.artifact.ArtifactTypeRegistry stereotypes = session.getArtifactTypeRegistry();
140             collectDependencyList( collectRequest, project, stereotypes );
141             collectManagedDependencyList( collectRequest, project, stereotypes );
142 
143             CollectResult collectResult = repositorySystem.collectDependencies( session, collectRequest );
144 
145             org.eclipse.aether.graph.DependencyNode rootNode = collectResult.getRoot();
146 
147             if ( getLogger().isDebugEnabled() )
148             {
149                 logTree( rootNode );
150             }
151 
152             return buildDependencyNode( null, rootNode, projectArtifact, filter );
153         }
154         catch ( DependencyCollectionException e )
155         {
156             throw new DependencyCollectorBuilderException( "Could not collect dependencies: " + e.getResult(), e );
157         }
158         finally
159         {
160             if ( session != null )
161             {
162                 session.setReadOnly();
163             }
164         }
165     }
166 
167     private void logTree( org.eclipse.aether.graph.DependencyNode rootNode )
168     {
169         // print the node tree with its associated data Map
170         rootNode.accept( new TreeDependencyVisitor( new DependencyVisitor()
171         {
172             String indent = "";
173 
174             @Override
175             public boolean visitEnter( org.eclipse.aether.graph.DependencyNode dependencyNode )
176             {
177                 getLogger().debug( indent + "Aether node: " + dependencyNode + " data map: "
178                     + dependencyNode.getData() );
179                 indent += "    ";
180                 return true;
181             }
182 
183             @Override
184             public boolean visitLeave( org.eclipse.aether.graph.DependencyNode dependencyNode )
185             {
186                 indent = indent.substring( 0, indent.length() - 4 );
187                 return true;
188             }
189         } ) );
190     }
191 
192     private void collectManagedDependencyList( CollectRequest collectRequest, MavenProject project,
193                                                ArtifactTypeRegistry stereotypes )
194         throws DependencyCollectorBuilderException
195     {
196         if ( project.getDependencyManagement() != null )
197         {
198             for ( Dependency dependency : project.getDependencyManagement().getDependencies() )
199             {
200                 org.eclipse.aether.graph.Dependency aetherDep = toAetherDependency( stereotypes, dependency );
201                 collectRequest.addManagedDependency( aetherDep );
202             }
203         }
204     }
205 
206     private void collectDependencyList( CollectRequest collectRequest, MavenProject project,
207                                         org.eclipse.aether.artifact.ArtifactTypeRegistry stereotypes )
208         throws DependencyCollectorBuilderException
209     {
210         for ( Dependency dependency : project.getDependencies() )
211         {
212             org.eclipse.aether.graph.Dependency aetherDep = toAetherDependency( stereotypes, dependency );
213             collectRequest.addDependency( aetherDep );
214         }
215     }
216 
217     // CHECKSTYLE_OFF: LineLength
218     private org.eclipse.aether.graph.Dependency toAetherDependency( org.eclipse.aether.artifact.ArtifactTypeRegistry stereotypes,
219                                                                     Dependency dependency )
220         throws DependencyCollectorBuilderException
221     {
222         org.eclipse.aether.graph.Dependency aetherDep =
223             (org.eclipse.aether.graph.Dependency) Invoker.invoke( RepositoryUtils.class, "toDependency",
224                                                                   Dependency.class,
225                                                                   org.eclipse.aether.artifact.ArtifactTypeRegistry.class,
226                                                                   dependency, stereotypes, exceptionHandler );
227         return aetherDep;
228     }
229     // CHECKSTYLE_ON: LineLength
230 
231     private Artifact getDependencyArtifact( org.eclipse.aether.graph.Dependency dep )
232     {
233         org.eclipse.aether.artifact.Artifact artifact = dep.getArtifact();
234 
235         try
236         {
237             Artifact mavenArtifact =
238                 (Artifact) Invoker.invoke( RepositoryUtils.class, "toArtifact",
239                                            org.eclipse.aether.artifact.Artifact.class, artifact, exceptionHandler );
240 
241             mavenArtifact.setScope( dep.getScope() );
242             mavenArtifact.setOptional( dep.isOptional() );
243 
244             return mavenArtifact;
245         }
246         catch ( DependencyCollectorBuilderException e )
247         {
248             // ReflectionException should not happen
249             throw new RuntimeException( e.getMessage(), e );
250         }
251     }
252 
253     private DependencyNode buildDependencyNode( DependencyNode parent, org.eclipse.aether.graph.DependencyNode node,
254                                                 Artifact artifact, ArtifactFilter filter )
255     {
256         String premanagedVersion = DependencyManagerUtils.getPremanagedVersion( node );
257         String premanagedScope = DependencyManagerUtils.getPremanagedScope( node );
258 
259         Boolean optional = null;
260         if ( node.getDependency() != null )
261         {
262             optional = node.getDependency().isOptional();
263         }
264         
265         List<org.apache.maven.model.Exclusion> exclusions = null;
266         if ( node.getDependency() != null )
267         {
268             exclusions = new ArrayList<>( node.getDependency().getExclusions().size() );
269             for ( Exclusion exclusion : node.getDependency().getExclusions() )
270             {
271                 org.apache.maven.model.Exclusion modelExclusion = new org.apache.maven.model.Exclusion();
272                 modelExclusion.setGroupId( exclusion.getGroupId() );
273                 modelExclusion.setArtifactId( exclusion.getArtifactId() );
274                 exclusions.add( modelExclusion );
275             }
276         }
277 
278         org.eclipse.aether.graph.DependencyNode winner =
279             (org.eclipse.aether.graph.DependencyNode) node.getData().get( ConflictResolver.NODE_DATA_WINNER );
280         String winnerVersion = null;
281         String ignoredScope = null;
282         if ( winner != null )
283         {
284             winnerVersion = winner.getArtifact().getBaseVersion();
285         }
286         else
287         {
288             ignoredScope = (String) node.getData().get( VerboseJavaScopeSelector.REDUCED_SCOPE );
289         }
290         
291         ConflictData data = new ConflictData( winnerVersion, ignoredScope );
292         
293         VerboseDependencyNode current =
294             new VerboseDependencyNode( parent, artifact, premanagedVersion, premanagedScope,
295                                        getVersionSelectedFromRange( node.getVersionConstraint() ), optional,
296                                        exclusions, data );
297 
298         List<DependencyNode> nodes = new ArrayList<DependencyNode>( node.getChildren().size() );
299         for ( org.eclipse.aether.graph.DependencyNode child : node.getChildren() )
300         {
301             Artifact childArtifact = getDependencyArtifact( child.getDependency() );
302 
303             if ( ( filter == null ) || filter.include( childArtifact ) )
304             {
305                 nodes.add( buildDependencyNode( current, child, childArtifact, filter ) );
306             }
307         }
308 
309         current.setChildren( Collections.unmodifiableList( nodes ) );
310 
311         return current;
312     }
313 
314     private String getVersionSelectedFromRange( VersionConstraint constraint )
315     {
316         if ( ( constraint == null ) || ( constraint.getVersion() != null ) )
317         {
318             return null;
319         }
320 
321         return constraint.getRange().toString();
322     }
323 }