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 DependencyManager childManager =
381 parentContext.depManager != null ? parentContext.depManager.deriveChildManager(context) : null;
382 DependencyTraverser childTraverser =
383 parentContext.depTraverser != null ? parentContext.depTraverser.deriveChildTraverser(context) : null;
384 VersionFilter childFilter =
385 parentContext.verFilter != null ? parentContext.verFilter.deriveChildFilter(context) : null;
386
387 final List<RemoteRepository> childRepos = args.ignoreRepos
388 ? parentContext.repositories
389 : remoteRepositoryManager.aggregateRepositories(
390 args.session, parentContext.repositories, descriptorResult.getRepositories(), true);
391
392 Object key = args.pool.toKey(
393 parentContext.dependency.getArtifact(),
394 childRepos,
395 childSelector,
396 childManager,
397 childTraverser,
398 childFilter);
399
400 List<DependencyNode> children = args.pool.getChildren(key);
401 if (children == null) {
402 boolean skipResolution = args.skipper.skipResolution(child, parentContext.parents);
403 if (!skipResolution) {
404 List<DependencyNode> parents = new ArrayList<>(parentContext.parents.size() + 1);
405 parents.addAll(parentContext.parents);
406 parents.add(child);
407 for (Dependency dependency : descriptorResult.getDependencies()) {
408 if (childSelector != null && !childSelector.selectDependency(dependency)) {
409 continue;
410 }
411 RequestTrace childTrace = collectStepTrace(
412 parentContext.trace, args.request.getRequestContext(), parents, dependency);
413 PremanagedDependency premanagedDependency = PremanagedDependency.create(
414 childManager, dependency, disableVersionManagement, args.premanagedState);
415 DependencyProcessingContext processingContext = new DependencyProcessingContext(
416 childSelector,
417 childManager,
418 childTraverser,
419 childFilter,
420 childTrace,
421 childRepos,
422 descriptorResult.getManagedDependencies(),
423 parents,
424 dependency,
425 premanagedDependency);
426
427 processingContext.withDependency(processingContext.premanagedDependency.getManagedDependency());
428 resolveArtifactDescriptorAsync(args, processingContext, results);
429 args.dependencyProcessingQueue.add(processingContext);
430 }
431 args.pool.putChildren(key, child.getChildren());
432 args.skipper.cache(child, parents);
433 }
434 } else {
435 child.setChildren(children);
436 }
437 }
438
439 private void resolveArtifactDescriptorAsync(Args args, DependencyProcessingContext context, Results results) {
440 Dependency dependency = context.dependency;
441 args.resolver.resolveDescriptors(dependency.getArtifact(), () -> {
442 VersionRangeRequest rangeRequest = createVersionRangeRequest(
443 args.request.getRequestContext(), context.trace, context.repositories, dependency);
444 VersionRangeResult rangeResult = cachedResolveRangeResult(rangeRequest, args.pool, args.session);
445 List<? extends Version> versions =
446 filterVersions(dependency, rangeResult, context.verFilter, args.versionContext);
447
448
449 Collections.reverse(versions);
450
451 Map<Version, ArtifactDescriptorResult> descriptors = new ConcurrentHashMap<>(versions.size());
452 Stream<? extends Version> stream = versions.size() > 1 ? versions.parallelStream() : versions.stream();
453 stream.forEach(version -> Optional.ofNullable(
454 resolveDescriptorForVersion(args, context, results, dependency, version))
455 .ifPresent(r -> descriptors.put(version, r)));
456
457 DescriptorResolutionResult resolutionResult =
458 new DescriptorResolutionResult(dependency.getArtifact(), rangeResult);
459
460 versions.forEach(version -> resolutionResult.descriptors.put(version, descriptors.get(version)));
461
462 resolutionResult.flatten().forEach(dr -> args.resolver.cacheVersionRangeDescriptor(dr.artifact, dr));
463
464 return resolutionResult;
465 });
466 }
467
468 private ArtifactDescriptorResult resolveDescriptorForVersion(
469 Args args, DependencyProcessingContext context, Results results, Dependency dependency, Version version) {
470 Artifact original = dependency.getArtifact();
471 Artifact newArtifact = original.setVersion(version.toString());
472 Dependency newDependency =
473 new Dependency(newArtifact, dependency.getScope(), dependency.isOptional(), dependency.getExclusions());
474 DependencyProcessingContext newContext = context.copy();
475
476 ArtifactDescriptorRequest descriptorRequest = createArtifactDescriptorRequest(
477 args.request.getRequestContext(), context.trace, newContext.repositories, newDependency);
478 return isLackingDescriptor(args.session, newArtifact)
479 ? new ArtifactDescriptorResult(descriptorRequest)
480 : resolveCachedArtifactDescriptor(
481 args.pool,
482 descriptorRequest,
483 args.session,
484 newContext.withDependency(newDependency).dependency,
485 results,
486 context.parents);
487 }
488
489 static class ParallelDescriptorResolver implements Closeable {
490 private final SmartExecutor smartExecutor;
491
492
493
494
495 private final Map<String, Future<DescriptorResolutionResult>> results = new ConcurrentHashMap<>(256);
496
497 ParallelDescriptorResolver(SmartExecutor smartExecutor) {
498 this.smartExecutor = smartExecutor;
499 }
500
501 void resolveDescriptors(Artifact artifact, Callable<DescriptorResolutionResult> callable) {
502 results.computeIfAbsent(ArtifactIdUtils.toId(artifact), key -> smartExecutor.submit(callable));
503 }
504
505 void cacheVersionRangeDescriptor(Artifact artifact, DescriptorResolutionResult resolutionResult) {
506 results.computeIfAbsent(
507 ArtifactIdUtils.toId(artifact), key -> CompletableFuture.completedFuture(resolutionResult));
508 }
509
510 Future<DescriptorResolutionResult> find(Artifact artifact) {
511 return results.get(ArtifactIdUtils.toId(artifact));
512 }
513
514 @Override
515 public void close() {
516 smartExecutor.close();
517 }
518 }
519
520 static class DescriptorResolutionResult {
521 Artifact artifact;
522
523 VersionRangeResult rangeResult;
524
525 Map<Version, ArtifactDescriptorResult> descriptors;
526
527 DescriptorResolutionResult(Artifact artifact, VersionRangeResult rangeResult) {
528 this.artifact = artifact;
529 this.rangeResult = rangeResult;
530 this.descriptors = new LinkedHashMap<>(rangeResult.getVersions().size());
531 }
532
533 DescriptorResolutionResult(
534 VersionRangeResult rangeResult, Version version, ArtifactDescriptorResult descriptor) {
535
536
537
538
539
540
541
542 this(descriptor.getRequest().getArtifact(), rangeResult);
543 this.descriptors.put(version, descriptor);
544 }
545
546 List<DescriptorResolutionResult> flatten() {
547 if (descriptors.size() > 1) {
548 return descriptors.entrySet().stream()
549 .map(e -> new DescriptorResolutionResult(rangeResult, e.getKey(), e.getValue()))
550 .collect(Collectors.toList());
551 } else {
552 return Collections.emptyList();
553 }
554 }
555 }
556
557 static class Args {
558
559 final RepositorySystemSession session;
560
561 final boolean ignoreRepos;
562
563 final boolean premanagedState;
564
565 final DataPool pool;
566
567 final Queue<DependencyProcessingContext> dependencyProcessingQueue = new ArrayDeque<>(128);
568
569 final AtomicReference<DefaultDependencyCollectionContext> collectionContext;
570
571 final DefaultVersionFilterContext versionContext;
572
573 final CollectRequest request;
574
575 final DependencyResolutionSkipper skipper;
576
577 final ParallelDescriptorResolver resolver;
578
579 final AtomicReference<InterruptedException> interruptedException;
580
581 Args(
582 RepositorySystemSession session,
583 DataPool pool,
584 DefaultDependencyCollectionContext collectionContext,
585 DefaultVersionFilterContext versionContext,
586 CollectRequest request,
587 DependencyResolutionSkipper skipper,
588 ParallelDescriptorResolver resolver) {
589 this.session = session;
590 this.request = request;
591 this.ignoreRepos = session.isIgnoreArtifactDescriptorRepositories();
592 this.premanagedState = ConfigUtils.getBoolean(session, false, DependencyManagerUtils.CONFIG_PROP_VERBOSE);
593 this.pool = pool;
594 this.collectionContext = new AtomicReference<>(collectionContext);
595 this.versionContext = versionContext;
596 this.skipper = skipper;
597 this.resolver = resolver;
598 this.interruptedException = new AtomicReference<>(null);
599 }
600 }
601 }