001package org.eclipse.aether.internal.impl.collect; 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 java.util.ArrayList; 023import java.util.Collection; 024import java.util.Collections; 025import java.util.HashSet; 026import java.util.LinkedHashMap; 027import java.util.List; 028import java.util.Map; 029 030import org.eclipse.aether.DefaultRepositorySystemSession; 031import org.eclipse.aether.RepositoryException; 032import org.eclipse.aether.RepositorySystemSession; 033import org.eclipse.aether.RequestTrace; 034import org.eclipse.aether.artifact.Artifact; 035import org.eclipse.aether.artifact.ArtifactProperties; 036import org.eclipse.aether.collection.CollectRequest; 037import org.eclipse.aether.collection.CollectResult; 038import org.eclipse.aether.collection.DependencyCollectionException; 039import org.eclipse.aether.collection.DependencyGraphTransformer; 040import org.eclipse.aether.collection.DependencyTraverser; 041import org.eclipse.aether.collection.VersionFilter; 042import org.eclipse.aether.graph.DefaultDependencyNode; 043import org.eclipse.aether.graph.Dependency; 044import org.eclipse.aether.graph.DependencyNode; 045import org.eclipse.aether.impl.ArtifactDescriptorReader; 046import org.eclipse.aether.impl.DependencyCollector; 047import org.eclipse.aether.impl.RemoteRepositoryManager; 048import org.eclipse.aether.impl.VersionRangeResolver; 049import org.eclipse.aether.repository.ArtifactRepository; 050import org.eclipse.aether.repository.RemoteRepository; 051import org.eclipse.aether.resolution.ArtifactDescriptorException; 052import org.eclipse.aether.resolution.ArtifactDescriptorRequest; 053import org.eclipse.aether.resolution.ArtifactDescriptorResult; 054import org.eclipse.aether.resolution.VersionRangeRequest; 055import org.eclipse.aether.resolution.VersionRangeResolutionException; 056import org.eclipse.aether.resolution.VersionRangeResult; 057import org.eclipse.aether.spi.locator.ServiceLocator; 058import org.eclipse.aether.util.ConfigUtils; 059import org.eclipse.aether.util.graph.transformer.TransformationContextKeys; 060import org.eclipse.aether.version.Version; 061import org.slf4j.Logger; 062import org.slf4j.LoggerFactory; 063 064import static java.util.Objects.requireNonNull; 065 066/** 067 * Helper class for delegate implementations, they MUST subclass this class. 068 * 069 * @since 1.8.0 070 */ 071public abstract class DependencyCollectorDelegate implements DependencyCollector 072{ 073 protected static final String CONFIG_PROP_MAX_EXCEPTIONS = "aether.dependencyCollector.maxExceptions"; 074 075 protected static final int CONFIG_PROP_MAX_EXCEPTIONS_DEFAULT = 50; 076 077 protected static final String CONFIG_PROP_MAX_CYCLES = "aether.dependencyCollector.maxCycles"; 078 079 protected static final int CONFIG_PROP_MAX_CYCLES_DEFAULT = 10; 080 081 protected final Logger logger = LoggerFactory.getLogger( getClass() ); 082 083 protected RemoteRepositoryManager remoteRepositoryManager; 084 085 protected ArtifactDescriptorReader descriptorReader; 086 087 protected VersionRangeResolver versionRangeResolver; 088 089 /** 090 * Default ctor for SL. 091 * 092 * @deprecated Will be dropped once SL gone. 093 */ 094 @Deprecated 095 protected DependencyCollectorDelegate() 096 { 097 // enables default constructor 098 } 099 100 protected DependencyCollectorDelegate( RemoteRepositoryManager remoteRepositoryManager, 101 ArtifactDescriptorReader artifactDescriptorReader, 102 VersionRangeResolver versionRangeResolver ) 103 { 104 setRemoteRepositoryManager( remoteRepositoryManager ); 105 setArtifactDescriptorReader( artifactDescriptorReader ); 106 setVersionRangeResolver( versionRangeResolver ); 107 } 108 109 public void initService( ServiceLocator locator ) 110 { 111 setRemoteRepositoryManager( locator.getService( RemoteRepositoryManager.class ) ); 112 setArtifactDescriptorReader( locator.getService( ArtifactDescriptorReader.class ) ); 113 setVersionRangeResolver( locator.getService( VersionRangeResolver.class ) ); 114 } 115 116 public DependencyCollector setRemoteRepositoryManager( RemoteRepositoryManager remoteRepositoryManager ) 117 { 118 this.remoteRepositoryManager = 119 requireNonNull( remoteRepositoryManager, "remote repository manager cannot be null" ); 120 return this; 121 } 122 123 public DependencyCollector setArtifactDescriptorReader( ArtifactDescriptorReader artifactDescriptorReader ) 124 { 125 descriptorReader = requireNonNull( artifactDescriptorReader, "artifact descriptor reader cannot be null" ); 126 return this; 127 } 128 129 public DependencyCollector setVersionRangeResolver( VersionRangeResolver versionRangeResolver ) 130 { 131 this.versionRangeResolver = 132 requireNonNull( versionRangeResolver, "version range resolver cannot be null" ); 133 return this; 134 } 135 136 @SuppressWarnings( "checkstyle:methodlength" ) 137 @Override 138 public final CollectResult collectDependencies( RepositorySystemSession session, CollectRequest request ) 139 throws DependencyCollectionException 140 { 141 requireNonNull( session, "session cannot be null" ); 142 requireNonNull( request, "request cannot be null" ); 143 session = optimizeSession( session ); 144 145 RequestTrace trace = RequestTrace.newChild( request.getTrace(), request ); 146 147 CollectResult result = new CollectResult( request ); 148 149 DependencyTraverser depTraverser = session.getDependencyTraverser(); 150 VersionFilter verFilter = session.getVersionFilter(); 151 152 Dependency root = request.getRoot(); 153 List<RemoteRepository> repositories = request.getRepositories(); 154 List<Dependency> dependencies = request.getDependencies(); 155 List<Dependency> managedDependencies = request.getManagedDependencies(); 156 157 Map<String, Object> stats = new LinkedHashMap<>(); 158 long time1 = System.nanoTime(); 159 160 DefaultDependencyNode node; 161 if ( root != null ) 162 { 163 List<? extends Version> versions; 164 VersionRangeResult rangeResult; 165 try 166 { 167 VersionRangeRequest rangeRequest = 168 new VersionRangeRequest( root.getArtifact(), request.getRepositories(), 169 request.getRequestContext() ); 170 rangeRequest.setTrace( trace ); 171 rangeResult = versionRangeResolver.resolveVersionRange( session, rangeRequest ); 172 versions = filterVersions( root, rangeResult, verFilter, new DefaultVersionFilterContext( session ) ); 173 } 174 catch ( VersionRangeResolutionException e ) 175 { 176 result.addException( e ); 177 throw new DependencyCollectionException( result, e.getMessage() ); 178 } 179 180 Version version = versions.get( versions.size() - 1 ); 181 root = root.setArtifact( root.getArtifact().setVersion( version.toString() ) ); 182 183 ArtifactDescriptorResult descriptorResult; 184 try 185 { 186 ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest(); 187 descriptorRequest.setArtifact( root.getArtifact() ); 188 descriptorRequest.setRepositories( request.getRepositories() ); 189 descriptorRequest.setRequestContext( request.getRequestContext() ); 190 descriptorRequest.setTrace( trace ); 191 if ( isLackingDescriptor( root.getArtifact() ) ) 192 { 193 descriptorResult = new ArtifactDescriptorResult( descriptorRequest ); 194 } 195 else 196 { 197 descriptorResult = descriptorReader.readArtifactDescriptor( session, descriptorRequest ); 198 } 199 } 200 catch ( ArtifactDescriptorException e ) 201 { 202 result.addException( e ); 203 throw new DependencyCollectionException( result, e.getMessage() ); 204 } 205 206 root = root.setArtifact( descriptorResult.getArtifact() ); 207 208 if ( !session.isIgnoreArtifactDescriptorRepositories() ) 209 { 210 repositories = remoteRepositoryManager.aggregateRepositories( session, repositories, 211 descriptorResult.getRepositories(), 212 true ); 213 } 214 dependencies = mergeDeps( dependencies, descriptorResult.getDependencies() ); 215 managedDependencies = mergeDeps( managedDependencies, descriptorResult.getManagedDependencies() ); 216 217 node = new DefaultDependencyNode( root ); 218 node.setRequestContext( request.getRequestContext() ); 219 node.setRelocations( descriptorResult.getRelocations() ); 220 node.setVersionConstraint( rangeResult.getVersionConstraint() ); 221 node.setVersion( version ); 222 node.setAliases( descriptorResult.getAliases() ); 223 node.setRepositories( request.getRepositories() ); 224 } 225 else 226 { 227 node = new DefaultDependencyNode( request.getRootArtifact() ); 228 node.setRequestContext( request.getRequestContext() ); 229 node.setRepositories( request.getRepositories() ); 230 } 231 232 result.setRoot( node ); 233 234 boolean traverse = root == null || depTraverser == null || depTraverser.traverseDependency( root ); 235 String errorPath = null; 236 if ( traverse && !dependencies.isEmpty() ) 237 { 238 DataPool pool = new DataPool( session ); 239 240 DefaultDependencyCollectionContext context = new DefaultDependencyCollectionContext( 241 session, request.getRootArtifact(), root, managedDependencies ); 242 243 DefaultVersionFilterContext versionContext = new DefaultVersionFilterContext( session ); 244 245 Results results = new Results( result, session ); 246 247 doCollectDependencies( 248 session, trace, pool, context, versionContext, request, node, repositories, dependencies, 249 managedDependencies, results 250 ); 251 252 errorPath = results.getErrorPath(); 253 } 254 255 long time2 = System.nanoTime(); 256 257 DependencyGraphTransformer transformer = session.getDependencyGraphTransformer(); 258 if ( transformer != null ) 259 { 260 try 261 { 262 DefaultDependencyGraphTransformationContext context = 263 new DefaultDependencyGraphTransformationContext( session ); 264 context.put( TransformationContextKeys.STATS, stats ); 265 result.setRoot( transformer.transformGraph( node, context ) ); 266 } 267 catch ( RepositoryException e ) 268 { 269 result.addException( e ); 270 } 271 } 272 273 long time3 = System.nanoTime(); 274 if ( logger.isDebugEnabled() ) 275 { 276 stats.put( getClass().getSimpleName() + ".collectTime", time2 - time1 ); 277 stats.put( getClass().getSimpleName() + ".transformTime", time3 - time2 ); 278 logger.debug( "Dependency collection stats {}", stats ); 279 } 280 281 if ( errorPath != null ) 282 { 283 throw new DependencyCollectionException( result, "Failed to collect dependencies at " + errorPath ); 284 } 285 if ( !result.getExceptions().isEmpty() ) 286 { 287 throw new DependencyCollectionException( result ); 288 } 289 290 return result; 291 } 292 293 /** 294 * Creates child {@link RequestTrace} instance from passed in {@link RequestTrace} and parameters by creating 295 * {@link CollectStepDataImpl} instance out of passed in data. Caller must ensure that passed in parameters are 296 * NOT affected by threading (or that there is no multi threading involved). In other words, the passed in values 297 * should be immutable. 298 * 299 * @param trace The current trace instance. 300 * @param context The context from {@link CollectRequest#getRequestContext()}, never {@code null}. 301 * @param path List representing the path of dependency nodes, never {@code null}. Caller must ensure, that this 302 * list does not change during the lifetime of the requested {@link RequestTrace} instance. If it may 303 * change, simplest is to pass here a copy of used list. 304 * @param node Currently collected node, that collector came by following the passed in path. 305 * @return A child request trance instance, never {@code null}. 306 */ 307 protected RequestTrace collectStepTrace( RequestTrace trace, String context, List<DependencyNode> path, 308 Dependency node ) 309 { 310 return RequestTrace.newChild( 311 trace, 312 new CollectStepDataImpl( 313 context, 314 path, 315 node 316 ) 317 ); 318 } 319 320 @SuppressWarnings( "checkstyle:parameternumber" ) 321 protected abstract void doCollectDependencies( RepositorySystemSession session, RequestTrace trace, DataPool pool, 322 DefaultDependencyCollectionContext context, 323 DefaultVersionFilterContext versionContext, 324 CollectRequest request, DependencyNode node, 325 List<RemoteRepository> repositories, List<Dependency> dependencies, 326 List<Dependency> managedDependencies, Results results ); 327 328 protected RepositorySystemSession optimizeSession( RepositorySystemSession session ) 329 { 330 DefaultRepositorySystemSession optimized = new DefaultRepositorySystemSession( session ); 331 optimized.setArtifactTypeRegistry( CachingArtifactTypeRegistry.newInstance( session ) ); 332 return optimized; 333 } 334 335 protected List<Dependency> mergeDeps( List<Dependency> dominant, List<Dependency> recessive ) 336 { 337 List<Dependency> result; 338 if ( dominant == null || dominant.isEmpty() ) 339 { 340 result = recessive; 341 } 342 else if ( recessive == null || recessive.isEmpty() ) 343 { 344 result = dominant; 345 } 346 else 347 { 348 int initialCapacity = dominant.size() + recessive.size(); 349 result = new ArrayList<>( initialCapacity ); 350 Collection<String> ids = new HashSet<>( initialCapacity, 1.0f ); 351 for ( Dependency dependency : dominant ) 352 { 353 ids.add( getId( dependency.getArtifact() ) ); 354 result.add( dependency ); 355 } 356 for ( Dependency dependency : recessive ) 357 { 358 if ( !ids.contains( getId( dependency.getArtifact() ) ) ) 359 { 360 result.add( dependency ); 361 } 362 } 363 } 364 return result; 365 } 366 367 protected static String getId( Artifact a ) 368 { 369 return a.getGroupId() + ':' + a.getArtifactId() + ':' + a.getClassifier() + ':' + a.getExtension(); 370 } 371 372 @SuppressWarnings( "checkstyle:parameternumber" ) 373 protected static DefaultDependencyNode createDependencyNode( List<Artifact> relocations, 374 PremanagedDependency preManaged, 375 VersionRangeResult rangeResult, Version version, 376 Dependency d, Collection<Artifact> aliases, 377 List<RemoteRepository> repos, String requestContext ) 378 { 379 DefaultDependencyNode child = new DefaultDependencyNode( d ); 380 preManaged.applyTo( child ); 381 child.setRelocations( relocations ); 382 child.setVersionConstraint( rangeResult.getVersionConstraint() ); 383 child.setVersion( version ); 384 child.setAliases( aliases ); 385 child.setRepositories( repos ); 386 child.setRequestContext( requestContext ); 387 return child; 388 } 389 390 protected static DefaultDependencyNode createDependencyNode( List<Artifact> relocations, 391 PremanagedDependency preManaged, 392 VersionRangeResult rangeResult, Version version, 393 Dependency d, 394 ArtifactDescriptorResult descriptorResult, 395 DependencyNode cycleNode ) 396 { 397 DefaultDependencyNode child = 398 createDependencyNode( relocations, preManaged, rangeResult, version, d, descriptorResult.getAliases(), 399 cycleNode.getRepositories(), cycleNode.getRequestContext() ); 400 child.setChildren( cycleNode.getChildren() ); 401 return child; 402 } 403 404 protected static ArtifactDescriptorRequest createArtifactDescriptorRequest( String requestContext, 405 RequestTrace requestTrace, 406 List<RemoteRepository> repositories, 407 Dependency d ) 408 { 409 ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest(); 410 descriptorRequest.setArtifact( d.getArtifact() ); 411 descriptorRequest.setRepositories( repositories ); 412 descriptorRequest.setRequestContext( requestContext ); 413 descriptorRequest.setTrace( requestTrace ); 414 return descriptorRequest; 415 } 416 417 protected static VersionRangeRequest createVersionRangeRequest( String requestContext, 418 RequestTrace requestTrace, 419 List<RemoteRepository> repositories, 420 Dependency dependency ) 421 { 422 VersionRangeRequest rangeRequest = new VersionRangeRequest(); 423 rangeRequest.setArtifact( dependency.getArtifact() ); 424 rangeRequest.setRepositories( repositories ); 425 rangeRequest.setRequestContext( requestContext ); 426 rangeRequest.setTrace( requestTrace ); 427 return rangeRequest; 428 } 429 430 protected VersionRangeResult cachedResolveRangeResult( VersionRangeRequest rangeRequest, DataPool pool, 431 RepositorySystemSession session ) 432 throws VersionRangeResolutionException 433 { 434 Object key = pool.toKey( rangeRequest ); 435 VersionRangeResult rangeResult = pool.getConstraint( key, rangeRequest ); 436 if ( rangeResult == null ) 437 { 438 rangeResult = versionRangeResolver.resolveVersionRange( session, rangeRequest ); 439 pool.putConstraint( key, rangeResult ); 440 } 441 return rangeResult; 442 } 443 444 protected static boolean isLackingDescriptor( Artifact artifact ) 445 { 446 return artifact.getProperty( ArtifactProperties.LOCAL_PATH, null ) != null; 447 } 448 449 protected static List<RemoteRepository> getRemoteRepositories( ArtifactRepository repository, 450 List<RemoteRepository> repositories ) 451 { 452 if ( repository instanceof RemoteRepository ) 453 { 454 return Collections.singletonList( (RemoteRepository) repository ); 455 } 456 if ( repository != null ) 457 { 458 return Collections.emptyList(); 459 } 460 return repositories; 461 } 462 463 protected static List<? extends Version> filterVersions( Dependency dependency, VersionRangeResult rangeResult, 464 VersionFilter verFilter, 465 DefaultVersionFilterContext verContext ) 466 throws VersionRangeResolutionException 467 { 468 if ( rangeResult.getVersions().isEmpty() ) 469 { 470 throw new VersionRangeResolutionException( rangeResult, 471 "No versions available for " + dependency.getArtifact() 472 + " within specified range" ); 473 } 474 475 List<? extends Version> versions; 476 if ( verFilter != null && rangeResult.getVersionConstraint().getRange() != null ) 477 { 478 verContext.set( dependency, rangeResult ); 479 try 480 { 481 verFilter.filterVersions( verContext ); 482 } 483 catch ( RepositoryException e ) 484 { 485 throw new VersionRangeResolutionException( rangeResult, 486 "Failed to filter versions for " + dependency.getArtifact(), e ); 487 } 488 versions = verContext.get(); 489 if ( versions.isEmpty() ) 490 { 491 throw new VersionRangeResolutionException( rangeResult, 492 "No acceptable versions for " + dependency.getArtifact() + ": " + rangeResult.getVersions() ); 493 } 494 } 495 else 496 { 497 versions = rangeResult.getVersions(); 498 } 499 return versions; 500 } 501 502 /** 503 * Helper class used during collection. 504 */ 505 protected static class Results 506 { 507 508 private final CollectResult result; 509 510 final int maxExceptions; 511 512 final int maxCycles; 513 514 String errorPath; 515 516 public Results( CollectResult result, RepositorySystemSession session ) 517 { 518 this.result = result; 519 520 maxExceptions = 521 ConfigUtils.getInteger( session, CONFIG_PROP_MAX_EXCEPTIONS_DEFAULT, CONFIG_PROP_MAX_EXCEPTIONS ); 522 523 maxCycles = ConfigUtils.getInteger( session, CONFIG_PROP_MAX_CYCLES_DEFAULT, CONFIG_PROP_MAX_CYCLES ); 524 } 525 526 public String getErrorPath() 527 { 528 return errorPath; 529 } 530 531 public void addException( Dependency dependency, Exception e, List<DependencyNode> nodes ) 532 { 533 if ( maxExceptions < 0 || result.getExceptions().size() < maxExceptions ) 534 { 535 result.addException( e ); 536 if ( errorPath == null ) 537 { 538 StringBuilder buffer = new StringBuilder( 256 ); 539 for ( DependencyNode node : nodes ) 540 { 541 if ( buffer.length() > 0 ) 542 { 543 buffer.append( " -> " ); 544 } 545 Dependency dep = node.getDependency(); 546 if ( dep != null ) 547 { 548 buffer.append( dep.getArtifact() ); 549 } 550 } 551 if ( buffer.length() > 0 ) 552 { 553 buffer.append( " -> " ); 554 } 555 buffer.append( dependency.getArtifact() ); 556 errorPath = buffer.toString(); 557 } 558 } 559 } 560 561 public void addCycle( List<DependencyNode> nodes, int cycleEntry, Dependency dependency ) 562 { 563 if ( maxCycles < 0 || result.getCycles().size() < maxCycles ) 564 { 565 result.addCycle( new DefaultDependencyCycle( nodes, cycleEntry, dependency ) ); 566 } 567 } 568 } 569 570}