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                     logger.warn( "The artifact " + child.getRelocations().get( 0 ) + " has been relocated to "
191                         + child.getDependency().getArtifact() );
192                 }
193             }
194         }
195 
196         if ( logger.isDebugEnabled() )
197         {
198             node.accept( new GraphLogger( project ) );
199         }
200 
201         try
202         {
203             process( result, repoSystem.resolveDependencies( session, depRequest ).getArtifactResults() );
204         }
205         catch ( org.eclipse.aether.resolution.DependencyResolutionException e )
206         {
207             process( result, e.getResult().getArtifactResults() );
208 
209             throw new DependencyResolutionException( result, "Could not resolve dependencies for project "
210                 + project.getId() + ": " + e.getMessage(), e );
211         }
212 
213         return result;
214     }
215 
216     private void process( DefaultDependencyResolutionResult result, Collection<ArtifactResult> results )
217     {
218         for ( ArtifactResult ar : results )
219         {
220             DependencyNode node = ar.getRequest().getDependencyNode();
221             if ( ar.isResolved() )
222             {
223                 result.addResolvedDependency( node.getDependency() );
224             }
225             else
226             {
227                 result.setResolutionErrors( node.getDependency(), ar.getExceptions() );
228             }
229         }
230     }
231 
232     // Keep this class in sync with org.apache.maven.plugin.internal.DefaultPluginDependenciesResolver.GraphLogger
233     class GraphLogger
234         implements DependencyVisitor
235     {
236 
237         private final MavenProject project;
238 
239         private String indent = "";
240 
241         GraphLogger( MavenProject project )
242         {
243             this.project = project;
244         }
245 
246         public boolean visitEnter( DependencyNode node )
247         {
248             StringBuilder buffer = new StringBuilder( 128 );
249             buffer.append( indent );
250             org.eclipse.aether.graph.Dependency dep = node.getDependency();
251             if ( dep != null )
252             {
253                 org.eclipse.aether.artifact.Artifact art = dep.getArtifact();
254 
255                 buffer.append( art );
256                 if ( StringUtils.isNotEmpty( dep.getScope() ) )
257                 {
258                     buffer.append( ':' ).append( dep.getScope() );
259                 }
260 
261                 if ( dep.isOptional() )
262                 {
263                     buffer.append( " (optional)" );
264                 }
265 
266                 // TODO We currently cannot tell which <dependencyManagement> section contained the management
267                 //      information. When the resolver provides this information, these log messages should be updated
268                 //      to contain it.
269                 if ( ( node.getManagedBits() & DependencyNode.MANAGED_SCOPE ) == DependencyNode.MANAGED_SCOPE )
270                 {
271                     final String premanagedScope = DependencyManagerUtils.getPremanagedScope( node );
272                     buffer.append( " (scope managed from " );
273                     buffer.append( Objects.toString( premanagedScope, "default" ) );
274                     buffer.append( ')' );
275                 }
276 
277                 if ( ( node.getManagedBits() & DependencyNode.MANAGED_VERSION ) == DependencyNode.MANAGED_VERSION )
278                 {
279                     final String premanagedVersion = DependencyManagerUtils.getPremanagedVersion( node );
280                     buffer.append( " (version managed from " );
281                     buffer.append( Objects.toString( premanagedVersion, "default" ) );
282                     buffer.append( ')' );
283                 }
284 
285                 if ( ( node.getManagedBits() & DependencyNode.MANAGED_OPTIONAL ) == DependencyNode.MANAGED_OPTIONAL )
286                 {
287                     final Boolean premanagedOptional = DependencyManagerUtils.getPremanagedOptional( node );
288                     buffer.append( " (optionality managed from " );
289                     buffer.append( Objects.toString( premanagedOptional, "default" ) );
290                     buffer.append( ')' );
291                 }
292 
293                 if ( ( node.getManagedBits() & DependencyNode.MANAGED_EXCLUSIONS )
294                          == DependencyNode.MANAGED_EXCLUSIONS )
295                 {
296                     final Collection<org.eclipse.aether.graph.Exclusion> premanagedExclusions =
297                         DependencyManagerUtils.getPremanagedExclusions( node );
298 
299                     buffer.append( " (exclusions managed from " );
300                     buffer.append( Objects.toString( premanagedExclusions, "default" ) );
301                     buffer.append( ')' );
302                 }
303 
304                 if ( ( node.getManagedBits() & DependencyNode.MANAGED_PROPERTIES )
305                          == DependencyNode.MANAGED_PROPERTIES )
306                 {
307                     final Map<String, String> premanagedProperties =
308                         DependencyManagerUtils.getPremanagedProperties( node );
309 
310                     buffer.append( " (properties managed from " );
311                     buffer.append( Objects.toString( premanagedProperties, "default" ) );
312                     buffer.append( ')' );
313                 }
314             }
315             else
316             {
317                 buffer.append( project.getGroupId() );
318                 buffer.append( ':' ).append( project.getArtifactId() );
319                 buffer.append( ':' ).append( project.getPackaging() );
320                 buffer.append( ':' ).append( project.getVersion() );
321             }
322 
323             logger.debug( buffer.toString() );
324             indent += "   ";
325             return true;
326         }
327 
328         public boolean visitLeave( DependencyNode node )
329         {
330             indent = indent.substring( 0, indent.length() - 3 );
331             return true;
332         }
333 
334     }
335 
336 }