View Javadoc

1   package org.apache.maven.lifecycle.internal;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
5    * agreements. See the NOTICE file distributed with this work for additional information regarding
6    * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance with the License. You may obtain a
8    * copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software distributed under the License
13   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
14   * or implied. See the License for the specific language governing permissions and limitations under                    
15   * the License.
16   */
17  
18  import org.apache.maven.RepositoryUtils;
19  import org.apache.maven.artifact.Artifact;
20  import org.apache.maven.artifact.ArtifactUtils;
21  import org.apache.maven.artifact.factory.ArtifactFactory;
22  import org.apache.maven.execution.MavenSession;
23  import org.apache.maven.lifecycle.LifecycleExecutionException;
24  import org.apache.maven.project.DefaultDependencyResolutionRequest;
25  import org.apache.maven.project.DependencyResolutionException;
26  import org.apache.maven.project.DependencyResolutionResult;
27  import org.apache.maven.project.MavenProject;
28  import org.apache.maven.project.ProjectDependenciesResolver;
29  import org.apache.maven.project.artifact.InvalidDependencyVersionException;
30  import org.codehaus.plexus.component.annotations.Component;
31  import org.codehaus.plexus.component.annotations.Requirement;
32  import org.codehaus.plexus.logging.Logger;
33  import org.sonatype.aether.graph.Dependency;
34  import org.sonatype.aether.graph.DependencyFilter;
35  import org.sonatype.aether.graph.DependencyNode;
36  import org.sonatype.aether.util.filter.AndDependencyFilter;
37  import org.sonatype.aether.util.filter.ScopeDependencyFilter;
38  
39  import java.util.*;
40  
41  /**
42   * Resolves dependencies for the artifacts in context of the lifecycle build
43   * 
44   * @since 3.0
45   * @author Benjamin Bentmann
46   * @author Jason van Zyl
47   * @author Kristian Rosenvold (extracted class)
48   *         <p/>
49   *         NOTE: This class is not part of any public api and can be changed or deleted without prior notice.
50   */
51  @Component(role = LifecycleDependencyResolver.class)
52  public class LifecycleDependencyResolver
53  {
54  
55      @Requirement
56      private ProjectDependenciesResolver dependenciesResolver;
57  
58      @Requirement
59      private Logger logger;
60  
61      @Requirement
62      private ArtifactFactory artifactFactory;
63  
64      @SuppressWarnings({"UnusedDeclaration"})
65      public LifecycleDependencyResolver()
66      {
67      }
68  
69      public LifecycleDependencyResolver( ProjectDependenciesResolver projectDependenciesResolver, Logger logger )
70      {
71          this.dependenciesResolver = projectDependenciesResolver;
72          this.logger = logger;
73      }
74  
75      public static List<MavenProject> getProjects( MavenProject project, MavenSession session, boolean aggregator )
76      {
77          if ( aggregator )
78          {
79              return session.getProjects();
80          }
81          else
82          {
83              return Collections.singletonList( project );
84          }
85      }
86  
87      public void resolveProjectDependencies( MavenProject project, Collection<String> scopesToCollect,
88                                              Collection<String> scopesToResolve, MavenSession session,
89                                              boolean aggregating, Set<Artifact> projectArtifacts )
90          throws LifecycleExecutionException
91      {
92          if ( project.getDependencyArtifacts() == null )
93          {
94              try
95              {
96                  project.setDependencyArtifacts( project.createArtifacts( artifactFactory, null, null ) );
97              }
98              catch ( InvalidDependencyVersionException e )
99              {
100                 throw new LifecycleExecutionException( e );
101             }
102         }
103 
104         Set<Artifact> artifacts =
105             getDependencies( project, scopesToCollect, scopesToResolve, session, aggregating, projectArtifacts );
106 
107         project.setResolvedArtifacts( artifacts );
108 
109         Map<String, Artifact> map = new HashMap<String, Artifact>();
110         for ( Artifact artifact : artifacts )
111         {
112             map.put( artifact.getDependencyConflictId(), artifact );
113         }
114         for ( Artifact artifact : project.getDependencyArtifacts() )
115         {
116             if ( artifact.getFile() == null )
117             {
118                 Artifact resolved = map.get( artifact.getDependencyConflictId() );
119                 if ( resolved != null )
120                 {
121                     artifact.setFile( resolved.getFile() );
122                     artifact.setDependencyTrail( resolved.getDependencyTrail() );
123                     artifact.setResolvedVersion( resolved.getVersion() );
124                     artifact.setResolved( true );
125                 }
126             }
127         }
128     }
129 
130     private Set<Artifact> getDependencies( MavenProject project, Collection<String> scopesToCollect,
131                                            Collection<String> scopesToResolve, MavenSession session,
132                                            boolean aggregating, Set<Artifact> projectArtifacts )
133         throws LifecycleExecutionException
134     {
135         if ( scopesToCollect == null )
136         {
137             scopesToCollect = Collections.emptySet();
138         }
139         if ( scopesToResolve == null )
140         {
141             scopesToResolve = Collections.emptySet();
142         }
143 
144         if ( scopesToCollect.isEmpty() && scopesToResolve.isEmpty() )
145         {
146             return new LinkedHashSet<Artifact>();
147         }
148 
149         scopesToCollect = new HashSet<String>( scopesToCollect );
150         scopesToCollect.addAll( scopesToResolve );
151 
152         DependencyFilter collectionFilter = new ScopeDependencyFilter( null, negate( scopesToCollect ) );
153         DependencyFilter resolutionFilter = new ScopeDependencyFilter( null, negate( scopesToResolve ) );
154         resolutionFilter = AndDependencyFilter.newInstance( collectionFilter, resolutionFilter );
155         resolutionFilter =
156             AndDependencyFilter.newInstance( resolutionFilter, new ReactorDependencyFilter( projectArtifacts ) );
157 
158         DependencyResolutionResult result;
159         try
160         {
161             DefaultDependencyResolutionRequest request =
162                 new DefaultDependencyResolutionRequest( project, session.getRepositorySession() );
163             request.setResolutionFilter( resolutionFilter );
164 
165             result = dependenciesResolver.resolve( request );
166         }
167         catch ( DependencyResolutionException e )
168         {
169             result = e.getResult();
170 
171             /*
172              * MNG-2277, the check below compensates for our bad plugin support where we ended up with aggregator
173              * plugins that require dependency resolution although they usually run in phases of the build where project
174              * artifacts haven't been assembled yet. The prime example of this is "mvn release:prepare".
175              */
176             if ( aggregating && areAllDependenciesInReactor( session.getProjects(), result.getUnresolvedDependencies() ) )
177             {
178                 logger.warn( "The following dependencies could not be resolved at this point of the build"
179                     + " but seem to be part of the reactor:" );
180 
181                 for ( Dependency dependency : result.getUnresolvedDependencies() )
182                 {
183                     logger.warn( "o " + dependency );
184                 }
185 
186                 logger.warn( "Try running the build up to the lifecycle phase \"package\"" );
187             }
188             else
189             {
190                 throw new LifecycleExecutionException( null, project, e );
191             }
192         }
193 
194         Set<Artifact> artifacts = new LinkedHashSet<Artifact>();
195         if ( result.getDependencyGraph() != null && !result.getDependencyGraph().getChildren().isEmpty() )
196         {
197             RepositoryUtils.toArtifacts( artifacts, result.getDependencyGraph().getChildren(),
198                                          Collections.singletonList( project.getArtifact().getId() ), collectionFilter );
199         }
200         return artifacts;
201     }
202 
203     private boolean areAllDependenciesInReactor( Collection<MavenProject> projects, Collection<Dependency> dependencies )
204     {
205         Set<String> projectKeys = getReactorProjectKeys( projects );
206 
207         for ( Dependency dependency : dependencies )
208         {
209             org.sonatype.aether.artifact.Artifact a = dependency.getArtifact();
210             String key = ArtifactUtils.key( a.getGroupId(), a.getArtifactId(), a.getVersion() );
211             if ( !projectKeys.contains( key ) )
212             {
213                 return false;
214             }
215         }
216 
217         return true;
218     }
219 
220     private Set<String> getReactorProjectKeys( Collection<MavenProject> projects )
221     {
222         Set<String> projectKeys = new HashSet<String>( projects.size() * 2 );
223         for ( MavenProject project : projects )
224         {
225             String key = ArtifactUtils.key( project.getGroupId(), project.getArtifactId(), project.getVersion() );
226             projectKeys.add( key );
227         }
228         return projectKeys;
229     }
230 
231     private Collection<String> negate( Collection<String> scopes )
232     {
233         Collection<String> result = new HashSet<String>();
234         Collections.addAll( result, "system", "compile", "provided", "runtime", "test" );
235 
236         for ( String scope : scopes )
237         {
238             if ( "compile".equals( scope ) )
239             {
240                 result.remove( "compile" );
241                 result.remove( "system" );
242                 result.remove( "provided" );
243             }
244             else if ( "runtime".equals( scope ) )
245             {
246                 result.remove( "compile" );
247                 result.remove( "runtime" );
248             }
249             else if ( "compile+runtime".equals( scope ) )
250             {
251                 result.remove( "compile" );
252                 result.remove( "system" );
253                 result.remove( "provided" );
254                 result.remove( "runtime" );
255             }
256             else if ( "runtime+system".equals( scope ) )
257             {
258                 result.remove( "compile" );
259                 result.remove( "system" );
260                 result.remove( "runtime" );
261             }
262             else if ( "test".equals( scope ) )
263             {
264                 result.clear();
265             }
266         }
267 
268         return result;
269     }
270 
271     private static class ReactorDependencyFilter
272         implements DependencyFilter
273     {
274 
275         private Set<String> keys = new HashSet<String>();
276 
277         public ReactorDependencyFilter( Collection<Artifact> artifacts )
278         {
279             for ( Artifact artifact : artifacts )
280             {
281                 String key = ArtifactUtils.key( artifact );
282                 keys.add( key );
283             }
284         }
285 
286         public boolean accept( DependencyNode node, List<DependencyNode> parents )
287         {
288             Dependency dependency = node.getDependency();
289             if ( dependency != null )
290             {
291                 org.sonatype.aether.artifact.Artifact a = dependency.getArtifact();
292                 String key = ArtifactUtils.key( a.getGroupId(), a.getArtifactId(), a.getVersion() );
293                 return !keys.contains( key );
294             }
295             return false;
296         }
297 
298     }
299 
300 }