1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.aether.internal.impl.collect;
20
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.HashSet;
25 import java.util.LinkedHashMap;
26 import java.util.List;
27 import java.util.Map;
28
29 import org.eclipse.aether.DefaultRepositorySystemSession;
30 import org.eclipse.aether.RepositoryException;
31 import org.eclipse.aether.RepositorySystemSession;
32 import org.eclipse.aether.RequestTrace;
33 import org.eclipse.aether.artifact.Artifact;
34 import org.eclipse.aether.collection.CollectRequest;
35 import org.eclipse.aether.collection.CollectResult;
36 import org.eclipse.aether.collection.DependencyCollectionException;
37 import org.eclipse.aether.collection.DependencyGraphTransformer;
38 import org.eclipse.aether.collection.DependencyTraverser;
39 import org.eclipse.aether.collection.VersionFilter;
40 import org.eclipse.aether.graph.DefaultDependencyNode;
41 import org.eclipse.aether.graph.Dependency;
42 import org.eclipse.aether.graph.DependencyNode;
43 import org.eclipse.aether.impl.ArtifactDescriptorReader;
44 import org.eclipse.aether.impl.DependencyCollector;
45 import org.eclipse.aether.impl.RemoteRepositoryManager;
46 import org.eclipse.aether.impl.VersionRangeResolver;
47 import org.eclipse.aether.repository.ArtifactRepository;
48 import org.eclipse.aether.repository.RemoteRepository;
49 import org.eclipse.aether.resolution.ArtifactDescriptorException;
50 import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
51 import org.eclipse.aether.resolution.ArtifactDescriptorResult;
52 import org.eclipse.aether.resolution.VersionRangeRequest;
53 import org.eclipse.aether.resolution.VersionRangeResolutionException;
54 import org.eclipse.aether.resolution.VersionRangeResult;
55 import org.eclipse.aether.util.ConfigUtils;
56 import org.eclipse.aether.util.graph.transformer.TransformationContextKeys;
57 import org.eclipse.aether.version.Version;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
60
61 import static java.util.Objects.requireNonNull;
62
63
64
65
66
67
68 public abstract class DependencyCollectorDelegate implements DependencyCollector {
69
70
71
72
73
74
75
76
77 public static final String CONFIG_PROP_MAX_EXCEPTIONS =
78 DefaultDependencyCollector.CONFIG_PROPS_PREFIX + "maxExceptions";
79
80 public static final int DEFAULT_MAX_EXCEPTIONS = 50;
81
82
83
84
85
86
87
88
89 public static final String CONFIG_PROP_MAX_CYCLES = DefaultDependencyCollector.CONFIG_PROPS_PREFIX + "maxCycles";
90
91 public static final int DEFAULT_MAX_CYCLES = 10;
92
93 protected final Logger logger = LoggerFactory.getLogger(getClass());
94
95 protected final RemoteRepositoryManager remoteRepositoryManager;
96
97 protected final ArtifactDescriptorReader descriptorReader;
98
99 protected final VersionRangeResolver versionRangeResolver;
100
101 protected DependencyCollectorDelegate(
102 RemoteRepositoryManager remoteRepositoryManager,
103 ArtifactDescriptorReader artifactDescriptorReader,
104 VersionRangeResolver versionRangeResolver) {
105 this.remoteRepositoryManager =
106 requireNonNull(remoteRepositoryManager, "remote repository manager cannot be null");
107 this.descriptorReader = requireNonNull(artifactDescriptorReader, "artifact descriptor reader cannot be null");
108 this.versionRangeResolver = requireNonNull(versionRangeResolver, "version range resolver cannot be null");
109 }
110
111 @SuppressWarnings("checkstyle:methodlength")
112 @Override
113 public final CollectResult collectDependencies(RepositorySystemSession session, CollectRequest request)
114 throws DependencyCollectionException {
115 requireNonNull(session, "session cannot be null");
116 requireNonNull(request, "request cannot be null");
117 session = optimizeSession(session);
118
119 RequestTrace trace = RequestTrace.newChild(request.getTrace(), request);
120
121 CollectResult result = new CollectResult(request);
122
123 DependencyTraverser depTraverser = session.getDependencyTraverser();
124 VersionFilter verFilter = session.getVersionFilter();
125
126 Dependency root = request.getRoot();
127 List<RemoteRepository> repositories = request.getRepositories();
128 List<Dependency> dependencies = request.getDependencies();
129 List<Dependency> managedDependencies = request.getManagedDependencies();
130
131 Map<String, Object> stats = new LinkedHashMap<>();
132 long time1 = System.nanoTime();
133
134 DefaultDependencyNode node;
135 if (root != null) {
136 List<? extends Version> versions;
137 VersionRangeResult rangeResult;
138 try {
139 VersionRangeRequest rangeRequest = new VersionRangeRequest(
140 root.getArtifact(), request.getRepositories(), request.getRequestContext());
141 rangeRequest.setTrace(trace);
142 rangeResult = versionRangeResolver.resolveVersionRange(session, rangeRequest);
143 versions = filterVersions(root, rangeResult, verFilter, new DefaultVersionFilterContext(session));
144 } catch (VersionRangeResolutionException e) {
145 result.addException(e);
146 throw new DependencyCollectionException(result, e.getMessage());
147 }
148
149 Version version = versions.get(versions.size() - 1);
150 root = root.setArtifact(root.getArtifact().setVersion(version.toString()));
151
152 ArtifactDescriptorResult descriptorResult;
153 try {
154 ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest();
155 descriptorRequest.setArtifact(root.getArtifact());
156 descriptorRequest.setRepositories(request.getRepositories());
157 descriptorRequest.setRequestContext(request.getRequestContext());
158 descriptorRequest.setTrace(trace);
159 if (isLackingDescriptor(session, root.getArtifact())) {
160 descriptorResult = new ArtifactDescriptorResult(descriptorRequest);
161 } else {
162 descriptorResult = descriptorReader.readArtifactDescriptor(session, descriptorRequest);
163 }
164 } catch (ArtifactDescriptorException e) {
165 result.addException(e);
166 throw new DependencyCollectionException(result, e.getMessage());
167 }
168
169 root = root.setArtifact(descriptorResult.getArtifact());
170
171 if (!session.isIgnoreArtifactDescriptorRepositories()) {
172 repositories = remoteRepositoryManager.aggregateRepositories(
173 session, repositories, descriptorResult.getRepositories(), true);
174 }
175 dependencies = mergeDeps(dependencies, descriptorResult.getDependencies());
176 managedDependencies = mergeDeps(managedDependencies, descriptorResult.getManagedDependencies());
177
178 node = new DefaultDependencyNode(root);
179 node.setRequestContext(request.getRequestContext());
180 node.setRelocations(descriptorResult.getRelocations());
181 node.setVersionConstraint(rangeResult.getVersionConstraint());
182 node.setVersion(version);
183 node.setAliases(descriptorResult.getAliases());
184 node.setRepositories(request.getRepositories());
185 } else {
186 node = new DefaultDependencyNode(request.getRootArtifact());
187 node.setRequestContext(request.getRequestContext());
188 node.setRepositories(request.getRepositories());
189 }
190
191 result.setRoot(node);
192
193 boolean traverse = root == null || depTraverser == null || depTraverser.traverseDependency(root);
194 String errorPath = null;
195 if (traverse && !dependencies.isEmpty()) {
196 DataPool pool = new DataPool(session);
197
198 DefaultDependencyCollectionContext context = new DefaultDependencyCollectionContext(
199 session, request.getRootArtifact(), root, managedDependencies);
200
201 DefaultVersionFilterContext versionContext = new DefaultVersionFilterContext(session);
202
203 Results results = new Results(result, session);
204
205 doCollectDependencies(
206 session,
207 trace,
208 pool,
209 context,
210 versionContext,
211 request,
212 node,
213 repositories,
214 dependencies,
215 managedDependencies,
216 results);
217
218 errorPath = results.getErrorPath();
219 }
220
221 long time2 = System.nanoTime();
222
223 DependencyGraphTransformer transformer = session.getDependencyGraphTransformer();
224 if (transformer != null) {
225 try {
226 DefaultDependencyGraphTransformationContext context =
227 new DefaultDependencyGraphTransformationContext(session);
228 context.put(TransformationContextKeys.STATS, stats);
229 result.setRoot(transformer.transformGraph(node, context));
230 } catch (RepositoryException e) {
231 result.addException(e);
232 }
233 }
234
235 long time3 = System.nanoTime();
236 if (logger.isDebugEnabled()) {
237 stats.put(getClass().getSimpleName() + ".collectTime", time2 - time1);
238 stats.put(getClass().getSimpleName() + ".transformTime", time3 - time2);
239 logger.debug("Dependency collection stats {}", stats);
240 }
241
242 if (errorPath != null) {
243 throw new DependencyCollectionException(result, "Failed to collect dependencies at " + errorPath);
244 }
245 if (!result.getExceptions().isEmpty()) {
246 throw new DependencyCollectionException(result);
247 }
248
249 return result;
250 }
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266 protected RequestTrace collectStepTrace(
267 RequestTrace trace, String context, List<DependencyNode> path, Dependency node) {
268 return RequestTrace.newChild(trace, new CollectStepDataImpl(context, path, node));
269 }
270
271 @SuppressWarnings("checkstyle:parameternumber")
272 protected abstract void doCollectDependencies(
273 RepositorySystemSession session,
274 RequestTrace trace,
275 DataPool pool,
276 DefaultDependencyCollectionContext context,
277 DefaultVersionFilterContext versionContext,
278 CollectRequest request,
279 DependencyNode node,
280 List<RemoteRepository> repositories,
281 List<Dependency> dependencies,
282 List<Dependency> managedDependencies,
283 Results results)
284 throws DependencyCollectionException;
285
286 protected RepositorySystemSession optimizeSession(RepositorySystemSession session) {
287 DefaultRepositorySystemSession optimized = new DefaultRepositorySystemSession(session);
288 optimized.setArtifactTypeRegistry(CachingArtifactTypeRegistry.newInstance(session));
289 return optimized;
290 }
291
292 protected List<Dependency> mergeDeps(List<Dependency> dominant, List<Dependency> recessive) {
293 List<Dependency> result;
294 if (dominant == null || dominant.isEmpty()) {
295 result = recessive;
296 } else if (recessive == null || recessive.isEmpty()) {
297 result = dominant;
298 } else {
299 int initialCapacity = dominant.size() + recessive.size();
300 result = new ArrayList<>(initialCapacity);
301 Collection<String> ids = new HashSet<>(initialCapacity, 1.0f);
302 for (Dependency dependency : dominant) {
303 ids.add(getId(dependency.getArtifact()));
304 result.add(dependency);
305 }
306 for (Dependency dependency : recessive) {
307 if (!ids.contains(getId(dependency.getArtifact()))) {
308 result.add(dependency);
309 }
310 }
311 }
312 return result;
313 }
314
315 protected static String getId(Artifact a) {
316 return a.getGroupId() + ':' + a.getArtifactId() + ':' + a.getClassifier() + ':' + a.getExtension();
317 }
318
319 @SuppressWarnings("checkstyle:parameternumber")
320 protected static DefaultDependencyNode createDependencyNode(
321 List<Artifact> relocations,
322 PremanagedDependency preManaged,
323 VersionRangeResult rangeResult,
324 Version version,
325 Dependency d,
326 Collection<Artifact> aliases,
327 List<RemoteRepository> repos,
328 String requestContext) {
329 DefaultDependencyNode child = new DefaultDependencyNode(d);
330 preManaged.applyTo(child);
331 child.setRelocations(relocations);
332 child.setVersionConstraint(rangeResult.getVersionConstraint());
333 child.setVersion(version);
334 child.setAliases(aliases);
335 child.setRepositories(repos);
336 child.setRequestContext(requestContext);
337 return child;
338 }
339
340 protected static DefaultDependencyNode createDependencyNode(
341 List<Artifact> relocations,
342 PremanagedDependency preManaged,
343 VersionRangeResult rangeResult,
344 Version version,
345 Dependency d,
346 ArtifactDescriptorResult descriptorResult,
347 DependencyNode cycleNode) {
348 DefaultDependencyNode child = createDependencyNode(
349 relocations,
350 preManaged,
351 rangeResult,
352 version,
353 d,
354 descriptorResult.getAliases(),
355 cycleNode.getRepositories(),
356 cycleNode.getRequestContext());
357 child.setChildren(cycleNode.getChildren());
358 return child;
359 }
360
361 protected static ArtifactDescriptorRequest createArtifactDescriptorRequest(
362 String requestContext, RequestTrace requestTrace, List<RemoteRepository> repositories, Dependency d) {
363 ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest();
364 descriptorRequest.setArtifact(d.getArtifact());
365 descriptorRequest.setRepositories(repositories);
366 descriptorRequest.setRequestContext(requestContext);
367 descriptorRequest.setTrace(requestTrace);
368 return descriptorRequest;
369 }
370
371 protected static VersionRangeRequest createVersionRangeRequest(
372 String requestContext,
373 RequestTrace requestTrace,
374 List<RemoteRepository> repositories,
375 Dependency dependency) {
376 VersionRangeRequest rangeRequest = new VersionRangeRequest();
377 rangeRequest.setArtifact(dependency.getArtifact());
378 rangeRequest.setRepositories(repositories);
379 rangeRequest.setRequestContext(requestContext);
380 rangeRequest.setTrace(requestTrace);
381 return rangeRequest;
382 }
383
384 protected VersionRangeResult cachedResolveRangeResult(
385 VersionRangeRequest rangeRequest, DataPool pool, RepositorySystemSession session)
386 throws VersionRangeResolutionException {
387 Object key = pool.toKey(rangeRequest);
388 VersionRangeResult rangeResult = pool.getConstraint(key, rangeRequest);
389 if (rangeResult == null) {
390 rangeResult = versionRangeResolver.resolveVersionRange(session, rangeRequest);
391 pool.putConstraint(key, rangeResult);
392 }
393 return rangeResult;
394 }
395
396 protected static boolean isLackingDescriptor(RepositorySystemSession session, Artifact artifact) {
397 return session.getSystemScopeHandler().getSystemPath(artifact) != null;
398 }
399
400 protected static List<RemoteRepository> getRemoteRepositories(
401 ArtifactRepository repository, List<RemoteRepository> repositories) {
402 if (repository instanceof RemoteRepository) {
403 return Collections.singletonList((RemoteRepository) repository);
404 }
405 if (repository != null) {
406 return Collections.emptyList();
407 }
408 return repositories;
409 }
410
411 protected static List<? extends Version> filterVersions(
412 Dependency dependency,
413 VersionRangeResult rangeResult,
414 VersionFilter verFilter,
415 DefaultVersionFilterContext verContext)
416 throws VersionRangeResolutionException {
417 if (rangeResult.getVersions().isEmpty()) {
418 throw new VersionRangeResolutionException(
419 rangeResult, "No versions available for " + dependency.getArtifact() + " within specified range");
420 }
421
422 List<? extends Version> versions;
423 if (verFilter != null && rangeResult.getVersionConstraint().getRange() != null) {
424 verContext.set(dependency, rangeResult);
425 try {
426 verFilter.filterVersions(verContext);
427 } catch (RepositoryException e) {
428 throw new VersionRangeResolutionException(
429 rangeResult, "Failed to filter versions for " + dependency.getArtifact(), e);
430 }
431 versions = verContext.get();
432 if (versions.isEmpty()) {
433 throw new VersionRangeResolutionException(
434 rangeResult,
435 "No acceptable versions for " + dependency.getArtifact() + ": " + rangeResult.getVersions());
436 }
437 } else {
438 versions = rangeResult.getVersions();
439 }
440 return versions;
441 }
442
443
444
445
446 protected static class Results {
447
448 private final CollectResult result;
449
450 final int maxExceptions;
451
452 final int maxCycles;
453
454 String errorPath;
455
456 public Results(CollectResult result, RepositorySystemSession session) {
457 this.result = result;
458
459 maxExceptions = ConfigUtils.getInteger(session, DEFAULT_MAX_EXCEPTIONS, CONFIG_PROP_MAX_EXCEPTIONS);
460
461 maxCycles = ConfigUtils.getInteger(session, DEFAULT_MAX_CYCLES, CONFIG_PROP_MAX_CYCLES);
462 }
463
464 public CollectResult getResult() {
465 return result;
466 }
467
468 public String getErrorPath() {
469 return errorPath;
470 }
471
472 public void addException(Dependency dependency, Exception e, List<DependencyNode> nodes) {
473 if (maxExceptions < 0 || result.getExceptions().size() < maxExceptions) {
474 result.addException(e);
475 if (errorPath == null) {
476 StringBuilder buffer = new StringBuilder(256);
477 for (DependencyNode node : nodes) {
478 if (buffer.length() > 0) {
479 buffer.append(" -> ");
480 }
481 Dependency dep = node.getDependency();
482 if (dep != null) {
483 buffer.append(dep.getArtifact());
484 }
485 }
486 if (buffer.length() > 0) {
487 buffer.append(" -> ");
488 }
489 buffer.append(dependency.getArtifact());
490 errorPath = buffer.toString();
491 }
492 }
493 }
494
495 public void addCycle(List<DependencyNode> nodes, int cycleEntry, Dependency dependency) {
496 if (maxCycles < 0 || result.getCycles().size() < maxCycles) {
497 result.addCycle(new DefaultDependencyCycle(nodes, cycleEntry, dependency));
498 }
499 }
500 }
501 }