1 package org.apache.maven.lifecycle.internal;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.File;
23
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.Collections;
27 import java.util.HashMap;
28 import java.util.HashSet;
29 import java.util.Iterator;
30 import java.util.LinkedHashSet;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Set;
34
35 import javax.inject.Inject;
36 import javax.inject.Named;
37
38 import org.apache.maven.RepositoryUtils;
39 import org.apache.maven.artifact.Artifact;
40 import org.apache.maven.artifact.ArtifactUtils;
41 import org.apache.maven.eventspy.internal.EventSpyDispatcher;
42 import org.apache.maven.execution.MavenSession;
43 import org.apache.maven.lifecycle.LifecycleExecutionException;
44 import org.apache.maven.project.DefaultDependencyResolutionRequest;
45 import org.apache.maven.project.DependencyResolutionException;
46 import org.apache.maven.project.DependencyResolutionResult;
47 import org.apache.maven.project.MavenProject;
48 import org.apache.maven.project.ProjectDependenciesResolver;
49 import org.apache.maven.project.artifact.InvalidDependencyVersionException;
50 import org.apache.maven.project.artifact.ProjectArtifactsCache;
51 import org.codehaus.plexus.logging.Logger;
52 import org.eclipse.aether.graph.Dependency;
53 import org.eclipse.aether.graph.DependencyFilter;
54 import org.eclipse.aether.graph.DependencyNode;
55 import org.eclipse.aether.util.filter.AndDependencyFilter;
56 import org.eclipse.aether.util.filter.ScopeDependencyFilter;
57
58
59
60
61
62
63
64
65
66
67
68 @Named
69 public class LifecycleDependencyResolver
70 {
71
72 @Inject
73 private ProjectDependenciesResolver dependenciesResolver;
74
75 @Inject
76 private Logger logger;
77
78 @Inject
79 private ProjectArtifactFactory artifactFactory;
80
81 @Inject
82 private EventSpyDispatcher eventSpyDispatcher;
83
84 @Inject
85 private ProjectArtifactsCache projectArtifactsCache;
86
87 public LifecycleDependencyResolver()
88 {
89 }
90
91 public LifecycleDependencyResolver( ProjectDependenciesResolver projectDependenciesResolver, Logger logger )
92 {
93 this.dependenciesResolver = projectDependenciesResolver;
94 this.logger = logger;
95 }
96
97 public static List<MavenProject> getProjects( MavenProject project, MavenSession session, boolean aggregator )
98 {
99 if ( aggregator && project.getCollectedProjects() != null )
100 {
101 List<MavenProject> sortedProjects = new ArrayList<>( session.getProjects() );
102 List<MavenProject> projectAndSubmodules = new ArrayList<>();
103 addProjectAndSubModules( projectAndSubmodules, project );
104 Iterator<MavenProject> sortedProjectsIterator = sortedProjects.listIterator();
105 while ( sortedProjectsIterator.hasNext() )
106 {
107 if ( !projectAndSubmodules.contains( sortedProjectsIterator.next() ) )
108 {
109 sortedProjectsIterator.remove();
110 }
111 }
112 return sortedProjects;
113 }
114 else
115 {
116 return Collections.singletonList( project );
117 }
118 }
119
120 private static void addProjectAndSubModules( List<MavenProject> projects, MavenProject project )
121 {
122 projects.add( project );
123 for ( MavenProject submodule : project.getCollectedProjects() )
124 {
125 addProjectAndSubModules( projects, submodule );
126 }
127 }
128
129
130 public void resolveProjectDependencies( MavenProject project, Collection<String> scopesToCollect,
131 Collection<String> scopesToResolve, MavenSession session,
132 boolean aggregating, Set<Artifact> projectArtifacts )
133 throws LifecycleExecutionException
134 {
135 ClassLoader tccl = Thread.currentThread().getContextClassLoader();
136 try
137 {
138 ClassLoader projectRealm = project.getClassRealm();
139 if ( projectRealm != null && projectRealm != tccl )
140 {
141 Thread.currentThread().setContextClassLoader( projectRealm );
142 }
143
144 if ( project.getDependencyArtifacts() == null )
145 {
146 try
147 {
148 project.setDependencyArtifacts( artifactFactory.createArtifacts( project ) );
149 }
150 catch ( InvalidDependencyVersionException e )
151 {
152 throw new LifecycleExecutionException( e );
153 }
154 }
155
156 Set<Artifact> resolvedArtifacts;
157 ProjectArtifactsCache.Key cacheKey = projectArtifactsCache.createKey( project, scopesToCollect,
158 scopesToResolve, aggregating, session.getRepositorySession() );
159 ProjectArtifactsCache.CacheRecord recordArtifacts;
160 recordArtifacts = projectArtifactsCache.get( cacheKey );
161
162 if ( recordArtifacts != null )
163 {
164 resolvedArtifacts = recordArtifacts.getArtifacts();
165 }
166 else
167 {
168 try
169 {
170 resolvedArtifacts = getDependencies( project, scopesToCollect, scopesToResolve, session,
171 aggregating, projectArtifacts );
172 recordArtifacts = projectArtifactsCache.put( cacheKey, resolvedArtifacts );
173 }
174 catch ( LifecycleExecutionException e )
175 {
176 projectArtifactsCache.put( cacheKey, e );
177 projectArtifactsCache.register( project, cacheKey, recordArtifacts );
178 throw e;
179 }
180 }
181 projectArtifactsCache.register( project, cacheKey, recordArtifacts );
182
183 Map<Artifact, File> reactorProjects = new HashMap<>( session.getProjects().size() );
184 for ( MavenProject reactorProject : session.getProjects() )
185 {
186 reactorProjects.put( reactorProject.getArtifact(), reactorProject.getArtifact().getFile() );
187 }
188
189 Map<String, Artifact> map = new HashMap<>();
190 for ( Artifact artifact : resolvedArtifacts )
191 {
192
193
194
195
196
197 File reactorProjectFile = reactorProjects.get( artifact );
198 if ( reactorProjectFile != null )
199 {
200 artifact.setFile( reactorProjectFile );
201 }
202
203 map.put( artifact.getDependencyConflictId(), artifact );
204 }
205
206 project.setResolvedArtifacts( resolvedArtifacts );
207
208 for ( Artifact artifact : project.getDependencyArtifacts() )
209 {
210 if ( artifact.getFile() == null )
211 {
212 Artifact resolved = map.get( artifact.getDependencyConflictId() );
213 if ( resolved != null )
214 {
215 artifact.setFile( resolved.getFile() );
216 artifact.setDependencyTrail( resolved.getDependencyTrail() );
217 artifact.setResolvedVersion( resolved.getVersion() );
218 artifact.setResolved( true );
219 }
220 }
221 }
222 }
223 finally
224 {
225 Thread.currentThread().setContextClassLoader( tccl );
226 }
227 }
228
229 private Set<Artifact> getDependencies( MavenProject project, Collection<String> scopesToCollect,
230 Collection<String> scopesToResolve, MavenSession session,
231 boolean aggregating, Set<Artifact> projectArtifacts )
232 throws LifecycleExecutionException
233 {
234 if ( scopesToCollect == null )
235 {
236 scopesToCollect = Collections.emptySet();
237 }
238 if ( scopesToResolve == null )
239 {
240 scopesToResolve = Collections.emptySet();
241 }
242
243 if ( scopesToCollect.isEmpty() && scopesToResolve.isEmpty() )
244 {
245 return new LinkedHashSet<>();
246 }
247
248 scopesToCollect = new HashSet<>( scopesToCollect );
249 scopesToCollect.addAll( scopesToResolve );
250
251 DependencyFilter collectionFilter = new ScopeDependencyFilter( null, negate( scopesToCollect ) );
252 DependencyFilter resolutionFilter = new ScopeDependencyFilter( null, negate( scopesToResolve ) );
253 resolutionFilter = AndDependencyFilter.newInstance( collectionFilter, resolutionFilter );
254 resolutionFilter =
255 AndDependencyFilter.newInstance( resolutionFilter, new ReactorDependencyFilter( projectArtifacts ) );
256
257 DependencyResolutionResult result;
258 try
259 {
260 DefaultDependencyResolutionRequest request =
261 new DefaultDependencyResolutionRequest( project, session.getRepositorySession() );
262 request.setResolutionFilter( resolutionFilter );
263
264 eventSpyDispatcher.onEvent( request );
265
266 result = dependenciesResolver.resolve( request );
267 }
268 catch ( DependencyResolutionException e )
269 {
270 result = e.getResult();
271
272
273
274
275
276
277 if ( aggregating && areAllDependenciesInReactor( session.getProjects(),
278 result.getUnresolvedDependencies() ) )
279 {
280 logger.warn( "The following dependencies could not be resolved at this point of the build"
281 + " but seem to be part of the reactor:" );
282
283 for ( Dependency dependency : result.getUnresolvedDependencies() )
284 {
285 logger.warn( "o " + dependency );
286 }
287
288 logger.warn( "Try running the build up to the lifecycle phase \"package\"" );
289 }
290 else
291 {
292 throw new LifecycleExecutionException( null, project, e );
293 }
294 }
295
296 eventSpyDispatcher.onEvent( result );
297
298 Set<Artifact> artifacts = new LinkedHashSet<>();
299 if ( result.getDependencyGraph() != null && !result.getDependencyGraph().getChildren().isEmpty() )
300 {
301 RepositoryUtils.toArtifacts( artifacts, result.getDependencyGraph().getChildren(),
302 Collections.singletonList( project.getArtifact().getId() ), collectionFilter );
303 }
304 return artifacts;
305 }
306
307 private boolean areAllDependenciesInReactor( Collection<MavenProject> projects,
308 Collection<Dependency> dependencies )
309 {
310 Set<String> projectKeys = getReactorProjectKeys( projects );
311
312 for ( Dependency dependency : dependencies )
313 {
314 org.eclipse.aether.artifact.Artifact a = dependency.getArtifact();
315 String key = ArtifactUtils.key( a.getGroupId(), a.getArtifactId(), a.getVersion() );
316 if ( !projectKeys.contains( key ) )
317 {
318 return false;
319 }
320 }
321
322 return true;
323 }
324
325 private Set<String> getReactorProjectKeys( Collection<MavenProject> projects )
326 {
327 Set<String> projectKeys = new HashSet<>( projects.size() * 2 );
328 for ( MavenProject project : projects )
329 {
330 String key = ArtifactUtils.key( project.getGroupId(), project.getArtifactId(), project.getVersion() );
331 projectKeys.add( key );
332 }
333 return projectKeys;
334 }
335
336 private Collection<String> negate( Collection<String> scopes )
337 {
338 Collection<String> result = new HashSet<>();
339 Collections.addAll( result, "system", "compile", "provided", "runtime", "test" );
340
341 for ( String scope : scopes )
342 {
343 if ( "compile".equals( scope ) )
344 {
345 result.remove( "compile" );
346 result.remove( "system" );
347 result.remove( "provided" );
348 }
349 else if ( "runtime".equals( scope ) )
350 {
351 result.remove( "compile" );
352 result.remove( "runtime" );
353 }
354 else if ( "compile+runtime".equals( scope ) )
355 {
356 result.remove( "compile" );
357 result.remove( "system" );
358 result.remove( "provided" );
359 result.remove( "runtime" );
360 }
361 else if ( "runtime+system".equals( scope ) )
362 {
363 result.remove( "compile" );
364 result.remove( "system" );
365 result.remove( "runtime" );
366 }
367 else if ( "test".equals( scope ) )
368 {
369 result.clear();
370 }
371 }
372
373 return result;
374 }
375
376 private static class ReactorDependencyFilter
377 implements DependencyFilter
378 {
379
380 private Set<String> keys = new HashSet<>();
381
382 ReactorDependencyFilter( Collection<Artifact> artifacts )
383 {
384 for ( Artifact artifact : artifacts )
385 {
386 String key = ArtifactUtils.key( artifact );
387 keys.add( key );
388 }
389 }
390
391 public boolean accept( DependencyNode node, List<DependencyNode> parents )
392 {
393 Dependency dependency = node.getDependency();
394 if ( dependency != null )
395 {
396 org.eclipse.aether.artifact.Artifact a = dependency.getArtifact();
397 String key = ArtifactUtils.key( a.getGroupId(), a.getArtifactId(), a.getVersion() );
398 return !keys.contains( key );
399 }
400 return false;
401 }
402
403 }
404
405 }