View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.eclipse.aether.internal.impl.collect.df;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  import javax.inject.Singleton;
24  
25  import java.util.Collections;
26  import java.util.List;
27  import java.util.concurrent.atomic.AtomicReference;
28  
29  import org.eclipse.aether.RepositorySystemSession;
30  import org.eclipse.aether.RequestTrace;
31  import org.eclipse.aether.artifact.Artifact;
32  import org.eclipse.aether.collection.CollectRequest;
33  import org.eclipse.aether.collection.DependencyCollectionException;
34  import org.eclipse.aether.collection.DependencyManager;
35  import org.eclipse.aether.collection.DependencySelector;
36  import org.eclipse.aether.collection.DependencyTraverser;
37  import org.eclipse.aether.collection.VersionFilter;
38  import org.eclipse.aether.graph.DefaultDependencyNode;
39  import org.eclipse.aether.graph.Dependency;
40  import org.eclipse.aether.graph.DependencyNode;
41  import org.eclipse.aether.impl.ArtifactDescriptorReader;
42  import org.eclipse.aether.impl.RemoteRepositoryManager;
43  import org.eclipse.aether.impl.VersionRangeResolver;
44  import org.eclipse.aether.internal.impl.collect.DataPool;
45  import org.eclipse.aether.internal.impl.collect.DefaultDependencyCollectionContext;
46  import org.eclipse.aether.internal.impl.collect.DefaultDependencyCycle;
47  import org.eclipse.aether.internal.impl.collect.DefaultVersionFilterContext;
48  import org.eclipse.aether.internal.impl.collect.DependencyCollectorDelegate;
49  import org.eclipse.aether.internal.impl.collect.PremanagedDependency;
50  import org.eclipse.aether.repository.RemoteRepository;
51  import org.eclipse.aether.resolution.ArtifactDescriptorException;
52  import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
53  import org.eclipse.aether.resolution.ArtifactDescriptorResult;
54  import org.eclipse.aether.resolution.VersionRangeRequest;
55  import org.eclipse.aether.resolution.VersionRangeResolutionException;
56  import org.eclipse.aether.resolution.VersionRangeResult;
57  import org.eclipse.aether.util.ConfigUtils;
58  import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
59  import org.eclipse.aether.version.Version;
60  
61  /**
62   * Depth-first {@link org.eclipse.aether.impl.DependencyCollector} (the "original" default). Originally
63   * this class was located a package higher (as "default" implementation).
64   *
65   * @since 1.8.0
66   */
67  @Singleton
68  @Named(DfDependencyCollector.NAME)
69  public class DfDependencyCollector extends DependencyCollectorDelegate {
70      public static final String NAME = "df";
71  
72      @Inject
73      public DfDependencyCollector(
74              RemoteRepositoryManager remoteRepositoryManager,
75              ArtifactDescriptorReader artifactDescriptorReader,
76              VersionRangeResolver versionRangeResolver) {
77          super(remoteRepositoryManager, artifactDescriptorReader, versionRangeResolver);
78      }
79  
80      @SuppressWarnings("checkstyle:parameternumber")
81      @Override
82      protected void doCollectDependencies(
83              RepositorySystemSession session,
84              RequestTrace trace,
85              DataPool pool,
86              DefaultDependencyCollectionContext context,
87              DefaultVersionFilterContext versionContext,
88              CollectRequest request,
89              DependencyNode node,
90              List<RemoteRepository> repositories,
91              List<Dependency> dependencies,
92              List<Dependency> managedDependencies,
93              Results results)
94              throws DependencyCollectionException {
95          NodeStack nodes = new NodeStack();
96          nodes.push(node);
97  
98          Args args = new Args(session, pool, nodes, context, versionContext, request);
99  
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 }