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