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