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