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