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 RequestTrace childTrace =
201 collectStepTrace(trace, args.request.getRequestContext(), parents, dependency);
202 DependencyProcessingContext processingContext = new DependencyProcessingContext(
203 rootDepSelector,
204 rootDepManager,
205 rootDepTraverser,
206 rootVerFilter,
207 childTrace,
208 repositories,
209 managedDependencies,
210 parents,
211 dependency,
212 PremanagedDependency.create(rootDepManager, dependency, false, args.premanagedState));
213 if (!filter(processingContext)) {
214 processingContext.withDependency(processingContext.premanagedDependency.getManagedDependency());
215 resolveArtifactDescriptorAsync(args, processingContext, results);
216 args.dependencyProcessingQueue.add(processingContext);
217 }
218 }
219
220 while (!args.dependencyProcessingQueue.isEmpty()) {
221 processDependency(
222 args, results, args.dependencyProcessingQueue.remove(), Collections.emptyList(), false);
223 }
224
225 if (args.interruptedException.get() != null) {
226 throw new DependencyCollectionException(
227 results.getResult(), "Collection interrupted", args.interruptedException.get());
228 }
229 }
230 }
231
232 @SuppressWarnings("checkstyle:parameternumber")
233 private void processDependency(
234 Args args,
235 Results results,
236 DependencyProcessingContext context,
237 List<Artifact> relocations,
238 boolean disableVersionManagement) {
239 if (Thread.interrupted()) {
240 args.interruptedException.set(new InterruptedException());
241 }
242 if (args.interruptedException.get() != null) {
243 return;
244 }
245 Dependency dependency = context.dependency;
246 PremanagedDependency preManaged = context.premanagedDependency;
247
248 boolean noDescriptor = isLackingDescriptor(args.session, dependency.getArtifact());
249 boolean traverse =
250 !noDescriptor && (context.depTraverser == null || context.depTraverser.traverseDependency(dependency));
251
252 Future<DescriptorResolutionResult> resolutionResultFuture = args.resolver.find(dependency.getArtifact());
253 DescriptorResolutionResult resolutionResult;
254 VersionRangeResult rangeResult;
255 try {
256 resolutionResult = resolutionResultFuture.get();
257 rangeResult = resolutionResult.rangeResult;
258 } catch (Exception e) {
259 results.addException(dependency, e, context.parents);
260 return;
261 }
262
263 Set<Version> versions = resolutionResult.descriptors.keySet();
264 for (Version version : versions) {
265 Artifact originalArtifact = dependency.getArtifact().setVersion(version.toString());
266 Dependency d = dependency.setArtifact(originalArtifact);
267
268 final ArtifactDescriptorResult descriptorResult = resolutionResult.descriptors.get(version);
269 if (descriptorResult != null) {
270 d = d.setArtifact(descriptorResult.getArtifact());
271
272 int cycleEntry = find(context.parents, d.getArtifact());
273 if (cycleEntry >= 0) {
274 results.addCycle(context.parents, cycleEntry, d);
275 DependencyNode cycleNode = context.parents.get(cycleEntry);
276 if (cycleNode.getDependency() != null) {
277 DefaultDependencyNode child = createDependencyNode(
278 relocations, preManaged, rangeResult, version, d, descriptorResult, cycleNode);
279 context.getParent().getChildren().add(child);
280 continue;
281 }
282 }
283
284 if (!descriptorResult.getRelocations().isEmpty()) {
285 boolean disableVersionManagementSubsequently =
286 originalArtifact.getGroupId().equals(d.getArtifact().getGroupId())
287 && originalArtifact
288 .getArtifactId()
289 .equals(d.getArtifact().getArtifactId());
290
291 PremanagedDependency premanagedDependency = PremanagedDependency.create(
292 context.depManager, d, disableVersionManagementSubsequently, args.premanagedState);
293 DependencyProcessingContext relocatedContext = new DependencyProcessingContext(
294 context.depSelector,
295 context.depManager,
296 context.depTraverser,
297 context.verFilter,
298 context.trace,
299 context.repositories,
300 descriptorResult.getManagedDependencies(),
301 context.parents,
302 d,
303 premanagedDependency);
304
305 if (!filter(relocatedContext)) {
306 relocatedContext.withDependency(premanagedDependency.getManagedDependency());
307 resolveArtifactDescriptorAsync(args, relocatedContext, results);
308 processDependency(
309 args,
310 results,
311 relocatedContext,
312 descriptorResult.getRelocations(),
313 disableVersionManagementSubsequently);
314 }
315
316 return;
317 } else {
318 d = args.pool.intern(d.setArtifact(args.pool.intern(d.getArtifact())));
319
320 List<RemoteRepository> repos =
321 getRemoteRepositories(rangeResult.getRepository(version), context.repositories);
322
323 DefaultDependencyNode child = createDependencyNode(
324 relocations,
325 preManaged,
326 rangeResult,
327 version,
328 d,
329 descriptorResult.getAliases(),
330 repos,
331 args.request.getRequestContext());
332
333 context.getParent().getChildren().add(child);
334
335 boolean recurse =
336 traverse && !descriptorResult.getDependencies().isEmpty();
337 DependencyProcessingContext parentContext = context.withDependency(d);
338 if (recurse) {
339 doRecurse(args, parentContext, descriptorResult, child, results, disableVersionManagement);
340 } else if (!args.skipper.skipResolution(child, parentContext.parents)) {
341 List<DependencyNode> parents = new ArrayList<>(parentContext.parents.size() + 1);
342 parents.addAll(parentContext.parents);
343 parents.add(child);
344 args.skipper.cache(child, parents);
345 }
346 }
347 } else {
348 List<RemoteRepository> repos =
349 getRemoteRepositories(rangeResult.getRepository(version), context.repositories);
350 DefaultDependencyNode child = createDependencyNode(
351 relocations,
352 preManaged,
353 rangeResult,
354 version,
355 d,
356 null,
357 repos,
358 args.request.getRequestContext());
359 context.getParent().getChildren().add(child);
360 }
361 }
362 }
363
364 @SuppressWarnings("checkstyle:parameternumber")
365 private void doRecurse(
366 Args args,
367 DependencyProcessingContext parentContext,
368 ArtifactDescriptorResult descriptorResult,
369 DefaultDependencyNode child,
370 Results results,
371 boolean disableVersionManagement) {
372 DefaultDependencyCollectionContext context = args.collectionContext.get();
373 args.collectionContext.compareAndSet(
374 context, context.set(parentContext.dependency, descriptorResult.getManagedDependencies()));
375 context = args.collectionContext.get();
376
377 DependencySelector childSelector =
378 parentContext.depSelector != null ? parentContext.depSelector.deriveChildSelector(context) : null;
379 DependencyManager childManager =
380 parentContext.depManager != null ? parentContext.depManager.deriveChildManager(context) : null;
381 DependencyTraverser childTraverser =
382 parentContext.depTraverser != null ? parentContext.depTraverser.deriveChildTraverser(context) : null;
383 VersionFilter childFilter =
384 parentContext.verFilter != null ? parentContext.verFilter.deriveChildFilter(context) : null;
385
386 final List<RemoteRepository> childRepos = args.ignoreRepos
387 ? parentContext.repositories
388 : remoteRepositoryManager.aggregateRepositories(
389 args.session, parentContext.repositories, descriptorResult.getRepositories(), true);
390
391 Object key = args.pool.toKey(
392 parentContext.dependency.getArtifact(),
393 childRepos,
394 childSelector,
395 childManager,
396 childTraverser,
397 childFilter);
398
399 List<DependencyNode> children = args.pool.getChildren(key);
400 if (children == null) {
401 boolean skipResolution = args.skipper.skipResolution(child, parentContext.parents);
402 if (!skipResolution) {
403 List<DependencyNode> parents = new ArrayList<>(parentContext.parents.size() + 1);
404 parents.addAll(parentContext.parents);
405 parents.add(child);
406 for (Dependency dependency : descriptorResult.getDependencies()) {
407 RequestTrace childTrace = collectStepTrace(
408 parentContext.trace, args.request.getRequestContext(), parents, dependency);
409 PremanagedDependency premanagedDependency = PremanagedDependency.create(
410 childManager, dependency, disableVersionManagement, args.premanagedState);
411 DependencyProcessingContext processingContext = new DependencyProcessingContext(
412 childSelector,
413 childManager,
414 childTraverser,
415 childFilter,
416 childTrace,
417 childRepos,
418 descriptorResult.getManagedDependencies(),
419 parents,
420 dependency,
421 premanagedDependency);
422 if (!filter(processingContext)) {
423
424 processingContext.withDependency(processingContext.premanagedDependency.getManagedDependency());
425 resolveArtifactDescriptorAsync(args, processingContext, results);
426 args.dependencyProcessingQueue.add(processingContext);
427 }
428 }
429 args.pool.putChildren(key, child.getChildren());
430 args.skipper.cache(child, parents);
431 }
432 } else {
433 child.setChildren(children);
434 }
435 }
436
437 private boolean filter(DependencyProcessingContext context) {
438 return context.depSelector != null && !context.depSelector.selectDependency(context.dependency);
439 }
440
441 private void resolveArtifactDescriptorAsync(Args args, DependencyProcessingContext context, Results results) {
442 Dependency dependency = context.dependency;
443 args.resolver.resolveDescriptors(dependency.getArtifact(), () -> {
444 VersionRangeRequest rangeRequest = createVersionRangeRequest(
445 args.request.getRequestContext(), context.trace, context.repositories, dependency);
446 VersionRangeResult rangeResult = cachedResolveRangeResult(rangeRequest, args.pool, args.session);
447 List<? extends Version> versions =
448 filterVersions(dependency, rangeResult, context.verFilter, args.versionContext);
449
450
451 Collections.reverse(versions);
452
453 Map<Version, ArtifactDescriptorResult> descriptors = new ConcurrentHashMap<>(versions.size());
454 Stream<? extends Version> stream = versions.size() > 1 ? versions.parallelStream() : versions.stream();
455 stream.forEach(version -> Optional.ofNullable(
456 resolveDescriptorForVersion(args, context, results, dependency, version))
457 .ifPresent(r -> descriptors.put(version, r)));
458
459 DescriptorResolutionResult resolutionResult =
460 new DescriptorResolutionResult(dependency.getArtifact(), rangeResult);
461
462 versions.forEach(version -> resolutionResult.descriptors.put(version, descriptors.get(version)));
463
464 resolutionResult.flatten().forEach(dr -> args.resolver.cacheVersionRangeDescriptor(dr.artifact, dr));
465
466 return resolutionResult;
467 });
468 }
469
470 private ArtifactDescriptorResult resolveDescriptorForVersion(
471 Args args, DependencyProcessingContext context, Results results, Dependency dependency, Version version) {
472 Artifact original = dependency.getArtifact();
473 Artifact newArtifact = original.setVersion(version.toString());
474 Dependency newDependency =
475 new Dependency(newArtifact, dependency.getScope(), dependency.isOptional(), dependency.getExclusions());
476 DependencyProcessingContext newContext = context.copy();
477
478 ArtifactDescriptorRequest descriptorRequest = createArtifactDescriptorRequest(
479 args.request.getRequestContext(), context.trace, newContext.repositories, newDependency);
480 return isLackingDescriptor(args.session, newArtifact)
481 ? new ArtifactDescriptorResult(descriptorRequest)
482 : resolveCachedArtifactDescriptor(
483 args.pool,
484 descriptorRequest,
485 args.session,
486 newContext.withDependency(newDependency).dependency,
487 results,
488 context.parents);
489 }
490
491 static class ParallelDescriptorResolver implements Closeable {
492 private final SmartExecutor smartExecutor;
493
494
495
496
497 private final Map<String, Future<DescriptorResolutionResult>> results = new ConcurrentHashMap<>(256);
498
499 ParallelDescriptorResolver(SmartExecutor smartExecutor) {
500 this.smartExecutor = smartExecutor;
501 }
502
503 void resolveDescriptors(Artifact artifact, Callable<DescriptorResolutionResult> callable) {
504 results.computeIfAbsent(ArtifactIdUtils.toId(artifact), key -> smartExecutor.submit(callable));
505 }
506
507 void cacheVersionRangeDescriptor(Artifact artifact, DescriptorResolutionResult resolutionResult) {
508 results.computeIfAbsent(
509 ArtifactIdUtils.toId(artifact), key -> CompletableFuture.completedFuture(resolutionResult));
510 }
511
512 Future<DescriptorResolutionResult> find(Artifact artifact) {
513 return results.get(ArtifactIdUtils.toId(artifact));
514 }
515
516 @Override
517 public void close() {
518 smartExecutor.close();
519 }
520 }
521
522 static class DescriptorResolutionResult {
523 Artifact artifact;
524
525 VersionRangeResult rangeResult;
526
527 Map<Version, ArtifactDescriptorResult> descriptors;
528
529 DescriptorResolutionResult(Artifact artifact, VersionRangeResult rangeResult) {
530 this.artifact = artifact;
531 this.rangeResult = rangeResult;
532 this.descriptors = new LinkedHashMap<>(rangeResult.getVersions().size());
533 }
534
535 DescriptorResolutionResult(
536 VersionRangeResult rangeResult, Version version, ArtifactDescriptorResult descriptor) {
537
538
539
540
541
542
543
544 this(descriptor.getRequest().getArtifact(), rangeResult);
545 this.descriptors.put(version, descriptor);
546 }
547
548 List<DescriptorResolutionResult> flatten() {
549 if (descriptors.size() > 1) {
550 return descriptors.entrySet().stream()
551 .map(e -> new DescriptorResolutionResult(rangeResult, e.getKey(), e.getValue()))
552 .collect(Collectors.toList());
553 } else {
554 return Collections.emptyList();
555 }
556 }
557 }
558
559 static class Args {
560
561 final RepositorySystemSession session;
562
563 final boolean ignoreRepos;
564
565 final boolean premanagedState;
566
567 final DataPool pool;
568
569 final Queue<DependencyProcessingContext> dependencyProcessingQueue = new ArrayDeque<>(128);
570
571 final AtomicReference<DefaultDependencyCollectionContext> collectionContext;
572
573 final DefaultVersionFilterContext versionContext;
574
575 final CollectRequest request;
576
577 final DependencyResolutionSkipper skipper;
578
579 final ParallelDescriptorResolver resolver;
580
581 final AtomicReference<InterruptedException> interruptedException;
582
583 Args(
584 RepositorySystemSession session,
585 DataPool pool,
586 DefaultDependencyCollectionContext collectionContext,
587 DefaultVersionFilterContext versionContext,
588 CollectRequest request,
589 DependencyResolutionSkipper skipper,
590 ParallelDescriptorResolver resolver) {
591 this.session = session;
592 this.request = request;
593 this.ignoreRepos = session.isIgnoreArtifactDescriptorRepositories();
594 this.premanagedState = ConfigUtils.getBoolean(session, false, DependencyManagerUtils.CONFIG_PROP_VERBOSE);
595 this.pool = pool;
596 this.collectionContext = new AtomicReference<>(collectionContext);
597 this.versionContext = versionContext;
598 this.skipper = skipper;
599 this.resolver = resolver;
600 this.interruptedException = new AtomicReference<>(null);
601 }
602 }
603 }