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