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          ClassLoader tccl = Thread.currentThread().getContextClassLoader();
97          try
98          {
99              ClassLoader projectRealm = project.getClassRealm();
100             if ( projectRealm != null && projectRealm != tccl )
101             {
102                 Thread.currentThread().setContextClassLoader( projectRealm );
103             }
104 
105             if ( project.getDependencyArtifacts() == null )
106             {
107                 try
108                 {
109                     project.setDependencyArtifacts( project.createArtifacts( artifactFactory, null, null ) );
110                 }
111                 catch ( InvalidDependencyVersionException e )
112                 {
113                     throw new LifecycleExecutionException( e );
114                 }
115             }
116 
117             Set<Artifact> artifacts =
118                 getDependencies( project, scopesToCollect, scopesToResolve, session, aggregating, projectArtifacts );
119 
120             project.setResolvedArtifacts( artifacts );
121 
122             Map<String, Artifact> map = new HashMap<String, Artifact>();
123             for ( Artifact artifact : artifacts )
124             {
125                 map.put( artifact.getDependencyConflictId(), artifact );
126             }
127             for ( Artifact artifact : project.getDependencyArtifacts() )
128             {
129                 if ( artifact.getFile() == null )
130                 {
131                     Artifact resolved = map.get( artifact.getDependencyConflictId() );
132                     if ( resolved != null )
133                     {
134                         artifact.setFile( resolved.getFile() );
135                         artifact.setDependencyTrail( resolved.getDependencyTrail() );
136                         artifact.setResolvedVersion( resolved.getVersion() );
137                         artifact.setResolved( true );
138                     }
139                 }
140             }
141         }
142         finally
143         {
144             Thread.currentThread().setContextClassLoader( tccl );
145         }
146     }
147 
148     private Set<Artifact> getDependencies( MavenProject project, Collection<String> scopesToCollect,
149                                            Collection<String> scopesToResolve, MavenSession session,
150                                            boolean aggregating, Set<Artifact> projectArtifacts )
151         throws LifecycleExecutionException
152     {
153         if ( scopesToCollect == null )
154         {
155             scopesToCollect = Collections.emptySet();
156         }
157         if ( scopesToResolve == null )
158         {
159             scopesToResolve = Collections.emptySet();
160         }
161 
162         if ( scopesToCollect.isEmpty() && scopesToResolve.isEmpty() )
163         {
164             return new LinkedHashSet<Artifact>();
165         }
166 
167         scopesToCollect = new HashSet<String>( scopesToCollect );
168         scopesToCollect.addAll( scopesToResolve );
169 
170         DependencyFilter collectionFilter = new ScopeDependencyFilter( null, negate( scopesToCollect ) );
171         DependencyFilter resolutionFilter = new ScopeDependencyFilter( null, negate( scopesToResolve ) );
172         resolutionFilter = AndDependencyFilter.newInstance( collectionFilter, resolutionFilter );
173         resolutionFilter =
174             AndDependencyFilter.newInstance( resolutionFilter, new ReactorDependencyFilter( projectArtifacts ) );
175 
176         DependencyResolutionResult result;
177         try
178         {
179             DefaultDependencyResolutionRequest request =
180                 new DefaultDependencyResolutionRequest( project, session.getRepositorySession() );
181             request.setResolutionFilter( resolutionFilter );
182 
183             eventSpyDispatcher.onEvent( request );
184 
185             result = dependenciesResolver.resolve( request );
186         }
187         catch ( DependencyResolutionException e )
188         {
189             result = e.getResult();
190 
191             /*
192              * MNG-2277, the check below compensates for our bad plugin support where we ended up with aggregator
193              * plugins that require dependency resolution although they usually run in phases of the build where project
194              * artifacts haven't been assembled yet. The prime example of this is "mvn release:prepare".
195              */
196             if ( aggregating && areAllDependenciesInReactor( session.getProjects(), result.getUnresolvedDependencies() ) )
197             {
198                 logger.warn( "The following dependencies could not be resolved at this point of the build"
199                     + " but seem to be part of the reactor:" );
200 
201                 for ( Dependency dependency : result.getUnresolvedDependencies() )
202                 {
203                     logger.warn( "o " + dependency );
204                 }
205 
206                 logger.warn( "Try running the build up to the lifecycle phase \"package\"" );
207             }
208             else
209             {
210                 throw new LifecycleExecutionException( null, project, e );
211             }
212         }
213 
214         eventSpyDispatcher.onEvent( result );
215 
216         Set<Artifact> artifacts = new LinkedHashSet<Artifact>();
217         if ( result.getDependencyGraph() != null && !result.getDependencyGraph().getChildren().isEmpty() )
218         {
219             RepositoryUtils.toArtifacts( artifacts, result.getDependencyGraph().getChildren(),
220                                          Collections.singletonList( project.getArtifact().getId() ), collectionFilter );
221         }
222         return artifacts;
223     }
224 
225     private boolean areAllDependenciesInReactor( Collection<MavenProject> projects, Collection<Dependency> dependencies )
226     {
227         Set<String> projectKeys = getReactorProjectKeys( projects );
228 
229         for ( Dependency dependency : dependencies )
230         {
231             org.sonatype.aether.artifact.Artifact a = dependency.getArtifact();
232             String key = ArtifactUtils.key( a.getGroupId(), a.getArtifactId(), a.getVersion() );
233             if ( !projectKeys.contains( key ) )
234             {
235                 return false;
236             }
237         }
238 
239         return true;
240     }
241 
242     private Set<String> getReactorProjectKeys( Collection<MavenProject> projects )
243     {
244         Set<String> projectKeys = new HashSet<String>( projects.size() * 2 );
245         for ( MavenProject project : projects )
246         {
247             String key = ArtifactUtils.key( project.getGroupId(), project.getArtifactId(), project.getVersion() );
248             projectKeys.add( key );
249         }
250         return projectKeys;
251     }
252 
253     private Collection<String> negate( Collection<String> scopes )
254     {
255         Collection<String> result = new HashSet<String>();
256         Collections.addAll( result, "system", "compile", "provided", "runtime", "test" );
257 
258         for ( String scope : scopes )
259         {
260             if ( "compile".equals( scope ) )
261             {
262                 result.remove( "compile" );
263                 result.remove( "system" );
264                 result.remove( "provided" );
265             }
266             else if ( "runtime".equals( scope ) )
267             {
268                 result.remove( "compile" );
269                 result.remove( "runtime" );
270             }
271             else if ( "compile+runtime".equals( scope ) )
272             {
273                 result.remove( "compile" );
274                 result.remove( "system" );
275                 result.remove( "provided" );
276                 result.remove( "runtime" );
277             }
278             else if ( "runtime+system".equals( scope ) )
279             {
280                 result.remove( "compile" );
281                 result.remove( "system" );
282                 result.remove( "runtime" );
283             }
284             else if ( "test".equals( scope ) )
285             {
286                 result.clear();
287             }
288         }
289 
290         return result;
291     }
292 
293     private static class ReactorDependencyFilter
294         implements DependencyFilter
295     {
296 
297         private Set<String> keys = new HashSet<String>();
298 
299         public ReactorDependencyFilter( Collection<Artifact> artifacts )
300         {
301             for ( Artifact artifact : artifacts )
302             {
303                 String key = ArtifactUtils.key( artifact );
304                 keys.add( key );
305             }
306         }
307 
308         public boolean accept( DependencyNode node, List<DependencyNode> parents )
309         {
310             Dependency dependency = node.getDependency();
311             if ( dependency != null )
312             {
313                 org.sonatype.aether.artifact.Artifact a = dependency.getArtifact();
314                 String key = ArtifactUtils.key( a.getGroupId(), a.getArtifactId(), a.getVersion() );
315                 return !keys.contains( key );
316             }
317             return false;
318         }
319 
320     }
321 
322 }