View Javadoc
1   package org.apache.maven.project;
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.Collection;
23  import java.util.HashMap;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Objects;
27  
28  import org.apache.maven.RepositoryUtils;
29  import org.apache.maven.artifact.Artifact;
30  import org.apache.maven.model.Dependency;
31  import org.apache.maven.model.DependencyManagement;
32  import org.apache.maven.model.Exclusion;
33  import org.codehaus.plexus.component.annotations.Component;
34  import org.codehaus.plexus.component.annotations.Requirement;
35  import org.codehaus.plexus.logging.Logger;
36  import org.codehaus.plexus.util.StringUtils;
37  import org.eclipse.aether.DefaultRepositorySystemSession;
38  import org.eclipse.aether.RepositorySystem;
39  import org.eclipse.aether.RepositorySystemSession;
40  import org.eclipse.aether.RequestTrace;
41  import org.eclipse.aether.artifact.ArtifactType;
42  import org.eclipse.aether.artifact.ArtifactTypeRegistry;
43  import org.eclipse.aether.collection.CollectRequest;
44  import org.eclipse.aether.collection.DependencyCollectionException;
45  import org.eclipse.aether.graph.DependencyFilter;
46  import org.eclipse.aether.graph.DependencyNode;
47  import org.eclipse.aether.graph.DependencyVisitor;
48  import org.eclipse.aether.resolution.ArtifactResult;
49  import org.eclipse.aether.resolution.DependencyRequest;
50  import org.eclipse.aether.util.artifact.ArtifactIdUtils;
51  import org.eclipse.aether.util.artifact.JavaScopes;
52  import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
53  
54  /**
55   * @author Benjamin Bentmann
56   */
57  @Component( role = ProjectDependenciesResolver.class )
58  public class DefaultProjectDependenciesResolver
59      implements ProjectDependenciesResolver
60  {
61  
62      @Requirement
63      private Logger logger;
64  
65      @Requirement
66      private RepositorySystem repoSystem;
67  
68      @Requirement
69      private List<RepositorySessionDecorator> decorators;
70  
71      public DependencyResolutionResult resolve( DependencyResolutionRequest request )
72          throws DependencyResolutionException
73      {
74          final RequestTrace trace = RequestTrace.newChild( null, request );
75  
76          final DefaultDependencyResolutionResult result = new DefaultDependencyResolutionResult();
77  
78          final MavenProject project = request.getMavenProject();
79          final DependencyFilter filter = request.getResolutionFilter();
80          RepositorySystemSession session = request.getRepositorySession();
81          ArtifactTypeRegistry stereotypes = session.getArtifactTypeRegistry();
82  
83          if ( logger.isDebugEnabled()
84              && session.getConfigProperties().get( DependencyManagerUtils.CONFIG_PROP_VERBOSE ) == null )
85          {
86              DefaultRepositorySystemSession verbose = new DefaultRepositorySystemSession( session );
87              verbose.setConfigProperty( DependencyManagerUtils.CONFIG_PROP_VERBOSE, Boolean.TRUE );
88              session = verbose;
89          }
90  
91          for ( RepositorySessionDecorator decorator : decorators )
92          {
93              RepositorySystemSession decorated = decorator.decorate( project, session );
94              if ( decorated != null )
95              {
96                  session = decorated;
97              }
98          }
99  
100         CollectRequest collect = new CollectRequest();
101         collect.setRootArtifact( RepositoryUtils.toArtifact( project.getArtifact() ) );
102         collect.setRequestContext( "project" );
103         collect.setRepositories( project.getRemoteProjectRepositories() );
104 
105         if ( project.getDependencyArtifacts() == null )
106         {
107             for ( Dependency dependency : project.getDependencies() )
108             {
109                 if ( StringUtils.isEmpty( dependency.getGroupId() ) || StringUtils.isEmpty( dependency.getArtifactId() )
110                     || StringUtils.isEmpty( dependency.getVersion() ) )
111                 {
112                     // guard against case where best-effort resolution for invalid models is requested
113                     continue;
114                 }
115                 collect.addDependency( RepositoryUtils.toDependency( dependency, stereotypes ) );
116             }
117         }
118         else
119         {
120             Map<String, Dependency> dependencies = new HashMap<>();
121             for ( Dependency dependency : project.getDependencies() )
122             {
123                 String classifier = dependency.getClassifier();
124                 if ( classifier == null )
125                 {
126                     ArtifactType type = stereotypes.get( dependency.getType() );
127                     if ( type != null )
128                     {
129                         classifier = type.getClassifier();
130                     }
131                 }
132                 String key =
133                     ArtifactIdUtils.toVersionlessId( dependency.getGroupId(), dependency.getArtifactId(),
134                                                     dependency.getType(), classifier );
135                 dependencies.put( key, dependency );
136             }
137             for ( Artifact artifact : project.getDependencyArtifacts() )
138             {
139                 String key = artifact.getDependencyConflictId();
140                 Dependency dependency = dependencies.get( key );
141                 Collection<Exclusion> exclusions = dependency != null ? dependency.getExclusions() : null;
142                 org.eclipse.aether.graph.Dependency dep = RepositoryUtils.toDependency( artifact, exclusions );
143                 if ( !JavaScopes.SYSTEM.equals( dep.getScope() ) && dep.getArtifact().getFile() != null )
144                 {
145                     // enable re-resolution
146                     org.eclipse.aether.artifact.Artifact art = dep.getArtifact();
147                     art = art.setFile( null ).setVersion( art.getBaseVersion() );
148                     dep = dep.setArtifact( art );
149                 }
150                 collect.addDependency( dep );
151             }
152         }
153 
154         DependencyManagement depMgmt = project.getDependencyManagement();
155         if ( depMgmt != null )
156         {
157             for ( Dependency dependency : depMgmt.getDependencies() )
158             {
159                 collect.addManagedDependency( RepositoryUtils.toDependency( dependency, stereotypes ) );
160             }
161         }
162 
163         DependencyRequest depRequest = new DependencyRequest( collect, filter );
164         depRequest.setTrace( trace );
165 
166         DependencyNode node;
167         try
168         {
169             collect.setTrace( RequestTrace.newChild( trace, depRequest ) );
170             node = repoSystem.collectDependencies( session, collect ).getRoot();
171             result.setDependencyGraph( node );
172         }
173         catch ( DependencyCollectionException e )
174         {
175             result.setDependencyGraph( e.getResult().getRoot() );
176             result.setCollectionErrors( e.getResult().getExceptions() );
177 
178             throw new DependencyResolutionException( result, "Could not resolve dependencies for project "
179                 + project.getId() + ": " + e.getMessage(), e );
180         }
181 
182         depRequest.setRoot( node );
183 
184         if ( logger.isWarnEnabled() )
185         {
186             for ( DependencyNode child : node.getChildren() )
187             {
188                 if ( !child.getRelocations().isEmpty() )
189                 {
190                     org.eclipse.aether.artifact.Artifact relocated = child.getDependency().getArtifact();
191                     String message = relocated instanceof org.apache.maven.repository.internal.RelocatedArtifact
192                             ? ( ( org.apache.maven.repository.internal.RelocatedArtifact ) relocated ).getMessage()
193                             : null;
194                     logger.warn( "The artifact " + child.getRelocations().get( 0 ) + " has been relocated to "
195                         + relocated + ( message != null ? ": " + message : "" ) );
196                 }
197             }
198         }
199 
200         if ( logger.isDebugEnabled() )
201         {
202             node.accept( new GraphLogger( project ) );
203         }
204 
205         try
206         {
207             process( result, repoSystem.resolveDependencies( session, depRequest ).getArtifactResults() );
208         }
209         catch ( org.eclipse.aether.resolution.DependencyResolutionException e )
210         {
211             process( result, e.getResult().getArtifactResults() );
212 
213             throw new DependencyResolutionException( result, "Could not resolve dependencies for project "
214                 + project.getId() + ": " + e.getMessage(), e );
215         }
216 
217         return result;
218     }
219 
220     private void process( DefaultDependencyResolutionResult result, Collection<ArtifactResult> results )
221     {
222         for ( ArtifactResult ar : results )
223         {
224             DependencyNode node = ar.getRequest().getDependencyNode();
225             if ( ar.isResolved() )
226             {
227                 result.addResolvedDependency( node.getDependency() );
228             }
229             else
230             {
231                 result.setResolutionErrors( node.getDependency(), ar.getExceptions() );
232             }
233         }
234     }
235 
236     // Keep this class in sync with org.apache.maven.plugin.internal.DefaultPluginDependenciesResolver.GraphLogger
237     class GraphLogger
238         implements DependencyVisitor
239     {
240 
241         private final MavenProject project;
242 
243         private String indent = "";
244 
245         GraphLogger( MavenProject project )
246         {
247             this.project = project;
248         }
249 
250         public boolean visitEnter( DependencyNode node )
251         {
252             StringBuilder buffer = new StringBuilder( 128 );
253             buffer.append( indent );
254             org.eclipse.aether.graph.Dependency dep = node.getDependency();
255             if ( dep != null )
256             {
257                 org.eclipse.aether.artifact.Artifact art = dep.getArtifact();
258 
259                 buffer.append( art );
260                 if ( StringUtils.isNotEmpty( dep.getScope() ) )
261                 {
262                     buffer.append( ':' ).append( dep.getScope() );
263                 }
264 
265                 if ( dep.isOptional() )
266                 {
267                     buffer.append( " (optional)" );
268                 }
269 
270                 // TODO We currently cannot tell which <dependencyManagement> section contained the management
271                 //      information. When the resolver provides this information, these log messages should be updated
272                 //      to contain it.
273                 if ( ( node.getManagedBits() & DependencyNode.MANAGED_SCOPE ) == DependencyNode.MANAGED_SCOPE )
274                 {
275                     final String premanagedScope = DependencyManagerUtils.getPremanagedScope( node );
276                     buffer.append( " (scope managed from " );
277                     buffer.append( Objects.toString( premanagedScope, "default" ) );
278                     buffer.append( ')' );
279                 }
280 
281                 if ( ( node.getManagedBits() & DependencyNode.MANAGED_VERSION ) == DependencyNode.MANAGED_VERSION )
282                 {
283                     final String premanagedVersion = DependencyManagerUtils.getPremanagedVersion( node );
284                     buffer.append( " (version managed from " );
285                     buffer.append( Objects.toString( premanagedVersion, "default" ) );
286                     buffer.append( ')' );
287                 }
288 
289                 if ( ( node.getManagedBits() & DependencyNode.MANAGED_OPTIONAL ) == DependencyNode.MANAGED_OPTIONAL )
290                 {
291                     final Boolean premanagedOptional = DependencyManagerUtils.getPremanagedOptional( node );
292                     buffer.append( " (optionality managed from " );
293                     buffer.append( Objects.toString( premanagedOptional, "default" ) );
294                     buffer.append( ')' );
295                 }
296 
297                 if ( ( node.getManagedBits() & DependencyNode.MANAGED_EXCLUSIONS )
298                          == DependencyNode.MANAGED_EXCLUSIONS )
299                 {
300                     final Collection<org.eclipse.aether.graph.Exclusion> premanagedExclusions =
301                         DependencyManagerUtils.getPremanagedExclusions( node );
302 
303                     buffer.append( " (exclusions managed from " );
304                     buffer.append( Objects.toString( premanagedExclusions, "default" ) );
305                     buffer.append( ')' );
306                 }
307 
308                 if ( ( node.getManagedBits() & DependencyNode.MANAGED_PROPERTIES )
309                          == DependencyNode.MANAGED_PROPERTIES )
310                 {
311                     final Map<String, String> premanagedProperties =
312                         DependencyManagerUtils.getPremanagedProperties( node );
313 
314                     buffer.append( " (properties managed from " );
315                     buffer.append( Objects.toString( premanagedProperties, "default" ) );
316                     buffer.append( ')' );
317                 }
318             }
319             else
320             {
321                 buffer.append( project.getGroupId() );
322                 buffer.append( ':' ).append( project.getArtifactId() );
323                 buffer.append( ':' ).append( project.getPackaging() );
324                 buffer.append( ':' ).append( project.getVersion() );
325             }
326 
327             logger.debug( buffer.toString() );
328             indent += "   ";
329             return true;
330         }
331 
332         public boolean visitLeave( DependencyNode node )
333         {
334             indent = indent.substring( 0, indent.length() - 3 );
335             return true;
336         }
337 
338     }
339 
340 }