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