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