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