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