View Javadoc
1   package org.eclipse.aether.internal.impl.collect.bf;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import javax.inject.Inject;
23  import javax.inject.Named;
24  import javax.inject.Singleton;
25  
26  import java.util.ArrayDeque;
27  import java.util.ArrayList;
28  import java.util.Collections;
29  import java.util.List;
30  import java.util.Queue;
31  
32  import org.eclipse.aether.RepositorySystemSession;
33  import org.eclipse.aether.RequestTrace;
34  import org.eclipse.aether.artifact.Artifact;
35  import org.eclipse.aether.collection.CollectRequest;
36  import org.eclipse.aether.collection.DependencyManager;
37  import org.eclipse.aether.collection.DependencySelector;
38  import org.eclipse.aether.collection.DependencyTraverser;
39  import org.eclipse.aether.collection.VersionFilter;
40  import org.eclipse.aether.graph.DefaultDependencyNode;
41  import org.eclipse.aether.graph.Dependency;
42  import org.eclipse.aether.graph.DependencyNode;
43  import org.eclipse.aether.impl.ArtifactDescriptorReader;
44  import org.eclipse.aether.impl.RemoteRepositoryManager;
45  import org.eclipse.aether.impl.VersionRangeResolver;
46  import org.eclipse.aether.internal.impl.collect.DataPool;
47  import org.eclipse.aether.internal.impl.collect.DefaultDependencyCollectionContext;
48  import org.eclipse.aether.internal.impl.collect.DefaultVersionFilterContext;
49  import org.eclipse.aether.internal.impl.collect.DependencyCollectorDelegate;
50  import org.eclipse.aether.internal.impl.collect.PremanagedDependency;
51  import org.eclipse.aether.repository.RemoteRepository;
52  import org.eclipse.aether.resolution.ArtifactDescriptorException;
53  import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
54  import org.eclipse.aether.resolution.ArtifactDescriptorResult;
55  import org.eclipse.aether.resolution.VersionRangeRequest;
56  import org.eclipse.aether.resolution.VersionRangeResolutionException;
57  import org.eclipse.aether.resolution.VersionRangeResult;
58  import org.eclipse.aether.spi.locator.Service;
59  import org.eclipse.aether.util.ConfigUtils;
60  import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
61  import org.eclipse.aether.version.Version;
62  
63  import static org.eclipse.aether.internal.impl.collect.DefaultDependencyCycle.find;
64  
65  /**
66   * Breadth-first {@link org.eclipse.aether.impl.DependencyCollector}
67   *
68   * @since 1.8.0
69   */
70  @Singleton
71  @Named( BfDependencyCollector.NAME )
72  public class BfDependencyCollector
73      extends DependencyCollectorDelegate implements Service
74  {
75      public static final String NAME = "bf";
76  
77      /**
78       * The key in the repository session's {@link RepositorySystemSession#getConfigProperties()
79       * configuration properties} used to store a {@link Boolean} flag controlling the resolver's skip mode.
80       *
81       * @since 1.8.0
82       */
83      public static final String CONFIG_PROP_SKIPPER = "aether.dependencyCollector.bf.skipper";
84  
85      /**
86       * The default value for {@link #CONFIG_PROP_SKIPPER}, {@code true}.
87       *
88       * @since 1.8.0
89       */
90      public static final boolean CONFIG_PROP_SKIPPER_DEFAULT = true;
91  
92      /**
93       * Default ctor for SL.
94       *
95       * @deprecated Will be dropped once SL gone.
96       */
97      @Deprecated
98      public BfDependencyCollector()
99      {
100         // enables default constructor
101     }
102 
103     @Inject
104     BfDependencyCollector( RemoteRepositoryManager remoteRepositoryManager,
105                            ArtifactDescriptorReader artifactDescriptorReader,
106                            VersionRangeResolver versionRangeResolver )
107     {
108         super( remoteRepositoryManager, artifactDescriptorReader, versionRangeResolver );
109     }
110 
111     @SuppressWarnings( "checkstyle:parameternumber" )
112     @Override
113     protected void doCollectDependencies( RepositorySystemSession session, RequestTrace trace, DataPool pool,
114                                           DefaultDependencyCollectionContext context,
115                                           DefaultVersionFilterContext versionContext,
116                                           CollectRequest request, DependencyNode node,
117                                           List<RemoteRepository> repositories, List<Dependency> dependencies,
118                                           List<Dependency> managedDependencies, Results results )
119     {
120         boolean useSkip = ConfigUtils.getBoolean(
121                 session, CONFIG_PROP_SKIPPER_DEFAULT, CONFIG_PROP_SKIPPER
122         );
123         if ( useSkip )
124         {
125             logger.debug( "Collector skip mode enabled" );
126         }
127 
128         Args args =
129                 new Args( session, pool, context, versionContext, request,
130                         useSkip ? DependencyResolutionSkipper.defaultSkipper()
131                                 : DependencyResolutionSkipper.neverSkipper() );
132 
133         DependencySelector rootDepSelector = session.getDependencySelector() != null
134                 ? session.getDependencySelector().deriveChildSelector( context ) : null;
135         DependencyManager rootDepManager = session.getDependencyManager() != null
136                 ? session.getDependencyManager().deriveChildManager( context ) : null;
137         DependencyTraverser rootDepTraverser = session.getDependencyTraverser() != null
138                 ? session.getDependencyTraverser().deriveChildTraverser( context ) : null;
139         VersionFilter rootVerFilter = session.getVersionFilter() != null
140                 ? session.getVersionFilter().deriveChildFilter( context ) : null;
141 
142         List<DependencyNode> parents = Collections.singletonList( node );
143         for ( Dependency dependency : dependencies )
144         {
145             args.dependencyProcessingQueue.add(
146                     new DependencyProcessingContext( rootDepSelector, rootDepManager, rootDepTraverser,
147                             rootVerFilter, repositories, managedDependencies, parents,
148                             dependency ) );
149         }
150 
151         while ( !args.dependencyProcessingQueue.isEmpty() )
152         {
153             processDependency( args, trace, results, args.dependencyProcessingQueue.remove(), Collections.emptyList(),
154                     false );
155         }
156 
157         args.skipper.report();
158     }
159 
160     @SuppressWarnings( "checkstyle:parameternumber" )
161     private void processDependency( Args args, RequestTrace parent, Results results,
162                                     DependencyProcessingContext context, List<Artifact> relocations,
163                                     boolean disableVersionManagement )
164     {
165         if ( context.depSelector != null && !context.depSelector.selectDependency( context.dependency ) )
166         {
167             return;
168         }
169 
170         RequestTrace trace = collectStepTrace( parent, args.request.getRequestContext(), context.parents,
171                 context.dependency );
172         PremanagedDependency preManaged =
173                 PremanagedDependency.create( context.depManager, context.dependency, disableVersionManagement,
174                         args.premanagedState );
175         Dependency dependency = preManaged.getManagedDependency();
176 
177         boolean noDescriptor = isLackingDescriptor( dependency.getArtifact() );
178 
179         boolean traverse =
180                 !noDescriptor && ( context.depTraverser == null || context.depTraverser.traverseDependency(
181                         dependency ) );
182 
183         List<? extends Version> versions;
184         VersionRangeResult rangeResult;
185         try
186         {
187             VersionRangeRequest rangeRequest = createVersionRangeRequest( args.request.getRequestContext(), trace,
188                     context.repositories, dependency );
189 
190             rangeResult = cachedResolveRangeResult( rangeRequest, args.pool, args.session );
191 
192             versions = filterVersions( dependency, rangeResult, context.verFilter, args.versionContext );
193         }
194         catch ( VersionRangeResolutionException e )
195         {
196             results.addException( dependency, e, context.parents );
197             return;
198         }
199 
200         //Resolve newer version first to maximize benefits of skipper
201         Collections.reverse( versions );
202         for ( Version version : versions )
203         {
204             Artifact originalArtifact = dependency.getArtifact().setVersion( version.toString() );
205             Dependency d = dependency.setArtifact( originalArtifact );
206 
207             ArtifactDescriptorRequest descriptorRequest = createArtifactDescriptorRequest(
208                     args.request.getRequestContext(), trace, context.repositories, d );
209 
210             final ArtifactDescriptorResult descriptorResult =
211                     noDescriptor
212                             ? new ArtifactDescriptorResult( descriptorRequest )
213                             : resolveCachedArtifactDescriptor( args.pool, descriptorRequest, args.session,
214                                     context.withDependency( d ), results );
215 
216             if ( descriptorResult != null )
217             {
218                 d = d.setArtifact( descriptorResult.getArtifact() );
219 
220                 int cycleEntry = find( context.parents, d.getArtifact() );
221                 if ( cycleEntry >= 0 )
222                 {
223                     results.addCycle( context.parents, cycleEntry, d );
224                     DependencyNode cycleNode = context.parents.get( cycleEntry );
225                     if ( cycleNode.getDependency() != null )
226                     {
227                         DefaultDependencyNode child =
228                                 createDependencyNode( relocations, preManaged, rangeResult, version, d,
229                                         descriptorResult, cycleNode );
230                         context.getParent().getChildren().add( child );
231                         continue;
232                     }
233                 }
234 
235                 if ( !descriptorResult.getRelocations().isEmpty() )
236                 {
237                     boolean disableVersionManagementSubsequently =
238                         originalArtifact.getGroupId().equals( d.getArtifact().getGroupId() )
239                             && originalArtifact.getArtifactId().equals( d.getArtifact().getArtifactId() );
240 
241                     processDependency( args, parent, results, context.withDependency( d ),
242                             descriptorResult.getRelocations(), disableVersionManagementSubsequently );
243                     return;
244                 }
245                 else
246                 {
247                     d = args.pool.intern( d.setArtifact( args.pool.intern( d.getArtifact() ) ) );
248 
249                     List<RemoteRepository> repos =
250                         getRemoteRepositories( rangeResult.getRepository( version ), context.repositories );
251 
252                     DefaultDependencyNode child =
253                         createDependencyNode( relocations, preManaged, rangeResult, version, d,
254                                               descriptorResult.getAliases(), repos, args.request.getRequestContext() );
255 
256                     context.getParent().getChildren().add( child );
257 
258                     boolean recurse = traverse && !descriptorResult.getDependencies().isEmpty();
259                     DependencyProcessingContext parentContext = context.withDependency( d );
260                     if ( recurse )
261                     {
262                         doRecurse( args, parentContext, descriptorResult, child );
263                     }
264                     else if ( !args.skipper.skipResolution( child, parentContext.parents ) )
265                     {
266                         List<DependencyNode> parents = new ArrayList<>( parentContext.parents.size() + 1 );
267                         parents.addAll( parentContext.parents );
268                         parents.add( child );
269                         args.skipper.cache( child, parents );
270                     }
271                 }
272             }
273             else
274             {
275                 List<RemoteRepository> repos =
276                     getRemoteRepositories( rangeResult.getRepository( version ), context.repositories );
277                 DefaultDependencyNode child =
278                     createDependencyNode( relocations, preManaged, rangeResult, version, d, null, repos,
279                                           args.request.getRequestContext() );
280                 context.getParent().getChildren().add( child );
281             }
282         }
283     }
284 
285     @SuppressWarnings( "checkstyle:parameternumber" )
286     private void doRecurse( Args args, DependencyProcessingContext parentContext,
287                             ArtifactDescriptorResult descriptorResult, DefaultDependencyNode child )
288     {
289         DefaultDependencyCollectionContext context = args.collectionContext;
290         context.set( parentContext.dependency, descriptorResult.getManagedDependencies() );
291 
292         DependencySelector childSelector =
293                 parentContext.depSelector != null ? parentContext.depSelector.deriveChildSelector( context ) : null;
294         DependencyManager childManager =
295                 parentContext.depManager != null ? parentContext.depManager.deriveChildManager( context ) : null;
296         DependencyTraverser childTraverser =
297                 parentContext.depTraverser != null ? parentContext.depTraverser.deriveChildTraverser( context ) : null;
298         VersionFilter childFilter =
299                 parentContext.verFilter != null ? parentContext.verFilter.deriveChildFilter( context ) : null;
300 
301         final List<RemoteRepository> childRepos =
302                 args.ignoreRepos
303                         ? parentContext.repositories
304                         : remoteRepositoryManager.aggregateRepositories( args.session, parentContext.repositories,
305                         descriptorResult.getRepositories(), true );
306 
307         Object key =
308                 args.pool.toKey( parentContext.dependency.getArtifact(), childRepos, childSelector, childManager,
309                         childTraverser, childFilter );
310 
311         List<DependencyNode> children = args.pool.getChildren( key );
312         if ( children == null )
313         {
314             boolean skipResolution = args.skipper.skipResolution( child, parentContext.parents );
315             if ( !skipResolution )
316             {
317                 List<DependencyNode> parents = new ArrayList<>( parentContext.parents.size() + 1 );
318                 parents.addAll( parentContext.parents );
319                 parents.add( child );
320                 for ( Dependency dependency : descriptorResult.getDependencies() )
321                 {
322                     args.dependencyProcessingQueue.add(
323                             new DependencyProcessingContext( childSelector, childManager, childTraverser, childFilter,
324                                     childRepos, descriptorResult.getManagedDependencies(), parents, dependency ) );
325 
326                 }
327                 args.pool.putChildren( key, child.getChildren() );
328                 args.skipper.cache( child, parents );
329             }
330         }
331         else
332         {
333             child.setChildren( children );
334         }
335     }
336 
337     private ArtifactDescriptorResult resolveCachedArtifactDescriptor( DataPool pool,
338                                                                       ArtifactDescriptorRequest descriptorRequest,
339                                                                       RepositorySystemSession session,
340                                                                       DependencyProcessingContext context,
341                                                                       Results results )
342     {
343         Object key = pool.toKey( descriptorRequest );
344         ArtifactDescriptorResult descriptorResult = pool.getDescriptor( key, descriptorRequest );
345         if ( descriptorResult == null )
346         {
347             try
348             {
349                 descriptorResult = descriptorReader.readArtifactDescriptor( session, descriptorRequest );
350                 pool.putDescriptor( key, descriptorResult );
351             }
352             catch ( ArtifactDescriptorException e )
353             {
354                 results.addException( context.dependency, e, context.parents );
355                 pool.putDescriptor( key, e );
356                 return null;
357             }
358 
359         }
360         else if ( descriptorResult == DataPool.NO_DESCRIPTOR )
361         {
362             return null;
363         }
364 
365         return descriptorResult;
366     }
367 
368     static class Args
369     {
370 
371         final RepositorySystemSession session;
372 
373         final boolean ignoreRepos;
374 
375         final boolean premanagedState;
376 
377         final DataPool pool;
378 
379         final Queue<DependencyProcessingContext> dependencyProcessingQueue = new ArrayDeque<>( 128 );
380 
381         final DefaultDependencyCollectionContext collectionContext;
382 
383         final DefaultVersionFilterContext versionContext;
384 
385         final CollectRequest request;
386 
387         final DependencyResolutionSkipper skipper;
388 
389         Args( RepositorySystemSession session, DataPool pool,
390                      DefaultDependencyCollectionContext collectionContext, DefaultVersionFilterContext versionContext,
391                      CollectRequest request, DependencyResolutionSkipper skipper )
392         {
393             this.session = session;
394             this.request = request;
395             this.ignoreRepos = session.isIgnoreArtifactDescriptorRepositories();
396             this.premanagedState = ConfigUtils.getBoolean( session, false, DependencyManagerUtils.CONFIG_PROP_VERBOSE );
397             this.pool = pool;
398             this.collectionContext = collectionContext;
399             this.versionContext = versionContext;
400             this.skipper = skipper;
401         }
402 
403     }
404 
405 }