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.df;
020
021import javax.inject.Inject;
022import javax.inject.Named;
023import javax.inject.Singleton;
024
025import java.util.Collections;
026import java.util.List;
027import java.util.Map;
028import java.util.concurrent.atomic.AtomicReference;
029
030import org.eclipse.aether.RepositorySystemSession;
031import org.eclipse.aether.RequestTrace;
032import org.eclipse.aether.artifact.Artifact;
033import org.eclipse.aether.collection.CollectRequest;
034import org.eclipse.aether.collection.DependencyCollectionException;
035import org.eclipse.aether.collection.DependencyManager;
036import org.eclipse.aether.collection.DependencySelector;
037import org.eclipse.aether.collection.DependencyTraverser;
038import org.eclipse.aether.collection.VersionFilter;
039import org.eclipse.aether.graph.DefaultDependencyNode;
040import org.eclipse.aether.graph.Dependency;
041import org.eclipse.aether.graph.DependencyNode;
042import org.eclipse.aether.impl.ArtifactDescriptorReader;
043import org.eclipse.aether.impl.RemoteRepositoryManager;
044import org.eclipse.aether.impl.VersionRangeResolver;
045import org.eclipse.aether.internal.impl.collect.DataPool;
046import org.eclipse.aether.internal.impl.collect.DefaultDependencyCollectionContext;
047import org.eclipse.aether.internal.impl.collect.DefaultDependencyCycle;
048import org.eclipse.aether.internal.impl.collect.DefaultVersionFilterContext;
049import org.eclipse.aether.internal.impl.collect.DependencyCollectorDelegate;
050import org.eclipse.aether.internal.impl.collect.PremanagedDependency;
051import org.eclipse.aether.repository.RemoteRepository;
052import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
053import org.eclipse.aether.resolution.ArtifactDescriptorResult;
054import org.eclipse.aether.resolution.VersionRangeRequest;
055import org.eclipse.aether.resolution.VersionRangeResolutionException;
056import org.eclipse.aether.resolution.VersionRangeResult;
057import org.eclipse.aether.spi.artifact.decorator.ArtifactDecoratorFactory;
058import org.eclipse.aether.util.ConfigUtils;
059import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
060import org.eclipse.aether.version.Version;
061
062/**
063 * Depth-first {@link org.eclipse.aether.impl.DependencyCollector} (the "original" default). Originally
064 * this class was located a package higher (as "default" implementation).
065 *
066 * @since 1.8.0
067 */
068@Singleton
069@Named(DfDependencyCollector.NAME)
070public class DfDependencyCollector extends DependencyCollectorDelegate {
071    public static final String NAME = "df";
072
073    @Inject
074    public DfDependencyCollector(
075            RemoteRepositoryManager remoteRepositoryManager,
076            ArtifactDescriptorReader artifactDescriptorReader,
077            VersionRangeResolver versionRangeResolver,
078            Map<String, ArtifactDecoratorFactory> artifactDecoratorFactories) {
079        super(remoteRepositoryManager, artifactDescriptorReader, versionRangeResolver, artifactDecoratorFactories);
080    }
081
082    @SuppressWarnings("checkstyle:parameternumber")
083    @Override
084    protected void doCollectDependencies(
085            RepositorySystemSession session,
086            RequestTrace trace,
087            DataPool pool,
088            DefaultDependencyCollectionContext context,
089            DefaultVersionFilterContext versionContext,
090            CollectRequest request,
091            DependencyNode node,
092            List<RemoteRepository> repositories,
093            List<Dependency> dependencies,
094            List<Dependency> managedDependencies,
095            Results results)
096            throws DependencyCollectionException {
097        NodeStack nodes = new NodeStack();
098        nodes.push(node);
099
100        Args args = new Args(session, pool, nodes, context, versionContext, request);
101
102        process(
103                args,
104                trace,
105                results,
106                dependencies,
107                repositories,
108                session.getDependencySelector() != null
109                        ? session.getDependencySelector().deriveChildSelector(context)
110                        : null,
111                session.getDependencyManager() != null
112                        ? session.getDependencyManager().deriveChildManager(context)
113                        : null,
114                session.getDependencyTraverser() != null
115                        ? session.getDependencyTraverser().deriveChildTraverser(context)
116                        : null,
117                session.getVersionFilter() != null ? session.getVersionFilter().deriveChildFilter(context) : null);
118
119        if (args.interruptedException.get() != null) {
120            throw new DependencyCollectionException(
121                    results.getResult(), "Collection interrupted", args.interruptedException.get());
122        }
123    }
124
125    @SuppressWarnings("checkstyle:parameternumber")
126    private void process(
127            final Args args,
128            RequestTrace trace,
129            Results results,
130            List<Dependency> dependencies,
131            List<RemoteRepository> repositories,
132            DependencySelector depSelector,
133            DependencyManager depManager,
134            DependencyTraverser depTraverser,
135            VersionFilter verFilter) {
136        if (Thread.interrupted()) {
137            args.interruptedException.set(new InterruptedException());
138        }
139        if (args.interruptedException.get() != null) {
140            return;
141        }
142        for (Dependency dependency : dependencies) {
143            processDependency(
144                    args, trace, results, repositories, depSelector, depManager, depTraverser, verFilter, dependency);
145        }
146    }
147
148    @SuppressWarnings("checkstyle:parameternumber")
149    private void processDependency(
150            Args args,
151            RequestTrace trace,
152            Results results,
153            List<RemoteRepository> repositories,
154            DependencySelector depSelector,
155            DependencyManager depManager,
156            DependencyTraverser depTraverser,
157            VersionFilter verFilter,
158            Dependency dependency) {
159
160        List<Artifact> relocations = Collections.emptyList();
161        processDependency(
162                args,
163                trace,
164                results,
165                repositories,
166                depSelector,
167                depManager,
168                depTraverser,
169                verFilter,
170                dependency,
171                relocations,
172                false);
173    }
174
175    @SuppressWarnings("checkstyle:parameternumber")
176    private void processDependency(
177            Args args,
178            RequestTrace parent,
179            Results results,
180            List<RemoteRepository> repositories,
181            DependencySelector depSelector,
182            DependencyManager depManager,
183            DependencyTraverser depTraverser,
184            VersionFilter verFilter,
185            Dependency dependency,
186            List<Artifact> relocations,
187            boolean disableVersionManagement) {
188        if (depSelector != null && !depSelector.selectDependency(dependency)) {
189            return;
190        }
191
192        RequestTrace trace = collectStepTrace(parent, args.request.getRequestContext(), args.nodes.nodes, dependency);
193        PremanagedDependency preManaged =
194                PremanagedDependency.create(depManager, dependency, disableVersionManagement, args.premanagedState);
195        dependency = preManaged.getManagedDependency();
196
197        boolean noDescriptor = isLackingDescriptor(args.session, dependency.getArtifact());
198
199        boolean traverse = !noDescriptor && (depTraverser == null || depTraverser.traverseDependency(dependency));
200
201        List<? extends Version> versions;
202        VersionRangeResult rangeResult;
203        try {
204            VersionRangeRequest rangeRequest =
205                    createVersionRangeRequest(args.request.getRequestContext(), trace, repositories, dependency);
206
207            rangeResult = cachedResolveRangeResult(rangeRequest, args.pool, args.session);
208
209            versions = filterVersions(dependency, rangeResult, verFilter, args.versionContext);
210        } catch (VersionRangeResolutionException e) {
211            results.addException(dependency, e, args.nodes.nodes);
212            return;
213        }
214
215        for (Version version : versions) {
216            Artifact originalArtifact = dependency.getArtifact().setVersion(version.toString());
217            Dependency d = dependency.setArtifact(originalArtifact);
218
219            ArtifactDescriptorRequest descriptorRequest =
220                    createArtifactDescriptorRequest(args.request.getRequestContext(), trace, repositories, d);
221
222            final ArtifactDescriptorResult descriptorResult =
223                    getArtifactDescriptorResult(args, results, noDescriptor, d, descriptorRequest);
224            if (descriptorResult != null) {
225                d = d.setArtifact(descriptorResult.getArtifact());
226
227                DependencyNode node = args.nodes.top();
228
229                int cycleEntry = DefaultDependencyCycle.find(args.nodes.nodes, d.getArtifact());
230                if (cycleEntry >= 0) {
231                    results.addCycle(args.nodes.nodes, cycleEntry, d);
232                    DependencyNode cycleNode = args.nodes.get(cycleEntry);
233                    if (cycleNode.getDependency() != null) {
234                        DefaultDependencyNode child = createDependencyNode(
235                                relocations, preManaged, rangeResult, version, d, descriptorResult, cycleNode);
236                        node.getChildren().add(child);
237                        continue;
238                    }
239                }
240
241                if (!descriptorResult.getRelocations().isEmpty()) {
242                    boolean disableVersionManagementSubsequently =
243                            originalArtifact.getGroupId().equals(d.getArtifact().getGroupId())
244                                    && originalArtifact
245                                            .getArtifactId()
246                                            .equals(d.getArtifact().getArtifactId());
247
248                    processDependency(
249                            args,
250                            parent,
251                            results,
252                            repositories,
253                            depSelector,
254                            depManager,
255                            depTraverser,
256                            verFilter,
257                            d,
258                            descriptorResult.getRelocations(),
259                            disableVersionManagementSubsequently);
260                    return;
261                } else {
262                    d = args.pool.intern(d.setArtifact(args.pool.intern(d.getArtifact())));
263
264                    List<RemoteRepository> repos =
265                            getRemoteRepositories(rangeResult.getRepository(version), repositories);
266
267                    DefaultDependencyNode child = createDependencyNode(
268                            relocations,
269                            preManaged,
270                            rangeResult,
271                            version,
272                            d,
273                            descriptorResult.getAliases(),
274                            repos,
275                            args.request.getRequestContext());
276
277                    node.getChildren().add(child);
278
279                    boolean recurse =
280                            traverse && !descriptorResult.getDependencies().isEmpty();
281                    if (recurse) {
282                        doRecurse(
283                                args,
284                                parent,
285                                results,
286                                repositories,
287                                depSelector,
288                                depManager,
289                                depTraverser,
290                                verFilter,
291                                d,
292                                descriptorResult,
293                                child);
294                    }
295                }
296            } else {
297                DependencyNode node = args.nodes.top();
298                List<RemoteRepository> repos = getRemoteRepositories(rangeResult.getRepository(version), repositories);
299                DefaultDependencyNode child = createDependencyNode(
300                        relocations,
301                        preManaged,
302                        rangeResult,
303                        version,
304                        d,
305                        null,
306                        repos,
307                        args.request.getRequestContext());
308                node.getChildren().add(child);
309            }
310        }
311    }
312
313    @SuppressWarnings("checkstyle:parameternumber")
314    private void doRecurse(
315            Args args,
316            RequestTrace trace,
317            Results results,
318            List<RemoteRepository> repositories,
319            DependencySelector depSelector,
320            DependencyManager depManager,
321            DependencyTraverser depTraverser,
322            VersionFilter verFilter,
323            Dependency d,
324            ArtifactDescriptorResult descriptorResult,
325            DefaultDependencyNode child) {
326        DefaultDependencyCollectionContext context = args.collectionContext.get();
327        args.collectionContext.compareAndSet(context, context.set(d, descriptorResult.getManagedDependencies()));
328        context = args.collectionContext.get();
329
330        DependencySelector childSelector = depSelector != null ? depSelector.deriveChildSelector(context) : null;
331        DependencyManager childManager = depManager != null ? depManager.deriveChildManager(context) : null;
332        DependencyTraverser childTraverser = depTraverser != null ? depTraverser.deriveChildTraverser(context) : null;
333        VersionFilter childFilter = verFilter != null ? verFilter.deriveChildFilter(context) : null;
334
335        final List<RemoteRepository> childRepos = args.ignoreRepos
336                ? repositories
337                : remoteRepositoryManager.aggregateRepositories(
338                        args.session, repositories, descriptorResult.getRepositories(), true);
339
340        Object key =
341                args.pool.toKey(d.getArtifact(), childRepos, childSelector, childManager, childTraverser, childFilter);
342
343        List<DependencyNode> children = args.pool.getChildren(key);
344        if (children == null) {
345            args.pool.putChildren(key, child.getChildren());
346
347            args.nodes.push(child);
348
349            process(
350                    args,
351                    trace,
352                    results,
353                    descriptorResult.getDependencies(),
354                    childRepos,
355                    childSelector,
356                    childManager,
357                    childTraverser,
358                    childFilter);
359
360            args.nodes.pop();
361        } else {
362            child.setChildren(children);
363        }
364    }
365
366    private ArtifactDescriptorResult getArtifactDescriptorResult(
367            Args args,
368            Results results,
369            boolean noDescriptor,
370            Dependency d,
371            ArtifactDescriptorRequest descriptorRequest) {
372        return noDescriptor
373                ? new ArtifactDescriptorResult(descriptorRequest)
374                : resolveCachedArtifactDescriptor(
375                        args.pool, descriptorRequest, args.session, d, results, args.nodes.nodes);
376    }
377
378    static class Args {
379
380        final RepositorySystemSession session;
381
382        final boolean ignoreRepos;
383
384        final boolean premanagedState;
385
386        final DataPool pool;
387
388        final NodeStack nodes;
389
390        final AtomicReference<DefaultDependencyCollectionContext> collectionContext;
391
392        final DefaultVersionFilterContext versionContext;
393
394        final CollectRequest request;
395
396        final AtomicReference<InterruptedException> interruptedException;
397
398        Args(
399                RepositorySystemSession session,
400                DataPool pool,
401                NodeStack nodes,
402                DefaultDependencyCollectionContext collectionContext,
403                DefaultVersionFilterContext versionContext,
404                CollectRequest request) {
405            this.session = session;
406            this.request = request;
407            this.ignoreRepos = session.isIgnoreArtifactDescriptorRepositories();
408            this.premanagedState = ConfigUtils.getBoolean(session, false, DependencyManagerUtils.CONFIG_PROP_VERBOSE);
409            this.pool = pool;
410            this.nodes = nodes;
411            this.collectionContext = new AtomicReference<>(collectionContext);
412            this.versionContext = versionContext;
413            this.interruptedException = new AtomicReference<>(null);
414        }
415    }
416}