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