001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.eclipse.aether.internal.impl.collect.bf;
020
021import javax.inject.Inject;
022import javax.inject.Named;
023import javax.inject.Singleton;
024
025import java.io.Closeable;
026import java.util.ArrayDeque;
027import java.util.ArrayList;
028import java.util.Collections;
029import java.util.LinkedHashMap;
030import java.util.List;
031import java.util.Map;
032import java.util.Optional;
033import java.util.Queue;
034import java.util.Set;
035import java.util.concurrent.Callable;
036import java.util.concurrent.ConcurrentHashMap;
037import java.util.concurrent.ExecutionException;
038import java.util.concurrent.ExecutorService;
039import java.util.concurrent.Future;
040import java.util.concurrent.TimeUnit;
041import java.util.concurrent.TimeoutException;
042import java.util.concurrent.atomic.AtomicReference;
043import java.util.stream.Collectors;
044import java.util.stream.Stream;
045
046import org.eclipse.aether.RepositorySystemSession;
047import org.eclipse.aether.RequestTrace;
048import org.eclipse.aether.artifact.Artifact;
049import org.eclipse.aether.artifact.ArtifactType;
050import org.eclipse.aether.artifact.DefaultArtifact;
051import org.eclipse.aether.collection.CollectRequest;
052import org.eclipse.aether.collection.DependencyCollectionException;
053import org.eclipse.aether.collection.DependencyManager;
054import org.eclipse.aether.collection.DependencySelector;
055import org.eclipse.aether.collection.DependencyTraverser;
056import org.eclipse.aether.collection.VersionFilter;
057import org.eclipse.aether.graph.DefaultDependencyNode;
058import org.eclipse.aether.graph.Dependency;
059import org.eclipse.aether.graph.DependencyNode;
060import org.eclipse.aether.impl.ArtifactDescriptorReader;
061import org.eclipse.aether.impl.RemoteRepositoryManager;
062import org.eclipse.aether.impl.VersionRangeResolver;
063import org.eclipse.aether.internal.impl.collect.DataPool;
064import org.eclipse.aether.internal.impl.collect.DefaultDependencyCollectionContext;
065import org.eclipse.aether.internal.impl.collect.DefaultDependencyCollector;
066import org.eclipse.aether.internal.impl.collect.DefaultVersionFilterContext;
067import org.eclipse.aether.internal.impl.collect.DependencyCollectorDelegate;
068import org.eclipse.aether.internal.impl.collect.PremanagedDependency;
069import org.eclipse.aether.repository.RemoteRepository;
070import org.eclipse.aether.resolution.ArtifactDescriptorException;
071import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
072import org.eclipse.aether.resolution.ArtifactDescriptorResult;
073import org.eclipse.aether.resolution.VersionRangeRequest;
074import org.eclipse.aether.resolution.VersionRangeResult;
075import org.eclipse.aether.util.ConfigUtils;
076import org.eclipse.aether.util.artifact.ArtifactIdUtils;
077import org.eclipse.aether.util.concurrency.ExecutorUtils;
078import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
079import org.eclipse.aether.version.Version;
080
081import static org.eclipse.aether.internal.impl.collect.DefaultDependencyCycle.find;
082
083/**
084 * Breadth-first {@link org.eclipse.aether.impl.DependencyCollector}
085 *
086 * @since 1.8.0
087 */
088@Singleton
089@Named(BfDependencyCollector.NAME)
090public class BfDependencyCollector extends DependencyCollectorDelegate {
091    public static final String NAME = "bf";
092
093    private static final String CONFIG_PROPS_PREFIX = DefaultDependencyCollector.CONFIG_PROPS_PREFIX + NAME + ".";
094
095    /**
096     * The key in the repository session's {@link RepositorySystemSession#getConfigProperties()
097     * configuration properties} used to store a {@link Boolean} flag controlling the resolver's skip mode.
098     *
099     * @since 1.8.0
100     * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
101     * @configurationType {@link java.lang.Boolean}
102     * @configurationDefaultValue {@link #DEFAULT_SKIPPER}
103     */
104    public static final String CONFIG_PROP_SKIPPER = CONFIG_PROPS_PREFIX + "skipper";
105
106    /**
107     * The default value for {@link #CONFIG_PROP_SKIPPER}, {@code true}.
108     *
109     * @since 1.8.0
110     */
111    public static final boolean DEFAULT_SKIPPER = true;
112
113    /**
114     * The count of threads to be used when collecting POMs in parallel.
115     *
116     * @since 1.9.0
117     * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
118     * @configurationType {@link java.lang.Integer}
119     * @configurationDefaultValue {@link #DEFAULT_THREADS}
120     */
121    public static final String CONFIG_PROP_THREADS = CONFIG_PROPS_PREFIX + "threads";
122
123    /**
124     * The default value for {@link #CONFIG_PROP_THREADS}, default value 5.
125     *
126     * @since 1.9.0
127     */
128    public static final int DEFAULT_THREADS = 5;
129
130    @Inject
131    public BfDependencyCollector(
132            RemoteRepositoryManager remoteRepositoryManager,
133            ArtifactDescriptorReader artifactDescriptorReader,
134            VersionRangeResolver versionRangeResolver) {
135        super(remoteRepositoryManager, artifactDescriptorReader, versionRangeResolver);
136    }
137
138    @SuppressWarnings("checkstyle:parameternumber")
139    @Override
140    protected void doCollectDependencies(
141            RepositorySystemSession session,
142            RequestTrace trace,
143            DataPool pool,
144            DefaultDependencyCollectionContext context,
145            DefaultVersionFilterContext versionContext,
146            CollectRequest request,
147            DependencyNode node,
148            List<RemoteRepository> repositories,
149            List<Dependency> dependencies,
150            List<Dependency> managedDependencies,
151            Results results)
152            throws DependencyCollectionException {
153        boolean useSkip = ConfigUtils.getBoolean(session, DEFAULT_SKIPPER, CONFIG_PROP_SKIPPER);
154        int nThreads = ExecutorUtils.threadCount(session, DEFAULT_THREADS, CONFIG_PROP_THREADS);
155        logger.debug("Using thread pool with {} threads to resolve descriptors.", nThreads);
156
157        if (useSkip) {
158            logger.debug("Collector skip mode enabled");
159        }
160
161        try (DependencyResolutionSkipper skipper = useSkip
162                        ? DependencyResolutionSkipper.defaultSkipper()
163                        : DependencyResolutionSkipper.neverSkipper();
164                ParallelDescriptorResolver parallelDescriptorResolver = new ParallelDescriptorResolver(nThreads)) {
165            Args args = new Args(session, pool, context, versionContext, request, skipper, parallelDescriptorResolver);
166
167            DependencySelector rootDepSelector = session.getDependencySelector() != null
168                    ? session.getDependencySelector().deriveChildSelector(context)
169                    : null;
170            DependencyManager rootDepManager = session.getDependencyManager() != null
171                    ? session.getDependencyManager().deriveChildManager(context)
172                    : null;
173            DependencyTraverser rootDepTraverser = session.getDependencyTraverser() != null
174                    ? session.getDependencyTraverser().deriveChildTraverser(context)
175                    : null;
176            VersionFilter rootVerFilter = session.getVersionFilter() != null
177                    ? session.getVersionFilter().deriveChildFilter(context)
178                    : null;
179
180            List<DependencyNode> parents = Collections.singletonList(node);
181            for (Dependency dependency : dependencies) {
182                RequestTrace childTrace =
183                        collectStepTrace(trace, args.request.getRequestContext(), parents, dependency);
184                DependencyProcessingContext processingContext = new DependencyProcessingContext(
185                        rootDepSelector,
186                        rootDepManager,
187                        rootDepTraverser,
188                        rootVerFilter,
189                        childTrace,
190                        repositories,
191                        managedDependencies,
192                        parents,
193                        dependency,
194                        PremanagedDependency.create(rootDepManager, dependency, false, args.premanagedState));
195                if (!filter(processingContext)) {
196                    processingContext.withDependency(processingContext.premanagedDependency.getManagedDependency());
197                    resolveArtifactDescriptorAsync(args, processingContext, results);
198                    args.dependencyProcessingQueue.add(processingContext);
199                }
200            }
201
202            while (!args.dependencyProcessingQueue.isEmpty()) {
203                processDependency(
204                        args, results, args.dependencyProcessingQueue.remove(), Collections.emptyList(), false);
205            }
206
207            if (args.interruptedException.get() != null) {
208                throw new DependencyCollectionException(
209                        results.getResult(), "Collection interrupted", args.interruptedException.get());
210            }
211        }
212    }
213
214    @SuppressWarnings("checkstyle:parameternumber")
215    private void processDependency(
216            Args args,
217            Results results,
218            DependencyProcessingContext context,
219            List<Artifact> relocations,
220            boolean disableVersionManagement) {
221        if (Thread.interrupted()) {
222            args.interruptedException.set(new InterruptedException());
223        }
224        if (args.interruptedException.get() != null) {
225            return;
226        }
227        Dependency dependency = context.dependency;
228        PremanagedDependency preManaged = context.premanagedDependency;
229
230        boolean noDescriptor = isLackingDescriptor(args.session, dependency.getArtifact());
231        boolean traverse =
232                !noDescriptor && (context.depTraverser == null || context.depTraverser.traverseDependency(dependency));
233
234        Future<DescriptorResolutionResult> resolutionResultFuture = args.resolver.find(dependency.getArtifact());
235        DescriptorResolutionResult resolutionResult;
236        VersionRangeResult rangeResult;
237        try {
238            resolutionResult = resolutionResultFuture.get();
239            rangeResult = resolutionResult.rangeResult;
240        } catch (Exception e) {
241            results.addException(dependency, e, context.parents);
242            return;
243        }
244
245        Set<Version> versions = resolutionResult.descriptors.keySet();
246        for (Version version : versions) {
247            Artifact originalArtifact = dependency.getArtifact().setVersion(version.toString());
248            Dependency d = dependency.setArtifact(originalArtifact);
249
250            final ArtifactDescriptorResult descriptorResult = resolutionResult.descriptors.get(version);
251            if (descriptorResult != null) {
252                d = d.setArtifact(descriptorResult.getArtifact());
253
254                int cycleEntry = find(context.parents, d.getArtifact());
255                if (cycleEntry >= 0) {
256                    results.addCycle(context.parents, cycleEntry, d);
257                    DependencyNode cycleNode = context.parents.get(cycleEntry);
258                    if (cycleNode.getDependency() != null) {
259                        DefaultDependencyNode child = createDependencyNode(
260                                relocations, preManaged, rangeResult, version, d, descriptorResult, cycleNode);
261                        context.getParent().getChildren().add(child);
262                        continue;
263                    }
264                }
265
266                if (!descriptorResult.getRelocations().isEmpty()) {
267                    boolean disableVersionManagementSubsequently =
268                            originalArtifact.getGroupId().equals(d.getArtifact().getGroupId())
269                                    && originalArtifact
270                                            .getArtifactId()
271                                            .equals(d.getArtifact().getArtifactId());
272
273                    PremanagedDependency premanagedDependency = PremanagedDependency.create(
274                            context.depManager, d, disableVersionManagementSubsequently, args.premanagedState);
275                    DependencyProcessingContext relocatedContext = new DependencyProcessingContext(
276                            context.depSelector,
277                            context.depManager,
278                            context.depTraverser,
279                            context.verFilter,
280                            context.trace,
281                            context.repositories,
282                            descriptorResult.getManagedDependencies(),
283                            context.parents,
284                            d,
285                            premanagedDependency);
286
287                    if (!filter(relocatedContext)) {
288                        relocatedContext.withDependency(premanagedDependency.getManagedDependency());
289                        resolveArtifactDescriptorAsync(args, relocatedContext, results);
290                        processDependency(
291                                args,
292                                results,
293                                relocatedContext,
294                                descriptorResult.getRelocations(),
295                                disableVersionManagementSubsequently);
296                    }
297
298                    return;
299                } else {
300                    d = args.pool.intern(d.setArtifact(args.pool.intern(d.getArtifact())));
301
302                    List<RemoteRepository> repos =
303                            getRemoteRepositories(rangeResult.getRepository(version), context.repositories);
304
305                    DefaultDependencyNode child = createDependencyNode(
306                            relocations,
307                            preManaged,
308                            rangeResult,
309                            version,
310                            d,
311                            descriptorResult.getAliases(),
312                            repos,
313                            args.request.getRequestContext());
314
315                    context.getParent().getChildren().add(child);
316
317                    boolean recurse =
318                            traverse && !descriptorResult.getDependencies().isEmpty();
319                    DependencyProcessingContext parentContext = context.withDependency(d);
320                    if (recurse) {
321                        doRecurse(args, parentContext, descriptorResult, child, results, disableVersionManagement);
322                    } else if (!args.skipper.skipResolution(child, parentContext.parents)) {
323                        List<DependencyNode> parents = new ArrayList<>(parentContext.parents.size() + 1);
324                        parents.addAll(parentContext.parents);
325                        parents.add(child);
326                        args.skipper.cache(child, parents);
327                    }
328                }
329            } else {
330                List<RemoteRepository> repos =
331                        getRemoteRepositories(rangeResult.getRepository(version), context.repositories);
332                DefaultDependencyNode child = createDependencyNode(
333                        relocations,
334                        preManaged,
335                        rangeResult,
336                        version,
337                        d,
338                        null,
339                        repos,
340                        args.request.getRequestContext());
341                context.getParent().getChildren().add(child);
342            }
343        }
344    }
345
346    @SuppressWarnings("checkstyle:parameternumber")
347    private void doRecurse(
348            Args args,
349            DependencyProcessingContext parentContext,
350            ArtifactDescriptorResult descriptorResult,
351            DefaultDependencyNode child,
352            Results results,
353            boolean disableVersionManagement) {
354        DefaultDependencyCollectionContext context = args.collectionContext;
355        context.set(parentContext.dependency, descriptorResult.getManagedDependencies());
356
357        DependencySelector childSelector =
358                parentContext.depSelector != null ? parentContext.depSelector.deriveChildSelector(context) : null;
359        DependencyManager childManager =
360                parentContext.depManager != null ? parentContext.depManager.deriveChildManager(context) : null;
361        DependencyTraverser childTraverser =
362                parentContext.depTraverser != null ? parentContext.depTraverser.deriveChildTraverser(context) : null;
363        VersionFilter childFilter =
364                parentContext.verFilter != null ? parentContext.verFilter.deriveChildFilter(context) : null;
365
366        final List<RemoteRepository> childRepos = args.ignoreRepos
367                ? parentContext.repositories
368                : remoteRepositoryManager.aggregateRepositories(
369                        args.session, parentContext.repositories, descriptorResult.getRepositories(), true);
370
371        Object key = args.pool.toKey(
372                parentContext.dependency.getArtifact(),
373                childRepos,
374                childSelector,
375                childManager,
376                childTraverser,
377                childFilter);
378
379        List<DependencyNode> children = args.pool.getChildren(key);
380        if (children == null) {
381            boolean skipResolution = args.skipper.skipResolution(child, parentContext.parents);
382            if (!skipResolution) {
383                List<DependencyNode> parents = new ArrayList<>(parentContext.parents.size() + 1);
384                parents.addAll(parentContext.parents);
385                parents.add(child);
386                for (Dependency dependency : descriptorResult.getDependencies()) {
387                    RequestTrace childTrace = collectStepTrace(
388                            parentContext.trace, args.request.getRequestContext(), parents, dependency);
389                    PremanagedDependency premanagedDependency = PremanagedDependency.create(
390                            childManager, dependency, disableVersionManagement, args.premanagedState);
391                    DependencyProcessingContext processingContext = new DependencyProcessingContext(
392                            childSelector,
393                            childManager,
394                            childTraverser,
395                            childFilter,
396                            childTrace,
397                            childRepos,
398                            descriptorResult.getManagedDependencies(),
399                            parents,
400                            dependency,
401                            premanagedDependency);
402                    if (!filter(processingContext)) {
403                        // resolve descriptors ahead for managed dependency
404                        processingContext.withDependency(processingContext.premanagedDependency.getManagedDependency());
405                        resolveArtifactDescriptorAsync(args, processingContext, results);
406                        args.dependencyProcessingQueue.add(processingContext);
407                    }
408                }
409                args.pool.putChildren(key, child.getChildren());
410                args.skipper.cache(child, parents);
411            }
412        } else {
413            child.setChildren(children);
414        }
415    }
416
417    private boolean filter(DependencyProcessingContext context) {
418        return context.depSelector != null && !context.depSelector.selectDependency(context.dependency);
419    }
420
421    private void resolveArtifactDescriptorAsync(Args args, DependencyProcessingContext context, Results results) {
422        Dependency dependency = context.dependency;
423        args.resolver.resolveDescriptors(dependency.getArtifact(), () -> {
424            VersionRangeRequest rangeRequest = createVersionRangeRequest(
425                    args.request.getRequestContext(), context.trace, context.repositories, dependency);
426            VersionRangeResult rangeResult = cachedResolveRangeResult(rangeRequest, args.pool, args.session);
427            List<? extends Version> versions =
428                    filterVersions(dependency, rangeResult, context.verFilter, args.versionContext);
429
430            // resolve newer version first to maximize benefits of skipper
431            Collections.reverse(versions);
432
433            Map<Version, ArtifactDescriptorResult> descriptors = new ConcurrentHashMap<>(versions.size());
434            Stream<? extends Version> stream = versions.size() > 1 ? versions.parallelStream() : versions.stream();
435            stream.forEach(version -> Optional.ofNullable(
436                            resolveDescriptorForVersion(args, context, results, dependency, version))
437                    .ifPresent(r -> descriptors.put(version, r)));
438
439            DescriptorResolutionResult resolutionResult =
440                    new DescriptorResolutionResult(dependency.getArtifact(), rangeResult);
441            // keep original sequence
442            versions.forEach(version -> resolutionResult.descriptors.put(version, descriptors.get(version)));
443            // populate for versions in version range
444            resolutionResult.flatten().forEach(dr -> args.resolver.cacheVersionRangeDescriptor(dr.artifact, dr));
445
446            return resolutionResult;
447        });
448    }
449
450    private ArtifactDescriptorResult resolveDescriptorForVersion(
451            Args args, DependencyProcessingContext context, Results results, Dependency dependency, Version version) {
452        Artifact original = dependency.getArtifact();
453        Artifact newArtifact = new DefaultArtifact(
454                original.getGroupId(),
455                original.getArtifactId(),
456                original.getClassifier(),
457                original.getExtension(),
458                version.toString(),
459                original.getProperties(),
460                (ArtifactType) null);
461        Dependency newDependency =
462                new Dependency(newArtifact, dependency.getScope(), dependency.isOptional(), dependency.getExclusions());
463        DependencyProcessingContext newContext = context.copy();
464
465        ArtifactDescriptorRequest descriptorRequest = createArtifactDescriptorRequest(
466                args.request.getRequestContext(), context.trace, newContext.repositories, newDependency);
467        return isLackingDescriptor(args.session, newArtifact)
468                ? new ArtifactDescriptorResult(descriptorRequest)
469                : resolveCachedArtifactDescriptor(
470                        args.pool, descriptorRequest, args.session, newContext.withDependency(newDependency), results);
471    }
472
473    private ArtifactDescriptorResult resolveCachedArtifactDescriptor(
474            DataPool pool,
475            ArtifactDescriptorRequest descriptorRequest,
476            RepositorySystemSession session,
477            DependencyProcessingContext context,
478            Results results) {
479        Object key = pool.toKey(descriptorRequest);
480        ArtifactDescriptorResult descriptorResult = pool.getDescriptor(key, descriptorRequest);
481        if (descriptorResult == null) {
482            try {
483                descriptorResult = descriptorReader.readArtifactDescriptor(session, descriptorRequest);
484                pool.putDescriptor(key, descriptorResult);
485            } catch (ArtifactDescriptorException e) {
486                results.addException(context.dependency, e, context.parents);
487                pool.putDescriptor(key, e);
488                return null;
489            }
490
491        } else if (descriptorResult == DataPool.NO_DESCRIPTOR) {
492            return null;
493        }
494
495        return descriptorResult;
496    }
497
498    static class ParallelDescriptorResolver implements Closeable {
499        private final ExecutorService executorService;
500
501        /**
502         * Artifact ID -> Future of DescriptorResolutionResult
503         */
504        private final Map<String, Future<DescriptorResolutionResult>> results = new ConcurrentHashMap<>(256);
505
506        ParallelDescriptorResolver(int threads) {
507            this.executorService = ExecutorUtils.threadPool(threads, getClass().getSimpleName() + "-");
508        }
509
510        void resolveDescriptors(Artifact artifact, Callable<DescriptorResolutionResult> callable) {
511            results.computeIfAbsent(ArtifactIdUtils.toId(artifact), key -> this.executorService.submit(callable));
512        }
513
514        void cacheVersionRangeDescriptor(Artifact artifact, DescriptorResolutionResult resolutionResult) {
515            results.computeIfAbsent(ArtifactIdUtils.toId(artifact), key -> new DoneFuture<>(resolutionResult));
516        }
517
518        Future<DescriptorResolutionResult> find(Artifact artifact) {
519            return results.get(ArtifactIdUtils.toId(artifact));
520        }
521
522        @Override
523        public void close() {
524            executorService.shutdown();
525        }
526    }
527
528    static class DoneFuture<V> implements Future<V> {
529        private final V v;
530
531        DoneFuture(V v) {
532            this.v = v;
533        }
534
535        @Override
536        public boolean cancel(boolean mayInterruptIfRunning) {
537            return false;
538        }
539
540        @Override
541        public boolean isCancelled() {
542            return false;
543        }
544
545        @Override
546        public boolean isDone() {
547            return true;
548        }
549
550        @Override
551        public V get() throws InterruptedException, ExecutionException {
552            return v;
553        }
554
555        @Override
556        public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
557            return v;
558        }
559    }
560
561    static class DescriptorResolutionResult {
562        Artifact artifact;
563
564        VersionRangeResult rangeResult;
565
566        Map<Version, ArtifactDescriptorResult> descriptors;
567
568        DescriptorResolutionResult(Artifact artifact, VersionRangeResult rangeResult) {
569            this.artifact = artifact;
570            this.rangeResult = rangeResult;
571            this.descriptors = new LinkedHashMap<>(rangeResult.getVersions().size());
572        }
573
574        DescriptorResolutionResult(
575                VersionRangeResult rangeResult, Version version, ArtifactDescriptorResult descriptor) {
576            // NOTE: In case of A1 -> A2 relocation this happens:
577            // ArtifactDescriptorResult read by ArtifactDescriptorResultReader for A1
578            // will return instance that will have artifact = A2 (as RelocatedArtifact).
579            // So to properly "key" this instance, we need to use "originally requested" A1 instead!
580            // In short:
581            // ArtifactDescriptorRequest.artifact != ArtifactDescriptorResult.artifact WHEN relocation in play
582            // otherwise (no relocation), they are EQUAL.
583            this(descriptor.getRequest().getArtifact(), rangeResult);
584            this.descriptors.put(version, descriptor);
585        }
586
587        List<DescriptorResolutionResult> flatten() {
588            if (descriptors.size() > 1) {
589                return descriptors.entrySet().stream()
590                        .map(e -> new DescriptorResolutionResult(rangeResult, e.getKey(), e.getValue()))
591                        .collect(Collectors.toList());
592            } else {
593                return Collections.emptyList();
594            }
595        }
596    }
597
598    static class Args {
599
600        final RepositorySystemSession session;
601
602        final boolean ignoreRepos;
603
604        final boolean premanagedState;
605
606        final DataPool pool;
607
608        final Queue<DependencyProcessingContext> dependencyProcessingQueue = new ArrayDeque<>(128);
609
610        final DefaultDependencyCollectionContext collectionContext;
611
612        final DefaultVersionFilterContext versionContext;
613
614        final CollectRequest request;
615
616        final DependencyResolutionSkipper skipper;
617
618        final ParallelDescriptorResolver resolver;
619
620        final AtomicReference<InterruptedException> interruptedException;
621
622        Args(
623                RepositorySystemSession session,
624                DataPool pool,
625                DefaultDependencyCollectionContext collectionContext,
626                DefaultVersionFilterContext versionContext,
627                CollectRequest request,
628                DependencyResolutionSkipper skipper,
629                ParallelDescriptorResolver resolver) {
630            this.session = session;
631            this.request = request;
632            this.ignoreRepos = session.isIgnoreArtifactDescriptorRepositories();
633            this.premanagedState = ConfigUtils.getBoolean(session, false, DependencyManagerUtils.CONFIG_PROP_VERBOSE);
634            this.pool = pool;
635            this.collectionContext = collectionContext;
636            this.versionContext = versionContext;
637            this.skipper = skipper;
638            this.resolver = resolver;
639            this.interruptedException = new AtomicReference<>(null);
640        }
641    }
642}