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