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