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