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