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