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.bf;
20
21 import javax.inject.Inject;
22 import javax.inject.Named;
23 import javax.inject.Singleton;
24
25 import java.io.Closeable;
26 import java.util.ArrayDeque;
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.LinkedHashMap;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Optional;
33 import java.util.Queue;
34 import java.util.Set;
35 import java.util.concurrent.Callable;
36 import java.util.concurrent.ConcurrentHashMap;
37 import java.util.concurrent.ExecutionException;
38 import java.util.concurrent.ExecutorService;
39 import java.util.concurrent.Future;
40 import java.util.concurrent.TimeUnit;
41 import java.util.concurrent.TimeoutException;
42 import java.util.stream.Collectors;
43 import java.util.stream.Stream;
44
45 import org.eclipse.aether.RepositorySystemSession;
46 import org.eclipse.aether.RequestTrace;
47 import org.eclipse.aether.artifact.Artifact;
48 import org.eclipse.aether.artifact.ArtifactType;
49 import org.eclipse.aether.artifact.DefaultArtifact;
50 import org.eclipse.aether.collection.CollectRequest;
51 import org.eclipse.aether.collection.DependencyManager;
52 import org.eclipse.aether.collection.DependencySelector;
53 import org.eclipse.aether.collection.DependencyTraverser;
54 import org.eclipse.aether.collection.VersionFilter;
55 import org.eclipse.aether.graph.DefaultDependencyNode;
56 import org.eclipse.aether.graph.Dependency;
57 import org.eclipse.aether.graph.DependencyNode;
58 import org.eclipse.aether.impl.ArtifactDescriptorReader;
59 import org.eclipse.aether.impl.RemoteRepositoryManager;
60 import org.eclipse.aether.impl.VersionRangeResolver;
61 import org.eclipse.aether.internal.impl.collect.DataPool;
62 import org.eclipse.aether.internal.impl.collect.DefaultDependencyCollectionContext;
63 import org.eclipse.aether.internal.impl.collect.DefaultVersionFilterContext;
64 import org.eclipse.aether.internal.impl.collect.DependencyCollectorDelegate;
65 import org.eclipse.aether.internal.impl.collect.PremanagedDependency;
66 import org.eclipse.aether.repository.RemoteRepository;
67 import org.eclipse.aether.resolution.ArtifactDescriptorException;
68 import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
69 import org.eclipse.aether.resolution.ArtifactDescriptorResult;
70 import org.eclipse.aether.resolution.VersionRangeRequest;
71 import org.eclipse.aether.resolution.VersionRangeResult;
72 import org.eclipse.aether.util.ConfigUtils;
73 import org.eclipse.aether.util.artifact.ArtifactIdUtils;
74 import org.eclipse.aether.util.concurrency.ExecutorUtils;
75 import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
76 import org.eclipse.aether.version.Version;
77
78 import static org.eclipse.aether.internal.impl.collect.DefaultDependencyCycle.find;
79
80
81
82
83
84
85 @Singleton
86 @Named(BfDependencyCollector.NAME)
87 public class BfDependencyCollector extends DependencyCollectorDelegate {
88 public static final String NAME = "bf";
89
90
91
92
93
94
95
96 static final String CONFIG_PROP_SKIPPER = "aether.dependencyCollector.bf.skipper";
97
98
99
100
101
102
103 static final boolean CONFIG_PROP_SKIPPER_DEFAULT = true;
104
105
106
107
108
109
110 static final String CONFIG_PROP_THREADS = "aether.dependencyCollector.bf.threads";
111
112 @Inject
113 public BfDependencyCollector(
114 RemoteRepositoryManager remoteRepositoryManager,
115 ArtifactDescriptorReader artifactDescriptorReader,
116 VersionRangeResolver versionRangeResolver) {
117 super(remoteRepositoryManager, artifactDescriptorReader, versionRangeResolver);
118 }
119
120 @SuppressWarnings("checkstyle:parameternumber")
121 @Override
122 protected void doCollectDependencies(
123 RepositorySystemSession session,
124 RequestTrace trace,
125 DataPool pool,
126 DefaultDependencyCollectionContext context,
127 DefaultVersionFilterContext versionContext,
128 CollectRequest request,
129 DependencyNode node,
130 List<RemoteRepository> repositories,
131 List<Dependency> dependencies,
132 List<Dependency> managedDependencies,
133 Results results) {
134 boolean useSkip = ConfigUtils.getBoolean(session, CONFIG_PROP_SKIPPER_DEFAULT, CONFIG_PROP_SKIPPER);
135 int nThreads = ExecutorUtils.threadCount(session, 5, CONFIG_PROP_THREADS);
136 logger.debug("Using thread pool with {} threads to resolve descriptors.", nThreads);
137
138 if (useSkip) {
139 logger.debug("Collector skip mode enabled");
140 }
141
142 try (DependencyResolutionSkipper skipper = useSkip
143 ? DependencyResolutionSkipper.defaultSkipper()
144 : DependencyResolutionSkipper.neverSkipper();
145 ParallelDescriptorResolver parallelDescriptorResolver = new ParallelDescriptorResolver(nThreads)) {
146 Args args = new Args(session, pool, context, versionContext, request, skipper, parallelDescriptorResolver);
147
148 DependencySelector rootDepSelector = session.getDependencySelector() != null
149 ? session.getDependencySelector().deriveChildSelector(context)
150 : null;
151 DependencyManager rootDepManager = session.getDependencyManager() != null
152 ? session.getDependencyManager().deriveChildManager(context)
153 : null;
154 DependencyTraverser rootDepTraverser = session.getDependencyTraverser() != null
155 ? session.getDependencyTraverser().deriveChildTraverser(context)
156 : null;
157 VersionFilter rootVerFilter = session.getVersionFilter() != null
158 ? session.getVersionFilter().deriveChildFilter(context)
159 : null;
160
161 List<DependencyNode> parents = Collections.singletonList(node);
162 for (Dependency dependency : dependencies) {
163 RequestTrace childTrace =
164 collectStepTrace(trace, args.request.getRequestContext(), parents, dependency);
165 DependencyProcessingContext processingContext = new DependencyProcessingContext(
166 rootDepSelector,
167 rootDepManager,
168 rootDepTraverser,
169 rootVerFilter,
170 childTrace,
171 repositories,
172 managedDependencies,
173 parents,
174 dependency,
175 PremanagedDependency.create(rootDepManager, dependency, false, args.premanagedState));
176 if (!filter(processingContext)) {
177 processingContext.withDependency(processingContext.premanagedDependency.getManagedDependency());
178 resolveArtifactDescriptorAsync(args, processingContext, results);
179 args.dependencyProcessingQueue.add(processingContext);
180 }
181 }
182
183 while (!args.dependencyProcessingQueue.isEmpty()) {
184 processDependency(
185 args, results, args.dependencyProcessingQueue.remove(), Collections.emptyList(), false);
186 }
187 }
188 }
189
190 @SuppressWarnings("checkstyle:parameternumber")
191 private void processDependency(
192 Args args,
193 Results results,
194 DependencyProcessingContext context,
195 List<Artifact> relocations,
196 boolean disableVersionManagement) {
197 Dependency dependency = context.dependency;
198 PremanagedDependency preManaged = context.premanagedDependency;
199
200 boolean noDescriptor = isLackingDescriptor(dependency.getArtifact());
201 boolean traverse =
202 !noDescriptor && (context.depTraverser == null || context.depTraverser.traverseDependency(dependency));
203
204 Future<DescriptorResolutionResult> resolutionResultFuture = args.resolver.find(dependency.getArtifact());
205 DescriptorResolutionResult resolutionResult;
206 VersionRangeResult rangeResult;
207 try {
208 resolutionResult = resolutionResultFuture.get();
209 rangeResult = resolutionResult.rangeResult;
210 } catch (Exception e) {
211 results.addException(dependency, e, context.parents);
212 return;
213 }
214
215 Set<Version> versions = resolutionResult.descriptors.keySet();
216 for (Version version : versions) {
217 Artifact originalArtifact = dependency.getArtifact().setVersion(version.toString());
218 Dependency d = dependency.setArtifact(originalArtifact);
219
220 final ArtifactDescriptorResult descriptorResult = resolutionResult.descriptors.get(version);
221 if (descriptorResult != null) {
222 d = d.setArtifact(descriptorResult.getArtifact());
223
224 int cycleEntry = find(context.parents, d.getArtifact());
225 if (cycleEntry >= 0) {
226 results.addCycle(context.parents, cycleEntry, d);
227 DependencyNode cycleNode = context.parents.get(cycleEntry);
228 if (cycleNode.getDependency() != null) {
229 DefaultDependencyNode child = createDependencyNode(
230 relocations, preManaged, rangeResult, version, d, descriptorResult, cycleNode);
231 context.getParent().getChildren().add(child);
232 continue;
233 }
234 }
235
236 if (!descriptorResult.getRelocations().isEmpty()) {
237 boolean disableVersionManagementSubsequently =
238 originalArtifact.getGroupId().equals(d.getArtifact().getGroupId())
239 && originalArtifact
240 .getArtifactId()
241 .equals(d.getArtifact().getArtifactId());
242
243 PremanagedDependency premanagedDependency = PremanagedDependency.create(
244 context.depManager, d, disableVersionManagementSubsequently, args.premanagedState);
245 DependencyProcessingContext relocatedContext = new DependencyProcessingContext(
246 context.depSelector,
247 context.depManager,
248 context.depTraverser,
249 context.verFilter,
250 context.trace,
251 context.repositories,
252 descriptorResult.getManagedDependencies(),
253 context.parents,
254 d,
255 premanagedDependency);
256
257 if (!filter(relocatedContext)) {
258 relocatedContext.withDependency(premanagedDependency.getManagedDependency());
259 resolveArtifactDescriptorAsync(args, relocatedContext, results);
260 processDependency(
261 args,
262 results,
263 relocatedContext,
264 descriptorResult.getRelocations(),
265 disableVersionManagementSubsequently);
266 }
267
268 return;
269 } else {
270 d = args.pool.intern(d.setArtifact(args.pool.intern(d.getArtifact())));
271
272 List<RemoteRepository> repos =
273 getRemoteRepositories(rangeResult.getRepository(version), context.repositories);
274
275 DefaultDependencyNode child = createDependencyNode(
276 relocations,
277 preManaged,
278 rangeResult,
279 version,
280 d,
281 descriptorResult.getAliases(),
282 repos,
283 args.request.getRequestContext());
284
285 context.getParent().getChildren().add(child);
286
287 boolean recurse =
288 traverse && !descriptorResult.getDependencies().isEmpty();
289 DependencyProcessingContext parentContext = context.withDependency(d);
290 if (recurse) {
291 doRecurse(args, parentContext, descriptorResult, child, results, disableVersionManagement);
292 } else if (!args.skipper.skipResolution(child, parentContext.parents)) {
293 List<DependencyNode> parents = new ArrayList<>(parentContext.parents.size() + 1);
294 parents.addAll(parentContext.parents);
295 parents.add(child);
296 args.skipper.cache(child, parents);
297 }
298 }
299 } else {
300 List<RemoteRepository> repos =
301 getRemoteRepositories(rangeResult.getRepository(version), context.repositories);
302 DefaultDependencyNode child = createDependencyNode(
303 relocations,
304 preManaged,
305 rangeResult,
306 version,
307 d,
308 null,
309 repos,
310 args.request.getRequestContext());
311 context.getParent().getChildren().add(child);
312 }
313 }
314 }
315
316 @SuppressWarnings("checkstyle:parameternumber")
317 private void doRecurse(
318 Args args,
319 DependencyProcessingContext parentContext,
320 ArtifactDescriptorResult descriptorResult,
321 DefaultDependencyNode child,
322 Results results,
323 boolean disableVersionManagement) {
324 DefaultDependencyCollectionContext context = args.collectionContext;
325 context.set(parentContext.dependency, descriptorResult.getManagedDependencies());
326
327 DependencySelector childSelector =
328 parentContext.depSelector != null ? parentContext.depSelector.deriveChildSelector(context) : null;
329 DependencyManager childManager =
330 parentContext.depManager != null ? parentContext.depManager.deriveChildManager(context) : null;
331 DependencyTraverser childTraverser =
332 parentContext.depTraverser != null ? parentContext.depTraverser.deriveChildTraverser(context) : null;
333 VersionFilter childFilter =
334 parentContext.verFilter != null ? parentContext.verFilter.deriveChildFilter(context) : null;
335
336 final List<RemoteRepository> childRepos = args.ignoreRepos
337 ? parentContext.repositories
338 : remoteRepositoryManager.aggregateRepositories(
339 args.session, parentContext.repositories, descriptorResult.getRepositories(), true);
340
341 Object key = args.pool.toKey(
342 parentContext.dependency.getArtifact(),
343 childRepos,
344 childSelector,
345 childManager,
346 childTraverser,
347 childFilter);
348
349 List<DependencyNode> children = args.pool.getChildren(key);
350 if (children == null) {
351 boolean skipResolution = args.skipper.skipResolution(child, parentContext.parents);
352 if (!skipResolution) {
353 List<DependencyNode> parents = new ArrayList<>(parentContext.parents.size() + 1);
354 parents.addAll(parentContext.parents);
355 parents.add(child);
356 for (Dependency dependency : descriptorResult.getDependencies()) {
357 RequestTrace childTrace = collectStepTrace(
358 parentContext.trace, args.request.getRequestContext(), parents, dependency);
359 PremanagedDependency premanagedDependency = PremanagedDependency.create(
360 childManager, dependency, disableVersionManagement, args.premanagedState);
361 DependencyProcessingContext processingContext = new DependencyProcessingContext(
362 childSelector,
363 childManager,
364 childTraverser,
365 childFilter,
366 childTrace,
367 childRepos,
368 descriptorResult.getManagedDependencies(),
369 parents,
370 dependency,
371 premanagedDependency);
372 if (!filter(processingContext)) {
373
374 processingContext.withDependency(processingContext.premanagedDependency.getManagedDependency());
375 resolveArtifactDescriptorAsync(args, processingContext, results);
376 args.dependencyProcessingQueue.add(processingContext);
377 }
378 }
379 args.pool.putChildren(key, child.getChildren());
380 args.skipper.cache(child, parents);
381 }
382 } else {
383 child.setChildren(children);
384 }
385 }
386
387 private boolean filter(DependencyProcessingContext context) {
388 return context.depSelector != null && !context.depSelector.selectDependency(context.dependency);
389 }
390
391 private void resolveArtifactDescriptorAsync(Args args, DependencyProcessingContext context, Results results) {
392 Dependency dependency = context.dependency;
393 args.resolver.resolveDescriptors(dependency.getArtifact(), () -> {
394 VersionRangeRequest rangeRequest = createVersionRangeRequest(
395 args.request.getRequestContext(), context.trace, context.repositories, dependency);
396 VersionRangeResult rangeResult = cachedResolveRangeResult(rangeRequest, args.pool, args.session);
397 List<? extends Version> versions =
398 filterVersions(dependency, rangeResult, context.verFilter, args.versionContext);
399
400
401 Collections.reverse(versions);
402
403 Map<Version, ArtifactDescriptorResult> descriptors = new ConcurrentHashMap<>(versions.size());
404 Stream<? extends Version> stream = versions.size() > 1 ? versions.parallelStream() : versions.stream();
405 stream.forEach(version -> Optional.ofNullable(
406 resolveDescriptorForVersion(args, context, results, dependency, version))
407 .ifPresent(r -> descriptors.put(version, r)));
408
409 DescriptorResolutionResult resolutionResult =
410 new DescriptorResolutionResult(dependency.getArtifact(), rangeResult);
411
412 versions.forEach(version -> resolutionResult.descriptors.put(version, descriptors.get(version)));
413
414 resolutionResult.flatten().forEach(dr -> args.resolver.cacheVersionRangeDescriptor(dr.artifact, dr));
415
416 return resolutionResult;
417 });
418 }
419
420 private ArtifactDescriptorResult resolveDescriptorForVersion(
421 Args args, DependencyProcessingContext context, Results results, Dependency dependency, Version version) {
422 Artifact original = dependency.getArtifact();
423 Artifact newArtifact = new DefaultArtifact(
424 original.getGroupId(),
425 original.getArtifactId(),
426 original.getClassifier(),
427 original.getExtension(),
428 version.toString(),
429 original.getProperties(),
430 (ArtifactType) null);
431 Dependency newDependency =
432 new Dependency(newArtifact, dependency.getScope(), dependency.isOptional(), dependency.getExclusions());
433 DependencyProcessingContext newContext = context.copy();
434
435 ArtifactDescriptorRequest descriptorRequest = createArtifactDescriptorRequest(
436 args.request.getRequestContext(), context.trace, newContext.repositories, newDependency);
437 return isLackingDescriptor(newArtifact)
438 ? new ArtifactDescriptorResult(descriptorRequest)
439 : resolveCachedArtifactDescriptor(
440 args.pool, descriptorRequest, args.session, newContext.withDependency(newDependency), results);
441 }
442
443 private ArtifactDescriptorResult resolveCachedArtifactDescriptor(
444 DataPool pool,
445 ArtifactDescriptorRequest descriptorRequest,
446 RepositorySystemSession session,
447 DependencyProcessingContext context,
448 Results results) {
449 Object key = pool.toKey(descriptorRequest);
450 ArtifactDescriptorResult descriptorResult = pool.getDescriptor(key, descriptorRequest);
451 if (descriptorResult == null) {
452 try {
453 descriptorResult = descriptorReader.readArtifactDescriptor(session, descriptorRequest);
454 pool.putDescriptor(key, descriptorResult);
455 } catch (ArtifactDescriptorException e) {
456 results.addException(context.dependency, e, context.parents);
457 pool.putDescriptor(key, e);
458 return null;
459 }
460
461 } else if (descriptorResult == DataPool.NO_DESCRIPTOR) {
462 return null;
463 }
464
465 return descriptorResult;
466 }
467
468 static class ParallelDescriptorResolver implements Closeable {
469 private final ExecutorService executorService;
470
471
472
473
474 private final Map<String, Future<DescriptorResolutionResult>> results = new ConcurrentHashMap<>(256);
475
476 ParallelDescriptorResolver(int threads) {
477 this.executorService = ExecutorUtils.threadPool(threads, getClass().getSimpleName() + "-");
478 }
479
480 void resolveDescriptors(Artifact artifact, Callable<DescriptorResolutionResult> callable) {
481 results.computeIfAbsent(ArtifactIdUtils.toId(artifact), key -> this.executorService.submit(callable));
482 }
483
484 void cacheVersionRangeDescriptor(Artifact artifact, DescriptorResolutionResult resolutionResult) {
485 results.computeIfAbsent(ArtifactIdUtils.toId(artifact), key -> new DoneFuture<>(resolutionResult));
486 }
487
488 Future<DescriptorResolutionResult> find(Artifact artifact) {
489 return results.get(ArtifactIdUtils.toId(artifact));
490 }
491
492 @Override
493 public void close() {
494 executorService.shutdown();
495 }
496 }
497
498 static class DoneFuture<V> implements Future<V> {
499 private final V v;
500
501 DoneFuture(V v) {
502 this.v = v;
503 }
504
505 @Override
506 public boolean cancel(boolean mayInterruptIfRunning) {
507 return false;
508 }
509
510 @Override
511 public boolean isCancelled() {
512 return false;
513 }
514
515 @Override
516 public boolean isDone() {
517 return true;
518 }
519
520 @Override
521 public V get() throws InterruptedException, ExecutionException {
522 return v;
523 }
524
525 @Override
526 public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
527 return v;
528 }
529 }
530
531 static class DescriptorResolutionResult {
532 Artifact artifact;
533
534 VersionRangeResult rangeResult;
535
536 Map<Version, ArtifactDescriptorResult> descriptors;
537
538 DescriptorResolutionResult(Artifact artifact, VersionRangeResult rangeResult) {
539 this.artifact = artifact;
540 this.rangeResult = rangeResult;
541 this.descriptors = new LinkedHashMap<>(rangeResult.getVersions().size());
542 }
543
544 DescriptorResolutionResult(
545 VersionRangeResult rangeResult, Version version, ArtifactDescriptorResult descriptor) {
546
547
548
549
550
551
552
553 this(descriptor.getRequest().getArtifact(), rangeResult);
554 this.descriptors.put(version, descriptor);
555 }
556
557 List<DescriptorResolutionResult> flatten() {
558 if (descriptors.size() > 1) {
559 return descriptors.entrySet().stream()
560 .map(e -> new DescriptorResolutionResult(rangeResult, e.getKey(), e.getValue()))
561 .collect(Collectors.toList());
562 } else {
563 return Collections.emptyList();
564 }
565 }
566 }
567
568 static class Args {
569
570 final RepositorySystemSession session;
571
572 final boolean ignoreRepos;
573
574 final boolean premanagedState;
575
576 final DataPool pool;
577
578 final Queue<DependencyProcessingContext> dependencyProcessingQueue = new ArrayDeque<>(128);
579
580 final DefaultDependencyCollectionContext collectionContext;
581
582 final DefaultVersionFilterContext versionContext;
583
584 final CollectRequest request;
585
586 final DependencyResolutionSkipper skipper;
587
588 final ParallelDescriptorResolver resolver;
589
590 Args(
591 RepositorySystemSession session,
592 DataPool pool,
593 DefaultDependencyCollectionContext collectionContext,
594 DefaultVersionFilterContext versionContext,
595 CollectRequest request,
596 DependencyResolutionSkipper skipper,
597 ParallelDescriptorResolver resolver) {
598 this.session = session;
599 this.request = request;
600 this.ignoreRepos = session.isIgnoreArtifactDescriptorRepositories();
601 this.premanagedState = ConfigUtils.getBoolean(session, false, DependencyManagerUtils.CONFIG_PROP_VERBOSE);
602 this.pool = pool;
603 this.collectionContext = collectionContext;
604 this.versionContext = versionContext;
605 this.skipper = skipper;
606 this.resolver = resolver;
607 }
608 }
609 }