001package org.eclipse.aether.internal.impl.collect.bf; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import javax.inject.Inject; 023import javax.inject.Named; 024import javax.inject.Singleton; 025 026import java.util.ArrayDeque; 027import java.util.ArrayList; 028import java.util.Collections; 029import java.util.List; 030import java.util.Queue; 031 032import org.eclipse.aether.RepositorySystemSession; 033import org.eclipse.aether.RequestTrace; 034import org.eclipse.aether.artifact.Artifact; 035import org.eclipse.aether.collection.CollectRequest; 036import org.eclipse.aether.collection.DependencyManager; 037import org.eclipse.aether.collection.DependencySelector; 038import org.eclipse.aether.collection.DependencyTraverser; 039import org.eclipse.aether.collection.VersionFilter; 040import org.eclipse.aether.graph.DefaultDependencyNode; 041import org.eclipse.aether.graph.Dependency; 042import org.eclipse.aether.graph.DependencyNode; 043import org.eclipse.aether.impl.ArtifactDescriptorReader; 044import org.eclipse.aether.impl.RemoteRepositoryManager; 045import org.eclipse.aether.impl.VersionRangeResolver; 046import org.eclipse.aether.internal.impl.collect.DataPool; 047import org.eclipse.aether.internal.impl.collect.DefaultDependencyCollectionContext; 048import org.eclipse.aether.internal.impl.collect.DefaultVersionFilterContext; 049import org.eclipse.aether.internal.impl.collect.DependencyCollectorDelegate; 050import org.eclipse.aether.internal.impl.collect.PremanagedDependency; 051import org.eclipse.aether.repository.RemoteRepository; 052import org.eclipse.aether.resolution.ArtifactDescriptorException; 053import org.eclipse.aether.resolution.ArtifactDescriptorRequest; 054import org.eclipse.aether.resolution.ArtifactDescriptorResult; 055import org.eclipse.aether.resolution.VersionRangeRequest; 056import org.eclipse.aether.resolution.VersionRangeResolutionException; 057import org.eclipse.aether.resolution.VersionRangeResult; 058import org.eclipse.aether.spi.locator.Service; 059import org.eclipse.aether.util.ConfigUtils; 060import org.eclipse.aether.util.graph.manager.DependencyManagerUtils; 061import org.eclipse.aether.version.Version; 062 063import static org.eclipse.aether.internal.impl.collect.DefaultDependencyCycle.find; 064 065/** 066 * Breadth-first {@link org.eclipse.aether.impl.DependencyCollector} 067 * 068 * @since 1.8.0 069 */ 070@Singleton 071@Named( BfDependencyCollector.NAME ) 072public class BfDependencyCollector 073 extends DependencyCollectorDelegate implements Service 074{ 075 public static final String NAME = "bf"; 076 077 /** 078 * The key in the repository session's {@link RepositorySystemSession#getConfigProperties() 079 * configuration properties} used to store a {@link Boolean} flag controlling the resolver's skip mode. 080 * 081 * @since 1.8.0 082 */ 083 public static final String CONFIG_PROP_SKIPPER = "aether.dependencyCollector.bf.skipper"; 084 085 /** 086 * The default value for {@link #CONFIG_PROP_SKIPPER}, {@code true}. 087 * 088 * @since 1.8.0 089 */ 090 public static final boolean CONFIG_PROP_SKIPPER_DEFAULT = true; 091 092 /** 093 * Default ctor for SL. 094 * 095 * @deprecated Will be dropped once SL gone. 096 */ 097 @Deprecated 098 public BfDependencyCollector() 099 { 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}