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