From d486deaedfc91d4b2175b6aeed58b57aa6a12156 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Mon, 4 Apr 2022 17:12:30 +0200 Subject: [PATCH 01/10] [MRESOLVER-248] Make BF and DF collectors coexists --- .../aether/impl/guice/AetherModule.java | 17 + .../collect/CachingArtifactTypeRegistry.java | 2 +- .../internal/impl/collect/DataPool.java | 20 +- .../DefaultDependencyCollectionContext.java | 4 +- .../collect/DefaultDependencyCollector.java | 899 +---------------- ...tDependencyGraphTransformationContext.java | 4 +- .../collect/DefaultVersionFilterContext.java | 4 +- .../collect/DependencyCollectorDelegate.java | 31 + .../collect/bf/BfDependencyCollector.java | 938 ++++++++++++++++++ .../BfDependencyCycle.java} | 7 +- .../BfProcessingContext.java} | 22 +- .../DefaultDependencyResolutionSkipper.java | 20 +- .../NeverDependencyResolutionSkipper.java | 8 +- .../BfDependencyCollectorTest.java} | 27 +- .../BfDependencyCollectorUseSkipTest.java} | 14 +- .../BfDependencyCycleTest.java} | 12 +- .../DependencyResolutionSkipperTest.java | 14 +- 17 files changed, 1097 insertions(+), 946 deletions(-) create mode 100644 maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java create mode 100644 maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java rename maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/{DefaultDependencyCycle.java => bf/BfDependencyCycle.java} (95%) rename maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/{DependencyProcessingContext.java => bf/BfProcessingContext.java} (76%) rename maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/{ => bf}/DefaultDependencyResolutionSkipper.java (95%) rename maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/{ => bf}/NeverDependencyResolutionSkipper.java (82%) rename maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/{DefaultDependencyCollectorTest.java => bf/BfDependencyCollectorTest.java} (98%) rename maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/{DefaultDependencyCollectorUseSkipTest.java => bf/BfDependencyCollectorUseSkipTest.java} (96%) rename maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/{DefaultDependencyCycleTest.java => bf/BfDependencyCycleTest.java} (90%) rename maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/{ => bf}/DependencyResolutionSkipperTest.java (99%) diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java index 9629dc979..7d7f9f0b4 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java @@ -48,6 +48,8 @@ import org.eclipse.aether.internal.impl.checksum.Sha256ChecksumAlgorithmFactory; import org.eclipse.aether.internal.impl.checksum.Sha512ChecksumAlgorithmFactory; import org.eclipse.aether.internal.impl.checksum.DefaultChecksumAlgorithmFactorySelector; +import org.eclipse.aether.internal.impl.collect.DependencyCollectorDelegate; +import org.eclipse.aether.internal.impl.collect.bf.BfDependencyCollector; import org.eclipse.aether.internal.impl.synccontext.DefaultSyncContextFactory; import org.eclipse.aether.internal.impl.synccontext.named.NamedLockFactorySelector; import org.eclipse.aether.internal.impl.synccontext.named.SimpleNamedLockFactorySelector; @@ -132,8 +134,12 @@ protected void configure() .to( DefaultRepositorySystem.class ).in( Singleton.class ); bind( ArtifactResolver.class ) // .to( DefaultArtifactResolver.class ).in( Singleton.class ); + bind( DependencyCollector.class ) // .to( DefaultDependencyCollector.class ).in( Singleton.class ); + bind( DependencyCollectorDelegate.class ).annotatedWith( Names.named( BfDependencyCollector.NAME ) ) + .to( BfDependencyCollector.class ).in( Singleton.class ); + bind( Deployer.class ) // .to( DefaultDeployer.class ).in( Singleton.class ); bind( Installer.class ) // @@ -223,6 +229,17 @@ Map provideChecksumSources( return providedChecksumsSource; } + @Provides + @Singleton + Map dependencyCollectorDelegates( + @Named( BfDependencyCollector.NAME ) DependencyCollectorDelegate bf + ) + { + Map providedChecksumsSource = new HashMap<>(); + providedChecksumsSource.put( BfDependencyCollector.NAME, bf ); + return providedChecksumsSource; + } + @Provides @Singleton Map provideChecksumTypes( diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/CachingArtifactTypeRegistry.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/CachingArtifactTypeRegistry.java index a26023440..bb03b142d 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/CachingArtifactTypeRegistry.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/CachingArtifactTypeRegistry.java @@ -29,7 +29,7 @@ /** * A short-lived artifact type registry that caches results from a presumedly slower type registry. */ -class CachingArtifactTypeRegistry +public class CachingArtifactTypeRegistry implements ArtifactTypeRegistry { diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DataPool.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DataPool.java index 4a145558d..d54a56347 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DataPool.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DataPool.java @@ -49,7 +49,7 @@ /** */ -final class DataPool +public final class DataPool { private static final String ARTIFACT_POOL = DataPool.class.getName() + "$Artifact"; @@ -58,7 +58,7 @@ final class DataPool private static final String DESCRIPTORS = DataPool.class.getName() + "$Descriptors"; - static final ArtifactDescriptorResult NO_DESCRIPTOR = + public static final ArtifactDescriptorResult NO_DESCRIPTOR = new ArtifactDescriptorResult( new ArtifactDescriptorRequest() ); private ObjectPool artifacts; @@ -72,7 +72,7 @@ final class DataPool private final Map> nodes = new HashMap<>( 256 ); @SuppressWarnings( "unchecked" ) - DataPool( RepositorySystemSession session ) + public DataPool( RepositorySystemSession session ) { RepositoryCache cache = session.getCache(); @@ -121,12 +121,12 @@ public Dependency intern( Dependency dependency ) return dependencies.intern( dependency ); } - Object toKey( ArtifactDescriptorRequest request ) + public Object toKey( ArtifactDescriptorRequest request ) { return request.getArtifact(); } - ArtifactDescriptorResult getDescriptor( Object key, ArtifactDescriptorRequest request ) + public ArtifactDescriptorResult getDescriptor( Object key, ArtifactDescriptorRequest request ) { Descriptor descriptor = descriptors.get( key ); if ( descriptor != null ) @@ -136,22 +136,22 @@ ArtifactDescriptorResult getDescriptor( Object key, ArtifactDescriptorRequest re return null; } - void putDescriptor( Object key, ArtifactDescriptorResult result ) + public void putDescriptor( Object key, ArtifactDescriptorResult result ) { descriptors.put( key, new GoodDescriptor( result ) ); } - void putDescriptor( Object key, ArtifactDescriptorException e ) + public void putDescriptor( Object key, ArtifactDescriptorException e ) { descriptors.put( key, BadDescriptor.INSTANCE ); } - Object toKey( VersionRangeRequest request ) + public Object toKey( VersionRangeRequest request ) { return new ConstraintKey( request ); } - VersionRangeResult getConstraint( Object key, VersionRangeRequest request ) + public VersionRangeResult getConstraint( Object key, VersionRangeRequest request ) { Constraint constraint = constraints.get( key ); if ( constraint != null ) @@ -161,7 +161,7 @@ VersionRangeResult getConstraint( Object key, VersionRangeRequest request ) return null; } - void putConstraint( Object key, VersionRangeResult result ) + public void putConstraint( Object key, VersionRangeResult result ) { constraints.put( key, new Constraint( result ) ); } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectionContext.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectionContext.java index 3bf4fe1a0..0c66fa79c 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectionContext.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectionContext.java @@ -29,7 +29,7 @@ /** * @see DefaultDependencyCollector */ -final class DefaultDependencyCollectionContext +public final class DefaultDependencyCollectionContext implements DependencyCollectionContext { @@ -41,7 +41,7 @@ final class DefaultDependencyCollectionContext private List managedDependencies; - DefaultDependencyCollectionContext( RepositorySystemSession session, Artifact artifact, + public DefaultDependencyCollectionContext( RepositorySystemSession session, Artifact artifact, Dependency dependency, List managedDependencies ) { this.session = session; diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java index a895bb9bd..1c0b6e956 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java @@ -19,914 +19,75 @@ * under the License. */ -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Queue; - -import static java.util.Objects.requireNonNull; -import static org.eclipse.aether.internal.impl.collect.DefaultDependencyCycle.find; - import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; -import org.eclipse.aether.DefaultRepositorySystemSession; -import org.eclipse.aether.RepositoryException; +import java.util.HashMap; +import java.util.Map; + import org.eclipse.aether.RepositorySystemSession; -import org.eclipse.aether.RequestTrace; -import org.eclipse.aether.artifact.Artifact; -import org.eclipse.aether.artifact.ArtifactProperties; import org.eclipse.aether.collection.CollectRequest; import org.eclipse.aether.collection.CollectResult; import org.eclipse.aether.collection.DependencyCollectionException; -import org.eclipse.aether.collection.DependencyGraphTransformer; -import org.eclipse.aether.collection.DependencyManagement; -import org.eclipse.aether.collection.DependencyManager; -import org.eclipse.aether.collection.DependencySelector; -import org.eclipse.aether.collection.DependencyTraverser; -import org.eclipse.aether.collection.VersionFilter; -import org.eclipse.aether.graph.DefaultDependencyNode; -import org.eclipse.aether.graph.Dependency; -import org.eclipse.aether.graph.DependencyNode; -import org.eclipse.aether.graph.Exclusion; -import org.eclipse.aether.impl.ArtifactDescriptorReader; import org.eclipse.aether.impl.DependencyCollector; -import org.eclipse.aether.impl.DependencyResolutionSkipper; -import org.eclipse.aether.impl.RemoteRepositoryManager; -import org.eclipse.aether.impl.VersionRangeResolver; -import org.eclipse.aether.repository.ArtifactRepository; -import org.eclipse.aether.repository.RemoteRepository; -import org.eclipse.aether.resolution.ArtifactDescriptorException; -import org.eclipse.aether.resolution.ArtifactDescriptorRequest; -import org.eclipse.aether.resolution.ArtifactDescriptorResult; -import org.eclipse.aether.resolution.VersionRangeRequest; -import org.eclipse.aether.resolution.VersionRangeResolutionException; -import org.eclipse.aether.resolution.VersionRangeResult; +import org.eclipse.aether.internal.impl.collect.bf.BfDependencyCollector; import org.eclipse.aether.spi.locator.Service; import org.eclipse.aether.spi.locator.ServiceLocator; import org.eclipse.aether.util.ConfigUtils; -import org.eclipse.aether.util.graph.manager.DependencyManagerUtils; -import org.eclipse.aether.util.graph.transformer.TransformationContextKeys; -import org.eclipse.aether.version.Version; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; + +import static java.util.Objects.requireNonNull; /** + * */ @Singleton @Named public class DefaultDependencyCollector - implements DependencyCollector, Service + implements DependencyCollector, Service { + private static final String CONFIG_PROP_COLLECTOR_IMPL = "aether.collector.impl"; - /** - * The key in the repository session's {@link org.eclipse.aether.RepositorySystemSession#getConfigProperties() - * configuration properties} used to store a {@link Boolean} flag controlling the resolver's skip mode. - * - * @since 1.8.0 - */ - public static final String CONFIG_PROP_USE_SKIP = "aether.dependencyCollector.useSkip"; + private static final String DEFAULT_COLLECTOR_IMPL = BfDependencyCollector.NAME; + + private final Map delegates; /** - * The default value for {@link #CONFIG_PROP_USE_SKIP}, {@code true}. + * Default ctor for SL. * - * @since 1.8.0 + * @deprecated SL is to be removed. */ - public static final boolean CONFIG_PROP_USE_SKIP_DEFAULT = true; - - private static final String CONFIG_PROP_MAX_EXCEPTIONS = "aether.dependencyCollector.maxExceptions"; - - private static final int CONFIG_PROP_MAX_EXCEPTIONS_DEFAULT = 50; - - private static final String CONFIG_PROP_MAX_CYCLES = "aether.dependencyCollector.maxCycles"; - - private static final int CONFIG_PROP_MAX_CYCLES_DEFAULT = 10; - - private static final Logger LOGGER = LoggerFactory.getLogger( DefaultDependencyCollector.class ); - - private RemoteRepositoryManager remoteRepositoryManager; - - private ArtifactDescriptorReader descriptorReader; - - private VersionRangeResolver versionRangeResolver; - + @Deprecated public DefaultDependencyCollector() { - // enables default constructor + this.delegates = new HashMap<>(); } @Inject - DefaultDependencyCollector( RemoteRepositoryManager remoteRepositoryManager, - ArtifactDescriptorReader artifactDescriptorReader, - VersionRangeResolver versionRangeResolver ) + public DefaultDependencyCollector( Map delegates ) { - setRemoteRepositoryManager( remoteRepositoryManager ); - setArtifactDescriptorReader( artifactDescriptorReader ); - setVersionRangeResolver( versionRangeResolver ); + this.delegates = requireNonNull( delegates ); } + @Override public void initService( ServiceLocator locator ) { - setRemoteRepositoryManager( locator.getService( RemoteRepositoryManager.class ) ); - setArtifactDescriptorReader( locator.getService( ArtifactDescriptorReader.class ) ); - setVersionRangeResolver( locator.getService( VersionRangeResolver.class ) ); - } - - public DefaultDependencyCollector setRemoteRepositoryManager( RemoteRepositoryManager remoteRepositoryManager ) - { - this.remoteRepositoryManager = - requireNonNull( remoteRepositoryManager, "remote repository provider cannot be null" ); - return this; - } - - public DefaultDependencyCollector setArtifactDescriptorReader( ArtifactDescriptorReader artifactDescriptorReader ) - { - descriptorReader = requireNonNull( artifactDescriptorReader, "artifact descriptor reader cannot be null" ); - return this; + BfDependencyCollector bf = new BfDependencyCollector(); + bf.initService( locator ); + this.delegates.put( BfDependencyCollector.NAME, bf ); } - public DefaultDependencyCollector setVersionRangeResolver( VersionRangeResolver versionRangeResolver ) - { - this.versionRangeResolver = - requireNonNull( versionRangeResolver, "version range resolver cannot be null" ); - return this; - } - - @SuppressWarnings( "checkstyle:methodlength" ) + @Override public CollectResult collectDependencies( RepositorySystemSession session, CollectRequest request ) - throws DependencyCollectionException - { - requireNonNull( session, "session cannot be null" ); - requireNonNull( request, "request cannot be null" ); - session = optimizeSession( session ); - - boolean useSkip = ConfigUtils.getBoolean( - session, CONFIG_PROP_USE_SKIP_DEFAULT, CONFIG_PROP_USE_SKIP - ); - if ( useSkip ) - { - LOGGER.debug( "Collector skip mode enabled" ); - } - - RequestTrace trace = RequestTrace.newChild( request.getTrace(), request ); - - CollectResult result = new CollectResult( request ); - - DependencySelector depSelector = session.getDependencySelector(); - DependencyManager depManager = session.getDependencyManager(); - DependencyTraverser depTraverser = session.getDependencyTraverser(); - VersionFilter verFilter = session.getVersionFilter(); - - Dependency root = request.getRoot(); - List repositories = request.getRepositories(); - List dependencies = request.getDependencies(); - List managedDependencies = request.getManagedDependencies(); - - Map stats = new LinkedHashMap<>(); - long time1 = System.nanoTime(); - - DefaultDependencyNode node; - if ( root != null ) - { - List versions; - VersionRangeResult rangeResult; - try - { - VersionRangeRequest rangeRequest = - new VersionRangeRequest( root.getArtifact(), request.getRepositories(), - request.getRequestContext() ); - rangeRequest.setTrace( trace ); - rangeResult = versionRangeResolver.resolveVersionRange( session, rangeRequest ); - versions = filterVersions( root, rangeResult, verFilter, new DefaultVersionFilterContext( session ) ); - } - catch ( VersionRangeResolutionException e ) - { - result.addException( e ); - throw new DependencyCollectionException( result, e.getMessage() ); - } - - Version version = versions.get( versions.size() - 1 ); - root = root.setArtifact( root.getArtifact().setVersion( version.toString() ) ); - - ArtifactDescriptorResult descriptorResult; - try - { - ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest(); - descriptorRequest.setArtifact( root.getArtifact() ); - descriptorRequest.setRepositories( request.getRepositories() ); - descriptorRequest.setRequestContext( request.getRequestContext() ); - descriptorRequest.setTrace( trace ); - if ( isLackingDescriptor( root.getArtifact() ) ) - { - descriptorResult = new ArtifactDescriptorResult( descriptorRequest ); - } - else - { - descriptorResult = descriptorReader.readArtifactDescriptor( session, descriptorRequest ); - } - } - catch ( ArtifactDescriptorException e ) - { - result.addException( e ); - throw new DependencyCollectionException( result, e.getMessage() ); - } - - root = root.setArtifact( descriptorResult.getArtifact() ); - - if ( !session.isIgnoreArtifactDescriptorRepositories() ) - { - repositories = remoteRepositoryManager.aggregateRepositories( session, repositories, - descriptorResult.getRepositories(), - true ); - } - dependencies = mergeDeps( dependencies, descriptorResult.getDependencies() ); - managedDependencies = mergeDeps( managedDependencies, descriptorResult.getManagedDependencies() ); - - node = new DefaultDependencyNode( root ); - node.setRequestContext( request.getRequestContext() ); - node.setRelocations( descriptorResult.getRelocations() ); - node.setVersionConstraint( rangeResult.getVersionConstraint() ); - node.setVersion( version ); - node.setAliases( descriptorResult.getAliases() ); - node.setRepositories( request.getRepositories() ); - } - else - { - node = new DefaultDependencyNode( request.getRootArtifact() ); - node.setRequestContext( request.getRequestContext() ); - node.setRepositories( request.getRepositories() ); - } - - result.setRoot( node ); - - boolean traverse = root == null || depTraverser == null || depTraverser.traverseDependency( root ); - String errorPath = null; - if ( traverse && !dependencies.isEmpty() ) - { - DataPool pool = new DataPool( session ); - - DefaultDependencyCollectionContext context = - new DefaultDependencyCollectionContext( session, request.getRootArtifact(), root, managedDependencies ); - - DefaultVersionFilterContext versionContext = new DefaultVersionFilterContext( session ); - - Args args = - new Args( session, trace, pool, context, versionContext, request, - useSkip ? new DefaultDependencyResolutionSkipper() - : NeverDependencyResolutionSkipper.INSTANCE ); - Results results = new Results( result, session ); - - DependencySelector rootDepSelector = - depSelector != null ? depSelector.deriveChildSelector( context ) : null; - DependencyManager rootDepManager = depManager != null ? depManager.deriveChildManager( context ) : null; - DependencyTraverser rootDepTraverser = - depTraverser != null ? depTraverser.deriveChildTraverser( context ) : null; - VersionFilter rootVerFilter = verFilter != null ? verFilter.deriveChildFilter( context ) : null; - - List parents = Collections.singletonList( node ); - for ( Dependency dependency : dependencies ) - { - args.dependencyProcessingQueue.add( - new DependencyProcessingContext( rootDepSelector, rootDepManager, rootDepTraverser, - rootVerFilter, repositories, managedDependencies, parents, - dependency ) ); - } - - while ( !args.dependencyProcessingQueue.isEmpty() ) - { - processDependency( args, results, args.dependencyProcessingQueue.remove(), Collections.emptyList(), - false ); - } - - args.skipper.report(); - errorPath = results.errorPath; - } - - long time2 = System.nanoTime(); - - DependencyGraphTransformer transformer = session.getDependencyGraphTransformer(); - if ( transformer != null ) - { - try - { - DefaultDependencyGraphTransformationContext context = - new DefaultDependencyGraphTransformationContext( session ); - context.put( TransformationContextKeys.STATS, stats ); - result.setRoot( transformer.transformGraph( node, context ) ); - } - catch ( RepositoryException e ) - { - result.addException( e ); - } - } - - long time3 = System.nanoTime(); - stats.put( "DefaultDependencyCollector.collectTime", time2 - time1 ); - stats.put( "DefaultDependencyCollector.transformTime", time3 - time2 ); - LOGGER.debug( "Dependency collection stats {}", stats ); - - if ( errorPath != null ) - { - throw new DependencyCollectionException( result, "Failed to collect dependencies at " + errorPath ); - } - if ( !result.getExceptions().isEmpty() ) - { - throw new DependencyCollectionException( result ); - } - - return result; - } - - private static RepositorySystemSession optimizeSession( RepositorySystemSession session ) - { - DefaultRepositorySystemSession optimized = new DefaultRepositorySystemSession( session ); - optimized.setArtifactTypeRegistry( CachingArtifactTypeRegistry.newInstance( session ) ); - return optimized; - } - - private List mergeDeps( List dominant, List recessive ) + throws DependencyCollectionException { - List result; - if ( dominant == null || dominant.isEmpty() ) - { - result = recessive; - } - else if ( recessive == null || recessive.isEmpty() ) + String delegateName = ConfigUtils.getString( session, DEFAULT_COLLECTOR_IMPL, CONFIG_PROP_COLLECTOR_IMPL ); + DependencyCollectorDelegate delegate = delegates.get( delegateName ); + if ( delegate == null ) { - result = dominant; + throw new IllegalArgumentException( "Unknown collector impl: '" + delegateName + + "', known implementations are " + delegates.keySet() ); } - else - { - int initialCapacity = dominant.size() + recessive.size(); - result = new ArrayList<>( initialCapacity ); - Collection ids = new HashSet<>( initialCapacity, 1.0f ); - for ( Dependency dependency : dominant ) - { - ids.add( getId( dependency.getArtifact() ) ); - result.add( dependency ); - } - for ( Dependency dependency : recessive ) - { - if ( !ids.contains( getId( dependency.getArtifact() ) ) ) - { - result.add( dependency ); - } - } - } - return result; - } - - private static String getId( Artifact a ) - { - return a.getGroupId() + ':' + a.getArtifactId() + ':' + a.getClassifier() + ':' + a.getExtension(); + return delegate.collectDependencies( session, request ); } - - @SuppressWarnings( "checkstyle:parameternumber" ) - private void processDependency( Args args, Results results, DependencyProcessingContext context, - List relocations, boolean disableVersionManagement ) - { - - if ( context.depSelector != null && !context.depSelector.selectDependency( context.dependency ) ) - { - return; - } - - PremanagedDependency preManaged = - PremanagedDependency.create( context.depManager, context.dependency, disableVersionManagement, - args.premanagedState ); - Dependency dependency = preManaged.managedDependency; - - boolean noDescriptor = isLackingDescriptor( dependency.getArtifact() ); - - boolean traverse = - !noDescriptor && ( context.depTraverser == null || context.depTraverser.traverseDependency( - dependency ) ); - - List versions; - VersionRangeResult rangeResult; - try - { - VersionRangeRequest rangeRequest = createVersionRangeRequest( args, context.repositories, dependency ); - - rangeResult = cachedResolveRangeResult( rangeRequest, args.pool, args.session ); - - versions = filterVersions( dependency, rangeResult, context.verFilter, args.versionContext ); - } - catch ( VersionRangeResolutionException e ) - { - results.addException( dependency, e, context.parents ); - return; - } - - //Resolve newer version first to maximize benefits of skipper - Collections.reverse( versions ); - for ( Version version : versions ) - { - Artifact originalArtifact = dependency.getArtifact().setVersion( version.toString() ); - Dependency d = dependency.setArtifact( originalArtifact ); - - ArtifactDescriptorRequest descriptorRequest = - createArtifactDescriptorRequest( args, context.repositories, d ); - - final ArtifactDescriptorResult descriptorResult = - noDescriptor - ? new ArtifactDescriptorResult( descriptorRequest ) - : resolveCachedArtifactDescriptor( args.pool, descriptorRequest, args.session, - context.withDependency( d ), results ); - - if ( descriptorResult != null ) - { - d = d.setArtifact( descriptorResult.getArtifact() ); - - int cycleEntry = find( context.parents, d.getArtifact() ); - if ( cycleEntry >= 0 ) - { - results.addCycle( context.parents, cycleEntry, d ); - DependencyNode cycleNode = context.parents.get( cycleEntry ); - if ( cycleNode.getDependency() != null ) - { - DefaultDependencyNode child = - createDependencyNode( relocations, preManaged, rangeResult, version, d, - descriptorResult, cycleNode ); - context.getParent().getChildren().add( child ); - continue; - } - } - - if ( !descriptorResult.getRelocations().isEmpty() ) - { - boolean disableVersionManagementSubsequently = - originalArtifact.getGroupId().equals( d.getArtifact().getGroupId() ) - && originalArtifact.getArtifactId().equals( d.getArtifact().getArtifactId() ); - - processDependency( args, results, context.withDependency( d ), descriptorResult.getRelocations(), - disableVersionManagementSubsequently ); - return; - } - else - { - d = args.pool.intern( d.setArtifact( args.pool.intern( d.getArtifact() ) ) ); - - List repos = - getRemoteRepositories( rangeResult.getRepository( version ), context.repositories ); - - DefaultDependencyNode child = - createDependencyNode( relocations, preManaged, rangeResult, version, d, - descriptorResult.getAliases(), repos, args.request.getRequestContext() ); - - context.getParent().getChildren().add( child ); - - boolean recurse = traverse && !descriptorResult.getDependencies().isEmpty(); - if ( recurse ) - { - doRecurse( args, context.withDependency( d ), descriptorResult, child ); - } - } - } - else - { - List repos = - getRemoteRepositories( rangeResult.getRepository( version ), context.repositories ); - DefaultDependencyNode child = - createDependencyNode( relocations, preManaged, rangeResult, version, d, null, repos, - args.request.getRequestContext() ); - context.getParent().getChildren().add( child ); - } - } - } - - @SuppressWarnings( "checkstyle:parameternumber" ) - private void doRecurse( Args args, DependencyProcessingContext parentContext, - ArtifactDescriptorResult descriptorResult, DefaultDependencyNode child ) - { - DefaultDependencyCollectionContext context = args.collectionContext; - context.set( parentContext.dependency, descriptorResult.getManagedDependencies() ); - - DependencySelector childSelector = - parentContext.depSelector != null ? parentContext.depSelector.deriveChildSelector( context ) : null; - DependencyManager childManager = - parentContext.depManager != null ? parentContext.depManager.deriveChildManager( context ) : null; - DependencyTraverser childTraverser = - parentContext.depTraverser != null ? parentContext.depTraverser.deriveChildTraverser( context ) : null; - VersionFilter childFilter = - parentContext.verFilter != null ? parentContext.verFilter.deriveChildFilter( context ) : null; - - final List childRepos = - args.ignoreRepos - ? parentContext.repositories - : remoteRepositoryManager.aggregateRepositories( args.session, parentContext.repositories, - descriptorResult.getRepositories(), true ); - - Object key = - args.pool.toKey( parentContext.dependency.getArtifact(), childRepos, childSelector, childManager, - childTraverser, childFilter ); - - List children = args.pool.getChildren( key ); - if ( children == null ) - { - boolean skipResolution = args.skipper.skipResolution( child, parentContext.parents ); - if ( !skipResolution ) - { - List parents = new ArrayList<>( parentContext.parents.size() + 1 ); - parents.addAll( parentContext.parents ); - parents.add( child ); - for ( Dependency dependency : descriptorResult.getDependencies() ) - { - args.dependencyProcessingQueue.add( - new DependencyProcessingContext( childSelector, childManager, childTraverser, childFilter, - childRepos, descriptorResult.getManagedDependencies(), parents, dependency ) ); - } - args.pool.putChildren( key, child.getChildren() ); - args.skipper.cache( child, parents ); - } - } - else - { - child.setChildren( children ); - } - } - - private ArtifactDescriptorResult resolveCachedArtifactDescriptor( DataPool pool, - ArtifactDescriptorRequest descriptorRequest, - RepositorySystemSession session, - DependencyProcessingContext context, - Results results ) - { - Object key = pool.toKey( descriptorRequest ); - ArtifactDescriptorResult descriptorResult = pool.getDescriptor( key, descriptorRequest ); - if ( descriptorResult == null ) - { - try - { - descriptorResult = descriptorReader.readArtifactDescriptor( session, descriptorRequest ); - pool.putDescriptor( key, descriptorResult ); - } - catch ( ArtifactDescriptorException e ) - { - results.addException( context.dependency, e, context.parents ); - pool.putDescriptor( key, e ); - return null; - } - - } - else if ( descriptorResult == DataPool.NO_DESCRIPTOR ) - { - return null; - } - - return descriptorResult; - } - - @SuppressWarnings( "checkstyle:parameternumber" ) - private static DefaultDependencyNode createDependencyNode( List relocations, - PremanagedDependency preManaged, - VersionRangeResult rangeResult, Version version, - Dependency d, Collection aliases, - List repos, String requestContext ) - { - DefaultDependencyNode child = new DefaultDependencyNode( d ); - preManaged.applyTo( child ); - child.setRelocations( relocations ); - child.setVersionConstraint( rangeResult.getVersionConstraint() ); - child.setVersion( version ); - child.setAliases( aliases ); - child.setRepositories( repos ); - child.setRequestContext( requestContext ); - return child; - } - - private static DefaultDependencyNode createDependencyNode( List relocations, - PremanagedDependency preManaged, - VersionRangeResult rangeResult, Version version, - Dependency d, ArtifactDescriptorResult descriptorResult, - DependencyNode cycleNode ) - { - DefaultDependencyNode child = - createDependencyNode( relocations, preManaged, rangeResult, version, d, descriptorResult.getAliases(), - cycleNode.getRepositories(), cycleNode.getRequestContext() ); - child.setChildren( cycleNode.getChildren() ); - return child; - } - - private static ArtifactDescriptorRequest createArtifactDescriptorRequest( Args args, - List repositories, - Dependency d ) - { - ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest(); - descriptorRequest.setArtifact( d.getArtifact() ); - descriptorRequest.setRepositories( repositories ); - descriptorRequest.setRequestContext( args.request.getRequestContext() ); - descriptorRequest.setTrace( args.trace ); - return descriptorRequest; - } - - private static VersionRangeRequest createVersionRangeRequest( Args args, List repositories, - Dependency dependency ) - { - VersionRangeRequest rangeRequest = new VersionRangeRequest(); - rangeRequest.setArtifact( dependency.getArtifact() ); - rangeRequest.setRepositories( repositories ); - rangeRequest.setRequestContext( args.request.getRequestContext() ); - rangeRequest.setTrace( args.trace ); - return rangeRequest; - } - - private VersionRangeResult cachedResolveRangeResult( VersionRangeRequest rangeRequest, DataPool pool, - RepositorySystemSession session ) - throws VersionRangeResolutionException - { - Object key = pool.toKey( rangeRequest ); - VersionRangeResult rangeResult = pool.getConstraint( key, rangeRequest ); - if ( rangeResult == null ) - { - rangeResult = versionRangeResolver.resolveVersionRange( session, rangeRequest ); - pool.putConstraint( key, rangeResult ); - } - return rangeResult; - } - - private static boolean isLackingDescriptor( Artifact artifact ) - { - return artifact.getProperty( ArtifactProperties.LOCAL_PATH, null ) != null; - } - - private static List getRemoteRepositories( ArtifactRepository repository, - List repositories ) - { - if ( repository instanceof RemoteRepository ) - { - return Collections.singletonList( (RemoteRepository) repository ); - } - if ( repository != null ) - { - return Collections.emptyList(); - } - return repositories; - } - - private static List filterVersions( Dependency dependency, VersionRangeResult rangeResult, - VersionFilter verFilter, - DefaultVersionFilterContext verContext ) - throws VersionRangeResolutionException - { - if ( rangeResult.getVersions().isEmpty() ) - { - throw new VersionRangeResolutionException( rangeResult, - "No versions available for " + dependency.getArtifact() - + " within specified range" ); - } - - List versions; - if ( verFilter != null && rangeResult.getVersionConstraint().getRange() != null ) - { - verContext.set( dependency, rangeResult ); - try - { - verFilter.filterVersions( verContext ); - } - catch ( RepositoryException e ) - { - throw new VersionRangeResolutionException( rangeResult, - "Failed to filter versions for " + dependency.getArtifact() - + ": " + e.getMessage(), e ); - } - versions = verContext.get(); - if ( versions.isEmpty() ) - { - throw new VersionRangeResolutionException( rangeResult, - "No acceptable versions for " + dependency.getArtifact() - + ": " + rangeResult.getVersions() ); - } - } - else - { - versions = rangeResult.getVersions(); - } - return versions; - } - - static class Args - { - - final RepositorySystemSession session; - - final boolean ignoreRepos; - - final boolean premanagedState; - - final RequestTrace trace; - - final DataPool pool; - - final Queue dependencyProcessingQueue = new ArrayDeque<>( 128 ); - - final DefaultDependencyCollectionContext collectionContext; - - final DefaultVersionFilterContext versionContext; - - final CollectRequest request; - - final DependencyResolutionSkipper skipper; - - Args( RepositorySystemSession session, RequestTrace trace, DataPool pool, - DefaultDependencyCollectionContext collectionContext, DefaultVersionFilterContext versionContext, - CollectRequest request, DependencyResolutionSkipper skipper ) - { - this.session = session; - this.request = request; - this.ignoreRepos = session.isIgnoreArtifactDescriptorRepositories(); - this.premanagedState = ConfigUtils.getBoolean( session, false, DependencyManagerUtils.CONFIG_PROP_VERBOSE ); - this.trace = trace; - this.pool = pool; - this.collectionContext = collectionContext; - this.versionContext = versionContext; - this.skipper = skipper; - } - - } - - static class Results - { - - private final CollectResult result; - - final int maxExceptions; - - final int maxCycles; - - String errorPath; - - Results( CollectResult result, RepositorySystemSession session ) - { - this.result = result; - - maxExceptions = - ConfigUtils.getInteger( session, CONFIG_PROP_MAX_EXCEPTIONS_DEFAULT, CONFIG_PROP_MAX_EXCEPTIONS ); - - maxCycles = ConfigUtils.getInteger( session, CONFIG_PROP_MAX_CYCLES_DEFAULT, CONFIG_PROP_MAX_CYCLES ); - } - - public void addException( Dependency dependency, Exception e, List nodes ) - { - if ( maxExceptions < 0 || result.getExceptions().size() < maxExceptions ) - { - result.addException( e ); - if ( errorPath == null ) - { - StringBuilder buffer = new StringBuilder( 256 ); - for ( DependencyNode node : nodes ) - { - if ( buffer.length() > 0 ) - { - buffer.append( " -> " ); - } - Dependency dep = node.getDependency(); - if ( dep != null ) - { - buffer.append( dep.getArtifact() ); - } - } - if ( buffer.length() > 0 ) - { - buffer.append( " -> " ); - } - buffer.append( dependency.getArtifact() ); - errorPath = buffer.toString(); - } - } - } - - public void addCycle( List nodes, int cycleEntry, Dependency dependency ) - { - if ( maxCycles < 0 || result.getCycles().size() < maxCycles ) - { - result.addCycle( new DefaultDependencyCycle( nodes, cycleEntry, dependency ) ); - } - } - - } - - static class PremanagedDependency - { - - final String premanagedVersion; - - final String premanagedScope; - - final Boolean premanagedOptional; - - /** - * @since 1.1.0 - */ - final Collection premanagedExclusions; - - /** - * @since 1.1.0 - */ - final Map premanagedProperties; - - final int managedBits; - - final Dependency managedDependency; - - final boolean premanagedState; - - @SuppressWarnings( "checkstyle:parameternumber" ) - PremanagedDependency( String premanagedVersion, String premanagedScope, Boolean premanagedOptional, - Collection premanagedExclusions, Map premanagedProperties, - int managedBits, Dependency managedDependency, boolean premanagedState ) - { - this.premanagedVersion = premanagedVersion; - this.premanagedScope = premanagedScope; - this.premanagedOptional = premanagedOptional; - this.premanagedExclusions = - premanagedExclusions != null - ? Collections.unmodifiableCollection( new ArrayList<>( premanagedExclusions ) ) - : null; - - this.premanagedProperties = - premanagedProperties != null - ? Collections.unmodifiableMap( new HashMap<>( premanagedProperties ) ) - : null; - - this.managedBits = managedBits; - this.managedDependency = managedDependency; - this.premanagedState = premanagedState; - } - - static PremanagedDependency create( DependencyManager depManager, Dependency dependency, - boolean disableVersionManagement, boolean premanagedState ) - { - DependencyManagement depMngt = depManager != null ? depManager.manageDependency( dependency ) : null; - - int managedBits = 0; - String premanagedVersion = null; - String premanagedScope = null; - Boolean premanagedOptional = null; - Collection premanagedExclusions = null; - Map premanagedProperties = null; - - if ( depMngt != null ) - { - if ( depMngt.getVersion() != null && !disableVersionManagement ) - { - Artifact artifact = dependency.getArtifact(); - premanagedVersion = artifact.getVersion(); - dependency = dependency.setArtifact( artifact.setVersion( depMngt.getVersion() ) ); - managedBits |= DependencyNode.MANAGED_VERSION; - } - if ( depMngt.getProperties() != null ) - { - Artifact artifact = dependency.getArtifact(); - premanagedProperties = artifact.getProperties(); - dependency = dependency.setArtifact( artifact.setProperties( depMngt.getProperties() ) ); - managedBits |= DependencyNode.MANAGED_PROPERTIES; - } - if ( depMngt.getScope() != null ) - { - premanagedScope = dependency.getScope(); - dependency = dependency.setScope( depMngt.getScope() ); - managedBits |= DependencyNode.MANAGED_SCOPE; - } - if ( depMngt.getOptional() != null ) - { - premanagedOptional = dependency.isOptional(); - dependency = dependency.setOptional( depMngt.getOptional() ); - managedBits |= DependencyNode.MANAGED_OPTIONAL; - } - if ( depMngt.getExclusions() != null ) - { - premanagedExclusions = dependency.getExclusions(); - dependency = dependency.setExclusions( depMngt.getExclusions() ); - managedBits |= DependencyNode.MANAGED_EXCLUSIONS; - } - } - return new PremanagedDependency( premanagedVersion, premanagedScope, premanagedOptional, - premanagedExclusions, premanagedProperties, managedBits, dependency, - premanagedState ); - - } - - public void applyTo( DefaultDependencyNode child ) - { - child.setManagedBits( managedBits ); - if ( premanagedState ) - { - child.setData( DependencyManagerUtils.NODE_DATA_PREMANAGED_VERSION, premanagedVersion ); - child.setData( DependencyManagerUtils.NODE_DATA_PREMANAGED_SCOPE, premanagedScope ); - child.setData( DependencyManagerUtils.NODE_DATA_PREMANAGED_OPTIONAL, premanagedOptional ); - child.setData( DependencyManagerUtils.NODE_DATA_PREMANAGED_EXCLUSIONS, premanagedExclusions ); - child.setData( DependencyManagerUtils.NODE_DATA_PREMANAGED_PROPERTIES, premanagedProperties ); - } - } - - } - } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyGraphTransformationContext.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyGraphTransformationContext.java index 41c012699..28ea74725 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyGraphTransformationContext.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyGraphTransformationContext.java @@ -28,7 +28,7 @@ /** */ -class DefaultDependencyGraphTransformationContext +public class DefaultDependencyGraphTransformationContext implements DependencyGraphTransformationContext { @@ -36,7 +36,7 @@ class DefaultDependencyGraphTransformationContext private final Map map; - DefaultDependencyGraphTransformationContext( RepositorySystemSession session ) + public DefaultDependencyGraphTransformationContext( RepositorySystemSession session ) { this.session = session; this.map = new HashMap<>(); diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultVersionFilterContext.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultVersionFilterContext.java index bfea0626e..55b6fba42 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultVersionFilterContext.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultVersionFilterContext.java @@ -36,7 +36,7 @@ /** * @see DefaultDependencyCollector */ -final class DefaultVersionFilterContext +public final class DefaultVersionFilterContext implements VersionFilter.VersionFilterContext { private final RepositorySystemSession session; @@ -47,7 +47,7 @@ final class DefaultVersionFilterContext private List versions; - DefaultVersionFilterContext( RepositorySystemSession session ) + public DefaultVersionFilterContext( RepositorySystemSession session ) { this.session = session; } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java new file mode 100644 index 000000000..5d17efc6c --- /dev/null +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java @@ -0,0 +1,31 @@ +package org.eclipse.aether.internal.impl.collect; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.eclipse.aether.impl.DependencyCollector; + +/** + * The delegate to the actual implementation. + * + * @since 1.8.0 + */ +public interface DependencyCollectorDelegate extends DependencyCollector +{ +} diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java new file mode 100644 index 000000000..4836d6357 --- /dev/null +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java @@ -0,0 +1,938 @@ +package org.eclipse.aether.internal.impl.collect.bf; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Queue; + +import org.eclipse.aether.DefaultRepositorySystemSession; +import org.eclipse.aether.RepositoryException; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.RequestTrace; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.artifact.ArtifactProperties; +import org.eclipse.aether.collection.CollectRequest; +import org.eclipse.aether.collection.CollectResult; +import org.eclipse.aether.collection.DependencyCollectionException; +import org.eclipse.aether.collection.DependencyGraphTransformer; +import org.eclipse.aether.collection.DependencyManagement; +import org.eclipse.aether.collection.DependencyManager; +import org.eclipse.aether.collection.DependencySelector; +import org.eclipse.aether.collection.DependencyTraverser; +import org.eclipse.aether.collection.VersionFilter; +import org.eclipse.aether.graph.DefaultDependencyNode; +import org.eclipse.aether.graph.Dependency; +import org.eclipse.aether.graph.DependencyNode; +import org.eclipse.aether.graph.Exclusion; +import org.eclipse.aether.impl.ArtifactDescriptorReader; +import org.eclipse.aether.impl.DependencyResolutionSkipper; +import org.eclipse.aether.impl.RemoteRepositoryManager; +import org.eclipse.aether.impl.VersionRangeResolver; +import org.eclipse.aether.internal.impl.collect.CachingArtifactTypeRegistry; +import org.eclipse.aether.internal.impl.collect.DataPool; +import org.eclipse.aether.internal.impl.collect.DefaultDependencyCollectionContext; +import org.eclipse.aether.internal.impl.collect.DefaultDependencyGraphTransformationContext; +import org.eclipse.aether.internal.impl.collect.DefaultVersionFilterContext; +import org.eclipse.aether.internal.impl.collect.DependencyCollectorDelegate; +import org.eclipse.aether.repository.ArtifactRepository; +import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.resolution.ArtifactDescriptorException; +import org.eclipse.aether.resolution.ArtifactDescriptorRequest; +import org.eclipse.aether.resolution.ArtifactDescriptorResult; +import org.eclipse.aether.resolution.VersionRangeRequest; +import org.eclipse.aether.resolution.VersionRangeResolutionException; +import org.eclipse.aether.resolution.VersionRangeResult; +import org.eclipse.aether.spi.locator.Service; +import org.eclipse.aether.spi.locator.ServiceLocator; +import org.eclipse.aether.util.ConfigUtils; +import org.eclipse.aether.util.graph.manager.DependencyManagerUtils; +import org.eclipse.aether.util.graph.transformer.TransformationContextKeys; +import org.eclipse.aether.version.Version; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static java.util.Objects.requireNonNull; +import static org.eclipse.aether.internal.impl.collect.bf.BfDependencyCycle.find; + +/** + */ +@Singleton +@Named( BfDependencyCollector.NAME ) +public class BfDependencyCollector + implements DependencyCollectorDelegate, Service +{ + public static final String NAME = "bf"; + + /** + * The key in the repository session's {@link RepositorySystemSession#getConfigProperties() + * configuration properties} used to store a {@link Boolean} flag controlling the resolver's skip mode. + * + * @since 1.8.0 + */ + public static final String CONFIG_PROP_USE_SKIP = "aether.dependencyCollector.useSkip"; + + /** + * The default value for {@link #CONFIG_PROP_USE_SKIP}, {@code true}. + * + * @since 1.8.0 + */ + public static final boolean CONFIG_PROP_USE_SKIP_DEFAULT = true; + + private static final String CONFIG_PROP_MAX_EXCEPTIONS = "aether.dependencyCollector.maxExceptions"; + + private static final int CONFIG_PROP_MAX_EXCEPTIONS_DEFAULT = 50; + + private static final String CONFIG_PROP_MAX_CYCLES = "aether.dependencyCollector.maxCycles"; + + private static final int CONFIG_PROP_MAX_CYCLES_DEFAULT = 10; + + private static final Logger LOGGER = LoggerFactory.getLogger( BfDependencyCollector.class ); + + private RemoteRepositoryManager remoteRepositoryManager; + + private ArtifactDescriptorReader descriptorReader; + + private VersionRangeResolver versionRangeResolver; + + public BfDependencyCollector() + { + // enables default constructor + } + + @Inject + BfDependencyCollector( RemoteRepositoryManager remoteRepositoryManager, + ArtifactDescriptorReader artifactDescriptorReader, + VersionRangeResolver versionRangeResolver ) + { + setRemoteRepositoryManager( remoteRepositoryManager ); + setArtifactDescriptorReader( artifactDescriptorReader ); + setVersionRangeResolver( versionRangeResolver ); + } + + public void initService( ServiceLocator locator ) + { + setRemoteRepositoryManager( locator.getService( RemoteRepositoryManager.class ) ); + setArtifactDescriptorReader( locator.getService( ArtifactDescriptorReader.class ) ); + setVersionRangeResolver( locator.getService( VersionRangeResolver.class ) ); + } + + public BfDependencyCollector setRemoteRepositoryManager( RemoteRepositoryManager remoteRepositoryManager ) + { + this.remoteRepositoryManager = + requireNonNull( remoteRepositoryManager, "remote repository provider cannot be null" ); + return this; + } + + public BfDependencyCollector setArtifactDescriptorReader( ArtifactDescriptorReader artifactDescriptorReader ) + { + descriptorReader = requireNonNull( artifactDescriptorReader, "artifact descriptor reader cannot be null" ); + return this; + } + + public BfDependencyCollector setVersionRangeResolver( VersionRangeResolver versionRangeResolver ) + { + this.versionRangeResolver = + requireNonNull( versionRangeResolver, "version range resolver cannot be null" ); + return this; + } + + @SuppressWarnings( "checkstyle:methodlength" ) + public CollectResult collectDependencies( RepositorySystemSession session, CollectRequest request ) + throws DependencyCollectionException + { + requireNonNull( session, "session cannot be null" ); + requireNonNull( request, "request cannot be null" ); + session = optimizeSession( session ); + + boolean useSkip = ConfigUtils.getBoolean( + session, CONFIG_PROP_USE_SKIP_DEFAULT, CONFIG_PROP_USE_SKIP + ); + if ( useSkip ) + { + LOGGER.debug( "Collector skip mode enabled" ); + } + + RequestTrace trace = RequestTrace.newChild( request.getTrace(), request ); + + CollectResult result = new CollectResult( request ); + + DependencySelector depSelector = session.getDependencySelector(); + DependencyManager depManager = session.getDependencyManager(); + DependencyTraverser depTraverser = session.getDependencyTraverser(); + VersionFilter verFilter = session.getVersionFilter(); + + Dependency root = request.getRoot(); + List repositories = request.getRepositories(); + List dependencies = request.getDependencies(); + List managedDependencies = request.getManagedDependencies(); + + Map stats = new LinkedHashMap<>(); + long time1 = System.nanoTime(); + + DefaultDependencyNode node; + if ( root != null ) + { + List versions; + VersionRangeResult rangeResult; + try + { + VersionRangeRequest rangeRequest = + new VersionRangeRequest( root.getArtifact(), request.getRepositories(), + request.getRequestContext() ); + rangeRequest.setTrace( trace ); + rangeResult = versionRangeResolver.resolveVersionRange( session, rangeRequest ); + versions = filterVersions( root, rangeResult, verFilter, new DefaultVersionFilterContext( session ) ); + } + catch ( VersionRangeResolutionException e ) + { + result.addException( e ); + throw new DependencyCollectionException( result, e.getMessage() ); + } + + Version version = versions.get( versions.size() - 1 ); + root = root.setArtifact( root.getArtifact().setVersion( version.toString() ) ); + + ArtifactDescriptorResult descriptorResult; + try + { + ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest(); + descriptorRequest.setArtifact( root.getArtifact() ); + descriptorRequest.setRepositories( request.getRepositories() ); + descriptorRequest.setRequestContext( request.getRequestContext() ); + descriptorRequest.setTrace( trace ); + if ( isLackingDescriptor( root.getArtifact() ) ) + { + descriptorResult = new ArtifactDescriptorResult( descriptorRequest ); + } + else + { + descriptorResult = descriptorReader.readArtifactDescriptor( session, descriptorRequest ); + } + } + catch ( ArtifactDescriptorException e ) + { + result.addException( e ); + throw new DependencyCollectionException( result, e.getMessage() ); + } + + root = root.setArtifact( descriptorResult.getArtifact() ); + + if ( !session.isIgnoreArtifactDescriptorRepositories() ) + { + repositories = remoteRepositoryManager.aggregateRepositories( session, repositories, + descriptorResult.getRepositories(), + true ); + } + dependencies = mergeDeps( dependencies, descriptorResult.getDependencies() ); + managedDependencies = mergeDeps( managedDependencies, descriptorResult.getManagedDependencies() ); + + node = new DefaultDependencyNode( root ); + node.setRequestContext( request.getRequestContext() ); + node.setRelocations( descriptorResult.getRelocations() ); + node.setVersionConstraint( rangeResult.getVersionConstraint() ); + node.setVersion( version ); + node.setAliases( descriptorResult.getAliases() ); + node.setRepositories( request.getRepositories() ); + } + else + { + node = new DefaultDependencyNode( request.getRootArtifact() ); + node.setRequestContext( request.getRequestContext() ); + node.setRepositories( request.getRepositories() ); + } + + result.setRoot( node ); + + boolean traverse = root == null || depTraverser == null || depTraverser.traverseDependency( root ); + String errorPath = null; + if ( traverse && !dependencies.isEmpty() ) + { + DataPool pool = new DataPool( session ); + + DefaultDependencyCollectionContext context = + new DefaultDependencyCollectionContext( session, request.getRootArtifact(), root, managedDependencies ); + + DefaultVersionFilterContext versionContext = new DefaultVersionFilterContext( session ); + + Args args = + new Args( session, trace, pool, context, versionContext, request, + useSkip ? new DefaultDependencyResolutionSkipper() + : NeverDependencyResolutionSkipper.INSTANCE ); + Results results = new Results( result, session ); + + DependencySelector rootDepSelector = + depSelector != null ? depSelector.deriveChildSelector( context ) : null; + DependencyManager rootDepManager = depManager != null ? depManager.deriveChildManager( context ) : null; + DependencyTraverser rootDepTraverser = + depTraverser != null ? depTraverser.deriveChildTraverser( context ) : null; + VersionFilter rootVerFilter = verFilter != null ? verFilter.deriveChildFilter( context ) : null; + + List parents = Collections.singletonList( node ); + for ( Dependency dependency : dependencies ) + { + args.dependencyProcessingQueue.add( + new BfProcessingContext( rootDepSelector, rootDepManager, rootDepTraverser, + rootVerFilter, repositories, managedDependencies, parents, + dependency ) ); + } + + while ( !args.dependencyProcessingQueue.isEmpty() ) + { + processDependency( args, results, args.dependencyProcessingQueue.remove(), Collections.emptyList(), + false ); + } + + args.skipper.report(); + errorPath = results.errorPath; + } + + long time2 = System.nanoTime(); + + DependencyGraphTransformer transformer = session.getDependencyGraphTransformer(); + if ( transformer != null ) + { + try + { + DefaultDependencyGraphTransformationContext context = + new DefaultDependencyGraphTransformationContext( session ); + context.put( TransformationContextKeys.STATS, stats ); + result.setRoot( transformer.transformGraph( node, context ) ); + } + catch ( RepositoryException e ) + { + result.addException( e ); + } + } + + long time3 = System.nanoTime(); + stats.put( "DefaultDependencyCollector.collectTime", time2 - time1 ); + stats.put( "DefaultDependencyCollector.transformTime", time3 - time2 ); + LOGGER.debug( "Dependency collection stats {}", stats ); + + if ( errorPath != null ) + { + throw new DependencyCollectionException( result, "Failed to collect dependencies at " + errorPath ); + } + if ( !result.getExceptions().isEmpty() ) + { + throw new DependencyCollectionException( result ); + } + + return result; + } + + private static RepositorySystemSession optimizeSession( RepositorySystemSession session ) + { + DefaultRepositorySystemSession optimized = new DefaultRepositorySystemSession( session ); + optimized.setArtifactTypeRegistry( CachingArtifactTypeRegistry.newInstance( session ) ); + return optimized; + } + + private List mergeDeps( List dominant, List recessive ) + { + List result; + if ( dominant == null || dominant.isEmpty() ) + { + result = recessive; + } + else if ( recessive == null || recessive.isEmpty() ) + { + result = dominant; + } + else + { + int initialCapacity = dominant.size() + recessive.size(); + result = new ArrayList<>( initialCapacity ); + Collection ids = new HashSet<>( initialCapacity, 1.0f ); + for ( Dependency dependency : dominant ) + { + ids.add( getId( dependency.getArtifact() ) ); + result.add( dependency ); + } + for ( Dependency dependency : recessive ) + { + if ( !ids.contains( getId( dependency.getArtifact() ) ) ) + { + result.add( dependency ); + } + } + } + return result; + } + + private static String getId( Artifact a ) + { + return a.getGroupId() + ':' + a.getArtifactId() + ':' + a.getClassifier() + ':' + a.getExtension(); + } + + @SuppressWarnings( "checkstyle:parameternumber" ) + private void processDependency( Args args, Results results, BfProcessingContext context, + List relocations, boolean disableVersionManagement ) + { + + if ( context.depSelector != null && !context.depSelector.selectDependency( context.dependency ) ) + { + return; + } + + PremanagedDependency preManaged = + PremanagedDependency.create( context.depManager, context.dependency, disableVersionManagement, + args.premanagedState ); + Dependency dependency = preManaged.managedDependency; + + boolean noDescriptor = isLackingDescriptor( dependency.getArtifact() ); + + boolean traverse = + !noDescriptor && ( context.depTraverser == null || context.depTraverser.traverseDependency( + dependency ) ); + + List versions; + VersionRangeResult rangeResult; + try + { + VersionRangeRequest rangeRequest = createVersionRangeRequest( args, context.repositories, dependency ); + + rangeResult = cachedResolveRangeResult( rangeRequest, args.pool, args.session ); + + versions = filterVersions( dependency, rangeResult, context.verFilter, args.versionContext ); + } + catch ( VersionRangeResolutionException e ) + { + results.addException( dependency, e, context.parents ); + return; + } + + //Resolve newer version first to maximize benefits of skipper + Collections.reverse( versions ); + for ( Version version : versions ) + { + Artifact originalArtifact = dependency.getArtifact().setVersion( version.toString() ); + Dependency d = dependency.setArtifact( originalArtifact ); + + ArtifactDescriptorRequest descriptorRequest = + createArtifactDescriptorRequest( args, context.repositories, d ); + + final ArtifactDescriptorResult descriptorResult = + noDescriptor + ? new ArtifactDescriptorResult( descriptorRequest ) + : resolveCachedArtifactDescriptor( args.pool, descriptorRequest, args.session, + context.withDependency( d ), results ); + + if ( descriptorResult != null ) + { + d = d.setArtifact( descriptorResult.getArtifact() ); + + int cycleEntry = find( context.parents, d.getArtifact() ); + if ( cycleEntry >= 0 ) + { + results.addCycle( context.parents, cycleEntry, d ); + DependencyNode cycleNode = context.parents.get( cycleEntry ); + if ( cycleNode.getDependency() != null ) + { + DefaultDependencyNode child = + createDependencyNode( relocations, preManaged, rangeResult, version, d, + descriptorResult, cycleNode ); + context.getParent().getChildren().add( child ); + continue; + } + } + + if ( !descriptorResult.getRelocations().isEmpty() ) + { + boolean disableVersionManagementSubsequently = + originalArtifact.getGroupId().equals( d.getArtifact().getGroupId() ) + && originalArtifact.getArtifactId().equals( d.getArtifact().getArtifactId() ); + + processDependency( args, results, context.withDependency( d ), descriptorResult.getRelocations(), + disableVersionManagementSubsequently ); + return; + } + else + { + d = args.pool.intern( d.setArtifact( args.pool.intern( d.getArtifact() ) ) ); + + List repos = + getRemoteRepositories( rangeResult.getRepository( version ), context.repositories ); + + DefaultDependencyNode child = + createDependencyNode( relocations, preManaged, rangeResult, version, d, + descriptorResult.getAliases(), repos, args.request.getRequestContext() ); + + context.getParent().getChildren().add( child ); + + boolean recurse = traverse && !descriptorResult.getDependencies().isEmpty(); + if ( recurse ) + { + doRecurse( args, context.withDependency( d ), descriptorResult, child ); + } + } + } + else + { + List repos = + getRemoteRepositories( rangeResult.getRepository( version ), context.repositories ); + DefaultDependencyNode child = + createDependencyNode( relocations, preManaged, rangeResult, version, d, null, repos, + args.request.getRequestContext() ); + context.getParent().getChildren().add( child ); + } + } + } + + @SuppressWarnings( "checkstyle:parameternumber" ) + private void doRecurse( Args args, BfProcessingContext parentContext, + ArtifactDescriptorResult descriptorResult, DefaultDependencyNode child ) + { + DefaultDependencyCollectionContext context = args.collectionContext; + context.set( parentContext.dependency, descriptorResult.getManagedDependencies() ); + + DependencySelector childSelector = + parentContext.depSelector != null ? parentContext.depSelector.deriveChildSelector( context ) : null; + DependencyManager childManager = + parentContext.depManager != null ? parentContext.depManager.deriveChildManager( context ) : null; + DependencyTraverser childTraverser = + parentContext.depTraverser != null ? parentContext.depTraverser.deriveChildTraverser( context ) : null; + VersionFilter childFilter = + parentContext.verFilter != null ? parentContext.verFilter.deriveChildFilter( context ) : null; + + final List childRepos = + args.ignoreRepos + ? parentContext.repositories + : remoteRepositoryManager.aggregateRepositories( args.session, parentContext.repositories, + descriptorResult.getRepositories(), true ); + + Object key = + args.pool.toKey( parentContext.dependency.getArtifact(), childRepos, childSelector, childManager, + childTraverser, childFilter ); + + List children = args.pool.getChildren( key ); + if ( children == null ) + { + boolean skipResolution = args.skipper.skipResolution( child, parentContext.parents ); + if ( !skipResolution ) + { + List parents = new ArrayList<>( parentContext.parents.size() + 1 ); + parents.addAll( parentContext.parents ); + parents.add( child ); + for ( Dependency dependency : descriptorResult.getDependencies() ) + { + args.dependencyProcessingQueue.add( + new BfProcessingContext( childSelector, childManager, childTraverser, childFilter, + childRepos, descriptorResult.getManagedDependencies(), parents, dependency ) ); + } + args.pool.putChildren( key, child.getChildren() ); + args.skipper.cache( child, parents ); + } + } + else + { + child.setChildren( children ); + } + } + + private ArtifactDescriptorResult resolveCachedArtifactDescriptor( DataPool pool, + ArtifactDescriptorRequest descriptorRequest, + RepositorySystemSession session, + BfProcessingContext context, + Results results ) + { + Object key = pool.toKey( descriptorRequest ); + ArtifactDescriptorResult descriptorResult = pool.getDescriptor( key, descriptorRequest ); + if ( descriptorResult == null ) + { + try + { + descriptorResult = descriptorReader.readArtifactDescriptor( session, descriptorRequest ); + pool.putDescriptor( key, descriptorResult ); + } + catch ( ArtifactDescriptorException e ) + { + results.addException( context.dependency, e, context.parents ); + pool.putDescriptor( key, e ); + return null; + } + + } + else if ( descriptorResult == DataPool.NO_DESCRIPTOR ) + { + return null; + } + + return descriptorResult; + } + + @SuppressWarnings( "checkstyle:parameternumber" ) + private static DefaultDependencyNode createDependencyNode( List relocations, + PremanagedDependency preManaged, + VersionRangeResult rangeResult, Version version, + Dependency d, Collection aliases, + List repos, String requestContext ) + { + DefaultDependencyNode child = new DefaultDependencyNode( d ); + preManaged.applyTo( child ); + child.setRelocations( relocations ); + child.setVersionConstraint( rangeResult.getVersionConstraint() ); + child.setVersion( version ); + child.setAliases( aliases ); + child.setRepositories( repos ); + child.setRequestContext( requestContext ); + return child; + } + + private static DefaultDependencyNode createDependencyNode( List relocations, + PremanagedDependency preManaged, + VersionRangeResult rangeResult, Version version, + Dependency d, ArtifactDescriptorResult descriptorResult, + DependencyNode cycleNode ) + { + DefaultDependencyNode child = + createDependencyNode( relocations, preManaged, rangeResult, version, d, descriptorResult.getAliases(), + cycleNode.getRepositories(), cycleNode.getRequestContext() ); + child.setChildren( cycleNode.getChildren() ); + return child; + } + + private static ArtifactDescriptorRequest createArtifactDescriptorRequest( Args args, + List repositories, + Dependency d ) + { + ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest(); + descriptorRequest.setArtifact( d.getArtifact() ); + descriptorRequest.setRepositories( repositories ); + descriptorRequest.setRequestContext( args.request.getRequestContext() ); + descriptorRequest.setTrace( args.trace ); + return descriptorRequest; + } + + private static VersionRangeRequest createVersionRangeRequest( Args args, List repositories, + Dependency dependency ) + { + VersionRangeRequest rangeRequest = new VersionRangeRequest(); + rangeRequest.setArtifact( dependency.getArtifact() ); + rangeRequest.setRepositories( repositories ); + rangeRequest.setRequestContext( args.request.getRequestContext() ); + rangeRequest.setTrace( args.trace ); + return rangeRequest; + } + + private VersionRangeResult cachedResolveRangeResult( VersionRangeRequest rangeRequest, DataPool pool, + RepositorySystemSession session ) + throws VersionRangeResolutionException + { + Object key = pool.toKey( rangeRequest ); + VersionRangeResult rangeResult = pool.getConstraint( key, rangeRequest ); + if ( rangeResult == null ) + { + rangeResult = versionRangeResolver.resolveVersionRange( session, rangeRequest ); + pool.putConstraint( key, rangeResult ); + } + return rangeResult; + } + + private static boolean isLackingDescriptor( Artifact artifact ) + { + return artifact.getProperty( ArtifactProperties.LOCAL_PATH, null ) != null; + } + + private static List getRemoteRepositories( ArtifactRepository repository, + List repositories ) + { + if ( repository instanceof RemoteRepository ) + { + return Collections.singletonList( (RemoteRepository) repository ); + } + if ( repository != null ) + { + return Collections.emptyList(); + } + return repositories; + } + + private static List filterVersions( Dependency dependency, VersionRangeResult rangeResult, + VersionFilter verFilter, + DefaultVersionFilterContext verContext ) + throws VersionRangeResolutionException + { + if ( rangeResult.getVersions().isEmpty() ) + { + throw new VersionRangeResolutionException( rangeResult, + "No versions available for " + dependency.getArtifact() + + " within specified range" ); + } + + List versions; + if ( verFilter != null && rangeResult.getVersionConstraint().getRange() != null ) + { + verContext.set( dependency, rangeResult ); + try + { + verFilter.filterVersions( verContext ); + } + catch ( RepositoryException e ) + { + throw new VersionRangeResolutionException( rangeResult, + "Failed to filter versions for " + dependency.getArtifact() + + ": " + e.getMessage(), e ); + } + versions = verContext.get(); + if ( versions.isEmpty() ) + { + throw new VersionRangeResolutionException( rangeResult, + "No acceptable versions for " + dependency.getArtifact() + + ": " + rangeResult.getVersions() ); + } + } + else + { + versions = rangeResult.getVersions(); + } + return versions; + } + + static class Args + { + + final RepositorySystemSession session; + + final boolean ignoreRepos; + + final boolean premanagedState; + + final RequestTrace trace; + + final DataPool pool; + + final Queue dependencyProcessingQueue = new ArrayDeque<>( 128 ); + + final DefaultDependencyCollectionContext collectionContext; + + final DefaultVersionFilterContext versionContext; + + final CollectRequest request; + + final DependencyResolutionSkipper skipper; + + Args( RepositorySystemSession session, RequestTrace trace, DataPool pool, + DefaultDependencyCollectionContext collectionContext, DefaultVersionFilterContext versionContext, + CollectRequest request, DependencyResolutionSkipper skipper ) + { + this.session = session; + this.request = request; + this.ignoreRepos = session.isIgnoreArtifactDescriptorRepositories(); + this.premanagedState = ConfigUtils.getBoolean( session, false, DependencyManagerUtils.CONFIG_PROP_VERBOSE ); + this.trace = trace; + this.pool = pool; + this.collectionContext = collectionContext; + this.versionContext = versionContext; + this.skipper = skipper; + } + + } + + static class Results + { + + private final CollectResult result; + + final int maxExceptions; + + final int maxCycles; + + String errorPath; + + Results( CollectResult result, RepositorySystemSession session ) + { + this.result = result; + + maxExceptions = + ConfigUtils.getInteger( session, CONFIG_PROP_MAX_EXCEPTIONS_DEFAULT, CONFIG_PROP_MAX_EXCEPTIONS ); + + maxCycles = ConfigUtils.getInteger( session, CONFIG_PROP_MAX_CYCLES_DEFAULT, CONFIG_PROP_MAX_CYCLES ); + } + + public void addException( Dependency dependency, Exception e, List nodes ) + { + if ( maxExceptions < 0 || result.getExceptions().size() < maxExceptions ) + { + result.addException( e ); + if ( errorPath == null ) + { + StringBuilder buffer = new StringBuilder( 256 ); + for ( DependencyNode node : nodes ) + { + if ( buffer.length() > 0 ) + { + buffer.append( " -> " ); + } + Dependency dep = node.getDependency(); + if ( dep != null ) + { + buffer.append( dep.getArtifact() ); + } + } + if ( buffer.length() > 0 ) + { + buffer.append( " -> " ); + } + buffer.append( dependency.getArtifact() ); + errorPath = buffer.toString(); + } + } + } + + public void addCycle( List nodes, int cycleEntry, Dependency dependency ) + { + if ( maxCycles < 0 || result.getCycles().size() < maxCycles ) + { + result.addCycle( new BfDependencyCycle( nodes, cycleEntry, dependency ) ); + } + } + + } + + static class PremanagedDependency + { + + final String premanagedVersion; + + final String premanagedScope; + + final Boolean premanagedOptional; + + /** + * @since 1.1.0 + */ + final Collection premanagedExclusions; + + /** + * @since 1.1.0 + */ + final Map premanagedProperties; + + final int managedBits; + + final Dependency managedDependency; + + final boolean premanagedState; + + @SuppressWarnings( "checkstyle:parameternumber" ) + PremanagedDependency( String premanagedVersion, String premanagedScope, Boolean premanagedOptional, + Collection premanagedExclusions, Map premanagedProperties, + int managedBits, Dependency managedDependency, boolean premanagedState ) + { + this.premanagedVersion = premanagedVersion; + this.premanagedScope = premanagedScope; + this.premanagedOptional = premanagedOptional; + this.premanagedExclusions = + premanagedExclusions != null + ? Collections.unmodifiableCollection( new ArrayList<>( premanagedExclusions ) ) + : null; + + this.premanagedProperties = + premanagedProperties != null + ? Collections.unmodifiableMap( new HashMap<>( premanagedProperties ) ) + : null; + + this.managedBits = managedBits; + this.managedDependency = managedDependency; + this.premanagedState = premanagedState; + } + + static PremanagedDependency create( DependencyManager depManager, Dependency dependency, + boolean disableVersionManagement, boolean premanagedState ) + { + DependencyManagement depMngt = depManager != null ? depManager.manageDependency( dependency ) : null; + + int managedBits = 0; + String premanagedVersion = null; + String premanagedScope = null; + Boolean premanagedOptional = null; + Collection premanagedExclusions = null; + Map premanagedProperties = null; + + if ( depMngt != null ) + { + if ( depMngt.getVersion() != null && !disableVersionManagement ) + { + Artifact artifact = dependency.getArtifact(); + premanagedVersion = artifact.getVersion(); + dependency = dependency.setArtifact( artifact.setVersion( depMngt.getVersion() ) ); + managedBits |= DependencyNode.MANAGED_VERSION; + } + if ( depMngt.getProperties() != null ) + { + Artifact artifact = dependency.getArtifact(); + premanagedProperties = artifact.getProperties(); + dependency = dependency.setArtifact( artifact.setProperties( depMngt.getProperties() ) ); + managedBits |= DependencyNode.MANAGED_PROPERTIES; + } + if ( depMngt.getScope() != null ) + { + premanagedScope = dependency.getScope(); + dependency = dependency.setScope( depMngt.getScope() ); + managedBits |= DependencyNode.MANAGED_SCOPE; + } + if ( depMngt.getOptional() != null ) + { + premanagedOptional = dependency.isOptional(); + dependency = dependency.setOptional( depMngt.getOptional() ); + managedBits |= DependencyNode.MANAGED_OPTIONAL; + } + if ( depMngt.getExclusions() != null ) + { + premanagedExclusions = dependency.getExclusions(); + dependency = dependency.setExclusions( depMngt.getExclusions() ); + managedBits |= DependencyNode.MANAGED_EXCLUSIONS; + } + } + return new PremanagedDependency( premanagedVersion, premanagedScope, premanagedOptional, + premanagedExclusions, premanagedProperties, managedBits, dependency, + premanagedState ); + + } + + public void applyTo( DefaultDependencyNode child ) + { + child.setManagedBits( managedBits ); + if ( premanagedState ) + { + child.setData( DependencyManagerUtils.NODE_DATA_PREMANAGED_VERSION, premanagedVersion ); + child.setData( DependencyManagerUtils.NODE_DATA_PREMANAGED_SCOPE, premanagedScope ); + child.setData( DependencyManagerUtils.NODE_DATA_PREMANAGED_OPTIONAL, premanagedOptional ); + child.setData( DependencyManagerUtils.NODE_DATA_PREMANAGED_EXCLUSIONS, premanagedExclusions ); + child.setData( DependencyManagerUtils.NODE_DATA_PREMANAGED_PROPERTIES, premanagedProperties ); + } + } + + } + +} diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycle.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCycle.java similarity index 95% rename from maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycle.java rename to maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCycle.java index dd1565c9c..94a566d44 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycle.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCycle.java @@ -1,4 +1,4 @@ -package org.eclipse.aether.internal.impl.collect; +package org.eclipse.aether.internal.impl.collect.bf; /* * Licensed to the Apache Software Foundation (ASF) under one @@ -27,12 +27,13 @@ import org.eclipse.aether.graph.Dependency; import org.eclipse.aether.graph.DependencyCycle; import org.eclipse.aether.graph.DependencyNode; +import org.eclipse.aether.internal.impl.collect.DefaultDependencyCollector; import org.eclipse.aether.util.artifact.ArtifactIdUtils; /** * @see DefaultDependencyCollector */ -final class DefaultDependencyCycle +final class BfDependencyCycle implements DependencyCycle { @@ -40,7 +41,7 @@ final class DefaultDependencyCycle private final int cycleEntry; - DefaultDependencyCycle( List nodes, int cycleEntry, Dependency dependency ) + BfDependencyCycle( List nodes, int cycleEntry, Dependency dependency ) { // skip root node unless it actually has a dependency or is considered the cycle entry (due to its label) int offset = ( cycleEntry > 0 && nodes.get( 0 ).getDependency() == null ) ? 1 : 0; diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyProcessingContext.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfProcessingContext.java similarity index 76% rename from maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyProcessingContext.java rename to maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfProcessingContext.java index 9820ad07a..f18376f22 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyProcessingContext.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfProcessingContext.java @@ -1,4 +1,4 @@ -package org.eclipse.aether.internal.impl.collect; +package org.eclipse.aether.internal.impl.collect.bf; /* * Licensed to the Apache Software Foundation (ASF) under one @@ -29,7 +29,7 @@ import org.eclipse.aether.graph.DependencyNode; import org.eclipse.aether.repository.RemoteRepository; -final class DependencyProcessingContext +final class BfProcessingContext { final DependencySelector depSelector; final DependencyManager depManager; @@ -45,14 +45,14 @@ final class DependencyProcessingContext Dependency dependency; @SuppressWarnings( "checkstyle:parameternumber" ) - DependencyProcessingContext( DependencySelector depSelector, - DependencyManager depManager, - DependencyTraverser depTraverser, - VersionFilter verFilter, - List repositories, - List managedDependencies, - List parents, - Dependency dependency ) + BfProcessingContext( DependencySelector depSelector, + DependencyManager depManager, + DependencyTraverser depTraverser, + VersionFilter verFilter, + List repositories, + List managedDependencies, + List parents, + Dependency dependency ) { this.depSelector = depSelector; this.depManager = depManager; @@ -64,7 +64,7 @@ final class DependencyProcessingContext this.parents = parents; } - DependencyProcessingContext withDependency( Dependency dependency ) + BfProcessingContext withDependency( Dependency dependency ) { this.dependency = dependency; return this; diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyResolutionSkipper.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/DefaultDependencyResolutionSkipper.java similarity index 95% rename from maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyResolutionSkipper.java rename to maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/DefaultDependencyResolutionSkipper.java index 408b0e169..38fcd4e73 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyResolutionSkipper.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/DefaultDependencyResolutionSkipper.java @@ -1,4 +1,4 @@ -package org.eclipse.aether.internal.impl.collect; +package org.eclipse.aether.internal.impl.collect.bf; /* * Licensed to the Apache Software Foundation (ASF) under one @@ -32,18 +32,18 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; -final class DefaultDependencyResolutionSkipper implements DependencyResolutionSkipper +/** + * Skipper for Skip-and-reconcile approach. + * + * @see BfDependencyCollector + */ +public final class DefaultDependencyResolutionSkipper implements DependencyResolutionSkipper { private static final Logger LOGGER = LoggerFactory.getLogger( DefaultDependencyResolutionSkipper.class ); - private Map results = new LinkedHashMap<>( 256 ); - private CacheManager cacheManager = new CacheManager(); - private CoordinateManager coordinateManager = new CoordinateManager(); - - DefaultDependencyResolutionSkipper() - { - // enables default constructor - } + private final Map results = new LinkedHashMap<>( 256 ); + private final CacheManager cacheManager = new CacheManager(); + private final CoordinateManager coordinateManager = new CoordinateManager(); @Override public boolean skipResolution( DependencyNode node, List parents ) diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/NeverDependencyResolutionSkipper.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/NeverDependencyResolutionSkipper.java similarity index 82% rename from maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/NeverDependencyResolutionSkipper.java rename to maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/NeverDependencyResolutionSkipper.java index acd6636fb..8ad4450d1 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/NeverDependencyResolutionSkipper.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/NeverDependencyResolutionSkipper.java @@ -1,4 +1,4 @@ -package org.eclipse.aether.internal.impl.collect; +package org.eclipse.aether.internal.impl.collect.bf; /* * Licensed to the Apache Software Foundation (ASF) under one @@ -26,10 +26,12 @@ /** * Skipper for Non-skip approach. + * + * @see BfDependencyCollector */ -final class NeverDependencyResolutionSkipper implements DependencyResolutionSkipper +public final class NeverDependencyResolutionSkipper implements DependencyResolutionSkipper { - static final DependencyResolutionSkipper INSTANCE = new NeverDependencyResolutionSkipper(); + public static final DependencyResolutionSkipper INSTANCE = new NeverDependencyResolutionSkipper(); @Override public boolean skipResolution( DependencyNode node, List parents ) diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectorTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollectorTest.java similarity index 98% rename from maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectorTest.java rename to maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollectorTest.java index 7d034bddd..3c4ea005a 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectorTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollectorTest.java @@ -1,4 +1,4 @@ -package org.eclipse.aether.internal.impl.collect; +package org.eclipse.aether.internal.impl.collect.bf; /* * Licensed to the Apache Software Foundation (ASF) under one @@ -18,14 +18,6 @@ * specific language governing permissions and limitations * under the License. */ -import static java.util.Objects.requireNonNull; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; import java.io.IOException; import java.util.ArrayList; @@ -73,12 +65,21 @@ import org.junit.Before; import org.junit.Test; +import static java.util.Objects.requireNonNull; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + /** */ -public class DefaultDependencyCollectorTest +public class BfDependencyCollectorTest { - protected DefaultDependencyCollector collector; + protected BfDependencyCollector collector; protected DefaultRepositorySystemSession session; @@ -110,9 +111,9 @@ public void setup() public void setupCollector( boolean useSkip ) { session = TestUtils.newSession(); - session.setConfigProperty( DefaultDependencyCollector.CONFIG_PROP_USE_SKIP, useSkip ); + session.setConfigProperty( BfDependencyCollector.CONFIG_PROP_USE_SKIP, useSkip ); - collector = new DefaultDependencyCollector(); + collector = new BfDependencyCollector(); collector.setArtifactDescriptorReader( newReader( "" ) ); collector.setVersionRangeResolver( new StubVersionRangeResolver() ); collector.setRemoteRepositoryManager( new StubRemoteRepositoryManager() ); diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectorUseSkipTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollectorUseSkipTest.java similarity index 96% rename from maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectorUseSkipTest.java rename to maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollectorUseSkipTest.java index 716ecdef7..5daa678b5 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectorUseSkipTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollectorUseSkipTest.java @@ -1,4 +1,4 @@ -package org.eclipse.aether.internal.impl.collect; +package org.eclipse.aether.internal.impl.collect.bf; /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -18,6 +18,11 @@ * under the License. */ +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + import org.eclipse.aether.artifact.DefaultArtifact; import org.eclipse.aether.collection.CollectRequest; import org.eclipse.aether.collection.CollectResult; @@ -29,14 +34,9 @@ import org.eclipse.aether.util.graph.selector.ExclusionDependencySelector; import org.junit.Test; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - import static org.junit.Assert.assertEquals; -public class DefaultDependencyCollectorUseSkipTest extends DefaultDependencyCollectorTest +public class BfDependencyCollectorUseSkipTest extends BfDependencyCollectorTest { private Dependency newDep( String coords, String scope, Collection exclusions ) diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycleTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCycleTest.java similarity index 90% rename from maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycleTest.java rename to maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCycleTest.java index 0a382f8c4..a1be7c926 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycleTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCycleTest.java @@ -1,4 +1,4 @@ -package org.eclipse.aether.internal.impl.collect; +package org.eclipse.aether.internal.impl.collect.bf; /* * Licensed to the Apache Software Foundation (ASF) under one @@ -19,6 +19,9 @@ * under the License. */ +import java.util.ArrayList; +import java.util.List; + import org.eclipse.aether.artifact.DefaultArtifact; import org.eclipse.aether.graph.DefaultDependencyNode; import org.eclipse.aether.graph.Dependency; @@ -26,12 +29,9 @@ import org.eclipse.aether.graph.DependencyNode; import org.junit.Test; -import java.util.ArrayList; -import java.util.List; - import static org.junit.Assert.assertEquals; -public class DefaultDependencyCycleTest +public class BfDependencyCycleTest { private static final Dependency FOO_DEPENDENCY = new Dependency( new DefaultArtifact( "group-id:foo:1.0" ), "test" ); private static final Dependency BAR_DEPENDENCY = new Dependency( new DefaultArtifact( "group-id:bar:1.0" ), "test" ); @@ -41,7 +41,7 @@ public void testToString() { List nodes = new ArrayList<>(); nodes.add( new DefaultDependencyNode( FOO_DEPENDENCY ) ); - DependencyCycle cycle = new DefaultDependencyCycle( nodes, 1, BAR_DEPENDENCY ); + DependencyCycle cycle = new BfDependencyCycle( nodes, 1, BAR_DEPENDENCY ); assertEquals( "group-id:foo:jar -> group-id:bar:jar", cycle.toString() ); } diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DependencyResolutionSkipperTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/DependencyResolutionSkipperTest.java similarity index 99% rename from maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DependencyResolutionSkipperTest.java rename to maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/DependencyResolutionSkipperTest.java index 1d92acc01..678b9e766 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DependencyResolutionSkipperTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/DependencyResolutionSkipperTest.java @@ -1,4 +1,4 @@ -package org.eclipse.aether.internal.impl.collect; +package org.eclipse.aether.internal.impl.collect.bf; /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -18,6 +18,12 @@ * under the License. */ +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + import org.eclipse.aether.RepositoryException; import org.eclipse.aether.artifact.DefaultArtifact; import org.eclipse.aether.graph.DefaultDependencyNode; @@ -27,12 +33,6 @@ import org.eclipse.aether.internal.test.util.TestVersionConstraint; import org.junit.Test; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; From 9572e88b276df9acd960aed632bfcfe857539f0c Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Mon, 4 Apr 2022 17:26:58 +0200 Subject: [PATCH 02/10] Resurrect replaced DF as well Along with tests --- .../aether/impl/guice/AetherModule.java | 21 +- .../collect/DefaultDependencyCollector.java | 6 +- .../collect/bf/BfDependencyCollector.java | 3 + .../collect/df/DfDependencyCollector.java | 912 ++++++++++++++++++ .../impl/collect/df/DfDependencyCycle.java | 88 ++ .../internal/impl/collect/df/NodeStack.java | 127 +++ .../collect/df/DfDependencyCollectorTest.java | 649 +++++++++++++ .../collect/df/DfDependencyCycleTest.java | 44 + 8 files changed, 1841 insertions(+), 9 deletions(-) create mode 100644 maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java create mode 100644 maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCycle.java create mode 100644 maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/NodeStack.java create mode 100644 maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollectorTest.java create mode 100644 maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCycleTest.java diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java index 7d7f9f0b4..e539d539e 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java @@ -50,6 +50,7 @@ import org.eclipse.aether.internal.impl.checksum.DefaultChecksumAlgorithmFactorySelector; import org.eclipse.aether.internal.impl.collect.DependencyCollectorDelegate; import org.eclipse.aether.internal.impl.collect.bf.BfDependencyCollector; +import org.eclipse.aether.internal.impl.collect.df.DfDependencyCollector; import org.eclipse.aether.internal.impl.synccontext.DefaultSyncContextFactory; import org.eclipse.aether.internal.impl.synccontext.named.NamedLockFactorySelector; import org.eclipse.aether.internal.impl.synccontext.named.SimpleNamedLockFactorySelector; @@ -139,6 +140,8 @@ protected void configure() .to( DefaultDependencyCollector.class ).in( Singleton.class ); bind( DependencyCollectorDelegate.class ).annotatedWith( Names.named( BfDependencyCollector.NAME ) ) .to( BfDependencyCollector.class ).in( Singleton.class ); + bind( DependencyCollectorDelegate.class ).annotatedWith( Names.named( DfDependencyCollector.NAME ) ) + .to( DfDependencyCollector.class ).in( Singleton.class ); bind( Deployer.class ) // .to( DefaultDeployer.class ).in( Singleton.class ); @@ -220,23 +223,25 @@ protected void configure() @Provides @Singleton - Map provideChecksumSources( - @Named( FileProvidedChecksumsSource.NAME ) ProvidedChecksumsSource fileProvidedChecksumSource + Map dependencyCollectorDelegates( + @Named( BfDependencyCollector.NAME ) DependencyCollectorDelegate bf, + @Named( DfDependencyCollector.NAME ) DependencyCollectorDelegate df ) { - Map providedChecksumsSource = new HashMap<>(); - providedChecksumsSource.put( FileProvidedChecksumsSource.NAME, fileProvidedChecksumSource ); + Map providedChecksumsSource = new HashMap<>(); + providedChecksumsSource.put( BfDependencyCollector.NAME, bf ); + providedChecksumsSource.put( DfDependencyCollector.NAME, df ); return providedChecksumsSource; } @Provides @Singleton - Map dependencyCollectorDelegates( - @Named( BfDependencyCollector.NAME ) DependencyCollectorDelegate bf + Map provideChecksumSources( + @Named( FileProvidedChecksumsSource.NAME ) ProvidedChecksumsSource fileProvidedChecksumSource ) { - Map providedChecksumsSource = new HashMap<>(); - providedChecksumsSource.put( BfDependencyCollector.NAME, bf ); + Map providedChecksumsSource = new HashMap<>(); + providedChecksumsSource.put( FileProvidedChecksumsSource.NAME, fileProvidedChecksumSource ); return providedChecksumsSource; } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java index 1c0b6e956..7b479835a 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java @@ -32,6 +32,7 @@ import org.eclipse.aether.collection.DependencyCollectionException; import org.eclipse.aether.impl.DependencyCollector; import org.eclipse.aether.internal.impl.collect.bf.BfDependencyCollector; +import org.eclipse.aether.internal.impl.collect.df.DfDependencyCollector; import org.eclipse.aether.spi.locator.Service; import org.eclipse.aether.spi.locator.ServiceLocator; import org.eclipse.aether.util.ConfigUtils; @@ -48,7 +49,7 @@ public class DefaultDependencyCollector { private static final String CONFIG_PROP_COLLECTOR_IMPL = "aether.collector.impl"; - private static final String DEFAULT_COLLECTOR_IMPL = BfDependencyCollector.NAME; + private static final String DEFAULT_COLLECTOR_IMPL = DfDependencyCollector.NAME; private final Map delegates; @@ -74,7 +75,10 @@ public void initService( ServiceLocator locator ) { BfDependencyCollector bf = new BfDependencyCollector(); bf.initService( locator ); + DfDependencyCollector df = new DfDependencyCollector(); + df.initService( locator ); this.delegates.put( BfDependencyCollector.NAME, bf ); + this.delegates.put( DfDependencyCollector.NAME, df ); } @Override diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java index 4836d6357..837af5285 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java @@ -84,6 +84,9 @@ import static org.eclipse.aether.internal.impl.collect.bf.BfDependencyCycle.find; /** + * Breadth-first {@link org.eclipse.aether.impl.DependencyCollector} + * + * @since 1.8.0 */ @Singleton @Named( BfDependencyCollector.NAME ) diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java new file mode 100644 index 000000000..528fc908b --- /dev/null +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java @@ -0,0 +1,912 @@ +package org.eclipse.aether.internal.impl.collect.df; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import static java.util.Objects.requireNonNull; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.eclipse.aether.DefaultRepositorySystemSession; +import org.eclipse.aether.RepositoryException; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.RequestTrace; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.artifact.ArtifactProperties; +import org.eclipse.aether.collection.CollectRequest; +import org.eclipse.aether.collection.CollectResult; +import org.eclipse.aether.collection.DependencyCollectionException; +import org.eclipse.aether.collection.DependencyGraphTransformer; +import org.eclipse.aether.collection.DependencyManagement; +import org.eclipse.aether.collection.DependencyManager; +import org.eclipse.aether.collection.DependencySelector; +import org.eclipse.aether.collection.DependencyTraverser; +import org.eclipse.aether.collection.VersionFilter; +import org.eclipse.aether.graph.DefaultDependencyNode; +import org.eclipse.aether.graph.Dependency; +import org.eclipse.aether.graph.DependencyNode; +import org.eclipse.aether.graph.Exclusion; +import org.eclipse.aether.impl.ArtifactDescriptorReader; +import org.eclipse.aether.impl.RemoteRepositoryManager; +import org.eclipse.aether.impl.VersionRangeResolver; +import org.eclipse.aether.internal.impl.collect.CachingArtifactTypeRegistry; +import org.eclipse.aether.internal.impl.collect.DataPool; +import org.eclipse.aether.internal.impl.collect.DefaultDependencyCollectionContext; +import org.eclipse.aether.internal.impl.collect.DefaultDependencyGraphTransformationContext; +import org.eclipse.aether.internal.impl.collect.DefaultVersionFilterContext; +import org.eclipse.aether.internal.impl.collect.DependencyCollectorDelegate; +import org.eclipse.aether.repository.ArtifactRepository; +import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.resolution.ArtifactDescriptorException; +import org.eclipse.aether.resolution.ArtifactDescriptorRequest; +import org.eclipse.aether.resolution.ArtifactDescriptorResult; +import org.eclipse.aether.resolution.VersionRangeRequest; +import org.eclipse.aether.resolution.VersionRangeResolutionException; +import org.eclipse.aether.resolution.VersionRangeResult; +import org.eclipse.aether.spi.locator.Service; +import org.eclipse.aether.spi.locator.ServiceLocator; +import org.eclipse.aether.util.ConfigUtils; +import org.eclipse.aether.util.graph.manager.DependencyManagerUtils; +import org.eclipse.aether.util.graph.transformer.TransformationContextKeys; +import org.eclipse.aether.version.Version; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Depth-first {@link org.eclipse.aether.impl.DependencyCollector} (the "original" default). + * + * @since 1.8.0 + */ +@Singleton +@Named( DfDependencyCollector.NAME ) +public class DfDependencyCollector + implements DependencyCollectorDelegate, Service +{ + public static final String NAME = "df"; + + private static final String CONFIG_PROP_MAX_EXCEPTIONS = "aether.dependencyCollector.maxExceptions"; + + private static final int CONFIG_PROP_MAX_EXCEPTIONS_DEFAULT = 50; + + private static final String CONFIG_PROP_MAX_CYCLES = "aether.dependencyCollector.maxCycles"; + + private static final int CONFIG_PROP_MAX_CYCLES_DEFAULT = 10; + + private static final Logger LOGGER = LoggerFactory.getLogger( DfDependencyCollector.class ); + + private RemoteRepositoryManager remoteRepositoryManager; + + private ArtifactDescriptorReader descriptorReader; + + private VersionRangeResolver versionRangeResolver; + + public DfDependencyCollector() + { + // enables default constructor + } + + @Inject + DfDependencyCollector( RemoteRepositoryManager remoteRepositoryManager, + ArtifactDescriptorReader artifactDescriptorReader, + VersionRangeResolver versionRangeResolver ) + { + setRemoteRepositoryManager( remoteRepositoryManager ); + setArtifactDescriptorReader( artifactDescriptorReader ); + setVersionRangeResolver( versionRangeResolver ); + } + + public void initService( ServiceLocator locator ) + { + setRemoteRepositoryManager( locator.getService( RemoteRepositoryManager.class ) ); + setArtifactDescriptorReader( locator.getService( ArtifactDescriptorReader.class ) ); + setVersionRangeResolver( locator.getService( VersionRangeResolver.class ) ); + } + + public DfDependencyCollector setRemoteRepositoryManager( RemoteRepositoryManager remoteRepositoryManager ) + { + this.remoteRepositoryManager = + requireNonNull( remoteRepositoryManager, "remote repository provider cannot be null" ); + return this; + } + + public DfDependencyCollector setArtifactDescriptorReader( ArtifactDescriptorReader artifactDescriptorReader ) + { + descriptorReader = requireNonNull( artifactDescriptorReader, "artifact descriptor reader cannot be null" ); + return this; + } + + public DfDependencyCollector setVersionRangeResolver( VersionRangeResolver versionRangeResolver ) + { + this.versionRangeResolver = + requireNonNull( versionRangeResolver, "version range resolver cannot be null" ); + return this; + } + + @SuppressWarnings( "checkstyle:methodlength" ) + public CollectResult collectDependencies( RepositorySystemSession session, CollectRequest request ) + throws DependencyCollectionException + { + requireNonNull( session, "session cannot be null" ); + requireNonNull( request, "request cannot be null" ); + session = optimizeSession( session ); + + RequestTrace trace = RequestTrace.newChild( request.getTrace(), request ); + + CollectResult result = new CollectResult( request ); + + DependencySelector depSelector = session.getDependencySelector(); + DependencyManager depManager = session.getDependencyManager(); + DependencyTraverser depTraverser = session.getDependencyTraverser(); + VersionFilter verFilter = session.getVersionFilter(); + + Dependency root = request.getRoot(); + List repositories = request.getRepositories(); + List dependencies = request.getDependencies(); + List managedDependencies = request.getManagedDependencies(); + + Map stats = new LinkedHashMap<>(); + long time1 = System.nanoTime(); + + DefaultDependencyNode node; + if ( root != null ) + { + List versions; + VersionRangeResult rangeResult; + try + { + VersionRangeRequest rangeRequest = + new VersionRangeRequest( root.getArtifact(), request.getRepositories(), + request.getRequestContext() ); + rangeRequest.setTrace( trace ); + rangeResult = versionRangeResolver.resolveVersionRange( session, rangeRequest ); + versions = filterVersions( root, rangeResult, verFilter, new DefaultVersionFilterContext( session ) ); + } + catch ( VersionRangeResolutionException e ) + { + result.addException( e ); + throw new DependencyCollectionException( result, e.getMessage() ); + } + + Version version = versions.get( versions.size() - 1 ); + root = root.setArtifact( root.getArtifact().setVersion( version.toString() ) ); + + ArtifactDescriptorResult descriptorResult; + try + { + ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest(); + descriptorRequest.setArtifact( root.getArtifact() ); + descriptorRequest.setRepositories( request.getRepositories() ); + descriptorRequest.setRequestContext( request.getRequestContext() ); + descriptorRequest.setTrace( trace ); + if ( isLackingDescriptor( root.getArtifact() ) ) + { + descriptorResult = new ArtifactDescriptorResult( descriptorRequest ); + } + else + { + descriptorResult = descriptorReader.readArtifactDescriptor( session, descriptorRequest ); + } + } + catch ( ArtifactDescriptorException e ) + { + result.addException( e ); + throw new DependencyCollectionException( result, e.getMessage() ); + } + + root = root.setArtifact( descriptorResult.getArtifact() ); + + if ( !session.isIgnoreArtifactDescriptorRepositories() ) + { + repositories = remoteRepositoryManager.aggregateRepositories( session, repositories, + descriptorResult.getRepositories(), + true ); + } + dependencies = mergeDeps( dependencies, descriptorResult.getDependencies() ); + managedDependencies = mergeDeps( managedDependencies, descriptorResult.getManagedDependencies() ); + + node = new DefaultDependencyNode( root ); + node.setRequestContext( request.getRequestContext() ); + node.setRelocations( descriptorResult.getRelocations() ); + node.setVersionConstraint( rangeResult.getVersionConstraint() ); + node.setVersion( version ); + node.setAliases( descriptorResult.getAliases() ); + node.setRepositories( request.getRepositories() ); + } + else + { + node = new DefaultDependencyNode( request.getRootArtifact() ); + node.setRequestContext( request.getRequestContext() ); + node.setRepositories( request.getRepositories() ); + } + + result.setRoot( node ); + + boolean traverse = root == null || depTraverser == null || depTraverser.traverseDependency( root ); + String errorPath = null; + if ( traverse && !dependencies.isEmpty() ) + { + DataPool pool = new DataPool( session ); + + NodeStack nodes = new NodeStack(); + nodes.push( node ); + + DefaultDependencyCollectionContext context = + new DefaultDependencyCollectionContext( session, request.getRootArtifact(), root, managedDependencies ); + + DefaultVersionFilterContext versionContext = new DefaultVersionFilterContext( session ); + + Args args = new Args( session, trace, pool, nodes, context, versionContext, request ); + Results results = new Results( result, session ); + + process( args, results, dependencies, repositories, + depSelector != null ? depSelector.deriveChildSelector( context ) : null, + depManager != null ? depManager.deriveChildManager( context ) : null, + depTraverser != null ? depTraverser.deriveChildTraverser( context ) : null, + verFilter != null ? verFilter.deriveChildFilter( context ) : null ); + + errorPath = results.errorPath; + } + + long time2 = System.nanoTime(); + + DependencyGraphTransformer transformer = session.getDependencyGraphTransformer(); + if ( transformer != null ) + { + try + { + DefaultDependencyGraphTransformationContext context = + new DefaultDependencyGraphTransformationContext( session ); + context.put( TransformationContextKeys.STATS, stats ); + result.setRoot( transformer.transformGraph( node, context ) ); + } + catch ( RepositoryException e ) + { + result.addException( e ); + } + } + + long time3 = System.nanoTime(); + stats.put( "DefaultDependencyCollector.collectTime", time2 - time1 ); + stats.put( "DefaultDependencyCollector.transformTime", time3 - time2 ); + LOGGER.debug( "Dependency collection stats {}", stats ); + + if ( errorPath != null ) + { + throw new DependencyCollectionException( result, "Failed to collect dependencies at " + errorPath ); + } + if ( !result.getExceptions().isEmpty() ) + { + throw new DependencyCollectionException( result ); + } + + return result; + } + + private static RepositorySystemSession optimizeSession( RepositorySystemSession session ) + { + DefaultRepositorySystemSession optimized = new DefaultRepositorySystemSession( session ); + optimized.setArtifactTypeRegistry( CachingArtifactTypeRegistry.newInstance( session ) ); + return optimized; + } + + private List mergeDeps( List dominant, List recessive ) + { + List result; + if ( dominant == null || dominant.isEmpty() ) + { + result = recessive; + } + else if ( recessive == null || recessive.isEmpty() ) + { + result = dominant; + } + else + { + int initialCapacity = dominant.size() + recessive.size(); + result = new ArrayList<>( initialCapacity ); + Collection ids = new HashSet<>( initialCapacity, 1.0f ); + for ( Dependency dependency : dominant ) + { + ids.add( getId( dependency.getArtifact() ) ); + result.add( dependency ); + } + for ( Dependency dependency : recessive ) + { + if ( !ids.contains( getId( dependency.getArtifact() ) ) ) + { + result.add( dependency ); + } + } + } + return result; + } + + private static String getId( Artifact a ) + { + return a.getGroupId() + ':' + a.getArtifactId() + ':' + a.getClassifier() + ':' + a.getExtension(); + } + + @SuppressWarnings( "checkstyle:parameternumber" ) + private void process( final Args args, Results results, List dependencies, + List repositories, DependencySelector depSelector, + DependencyManager depManager, DependencyTraverser depTraverser, VersionFilter verFilter ) + { + for ( Dependency dependency : dependencies ) + { + processDependency( args, results, repositories, depSelector, depManager, depTraverser, verFilter, + dependency ); + } + } + + @SuppressWarnings( "checkstyle:parameternumber" ) + private void processDependency( Args args, Results results, List repositories, + DependencySelector depSelector, DependencyManager depManager, + DependencyTraverser depTraverser, VersionFilter verFilter, Dependency dependency ) + { + + List relocations = Collections.emptyList(); + processDependency( args, results, repositories, depSelector, depManager, depTraverser, verFilter, dependency, + relocations, false ); + } + + @SuppressWarnings( "checkstyle:parameternumber" ) + private void processDependency( Args args, Results results, List repositories, + DependencySelector depSelector, DependencyManager depManager, + DependencyTraverser depTraverser, VersionFilter verFilter, Dependency dependency, + List relocations, boolean disableVersionManagement ) + { + + if ( depSelector != null && !depSelector.selectDependency( dependency ) ) + { + return; + } + + PremanagedDependency preManaged = + PremanagedDependency.create( depManager, dependency, disableVersionManagement, args.premanagedState ); + dependency = preManaged.managedDependency; + + boolean noDescriptor = isLackingDescriptor( dependency.getArtifact() ); + + boolean traverse = !noDescriptor && ( depTraverser == null || depTraverser.traverseDependency( dependency ) ); + + List versions; + VersionRangeResult rangeResult; + try + { + VersionRangeRequest rangeRequest = createVersionRangeRequest( args, repositories, dependency ); + + rangeResult = cachedResolveRangeResult( rangeRequest, args.pool, args.session ); + + versions = filterVersions( dependency, rangeResult, verFilter, args.versionContext ); + } + catch ( VersionRangeResolutionException e ) + { + results.addException( dependency, e, args.nodes ); + return; + } + + for ( Version version : versions ) + { + Artifact originalArtifact = dependency.getArtifact().setVersion( version.toString() ); + Dependency d = dependency.setArtifact( originalArtifact ); + + ArtifactDescriptorRequest descriptorRequest = createArtifactDescriptorRequest( args, repositories, d ); + + final ArtifactDescriptorResult descriptorResult = + getArtifactDescriptorResult( args, results, noDescriptor, d, descriptorRequest ); + if ( descriptorResult != null ) + { + d = d.setArtifact( descriptorResult.getArtifact() ); + + DependencyNode node = args.nodes.top(); + + int cycleEntry = args.nodes.find( d.getArtifact() ); + if ( cycleEntry >= 0 ) + { + results.addCycle( args.nodes, cycleEntry, d ); + DependencyNode cycleNode = args.nodes.get( cycleEntry ); + if ( cycleNode.getDependency() != null ) + { + DefaultDependencyNode child = + createDependencyNode( relocations, preManaged, rangeResult, version, d, descriptorResult, + cycleNode ); + node.getChildren().add( child ); + continue; + } + } + + if ( !descriptorResult.getRelocations().isEmpty() ) + { + boolean disableVersionManagementSubsequently = + originalArtifact.getGroupId().equals( d.getArtifact().getGroupId() ) + && originalArtifact.getArtifactId().equals( d.getArtifact().getArtifactId() ); + + processDependency( args, results, repositories, depSelector, depManager, depTraverser, verFilter, d, + descriptorResult.getRelocations(), disableVersionManagementSubsequently ); + return; + } + else + { + d = args.pool.intern( d.setArtifact( args.pool.intern( d.getArtifact() ) ) ); + + List repos = + getRemoteRepositories( rangeResult.getRepository( version ), repositories ); + + DefaultDependencyNode child = + createDependencyNode( relocations, preManaged, rangeResult, version, d, + descriptorResult.getAliases(), repos, args.request.getRequestContext() ); + + node.getChildren().add( child ); + + boolean recurse = traverse && !descriptorResult.getDependencies().isEmpty(); + if ( recurse ) + { + doRecurse( args, results, repositories, depSelector, depManager, depTraverser, verFilter, d, + descriptorResult, child ); + } + } + } + else + { + DependencyNode node = args.nodes.top(); + List repos = + getRemoteRepositories( rangeResult.getRepository( version ), repositories ); + DefaultDependencyNode child = + createDependencyNode( relocations, preManaged, rangeResult, version, d, null, repos, + args.request.getRequestContext() ); + node.getChildren().add( child ); + } + } + } + + @SuppressWarnings( "checkstyle:parameternumber" ) + private void doRecurse( Args args, Results results, List repositories, + DependencySelector depSelector, DependencyManager depManager, + DependencyTraverser depTraverser, VersionFilter verFilter, Dependency d, + ArtifactDescriptorResult descriptorResult, DefaultDependencyNode child ) + { + DefaultDependencyCollectionContext context = args.collectionContext; + context.set( d, descriptorResult.getManagedDependencies() ); + + DependencySelector childSelector = depSelector != null ? depSelector.deriveChildSelector( context ) : null; + DependencyManager childManager = depManager != null ? depManager.deriveChildManager( context ) : null; + DependencyTraverser childTraverser = depTraverser != null ? depTraverser.deriveChildTraverser( context ) : null; + VersionFilter childFilter = verFilter != null ? verFilter.deriveChildFilter( context ) : null; + + final List childRepos = + args.ignoreRepos + ? repositories + : remoteRepositoryManager.aggregateRepositories( args.session, repositories, + descriptorResult.getRepositories(), true ); + + Object key = + args.pool.toKey( d.getArtifact(), childRepos, childSelector, childManager, childTraverser, childFilter ); + + List children = args.pool.getChildren( key ); + if ( children == null ) + { + args.pool.putChildren( key, child.getChildren() ); + + args.nodes.push( child ); + + process( args, results, descriptorResult.getDependencies(), childRepos, childSelector, childManager, + childTraverser, childFilter ); + + args.nodes.pop(); + } + else + { + child.setChildren( children ); + } + } + + private ArtifactDescriptorResult getArtifactDescriptorResult( Args args, Results results, boolean noDescriptor, + Dependency d, + ArtifactDescriptorRequest descriptorRequest ) + { + return noDescriptor + ? new ArtifactDescriptorResult( descriptorRequest ) + : resolveCachedArtifactDescriptor( args.pool, descriptorRequest, args.session, d, results, args ); + + } + + private ArtifactDescriptorResult resolveCachedArtifactDescriptor( DataPool pool, + ArtifactDescriptorRequest descriptorRequest, + RepositorySystemSession session, Dependency d, + Results results, Args args ) + { + Object key = pool.toKey( descriptorRequest ); + ArtifactDescriptorResult descriptorResult = pool.getDescriptor( key, descriptorRequest ); + if ( descriptorResult == null ) + { + try + { + descriptorResult = descriptorReader.readArtifactDescriptor( session, descriptorRequest ); + pool.putDescriptor( key, descriptorResult ); + } + catch ( ArtifactDescriptorException e ) + { + results.addException( d, e, args.nodes ); + pool.putDescriptor( key, e ); + return null; + } + + } + else if ( descriptorResult == DataPool.NO_DESCRIPTOR ) + { + return null; + } + + return descriptorResult; + } + + @SuppressWarnings( "checkstyle:parameternumber" ) + private static DefaultDependencyNode createDependencyNode( List relocations, + PremanagedDependency preManaged, + VersionRangeResult rangeResult, Version version, + Dependency d, Collection aliases, + List repos, String requestContext ) + { + DefaultDependencyNode child = new DefaultDependencyNode( d ); + preManaged.applyTo( child ); + child.setRelocations( relocations ); + child.setVersionConstraint( rangeResult.getVersionConstraint() ); + child.setVersion( version ); + child.setAliases( aliases ); + child.setRepositories( repos ); + child.setRequestContext( requestContext ); + return child; + } + + private static DefaultDependencyNode createDependencyNode( List relocations, + PremanagedDependency preManaged, + VersionRangeResult rangeResult, Version version, + Dependency d, ArtifactDescriptorResult descriptorResult, + DependencyNode cycleNode ) + { + DefaultDependencyNode child = + createDependencyNode( relocations, preManaged, rangeResult, version, d, descriptorResult.getAliases(), + cycleNode.getRepositories(), cycleNode.getRequestContext() ); + child.setChildren( cycleNode.getChildren() ); + return child; + } + + private static ArtifactDescriptorRequest createArtifactDescriptorRequest( Args args, + List repositories, + Dependency d ) + { + ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest(); + descriptorRequest.setArtifact( d.getArtifact() ); + descriptorRequest.setRepositories( repositories ); + descriptorRequest.setRequestContext( args.request.getRequestContext() ); + descriptorRequest.setTrace( args.trace ); + return descriptorRequest; + } + + private static VersionRangeRequest createVersionRangeRequest( Args args, List repositories, + Dependency dependency ) + { + VersionRangeRequest rangeRequest = new VersionRangeRequest(); + rangeRequest.setArtifact( dependency.getArtifact() ); + rangeRequest.setRepositories( repositories ); + rangeRequest.setRequestContext( args.request.getRequestContext() ); + rangeRequest.setTrace( args.trace ); + return rangeRequest; + } + + private VersionRangeResult cachedResolveRangeResult( VersionRangeRequest rangeRequest, DataPool pool, + RepositorySystemSession session ) + throws VersionRangeResolutionException + { + Object key = pool.toKey( rangeRequest ); + VersionRangeResult rangeResult = pool.getConstraint( key, rangeRequest ); + if ( rangeResult == null ) + { + rangeResult = versionRangeResolver.resolveVersionRange( session, rangeRequest ); + pool.putConstraint( key, rangeResult ); + } + return rangeResult; + } + + private static boolean isLackingDescriptor( Artifact artifact ) + { + return artifact.getProperty( ArtifactProperties.LOCAL_PATH, null ) != null; + } + + private static List getRemoteRepositories( ArtifactRepository repository, + List repositories ) + { + if ( repository instanceof RemoteRepository ) + { + return Collections.singletonList( (RemoteRepository) repository ); + } + if ( repository != null ) + { + return Collections.emptyList(); + } + return repositories; + } + + private static List filterVersions( Dependency dependency, VersionRangeResult rangeResult, + VersionFilter verFilter, + DefaultVersionFilterContext verContext ) + throws VersionRangeResolutionException + { + if ( rangeResult.getVersions().isEmpty() ) + { + throw new VersionRangeResolutionException( rangeResult, + "No versions available for " + dependency.getArtifact() + + " within specified range" ); + } + + List versions; + if ( verFilter != null && rangeResult.getVersionConstraint().getRange() != null ) + { + verContext.set( dependency, rangeResult ); + try + { + verFilter.filterVersions( verContext ); + } + catch ( RepositoryException e ) + { + throw new VersionRangeResolutionException( rangeResult, + "Failed to filter versions for " + dependency.getArtifact() + + ": " + e.getMessage(), e ); + } + versions = verContext.get(); + if ( versions.isEmpty() ) + { + throw new VersionRangeResolutionException( rangeResult, + "No acceptable versions for " + dependency.getArtifact() + + ": " + rangeResult.getVersions() ); + } + } + else + { + versions = rangeResult.getVersions(); + } + return versions; + } + + static class Args + { + + final RepositorySystemSession session; + + final boolean ignoreRepos; + + final boolean premanagedState; + + final RequestTrace trace; + + final DataPool pool; + + final NodeStack nodes; + + final DefaultDependencyCollectionContext collectionContext; + + final DefaultVersionFilterContext versionContext; + + final CollectRequest request; + + Args( RepositorySystemSession session, RequestTrace trace, DataPool pool, NodeStack nodes, + DefaultDependencyCollectionContext collectionContext, DefaultVersionFilterContext versionContext, + CollectRequest request ) + { + this.session = session; + this.request = request; + this.ignoreRepos = session.isIgnoreArtifactDescriptorRepositories(); + this.premanagedState = ConfigUtils.getBoolean( session, false, DependencyManagerUtils.CONFIG_PROP_VERBOSE ); + this.trace = trace; + this.pool = pool; + this.nodes = nodes; + this.collectionContext = collectionContext; + this.versionContext = versionContext; + } + + } + + static class Results + { + + private final CollectResult result; + + final int maxExceptions; + + final int maxCycles; + + String errorPath; + + Results( CollectResult result, RepositorySystemSession session ) + { + this.result = result; + + maxExceptions = + ConfigUtils.getInteger( session, CONFIG_PROP_MAX_EXCEPTIONS_DEFAULT, CONFIG_PROP_MAX_EXCEPTIONS ); + + maxCycles = ConfigUtils.getInteger( session, CONFIG_PROP_MAX_CYCLES_DEFAULT, CONFIG_PROP_MAX_CYCLES ); + } + + public void addException( Dependency dependency, Exception e, NodeStack nodes ) + { + if ( maxExceptions < 0 || result.getExceptions().size() < maxExceptions ) + { + result.addException( e ); + if ( errorPath == null ) + { + StringBuilder buffer = new StringBuilder( 256 ); + for ( int i = 0; i < nodes.size(); i++ ) + { + if ( buffer.length() > 0 ) + { + buffer.append( " -> " ); + } + Dependency dep = nodes.get( i ).getDependency(); + if ( dep != null ) + { + buffer.append( dep.getArtifact() ); + } + } + if ( buffer.length() > 0 ) + { + buffer.append( " -> " ); + } + buffer.append( dependency.getArtifact() ); + errorPath = buffer.toString(); + } + } + } + + public void addCycle( NodeStack nodes, int cycleEntry, Dependency dependency ) + { + if ( maxCycles < 0 || result.getCycles().size() < maxCycles ) + { + result.addCycle( new DfDependencyCycle( nodes, cycleEntry, dependency ) ); + } + } + + } + + static class PremanagedDependency + { + + final String premanagedVersion; + + final String premanagedScope; + + final Boolean premanagedOptional; + + /** + * @since 1.1.0 + */ + final Collection premanagedExclusions; + + /** + * @since 1.1.0 + */ + final Map premanagedProperties; + + final int managedBits; + + final Dependency managedDependency; + + final boolean premanagedState; + + @SuppressWarnings( "checkstyle:parameternumber" ) + PremanagedDependency( String premanagedVersion, String premanagedScope, Boolean premanagedOptional, + Collection premanagedExclusions, Map premanagedProperties, + int managedBits, Dependency managedDependency, boolean premanagedState ) + { + this.premanagedVersion = premanagedVersion; + this.premanagedScope = premanagedScope; + this.premanagedOptional = premanagedOptional; + this.premanagedExclusions = + premanagedExclusions != null + ? Collections.unmodifiableCollection( new ArrayList<>( premanagedExclusions ) ) + : null; + + this.premanagedProperties = + premanagedProperties != null + ? Collections.unmodifiableMap( new HashMap<>( premanagedProperties ) ) + : null; + + this.managedBits = managedBits; + this.managedDependency = managedDependency; + this.premanagedState = premanagedState; + } + + static PremanagedDependency create( DependencyManager depManager, Dependency dependency, + boolean disableVersionManagement, boolean premanagedState ) + { + DependencyManagement depMngt = depManager != null ? depManager.manageDependency( dependency ) : null; + + int managedBits = 0; + String premanagedVersion = null; + String premanagedScope = null; + Boolean premanagedOptional = null; + Collection premanagedExclusions = null; + Map premanagedProperties = null; + + if ( depMngt != null ) + { + if ( depMngt.getVersion() != null && !disableVersionManagement ) + { + Artifact artifact = dependency.getArtifact(); + premanagedVersion = artifact.getVersion(); + dependency = dependency.setArtifact( artifact.setVersion( depMngt.getVersion() ) ); + managedBits |= DependencyNode.MANAGED_VERSION; + } + if ( depMngt.getProperties() != null ) + { + Artifact artifact = dependency.getArtifact(); + premanagedProperties = artifact.getProperties(); + dependency = dependency.setArtifact( artifact.setProperties( depMngt.getProperties() ) ); + managedBits |= DependencyNode.MANAGED_PROPERTIES; + } + if ( depMngt.getScope() != null ) + { + premanagedScope = dependency.getScope(); + dependency = dependency.setScope( depMngt.getScope() ); + managedBits |= DependencyNode.MANAGED_SCOPE; + } + if ( depMngt.getOptional() != null ) + { + premanagedOptional = dependency.isOptional(); + dependency = dependency.setOptional( depMngt.getOptional() ); + managedBits |= DependencyNode.MANAGED_OPTIONAL; + } + if ( depMngt.getExclusions() != null ) + { + premanagedExclusions = dependency.getExclusions(); + dependency = dependency.setExclusions( depMngt.getExclusions() ); + managedBits |= DependencyNode.MANAGED_EXCLUSIONS; + } + } + return new PremanagedDependency( premanagedVersion, premanagedScope, premanagedOptional, + premanagedExclusions, premanagedProperties, managedBits, dependency, + premanagedState ); + + } + + public void applyTo( DefaultDependencyNode child ) + { + child.setManagedBits( managedBits ); + if ( premanagedState ) + { + child.setData( DependencyManagerUtils.NODE_DATA_PREMANAGED_VERSION, premanagedVersion ); + child.setData( DependencyManagerUtils.NODE_DATA_PREMANAGED_SCOPE, premanagedScope ); + child.setData( DependencyManagerUtils.NODE_DATA_PREMANAGED_OPTIONAL, premanagedOptional ); + child.setData( DependencyManagerUtils.NODE_DATA_PREMANAGED_EXCLUSIONS, premanagedExclusions ); + child.setData( DependencyManagerUtils.NODE_DATA_PREMANAGED_PROPERTIES, premanagedProperties ); + } + } + + } + +} \ No newline at end of file diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCycle.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCycle.java new file mode 100644 index 000000000..ff8d639dd --- /dev/null +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCycle.java @@ -0,0 +1,88 @@ +package org.eclipse.aether.internal.impl.collect.df; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.eclipse.aether.graph.Dependency; +import org.eclipse.aether.graph.DependencyCycle; +import org.eclipse.aether.graph.DependencyNode; +import org.eclipse.aether.util.artifact.ArtifactIdUtils; + +/** + * @see DfDependencyCollector + */ +final class DfDependencyCycle + implements DependencyCycle +{ + + private final List dependencies; + + private final int cycleEntry; + + DfDependencyCycle( NodeStack nodes, int cycleEntry, Dependency dependency ) + { + // skip root node unless it actually has a dependency or is considered the cycle entry (due to its label) + int offset = ( cycleEntry > 0 && nodes.get( 0 ).getDependency() == null ) ? 1 : 0; + Dependency[] dependencies = new Dependency[nodes.size() - offset + 1]; + for ( int i = 0, n = dependencies.length - 1; i < n; i++ ) + { + DependencyNode node = nodes.get( i + offset ); + dependencies[i] = node.getDependency(); + // when cycle starts at root artifact as opposed to root dependency, synthesize a dependency + if ( dependencies[i] == null ) + { + dependencies[i] = new Dependency( node.getArtifact(), null ); + } + } + dependencies[dependencies.length - 1] = dependency; + this.dependencies = Collections.unmodifiableList( Arrays.asList( dependencies ) ); + this.cycleEntry = cycleEntry; + } + + public List getPrecedingDependencies() + { + return dependencies.subList( 0, cycleEntry ); + } + + public List getCyclicDependencies() + { + return dependencies.subList( cycleEntry, dependencies.size() ); + } + + @Override + public String toString() + { + StringBuilder buffer = new StringBuilder( 256 ); + int i = 0; + for ( Dependency dependency : dependencies ) + { + if ( i++ > 0 ) + { + buffer.append( " -> " ); + } + buffer.append( ArtifactIdUtils.toVersionlessId( dependency.getArtifact() ) ); + } + return buffer.toString(); + } + +} \ No newline at end of file diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/NodeStack.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/NodeStack.java new file mode 100644 index 000000000..8266aefd8 --- /dev/null +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/NodeStack.java @@ -0,0 +1,127 @@ +package org.eclipse.aether.internal.impl.collect.df; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.util.Arrays; + +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.graph.DependencyNode; + +/** + * @see DfDependencyCollector + */ +final class NodeStack +{ + + @SuppressWarnings( {"unchecked", "checkstyle:magicnumber" } ) + // CHECKSTYLE_OFF: MagicNumber + private DependencyNode[] nodes = new DependencyNode[96]; + // CHECKSTYLE_ON: MagicNumber + + private int size; + + public DependencyNode top() + { + if ( size <= 0 ) + { + throw new IllegalStateException( "stack empty" ); + } + return nodes[size - 1]; + } + + public void push( DependencyNode node ) + { + if ( size >= nodes.length ) + { + DependencyNode[] tmp = new DependencyNode[size + 64]; + System.arraycopy( nodes, 0, tmp, 0, nodes.length ); + nodes = tmp; + } + nodes[size++] = node; + } + + public void pop() + { + if ( size <= 0 ) + { + throw new IllegalStateException( "stack empty" ); + } + size--; + } + + public int find( Artifact artifact ) + { + for ( int i = size - 1; i >= 0; i-- ) + { + DependencyNode node = nodes[i]; + + Artifact a = node.getArtifact(); + if ( a == null ) + { + break; + } + + if ( !a.getArtifactId().equals( artifact.getArtifactId() ) ) + { + continue; + } + if ( !a.getGroupId().equals( artifact.getGroupId() ) ) + { + continue; + } + if ( !a.getExtension().equals( artifact.getExtension() ) ) + { + continue; + } + if ( !a.getClassifier().equals( artifact.getClassifier() ) ) + { + continue; + } + /* + * NOTE: While a:1 and a:2 are technically different artifacts, we want to consider the path a:2 -> b:2 -> + * a:1 a cycle in the current context. The artifacts themselves might not form a cycle but their producing + * projects surely do. Furthermore, conflict resolution will always have to consider a:1 a loser (otherwise + * its ancestor a:2 would get pruned and so would a:1) so there is no point in building the sub graph of + * a:1. + */ + + return i; + } + + return -1; + } + + public int size() + { + return size; + } + + public DependencyNode get( int index ) + { + return nodes[index]; + } + + @Override + public String toString() + { + return Arrays.toString( nodes ); + } + +} \ No newline at end of file diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollectorTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollectorTest.java new file mode 100644 index 000000000..b641bbd64 --- /dev/null +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollectorTest.java @@ -0,0 +1,649 @@ +package org.eclipse.aether.internal.impl.collect.df; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import static java.util.Objects.requireNonNull; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.eclipse.aether.DefaultRepositorySystemSession; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.artifact.ArtifactProperties; +import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.collection.CollectRequest; +import org.eclipse.aether.collection.CollectResult; +import org.eclipse.aether.collection.DependencyCollectionContext; +import org.eclipse.aether.collection.DependencyCollectionException; +import org.eclipse.aether.collection.DependencyManagement; +import org.eclipse.aether.collection.DependencyManager; +import org.eclipse.aether.graph.DefaultDependencyNode; +import org.eclipse.aether.graph.Dependency; +import org.eclipse.aether.graph.DependencyCycle; +import org.eclipse.aether.graph.DependencyNode; +import org.eclipse.aether.graph.Exclusion; +import org.eclipse.aether.impl.ArtifactDescriptorReader; +import org.eclipse.aether.internal.impl.IniArtifactDescriptorReader; +import org.eclipse.aether.internal.impl.StubRemoteRepositoryManager; +import org.eclipse.aether.internal.impl.StubVersionRangeResolver; +import org.eclipse.aether.internal.test.util.DependencyGraphParser; +import org.eclipse.aether.internal.test.util.TestUtils; +import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.resolution.ArtifactDescriptorException; +import org.eclipse.aether.resolution.ArtifactDescriptorRequest; +import org.eclipse.aether.resolution.ArtifactDescriptorResult; +import org.eclipse.aether.util.artifact.ArtifactIdUtils; +import org.eclipse.aether.util.graph.manager.ClassicDependencyManager; +import org.eclipse.aether.util.graph.manager.DefaultDependencyManager; +import org.eclipse.aether.util.graph.manager.DependencyManagerUtils; +import org.eclipse.aether.util.graph.manager.TransitiveDependencyManager; +import org.eclipse.aether.util.graph.version.HighestVersionFilter; +import org.junit.Before; +import org.junit.Test; + +/** + */ +public class DfDependencyCollectorTest +{ + + private DfDependencyCollector collector; + + private DefaultRepositorySystemSession session; + + private DependencyGraphParser parser; + + private RemoteRepository repository; + + private IniArtifactDescriptorReader newReader( String prefix ) + { + return new IniArtifactDescriptorReader( "artifact-descriptions/" + prefix ); + } + + private Dependency newDep( String coords ) + { + return newDep( coords, "" ); + } + + private Dependency newDep( String coords, String scope ) + { + return new Dependency( new DefaultArtifact( coords ), scope ); + } + + @Before + public void setup() + { + session = TestUtils.newSession(); + + collector = new DfDependencyCollector(); + collector.setArtifactDescriptorReader( newReader( "" ) ); + collector.setVersionRangeResolver( new StubVersionRangeResolver() ); + collector.setRemoteRepositoryManager( new StubRemoteRepositoryManager() ); + + parser = new DependencyGraphParser( "artifact-descriptions/" ); + + repository = new RemoteRepository.Builder( "id", "default", "file:///" ).build(); + } + + private static void assertEqualSubtree( DependencyNode expected, DependencyNode actual ) + { + assertEqualSubtree( expected, actual, new LinkedList() ); + } + + private static void assertEqualSubtree( DependencyNode expected, DependencyNode actual, + LinkedList parents ) + { + assertEquals( "path: " + parents, expected.getDependency(), actual.getDependency() ); + + if ( actual.getDependency() != null ) + { + Artifact artifact = actual.getDependency().getArtifact(); + for ( DependencyNode parent : parents ) + { + if ( parent.getDependency() != null && artifact.equals( parent.getDependency().getArtifact() ) ) + { + return; + } + } + } + + parents.addLast( expected ); + + assertEquals( "path: " + parents + ", expected: " + expected.getChildren() + ", actual: " + + actual.getChildren(), expected.getChildren().size(), actual.getChildren().size() ); + + Iterator iterator1 = expected.getChildren().iterator(); + Iterator iterator2 = actual.getChildren().iterator(); + + while ( iterator1.hasNext() ) + { + assertEqualSubtree( iterator1.next(), iterator2.next(), parents ); + } + + parents.removeLast(); + } + + private Dependency dep( DependencyNode root, int... coords ) + { + return path( root, coords ).getDependency(); + } + + private DependencyNode path( DependencyNode root, int... coords ) + { + try + { + DependencyNode node = root; + for ( int coord : coords ) + { + node = node.getChildren().get( coord ); + } + + return node; + } + catch ( IndexOutOfBoundsException | NullPointerException e ) + { + throw new IllegalArgumentException( "illegal coordinates for child", e ); + } + } + + @Test + public void testSimpleCollection() + throws DependencyCollectionException + { + Dependency dependency = newDep( "gid:aid:ext:ver", "compile" ); + CollectRequest request = new CollectRequest( dependency, Arrays.asList( repository ) ); + CollectResult result = collector.collectDependencies( session, request ); + + assertEquals( 0, result.getExceptions().size() ); + + DependencyNode root = result.getRoot(); + Dependency newDependency = root.getDependency(); + + assertEquals( dependency, newDependency ); + assertEquals( dependency.getArtifact(), newDependency.getArtifact() ); + + assertEquals( 1, root.getChildren().size() ); + + Dependency expect = newDep( "gid:aid2:ext:ver", "compile" ); + assertEquals( expect, root.getChildren().get( 0 ).getDependency() ); + } + + @Test + public void testMissingDependencyDescription() + { + CollectRequest request = + new CollectRequest( newDep( "missing:description:ext:ver" ), Arrays.asList( repository ) ); + try + { + collector.collectDependencies( session, request ); + fail( "expected exception" ); + } + catch ( DependencyCollectionException e ) + { + CollectResult result = e.getResult(); + assertSame( request, result.getRequest() ); + assertNotNull( result.getExceptions() ); + assertEquals( 1, result.getExceptions().size() ); + + assertTrue( result.getExceptions().get( 0 ) instanceof ArtifactDescriptorException ); + + assertEquals( request.getRoot(), result.getRoot().getDependency() ); + } + } + + @Test + public void testDuplicates() + throws DependencyCollectionException + { + Dependency dependency = newDep( "duplicate:transitive:ext:dependency" ); + CollectRequest request = new CollectRequest( dependency, Arrays.asList( repository ) ); + + CollectResult result = collector.collectDependencies( session, request ); + + assertEquals( 0, result.getExceptions().size() ); + + DependencyNode root = result.getRoot(); + Dependency newDependency = root.getDependency(); + + assertEquals( dependency, newDependency ); + assertEquals( dependency.getArtifact(), newDependency.getArtifact() ); + + assertEquals( 2, root.getChildren().size() ); + + Dependency dep = newDep( "gid:aid:ext:ver", "compile" ); + assertEquals( dep, dep( root, 0 ) ); + + dep = newDep( "gid:aid2:ext:ver", "compile" ); + assertEquals( dep, dep( root, 1 ) ); + assertEquals( dep, dep( root, 0, 0 ) ); + assertEquals( dep( root, 1 ), dep( root, 0, 0 ) ); + } + + @Test + public void testEqualSubtree() + throws IOException, DependencyCollectionException + { + DependencyNode root = parser.parseResource( "expectedSubtreeComparisonResult.txt" ); + Dependency dependency = root.getDependency(); + CollectRequest request = new CollectRequest( dependency, Arrays.asList( repository ) ); + + CollectResult result = collector.collectDependencies( session, request ); + assertEqualSubtree( root, result.getRoot() ); + } + + @Test + public void testCyclicDependencies() + throws Exception + { + DependencyNode root = parser.parseResource( "cycle.txt" ); + CollectRequest request = new CollectRequest( root.getDependency(), Arrays.asList( repository ) ); + CollectResult result = collector.collectDependencies( session, request ); + assertEqualSubtree( root, result.getRoot() ); + } + + @Test + public void testCyclicDependenciesBig() + throws Exception + { + CollectRequest request = new CollectRequest( newDep( "1:2:pom:5.50-SNAPSHOT" ), Arrays.asList( repository ) ); + collector.setArtifactDescriptorReader( newReader( "cycle-big/" ) ); + CollectResult result = collector.collectDependencies( session, request ); + assertNotNull( result.getRoot() ); + // we only care about the performance here, this test must not hang or run out of mem + } + + @Test + public void testCyclicProjects() + throws Exception + { + CollectRequest request = new CollectRequest( newDep( "test:a:2" ), Arrays.asList( repository ) ); + collector.setArtifactDescriptorReader( newReader( "versionless-cycle/" ) ); + CollectResult result = collector.collectDependencies( session, request ); + DependencyNode root = result.getRoot(); + DependencyNode a1 = path( root, 0, 0 ); + assertEquals( "a", a1.getArtifact().getArtifactId() ); + assertEquals( "1", a1.getArtifact().getVersion() ); + for ( DependencyNode child : a1.getChildren() ) + { + assertNotEquals( "1", child.getArtifact().getVersion() ); + } + + assertEquals( 1, result.getCycles().size() ); + DependencyCycle cycle = result.getCycles().get( 0 ); + assertEquals( Arrays.asList(), cycle.getPrecedingDependencies() ); + assertEquals( Arrays.asList( root.getDependency(), path( root, 0 ).getDependency(), a1.getDependency() ), + cycle.getCyclicDependencies() ); + } + + @Test + public void testCyclicProjects_ConsiderLabelOfRootlessGraph() + throws Exception + { + Dependency dep = newDep( "gid:aid:ver", "compile" ); + CollectRequest request = + new CollectRequest().addDependency( dep ).addRepository( repository ).setRootArtifact( dep.getArtifact() ); + CollectResult result = collector.collectDependencies( session, request ); + DependencyNode root = result.getRoot(); + DependencyNode a1 = root.getChildren().get( 0 ); + assertEquals( "aid", a1.getArtifact().getArtifactId() ); + assertEquals( "ver", a1.getArtifact().getVersion() ); + DependencyNode a2 = a1.getChildren().get( 0 ); + assertEquals( "aid2", a2.getArtifact().getArtifactId() ); + assertEquals( "ver", a2.getArtifact().getVersion() ); + + assertEquals( 1, result.getCycles().size() ); + DependencyCycle cycle = result.getCycles().get( 0 ); + assertEquals( Arrays.asList(), cycle.getPrecedingDependencies() ); + assertEquals( Arrays.asList( new Dependency( dep.getArtifact(), null ), a1.getDependency() ), + cycle.getCyclicDependencies() ); + } + + @Test + public void testPartialResultOnError() + throws IOException + { + DependencyNode root = parser.parseResource( "expectedPartialSubtreeOnError.txt" ); + + Dependency dependency = root.getDependency(); + CollectRequest request = new CollectRequest( dependency, Arrays.asList( repository ) ); + + CollectResult result; + try + { + result = collector.collectDependencies( session, request ); + fail( "expected exception " ); + } + catch ( DependencyCollectionException e ) + { + result = e.getResult(); + + assertSame( request, result.getRequest() ); + assertNotNull( result.getExceptions() ); + assertEquals( 1, result.getExceptions().size() ); + + assertTrue( result.getExceptions().get( 0 ) instanceof ArtifactDescriptorException ); + + assertEqualSubtree( root, result.getRoot() ); + } + } + + @Test + public void testCollectMultipleDependencies() + throws DependencyCollectionException + { + Dependency root1 = newDep( "gid:aid:ext:ver", "compile" ); + Dependency root2 = newDep( "gid:aid2:ext:ver", "compile" ); + List dependencies = Arrays.asList( root1, root2 ); + CollectRequest request = new CollectRequest( dependencies, null, Arrays.asList( repository ) ); + CollectResult result = collector.collectDependencies( session, request ); + + assertEquals( 0, result.getExceptions().size() ); + assertEquals( 2, result.getRoot().getChildren().size() ); + assertEquals( root1, dep( result.getRoot(), 0 ) ); + + assertEquals( 1, path( result.getRoot(), 0 ).getChildren().size() ); + assertEquals( root2, dep( result.getRoot(), 0, 0 ) ); + + assertEquals( 0, path( result.getRoot(), 1 ).getChildren().size() ); + assertEquals( root2, dep( result.getRoot(), 1 ) ); + } + + @Test + public void testArtifactDescriptorResolutionNotRestrictedToRepoHostingSelectedVersion() + throws Exception + { + RemoteRepository repo2 = new RemoteRepository.Builder( "test", "default", "file:///" ).build(); + + final List repos = new ArrayList<>(); + + collector.setArtifactDescriptorReader( new ArtifactDescriptorReader() + { + public ArtifactDescriptorResult readArtifactDescriptor( RepositorySystemSession session, + ArtifactDescriptorRequest request ) + { + repos.addAll( request.getRepositories() ); + return new ArtifactDescriptorResult( request ); + } + } ); + + List dependencies = Arrays.asList( newDep( "verrange:parent:jar:1[1,)", "compile" ) ); + CollectRequest request = new CollectRequest( dependencies, null, Arrays.asList( repository, repo2 ) ); + CollectResult result = collector.collectDependencies( session, request ); + + assertEquals( 0, result.getExceptions().size() ); + assertEquals( 2, repos.size() ); + assertEquals( "id", repos.get( 0 ).getId() ); + assertEquals( "test", repos.get( 1 ).getId() ); + } + + @Test + public void testManagedVersionScope() + throws DependencyCollectionException + { + Dependency dependency = newDep( "managed:aid:ext:ver" ); + CollectRequest request = new CollectRequest( dependency, Arrays.asList( repository ) ); + + session.setDependencyManager( new ClassicDependencyManager() ); + + CollectResult result = collector.collectDependencies( session, request ); + + assertEquals( 0, result.getExceptions().size() ); + + DependencyNode root = result.getRoot(); + + assertEquals( dependency, dep( root ) ); + assertEquals( dependency.getArtifact(), dep( root ).getArtifact() ); + + assertEquals( 1, root.getChildren().size() ); + Dependency expect = newDep( "gid:aid:ext:ver", "compile" ); + assertEquals( expect, dep( root, 0 ) ); + + assertEquals( 1, path( root, 0 ).getChildren().size() ); + expect = newDep( "gid:aid2:ext:managedVersion", "managedScope" ); + assertEquals( expect, dep( root, 0, 0 ) ); + } + + @Test + public void testDependencyManagement() + throws IOException, DependencyCollectionException + { + collector.setArtifactDescriptorReader( newReader( "managed/" ) ); + + DependencyNode root = parser.parseResource( "expectedSubtreeComparisonResult.txt" ); + TestDependencyManager depMgmt = new TestDependencyManager(); + depMgmt.add( dep( root, 0 ), "managed", null, null ); + depMgmt.add( dep( root, 0, 1 ), "managed", "managed", null ); + depMgmt.add( dep( root, 1 ), null, null, "managed" ); + session.setDependencyManager( depMgmt ); + + // collect result will differ from expectedSubtreeComparisonResult.txt + // set localPath -> no dependency traversal + CollectRequest request = new CollectRequest( dep( root ), Arrays.asList( repository ) ); + CollectResult result = collector.collectDependencies( session, request ); + + DependencyNode node = result.getRoot(); + assertEquals( "managed", dep( node, 0, 1 ).getArtifact().getVersion() ); + assertEquals( "managed", dep( node, 0, 1 ).getScope() ); + + assertEquals( "managed", dep( node, 1 ).getArtifact().getProperty( ArtifactProperties.LOCAL_PATH, null ) ); + assertEquals( "managed", dep( node, 0, 0 ).getArtifact().getProperty( ArtifactProperties.LOCAL_PATH, null ) ); + } + + @Test + public void testDependencyManagement_VerboseMode() + throws Exception + { + String depId = "gid:aid2:ext"; + TestDependencyManager depMgmt = new TestDependencyManager(); + depMgmt.version( depId, "managedVersion" ); + depMgmt.scope( depId, "managedScope" ); + depMgmt.optional( depId, Boolean.TRUE ); + depMgmt.path( depId, "managedPath" ); + depMgmt.exclusions( depId, new Exclusion( "gid", "aid", "*", "*" ) ); + session.setDependencyManager( depMgmt ); + session.setConfigProperty( DependencyManagerUtils.CONFIG_PROP_VERBOSE, Boolean.TRUE ); + + CollectRequest request = new CollectRequest().setRoot( newDep( "gid:aid:ver" ) ); + CollectResult result = collector.collectDependencies( session, request ); + DependencyNode node = result.getRoot().getChildren().get( 0 ); + assertEquals( DependencyNode.MANAGED_VERSION | DependencyNode.MANAGED_SCOPE | DependencyNode.MANAGED_OPTIONAL + | DependencyNode.MANAGED_PROPERTIES | DependencyNode.MANAGED_EXCLUSIONS, node.getManagedBits() ); + assertEquals( "ver", DependencyManagerUtils.getPremanagedVersion( node ) ); + assertEquals( "compile", DependencyManagerUtils.getPremanagedScope( node ) ); + assertEquals( Boolean.FALSE, DependencyManagerUtils.getPremanagedOptional( node ) ); + } + + @Test + public void testDependencyManagement_TransitiveDependencyManager() + throws DependencyCollectionException, IOException + { + collector.setArtifactDescriptorReader( newReader( "managed/" ) ); + parser = new DependencyGraphParser( "artifact-descriptions/managed/" ); + session.setDependencyManager( new TransitiveDependencyManager() ); + final Dependency root = newDep( "gid:root:ext:ver", "compile" ); + CollectRequest request = new CollectRequest( root, Collections.singletonList( repository ) ); + request.addManagedDependency( newDep( "gid:root:ext:must-retain-core-management" ) ); + CollectResult result = collector.collectDependencies( session, request ); + + final DependencyNode expectedTree = parser.parseResource( "management-tree.txt" ); + assertEqualSubtree( expectedTree, result.getRoot() ); + + // Same test for root artifact (POM) request. + final CollectRequest rootArtifactRequest = new CollectRequest(); + rootArtifactRequest.setRepositories( Collections.singletonList( repository ) ); + rootArtifactRequest.setRootArtifact( new DefaultArtifact( "gid:root:ext:ver" ) ); + rootArtifactRequest.addDependency( newDep( "gid:direct:ext:ver", "compile" ) ); + rootArtifactRequest.addManagedDependency( newDep( "gid:root:ext:must-retain-core-management" ) ); + rootArtifactRequest.addManagedDependency( newDep( "gid:direct:ext:must-retain-core-management" ) ); + rootArtifactRequest.addManagedDependency( newDep( "gid:transitive-1:ext:managed-by-root" ) ); + session.setDependencyManager( new TransitiveDependencyManager() ); + result = collector.collectDependencies( session, rootArtifactRequest ); + assertEqualSubtree( expectedTree, toDependencyResult( result.getRoot(), "compile", null ) ); + } + + @Test + public void testDependencyManagement_DefaultDependencyManager() + throws DependencyCollectionException, IOException + { + collector.setArtifactDescriptorReader( newReader( "managed/" ) ); + parser = new DependencyGraphParser( "artifact-descriptions/managed/" ); + session.setDependencyManager( new DefaultDependencyManager() ); + final Dependency root = newDep( "gid:root:ext:ver", "compile" ); + CollectRequest request = new CollectRequest( root, Arrays.asList( repository ) ); + request.addManagedDependency( newDep( "gid:root:ext:must-not-manage-root" ) ); + request.addManagedDependency( newDep( "gid:direct:ext:managed-by-dominant-request" ) ); + CollectResult result = collector.collectDependencies( session, request ); + + final DependencyNode expectedTree = parser.parseResource( "default-management-tree.txt" ); + assertEqualSubtree( expectedTree, result.getRoot() ); + + // Same test for root artifact (POM) request. + final CollectRequest rootArtifactRequest = new CollectRequest(); + rootArtifactRequest.setRepositories( Arrays.asList( repository ) ); + rootArtifactRequest.setRootArtifact( new DefaultArtifact( "gid:root:ext:ver" ) ); + rootArtifactRequest.addDependency( newDep( "gid:direct:ext:ver", "compile" ) ); + rootArtifactRequest.addManagedDependency( newDep( "gid:root:ext:must-not-manage-root" ) ); + rootArtifactRequest.addManagedDependency( newDep( "gid:direct:ext:managed-by-dominant-request" ) ); + rootArtifactRequest.addManagedDependency( newDep( "gid:transitive-1:ext:managed-by-root" ) ); + session.setDependencyManager( new DefaultDependencyManager() ); + result = collector.collectDependencies( session, rootArtifactRequest ); + assertEqualSubtree( expectedTree, toDependencyResult( result.getRoot(), "compile", null ) ); + } + + private DependencyNode toDependencyResult( final DependencyNode root, final String rootScope, + final Boolean optional ) + { + // Make the root artifact resultion result a dependency resolution result for the subtree check. + assertNull( "Expected root artifact resolution result.", root.getDependency() ); + final DefaultDependencyNode defaultNode = + new DefaultDependencyNode( new Dependency( root.getArtifact(), rootScope ) ); + + defaultNode.setChildren( root.getChildren() ); + + if ( optional != null ) + { + defaultNode.setOptional( optional ); + } + + return defaultNode; + } + + @Test + public void testVersionFilter() + throws Exception + { + session.setVersionFilter( new HighestVersionFilter() ); + CollectRequest request = new CollectRequest().setRoot( newDep( "gid:aid:1" ) ); + CollectResult result = collector.collectDependencies( session, request ); + assertEquals( 1, result.getRoot().getChildren().size() ); + } + + static class TestDependencyManager + implements DependencyManager + { + + private Map versions = new HashMap<>(); + + private Map scopes = new HashMap<>(); + + private Map optionals = new HashMap<>(); + + private Map paths = new HashMap<>(); + + private Map> exclusions = new HashMap<>(); + + public void add( Dependency d, String version, String scope, String localPath ) + { + String id = toKey( d ); + version( id, version ); + scope( id, scope ); + path( id, localPath ); + } + + public void version( String id, String version ) + { + versions.put( id, version ); + } + + public void scope( String id, String scope ) + { + scopes.put( id, scope ); + } + + public void optional( String id, Boolean optional ) + { + optionals.put( id, optional ); + } + + public void path( String id, String path ) + { + paths.put( id, path ); + } + + public void exclusions( String id, Exclusion... exclusions ) + { + this.exclusions.put( id, exclusions != null ? Arrays.asList( exclusions ) : null ); + } + + public DependencyManagement manageDependency( Dependency dependency ) + { + requireNonNull( dependency, "dependency cannot be null" ); + String id = toKey( dependency ); + DependencyManagement mgmt = new DependencyManagement(); + mgmt.setVersion( versions.get( id ) ); + mgmt.setScope( scopes.get( id ) ); + mgmt.setOptional( optionals.get( id ) ); + String path = paths.get( id ); + if ( path != null ) + { + mgmt.setProperties( Collections.singletonMap( ArtifactProperties.LOCAL_PATH, path ) ); + } + mgmt.setExclusions( exclusions.get( id ) ); + return mgmt; + } + + private String toKey( Dependency dependency ) + { + return ArtifactIdUtils.toVersionlessId( dependency.getArtifact() ); + } + + public DependencyManager deriveChildManager( DependencyCollectionContext context ) + { + requireNonNull( context, "context cannot be null" ); + return this; + } + + } + +} \ No newline at end of file diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCycleTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCycleTest.java new file mode 100644 index 000000000..d9708476a --- /dev/null +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCycleTest.java @@ -0,0 +1,44 @@ +package org.eclipse.aether.internal.impl.collect.df; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.graph.DefaultDependencyNode; +import org.eclipse.aether.graph.Dependency; +import org.eclipse.aether.graph.DependencyCycle; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class DfDependencyCycleTest +{ + private static final Dependency FOO_DEPENDENCY = new Dependency( new DefaultArtifact( "group-id:foo:1.0" ), "test" ); + private static final Dependency BAR_DEPENDENCY = new Dependency( new DefaultArtifact( "group-id:bar:1.0" ), "test" ); + + @Test + public void testToString() + { + NodeStack nodeStack = new NodeStack(); + nodeStack.push( new DefaultDependencyNode( FOO_DEPENDENCY ) ); + DependencyCycle cycle = new DfDependencyCycle( nodeStack, 1, BAR_DEPENDENCY ); + + assertEquals( "group-id:foo:jar -> group-id:bar:jar", cycle.toString() ); + } +} \ No newline at end of file From d8e3512e1c291b364342914bcec256fda52b82ce Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Mon, 4 Apr 2022 20:29:00 +0200 Subject: [PATCH 03/10] Collapse DependencyCycle implementation into one --- ...Cycle.java => DefaultDependencyCycle.java} | 10 +-- .../collect/bf/BfDependencyCollector.java | 6 +- .../DefaultDependencyResolutionSkipper.java | 1 - .../bf}/DependencyResolutionSkipper.java | 2 +- .../bf/NeverDependencyResolutionSkipper.java | 1 - .../collect/df/DfDependencyCollector.java | 3 +- .../impl/collect/df/DfDependencyCycle.java | 88 ------------------- .../internal/impl/collect/df/NodeStack.java | 32 +++---- ...t.java => DefaultDependencyCycleTest.java} | 6 +- .../collect/df/DfDependencyCycleTest.java | 44 ---------- 10 files changed, 25 insertions(+), 168 deletions(-) rename maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/{bf/BfDependencyCycle.java => DefaultDependencyCycle.java} (94%) rename maven-resolver-impl/src/main/java/org/eclipse/aether/{impl => internal/impl/collect/bf}/DependencyResolutionSkipper.java (97%) delete mode 100644 maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCycle.java rename maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/{bf/BfDependencyCycleTest.java => DefaultDependencyCycleTest.java} (90%) delete mode 100644 maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCycleTest.java diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCycle.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycle.java similarity index 94% rename from maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCycle.java rename to maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycle.java index 94a566d44..0a3ff2ba2 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCycle.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycle.java @@ -1,4 +1,4 @@ -package org.eclipse.aether.internal.impl.collect.bf; +package org.eclipse.aether.internal.impl.collect; /* * Licensed to the Apache Software Foundation (ASF) under one @@ -27,21 +27,19 @@ import org.eclipse.aether.graph.Dependency; import org.eclipse.aether.graph.DependencyCycle; import org.eclipse.aether.graph.DependencyNode; -import org.eclipse.aether.internal.impl.collect.DefaultDependencyCollector; import org.eclipse.aether.util.artifact.ArtifactIdUtils; /** - * @see DefaultDependencyCollector + * Default implementation of {@link DependencyCycle}. */ -final class BfDependencyCycle +public final class DefaultDependencyCycle implements DependencyCycle { - private final List dependencies; private final int cycleEntry; - BfDependencyCycle( List nodes, int cycleEntry, Dependency dependency ) + public DefaultDependencyCycle( List nodes, int cycleEntry, Dependency dependency ) { // skip root node unless it actually has a dependency or is considered the cycle entry (due to its label) int offset = ( cycleEntry > 0 && nodes.get( 0 ).getDependency() == null ) ? 1 : 0; diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java index 837af5285..cea0c9387 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java @@ -54,12 +54,12 @@ import org.eclipse.aether.graph.DependencyNode; import org.eclipse.aether.graph.Exclusion; import org.eclipse.aether.impl.ArtifactDescriptorReader; -import org.eclipse.aether.impl.DependencyResolutionSkipper; import org.eclipse.aether.impl.RemoteRepositoryManager; import org.eclipse.aether.impl.VersionRangeResolver; import org.eclipse.aether.internal.impl.collect.CachingArtifactTypeRegistry; import org.eclipse.aether.internal.impl.collect.DataPool; import org.eclipse.aether.internal.impl.collect.DefaultDependencyCollectionContext; +import org.eclipse.aether.internal.impl.collect.DefaultDependencyCycle; import org.eclipse.aether.internal.impl.collect.DefaultDependencyGraphTransformationContext; import org.eclipse.aether.internal.impl.collect.DefaultVersionFilterContext; import org.eclipse.aether.internal.impl.collect.DependencyCollectorDelegate; @@ -81,7 +81,7 @@ import org.slf4j.LoggerFactory; import static java.util.Objects.requireNonNull; -import static org.eclipse.aether.internal.impl.collect.bf.BfDependencyCycle.find; +import static org.eclipse.aether.internal.impl.collect.DefaultDependencyCycle.find; /** * Breadth-first {@link org.eclipse.aether.impl.DependencyCollector} @@ -816,7 +816,7 @@ public void addCycle( List nodes, int cycleEntry, Dependency dep { if ( maxCycles < 0 || result.getCycles().size() < maxCycles ) { - result.addCycle( new BfDependencyCycle( nodes, cycleEntry, dependency ) ); + result.addCycle( new DefaultDependencyCycle( nodes, cycleEntry, dependency ) ); } } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/DefaultDependencyResolutionSkipper.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/DefaultDependencyResolutionSkipper.java index 38fcd4e73..b6dfe93eb 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/DefaultDependencyResolutionSkipper.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/DefaultDependencyResolutionSkipper.java @@ -21,7 +21,6 @@ import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.graph.DependencyNode; -import org.eclipse.aether.impl.DependencyResolutionSkipper; import org.eclipse.aether.util.artifact.ArtifactIdUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/DependencyResolutionSkipper.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/DependencyResolutionSkipper.java similarity index 97% rename from maven-resolver-impl/src/main/java/org/eclipse/aether/impl/DependencyResolutionSkipper.java rename to maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/DependencyResolutionSkipper.java index 649f6d36d..45fb90e89 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/DependencyResolutionSkipper.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/DependencyResolutionSkipper.java @@ -1,4 +1,4 @@ -package org.eclipse.aether.impl; +package org.eclipse.aether.internal.impl.collect.bf; /* * Licensed to the Apache Software Foundation (ASF) under one diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/NeverDependencyResolutionSkipper.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/NeverDependencyResolutionSkipper.java index 8ad4450d1..c1692d4b5 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/NeverDependencyResolutionSkipper.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/NeverDependencyResolutionSkipper.java @@ -20,7 +20,6 @@ */ import org.eclipse.aether.graph.DependencyNode; -import org.eclipse.aether.impl.DependencyResolutionSkipper; import java.util.List; diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java index 528fc908b..1630fb9ae 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java @@ -58,6 +58,7 @@ import org.eclipse.aether.internal.impl.collect.CachingArtifactTypeRegistry; import org.eclipse.aether.internal.impl.collect.DataPool; import org.eclipse.aether.internal.impl.collect.DefaultDependencyCollectionContext; +import org.eclipse.aether.internal.impl.collect.DefaultDependencyCycle; import org.eclipse.aether.internal.impl.collect.DefaultDependencyGraphTransformationContext; import org.eclipse.aether.internal.impl.collect.DefaultVersionFilterContext; import org.eclipse.aether.internal.impl.collect.DependencyCollectorDelegate; @@ -787,7 +788,7 @@ public void addCycle( NodeStack nodes, int cycleEntry, Dependency dependency ) { if ( maxCycles < 0 || result.getCycles().size() < maxCycles ) { - result.addCycle( new DfDependencyCycle( nodes, cycleEntry, dependency ) ); + result.addCycle( new DefaultDependencyCycle( nodes.nodes, cycleEntry, dependency ) ); } } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCycle.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCycle.java deleted file mode 100644 index ff8d639dd..000000000 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCycle.java +++ /dev/null @@ -1,88 +0,0 @@ -package org.eclipse.aether.internal.impl.collect.df; - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.eclipse.aether.graph.Dependency; -import org.eclipse.aether.graph.DependencyCycle; -import org.eclipse.aether.graph.DependencyNode; -import org.eclipse.aether.util.artifact.ArtifactIdUtils; - -/** - * @see DfDependencyCollector - */ -final class DfDependencyCycle - implements DependencyCycle -{ - - private final List dependencies; - - private final int cycleEntry; - - DfDependencyCycle( NodeStack nodes, int cycleEntry, Dependency dependency ) - { - // skip root node unless it actually has a dependency or is considered the cycle entry (due to its label) - int offset = ( cycleEntry > 0 && nodes.get( 0 ).getDependency() == null ) ? 1 : 0; - Dependency[] dependencies = new Dependency[nodes.size() - offset + 1]; - for ( int i = 0, n = dependencies.length - 1; i < n; i++ ) - { - DependencyNode node = nodes.get( i + offset ); - dependencies[i] = node.getDependency(); - // when cycle starts at root artifact as opposed to root dependency, synthesize a dependency - if ( dependencies[i] == null ) - { - dependencies[i] = new Dependency( node.getArtifact(), null ); - } - } - dependencies[dependencies.length - 1] = dependency; - this.dependencies = Collections.unmodifiableList( Arrays.asList( dependencies ) ); - this.cycleEntry = cycleEntry; - } - - public List getPrecedingDependencies() - { - return dependencies.subList( 0, cycleEntry ); - } - - public List getCyclicDependencies() - { - return dependencies.subList( cycleEntry, dependencies.size() ); - } - - @Override - public String toString() - { - StringBuilder buffer = new StringBuilder( 256 ); - int i = 0; - for ( Dependency dependency : dependencies ) - { - if ( i++ > 0 ) - { - buffer.append( " -> " ); - } - buffer.append( ArtifactIdUtils.toVersionlessId( dependency.getArtifact() ) ); - } - return buffer.toString(); - } - -} \ No newline at end of file diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/NodeStack.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/NodeStack.java index 8266aefd8..2abdddcad 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/NodeStack.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/NodeStack.java @@ -19,7 +19,7 @@ * under the License. */ -import java.util.Arrays; +import java.util.ArrayList; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.graph.DependencyNode; @@ -32,45 +32,37 @@ final class NodeStack @SuppressWarnings( {"unchecked", "checkstyle:magicnumber" } ) // CHECKSTYLE_OFF: MagicNumber - private DependencyNode[] nodes = new DependencyNode[96]; + ArrayList nodes = new ArrayList<>( 96 ); // CHECKSTYLE_ON: MagicNumber - private int size; - public DependencyNode top() { - if ( size <= 0 ) + if ( nodes.isEmpty() ) { throw new IllegalStateException( "stack empty" ); } - return nodes[size - 1]; + return nodes.get( nodes.size() - 1 ); } public void push( DependencyNode node ) { - if ( size >= nodes.length ) - { - DependencyNode[] tmp = new DependencyNode[size + 64]; - System.arraycopy( nodes, 0, tmp, 0, nodes.length ); - nodes = tmp; - } - nodes[size++] = node; + nodes.add( node ); } public void pop() { - if ( size <= 0 ) + if ( nodes.isEmpty() ) { throw new IllegalStateException( "stack empty" ); } - size--; + nodes.remove( nodes.size() - 1 ); } public int find( Artifact artifact ) { - for ( int i = size - 1; i >= 0; i-- ) + for ( int i = nodes.size() - 1; i >= 0; i-- ) { - DependencyNode node = nodes[i]; + DependencyNode node = nodes.get( i ); Artifact a = node.getArtifact(); if ( a == null ) @@ -110,18 +102,18 @@ public int find( Artifact artifact ) public int size() { - return size; + return nodes.size(); } public DependencyNode get( int index ) { - return nodes[index]; + return nodes.get( index ); } @Override public String toString() { - return Arrays.toString( nodes ); + return nodes.toString(); } } \ No newline at end of file diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCycleTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycleTest.java similarity index 90% rename from maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCycleTest.java rename to maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycleTest.java index a1be7c926..66dade8c1 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCycleTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycleTest.java @@ -1,4 +1,4 @@ -package org.eclipse.aether.internal.impl.collect.bf; +package org.eclipse.aether.internal.impl.collect; /* * Licensed to the Apache Software Foundation (ASF) under one @@ -31,7 +31,7 @@ import static org.junit.Assert.assertEquals; -public class BfDependencyCycleTest +public class DefaultDependencyCycleTest { private static final Dependency FOO_DEPENDENCY = new Dependency( new DefaultArtifact( "group-id:foo:1.0" ), "test" ); private static final Dependency BAR_DEPENDENCY = new Dependency( new DefaultArtifact( "group-id:bar:1.0" ), "test" ); @@ -41,7 +41,7 @@ public void testToString() { List nodes = new ArrayList<>(); nodes.add( new DefaultDependencyNode( FOO_DEPENDENCY ) ); - DependencyCycle cycle = new BfDependencyCycle( nodes, 1, BAR_DEPENDENCY ); + DependencyCycle cycle = new DefaultDependencyCycle( nodes, 1, BAR_DEPENDENCY ); assertEquals( "group-id:foo:jar -> group-id:bar:jar", cycle.toString() ); } diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCycleTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCycleTest.java deleted file mode 100644 index d9708476a..000000000 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCycleTest.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.eclipse.aether.internal.impl.collect.df; - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import org.eclipse.aether.artifact.DefaultArtifact; -import org.eclipse.aether.graph.DefaultDependencyNode; -import org.eclipse.aether.graph.Dependency; -import org.eclipse.aether.graph.DependencyCycle; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; - -public class DfDependencyCycleTest -{ - private static final Dependency FOO_DEPENDENCY = new Dependency( new DefaultArtifact( "group-id:foo:1.0" ), "test" ); - private static final Dependency BAR_DEPENDENCY = new Dependency( new DefaultArtifact( "group-id:bar:1.0" ), "test" ); - - @Test - public void testToString() - { - NodeStack nodeStack = new NodeStack(); - nodeStack.push( new DefaultDependencyNode( FOO_DEPENDENCY ) ); - DependencyCycle cycle = new DfDependencyCycle( nodeStack, 1, BAR_DEPENDENCY ); - - assertEquals( "group-id:foo:jar -> group-id:bar:jar", cycle.toString() ); - } -} \ No newline at end of file From 79e17341f6a4771859b290bd090ad514be91f8c4 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Mon, 4 Apr 2022 20:53:58 +0200 Subject: [PATCH 04/10] Collapse skipper It is really internal thing of BF skipper, no need for iface + 2 separate classes. Expose and adjust tests. --- .../collect/bf/BfDependencyCollector.java | 4 +- .../DefaultDependencyResolutionSkipper.java | 289 ---------------- .../bf/DependencyResolutionSkipper.java | 310 +++++++++++++++++- .../bf/NeverDependencyResolutionSkipper.java | 52 --- .../bf/DependencyResolutionSkipperTest.java | 43 ++- 5 files changed, 329 insertions(+), 369 deletions(-) delete mode 100644 maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/DefaultDependencyResolutionSkipper.java delete mode 100644 maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/NeverDependencyResolutionSkipper.java diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java index cea0c9387..18d9341ed 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java @@ -288,8 +288,8 @@ public CollectResult collectDependencies( RepositorySystemSession session, Colle Args args = new Args( session, trace, pool, context, versionContext, request, - useSkip ? new DefaultDependencyResolutionSkipper() - : NeverDependencyResolutionSkipper.INSTANCE ); + useSkip ? DependencyResolutionSkipper.defaultSkipper() + : DependencyResolutionSkipper.neverSkipper() ); Results results = new Results( result, session ); DependencySelector rootDepSelector = diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/DefaultDependencyResolutionSkipper.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/DefaultDependencyResolutionSkipper.java deleted file mode 100644 index b6dfe93eb..000000000 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/DefaultDependencyResolutionSkipper.java +++ /dev/null @@ -1,289 +0,0 @@ -package org.eclipse.aether.internal.impl.collect.bf; - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import org.eclipse.aether.artifact.Artifact; -import org.eclipse.aether.graph.DependencyNode; -import org.eclipse.aether.util.artifact.ArtifactIdUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * Skipper for Skip-and-reconcile approach. - * - * @see BfDependencyCollector - */ -public final class DefaultDependencyResolutionSkipper implements DependencyResolutionSkipper -{ - private static final Logger LOGGER = LoggerFactory.getLogger( DefaultDependencyResolutionSkipper.class ); - - private final Map results = new LinkedHashMap<>( 256 ); - private final CacheManager cacheManager = new CacheManager(); - private final CoordinateManager coordinateManager = new CoordinateManager(); - - @Override - public boolean skipResolution( DependencyNode node, List parents ) - { - DependencyResolutionResult result = new DependencyResolutionResult( node ); - results.put( node, result ); - - int depth = parents.size() + 1; - coordinateManager.createCoordinate( node, depth ); - - if ( cacheManager.isVersionConflict( node ) ) - { - /* - * Skip resolving version conflict losers (omitted for conflict) - */ - result.skippedAsVersionConflict = true; - if ( LOGGER.isTraceEnabled() ) - { - LOGGER.trace( "Skipped resolving node: {} as version conflict", - ArtifactIdUtils.toId( node.getArtifact() ) ); - } - } - else if ( cacheManager.isDuplicate( node ) ) - { - if ( coordinateManager.isLeftmost( node, parents ) ) - { - /* - * Force resolving the node to retain conflict paths when its coordinate is more left than last resolved - * This is because Maven picks the widest scope present among conflicting dependencies - */ - result.forceResolution = true; - if ( LOGGER.isTraceEnabled() ) - { - LOGGER.trace( "Force resolving node: {} for scope selection", - ArtifactIdUtils.toId( node.getArtifact() ) ); - } - } - else - { - /* - * Skip resolving as duplicate (depth deeper, omitted for duplicate) - * No need to compare depth as the depth of winner for given artifact is always shallower - */ - result.skippedAsDuplicate = true; - if ( LOGGER.isTraceEnabled() ) - { - LOGGER.trace( "Skipped resolving node: {} as duplicate", - ArtifactIdUtils.toId( node.getArtifact() ) ); - } - } - } - else - { - result.resolve = true; - if ( LOGGER.isTraceEnabled() ) - { - LOGGER.trace( "Resolving node: {}", - ArtifactIdUtils.toId( node.getArtifact() ) ); - } - } - - if ( result.toResolve() ) - { - coordinateManager.updateLeftmost( node ); - return false; - } - - return true; - } - - @Override - public void cache( DependencyNode node, List parents ) - { - boolean parentForceResolution = parents.stream() - .anyMatch( n -> results.containsKey( n ) && results.get( n ).forceResolution ); - if ( parentForceResolution ) - { - if ( LOGGER.isTraceEnabled() ) - { - LOGGER.trace( - "Won't cache as node: {} inherits from a force-resolved node and will be omitted for duplicate", - ArtifactIdUtils.toId( node.getArtifact() ) ); - } - } - else - { - cacheManager.cacheWinner( node ); - } - } - - @Override - public void report() - { - if ( LOGGER.isTraceEnabled() ) - { - LOGGER.trace( "Skipped {} nodes as duplicate", - results.entrySet().stream().filter( n -> n.getValue().skippedAsDuplicate ).count() ); - LOGGER.trace( "Skipped {} nodes as having version conflict", - results.entrySet().stream().filter( n -> n.getValue().skippedAsVersionConflict ).count() ); - LOGGER.trace( "Resolved {} nodes", - results.entrySet().stream().filter( n -> n.getValue().resolve ).count() ); - LOGGER.trace( "Forced resolving {} nodes for scope selection", - results.entrySet().stream().filter( n -> n.getValue().forceResolution ).count() ); - } - } - - public Map getResults() - { - return results; - } - - static final class DependencyResolutionResult - { - DependencyNode current; - boolean skippedAsVersionConflict; //omitted for conflict - boolean skippedAsDuplicate; //omitted for duplicate, depth is deeper - boolean resolve; //node to resolve (winner node) - boolean forceResolution; //force resolving (duplicate node) for scope selection - - DependencyResolutionResult( DependencyNode current ) - { - this.current = current; - } - - boolean toResolve() - { - return resolve || forceResolution; - } - } - - static final class CacheManager - { - - /** - * artifact -> node - */ - private final Map winners = new HashMap<>( 256 ); - - - /** - * versionLessId -> Artifact, only cache winners - */ - private final Map winnerGAs = new HashMap<>( 256 ); - - boolean isVersionConflict( DependencyNode node ) - { - String ga = ArtifactIdUtils.toVersionlessId( node.getArtifact() ); - if ( winnerGAs.containsKey( ga ) ) - { - Artifact result = winnerGAs.get( ga ); - return !node.getArtifact().getVersion().equals( result.getVersion() ); - } - - return false; - } - - void cacheWinner( DependencyNode node ) - { - winners.put( node.getArtifact(), node ); - winnerGAs.put( ArtifactIdUtils.toVersionlessId( node.getArtifact() ), node.getArtifact() ); - } - - boolean isDuplicate( DependencyNode node ) - { - return winners.containsKey( node.getArtifact() ); - } - - } - - - static final class CoordinateManager - { - private final Map sequenceGen = new HashMap<>( 256 ); - - /** - * Dependency node -> Coordinate - */ - private final Map coordinateMap = new HashMap<>( 256 ); - - /** - * Leftmost coordinate of given artifact - */ - private final Map leftmostCoordinates = new HashMap<>( 256 ); - - - Coordinate getCoordinate( DependencyNode node ) - { - return coordinateMap.get( node ); - } - - Coordinate createCoordinate( DependencyNode node, int depth ) - { - int seq = sequenceGen.computeIfAbsent( depth, k -> new AtomicInteger() ).incrementAndGet(); - Coordinate coordinate = new Coordinate( depth, seq ); - coordinateMap.put( node, coordinate ); - return coordinate; - } - - void updateLeftmost( DependencyNode current ) - { - leftmostCoordinates.put( current.getArtifact(), getCoordinate( current ) ); - } - - boolean isLeftmost( DependencyNode node, List parents ) - { - Coordinate leftmost = leftmostCoordinates.get( node.getArtifact() ); - if ( leftmost != null && leftmost.depth <= parents.size() ) - { - DependencyNode sameLevelNode = parents.get( leftmost.depth - 1 ); - if ( getCoordinate( sameLevelNode ).sequence < leftmost.sequence ) - { - return true; - } - } - - return false; - } - } - - static final class Coordinate - { - int depth; - int sequence; - - Coordinate( int depth, int sequence ) - { - this.depth = depth; - this.sequence = sequence; - } - - @Override - public String toString() - { - return "{" - + "depth=" - + depth - + ", sequence=" - + sequence - + '}'; - } - } - - -} diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/DependencyResolutionSkipper.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/DependencyResolutionSkipper.java index 45fb90e89..119788168 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/DependencyResolutionSkipper.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/DependencyResolutionSkipper.java @@ -19,9 +19,17 @@ * under the License. */ +import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.graph.DependencyNode; +import org.eclipse.aether.util.artifact.ArtifactIdUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; /** * A skipper that determines whether to skip resolving given node during the dependency collection. @@ -31,7 +39,7 @@ * @provisional This type is provisional and can be changed, moved or removed without prior notice. * @since 1.8.0 */ -public interface DependencyResolutionSkipper +abstract class DependencyResolutionSkipper { /** * Check whether the resolution of current node can be skipped before resolving. @@ -41,7 +49,7 @@ public interface DependencyResolutionSkipper * * @return {@code true} if the node can be skipped for resolution, {@code false} if resolution required. */ - boolean skipResolution( DependencyNode node, List parents ); + abstract boolean skipResolution( DependencyNode node, List parents ); /** * Cache the resolution result when a node is resolved by @See DependencyCollector after resolution. @@ -49,11 +57,305 @@ public interface DependencyResolutionSkipper * @param node Current node * @param parents All parent nodes of current node */ - void cache( DependencyNode node, List parents ); + abstract void cache( DependencyNode node, List parents ); /** * Print the skip/resolve status report for all nodes. */ - void report(); + abstract void report(); + /** + * Returns new instance of "default" skipper. + * + * Note: type is specialized for testing purposes. + */ + public static DefaultDependencyResolutionSkipper defaultSkipper() + { + return new DefaultDependencyResolutionSkipper(); + } + + /** + * Returns instance of "never" skipper. + */ + public static DependencyResolutionSkipper neverSkipper() + { + return NeverDependencyResolutionSkipper.INSTANCE; + } + + /** + * NEVER implementation. + */ + private static final class NeverDependencyResolutionSkipper extends DependencyResolutionSkipper + { + private static final DependencyResolutionSkipper INSTANCE = new NeverDependencyResolutionSkipper(); + + @Override + public boolean skipResolution( DependencyNode node, List parents ) + { + return false; + } + + @Override + public void cache( DependencyNode node, List parents ) + { + } + + @Override + public void report() + { + } + } + + /** + * Visible for testing. + */ + static final class DefaultDependencyResolutionSkipper extends DependencyResolutionSkipper + { + private static final Logger LOGGER = LoggerFactory.getLogger( DependencyResolutionSkipper.class ); + + private final Map results = new LinkedHashMap<>( 256 ); + private final CacheManager cacheManager = new CacheManager(); + private final CoordinateManager coordinateManager = new CoordinateManager(); + + @Override + public boolean skipResolution( DependencyNode node, List parents ) + { + DependencyResolutionResult result = new DependencyResolutionResult( node ); + results.put( node, result ); + + int depth = parents.size() + 1; + coordinateManager.createCoordinate( node, depth ); + + if ( cacheManager.isVersionConflict( node ) ) + { + /* + * Skip resolving version conflict losers (omitted for conflict) + */ + result.skippedAsVersionConflict = true; + if ( LOGGER.isTraceEnabled() ) + { + LOGGER.trace( "Skipped resolving node: {} as version conflict", + ArtifactIdUtils.toId( node.getArtifact() ) ); + } + } + else if ( cacheManager.isDuplicate( node ) ) + { + if ( coordinateManager.isLeftmost( node, parents ) ) + { + /* + * Force resolving the node to retain conflict paths when its coordinate is + * more left than last resolved + * This is because Maven picks the widest scope present among conflicting dependencies + */ + result.forceResolution = true; + if ( LOGGER.isTraceEnabled() ) + { + LOGGER.trace( "Force resolving node: {} for scope selection", + ArtifactIdUtils.toId( node.getArtifact() ) ); + } + } + else + { + /* + * Skip resolving as duplicate (depth deeper, omitted for duplicate) + * No need to compare depth as the depth of winner for given artifact is always shallower + */ + result.skippedAsDuplicate = true; + if ( LOGGER.isTraceEnabled() ) + { + LOGGER.trace( "Skipped resolving node: {} as duplicate", + ArtifactIdUtils.toId( node.getArtifact() ) ); + } + } + } + else + { + result.resolve = true; + if ( LOGGER.isTraceEnabled() ) + { + LOGGER.trace( "Resolving node: {}", + ArtifactIdUtils.toId( node.getArtifact() ) ); + } + } + + if ( result.toResolve() ) + { + coordinateManager.updateLeftmost( node ); + return false; + } + + return true; + } + + @Override + public void cache( DependencyNode node, List parents ) + { + boolean parentForceResolution = parents.stream() + .anyMatch( n -> results.containsKey( n ) && results.get( n ).forceResolution ); + if ( parentForceResolution ) + { + if ( LOGGER.isTraceEnabled() ) + { + LOGGER.trace( "Won't cache as node: {} inherits from a force-resolved node " + + "and will be omitted for duplicate", ArtifactIdUtils.toId( node.getArtifact() ) ); + } + } + else + { + cacheManager.cacheWinner( node ); + } + } + + @Override + public void report() + { + if ( LOGGER.isTraceEnabled() ) + { + LOGGER.trace( "Skipped {} nodes as duplicate", + results.entrySet().stream().filter( n -> n.getValue().skippedAsDuplicate ).count() ); + LOGGER.trace( "Skipped {} nodes as having version conflict", + results.entrySet().stream().filter( n -> n.getValue().skippedAsVersionConflict ).count() ); + LOGGER.trace( "Resolved {} nodes", + results.entrySet().stream().filter( n -> n.getValue().resolve ).count() ); + LOGGER.trace( "Forced resolving {} nodes for scope selection", + results.entrySet().stream().filter( n -> n.getValue().forceResolution ).count() ); + } + } + + public Map getResults() + { + return results; + } + + private static final class CacheManager + { + + /** + * artifact -> node + */ + private final Map winners = new HashMap<>( 256 ); + + + /** + * versionLessId -> Artifact, only cache winners + */ + private final Map winnerGAs = new HashMap<>( 256 ); + + boolean isVersionConflict( DependencyNode node ) + { + String ga = ArtifactIdUtils.toVersionlessId( node.getArtifact() ); + if ( winnerGAs.containsKey( ga ) ) + { + Artifact result = winnerGAs.get( ga ); + return !node.getArtifact().getVersion().equals( result.getVersion() ); + } + + return false; + } + + void cacheWinner( DependencyNode node ) + { + winners.put( node.getArtifact(), node ); + winnerGAs.put( ArtifactIdUtils.toVersionlessId( node.getArtifact() ), node.getArtifact() ); + } + + boolean isDuplicate( DependencyNode node ) + { + return winners.containsKey( node.getArtifact() ); + } + + } + + + private static final class CoordinateManager + { + private final Map sequenceGen = new HashMap<>( 256 ); + + /** + * Dependency node -> Coordinate + */ + private final Map coordinateMap = new HashMap<>( 256 ); + + /** + * Leftmost coordinate of given artifact + */ + private final Map leftmostCoordinates = new HashMap<>( 256 ); + + + Coordinate getCoordinate( DependencyNode node ) + { + return coordinateMap.get( node ); + } + + Coordinate createCoordinate( DependencyNode node, int depth ) + { + int seq = sequenceGen.computeIfAbsent( depth, k -> new AtomicInteger() ).incrementAndGet(); + Coordinate coordinate = new Coordinate( depth, seq ); + coordinateMap.put( node, coordinate ); + return coordinate; + } + + void updateLeftmost( DependencyNode current ) + { + leftmostCoordinates.put( current.getArtifact(), getCoordinate( current ) ); + } + + boolean isLeftmost( DependencyNode node, List parents ) + { + Coordinate leftmost = leftmostCoordinates.get( node.getArtifact() ); + if ( leftmost != null && leftmost.depth <= parents.size() ) + { + DependencyNode sameLevelNode = parents.get( leftmost.depth - 1 ); + return getCoordinate( sameLevelNode ).sequence < leftmost.sequence; + } + + return false; + } + } + + private static final class Coordinate + { + int depth; + int sequence; + + Coordinate( int depth, int sequence ) + { + this.depth = depth; + this.sequence = sequence; + } + + @Override + public String toString() + { + return "{" + + "depth=" + + depth + + ", sequence=" + + sequence + + '}'; + } + } + } + + /** + * Visible for testing. + */ + static final class DependencyResolutionResult + { + DependencyNode current; + boolean skippedAsVersionConflict; //omitted for conflict + boolean skippedAsDuplicate; //omitted for duplicate, depth is deeper + boolean resolve; //node to resolve (winner node) + boolean forceResolution; //force resolving (duplicate node) for scope selection + + DependencyResolutionResult( DependencyNode current ) + { + this.current = current; + } + + boolean toResolve() + { + return resolve || forceResolution; + } + } } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/NeverDependencyResolutionSkipper.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/NeverDependencyResolutionSkipper.java deleted file mode 100644 index c1692d4b5..000000000 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/NeverDependencyResolutionSkipper.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.eclipse.aether.internal.impl.collect.bf; - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import org.eclipse.aether.graph.DependencyNode; - -import java.util.List; - -/** - * Skipper for Non-skip approach. - * - * @see BfDependencyCollector - */ -public final class NeverDependencyResolutionSkipper implements DependencyResolutionSkipper -{ - public static final DependencyResolutionSkipper INSTANCE = new NeverDependencyResolutionSkipper(); - - @Override - public boolean skipResolution( DependencyNode node, List parents ) - { - return false; - } - - @Override - public void cache( DependencyNode node, List parents ) - { - - } - - @Override - public void report() - { - - } -} diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/DependencyResolutionSkipperTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/DependencyResolutionSkipperTest.java index 678b9e766..eeb0a02d7 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/DependencyResolutionSkipperTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/DependencyResolutionSkipperTest.java @@ -24,7 +24,6 @@ import java.util.Map; import java.util.stream.Collectors; -import org.eclipse.aether.RepositoryException; import org.eclipse.aether.artifact.DefaultArtifact; import org.eclipse.aether.graph.DefaultDependencyNode; import org.eclipse.aether.graph.Dependency; @@ -61,7 +60,7 @@ private static DependencyNode makeDependencyNode( String groupId, String artifac } @Test - public void testSkipVersionConflict() throws RepositoryException + public void testSkipVersionConflict() { // A -> B -> C 3.0 -> D => C3.0 SHOULD BE SKIPPED // | -> E -> F -> G @@ -84,7 +83,7 @@ public void testSkipVersionConflict() throws RepositoryException c2Node.setChildren( mutableList( hNode ) ); //follow the BFS resolve sequence - DefaultDependencyResolutionSkipper skipper = new DefaultDependencyResolutionSkipper(); + DependencyResolutionSkipper.DefaultDependencyResolutionSkipper skipper = DependencyResolutionSkipper.defaultSkipper(); assertFalse( skipper.skipResolution( aNode, new ArrayList<>() ) ); skipper.cache( aNode, new ArrayList<>() ); assertFalse( skipper.skipResolution( bNode, mutableList( aNode ) ) ); @@ -99,19 +98,19 @@ public void testSkipVersionConflict() throws RepositoryException assertFalse( skipper.skipResolution( gNode, mutableList( aNode, eNode, fNode ) ) ); skipper.cache( gNode, mutableList( aNode, eNode, fNode ) ); - Map results = skipper.getResults(); + Map results = skipper.getResults(); assertEquals( results.size(), 7 ); - List skipped = - results.entrySet().stream().filter( n -> n.getValue().skippedAsVersionConflict ) - .map( s -> s.getValue() ).collect( - Collectors.toList() ); + List skipped = + results.values().stream() + .filter( dependencyResolutionResult -> dependencyResolutionResult.skippedAsVersionConflict ) + .collect( Collectors.toList() ); assertEquals( skipped.size(), 1 ); assertTrue( skipped.get( 0 ).current == c3Node ); } @Test - public void testSkipDeeperDuplicateNode() throws RepositoryException + public void testSkipDeeperDuplicateNode() { // A -> B // |--> C -> B => B here will be skipped @@ -129,7 +128,7 @@ public void testSkipDeeperDuplicateNode() throws RepositoryException dNode.setChildren( mutableList( c1Node ) ); //follow the BFS resolve sequence - DefaultDependencyResolutionSkipper skipper = new DefaultDependencyResolutionSkipper(); + DependencyResolutionSkipper.DefaultDependencyResolutionSkipper skipper = DependencyResolutionSkipper.defaultSkipper(); assertFalse( skipper.skipResolution( aNode, new ArrayList<>() ) ); skipper.cache( aNode, new ArrayList<>() ); assertFalse( skipper.skipResolution( bNode, mutableList( aNode ) ) ); @@ -145,13 +144,13 @@ public void testSkipDeeperDuplicateNode() throws RepositoryException assertTrue( skipper.skipResolution( c1Node, mutableList( aNode, dNode ) ) ); skipper.cache( c1Node, mutableList( aNode, dNode ) ); - Map results = skipper.getResults(); + Map results = skipper.getResults(); assertEquals( results.size(), 6 ); - List skipped = - results.entrySet().stream().filter( n -> n.getValue().skippedAsDuplicate ) - .map( s -> s.getValue() ).collect( - Collectors.toList() ); + List skipped = + results.values().stream() + .filter( dependencyResolutionResult -> dependencyResolutionResult.skippedAsDuplicate ) + .collect( Collectors.toList() ); assertEquals( skipped.size(), 2 ); assertTrue( skipped.get( 0 ).current == b1Node ); assertTrue( skipped.get( 1 ).current == c1Node ); @@ -159,7 +158,7 @@ public void testSkipDeeperDuplicateNode() throws RepositoryException @Test - public void testForceResolution() throws RepositoryException + public void testForceResolution() { // A -> B -> C -> D => 3rd D here will be force-resolved // |--> C -> D => 2nd D will be force-resolved @@ -179,7 +178,7 @@ public void testForceResolution() throws RepositoryException dNode.setChildren( new ArrayList<>() ); //follow the BFS resolve sequence - DefaultDependencyResolutionSkipper skipper = new DefaultDependencyResolutionSkipper(); + DependencyResolutionSkipper.DefaultDependencyResolutionSkipper skipper = DependencyResolutionSkipper.defaultSkipper(); assertFalse( skipper.skipResolution( aNode, new ArrayList<>() ) ); skipper.cache( aNode, new ArrayList<>() ); assertFalse( skipper.skipResolution( bNode, mutableList( aNode ) ) ); @@ -198,13 +197,13 @@ public void testForceResolution() throws RepositoryException assertFalse( skipper.skipResolution( d2Node, mutableList( aNode, bNode, c1Node ) ) ); skipper.cache( d2Node, mutableList( aNode, bNode, c1Node ) ); - Map results = skipper.getResults(); + Map results = skipper.getResults(); assertEquals( results.size(), 7 ); - List forceResolved = - results.entrySet().stream().filter( n -> n.getValue().forceResolution ) - .map( s -> s.getValue() ).collect( - Collectors.toList() ); + List forceResolved = + results.values().stream() + .filter( dependencyResolutionResult -> dependencyResolutionResult.forceResolution ) + .collect( Collectors.toList() ); assertEquals( forceResolved.size(), 3 ); assertTrue( forceResolved.get( 0 ).current == c1Node ); assertTrue( forceResolved.get( 1 ).current == d1Node ); From 9ef46cfc691b3556706a011bce6d8035937a16b5 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Mon, 4 Apr 2022 21:08:02 +0200 Subject: [PATCH 05/10] Pull out common config and shared bits. --- .../collect/DependencyCollectorDelegate.java | 67 ++++++++++++++++++- .../collect/bf/BfDependencyCollector.java | 58 ++-------------- .../collect/df/DfDependencyCollector.java | 54 +-------------- 3 files changed, 74 insertions(+), 105 deletions(-) diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java index 5d17efc6c..9d8a15605 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java @@ -19,13 +19,78 @@ * under the License. */ +import org.eclipse.aether.impl.ArtifactDescriptorReader; import org.eclipse.aether.impl.DependencyCollector; +import org.eclipse.aether.impl.RemoteRepositoryManager; +import org.eclipse.aether.impl.VersionRangeResolver; +import org.eclipse.aether.spi.locator.ServiceLocator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static java.util.Objects.requireNonNull; /** * The delegate to the actual implementation. * * @since 1.8.0 */ -public interface DependencyCollectorDelegate extends DependencyCollector +public abstract class DependencyCollectorDelegate implements DependencyCollector { + protected static final String CONFIG_PROP_MAX_EXCEPTIONS = "aether.dependencyCollector.maxExceptions"; + + protected static final int CONFIG_PROP_MAX_EXCEPTIONS_DEFAULT = 50; + + protected static final String CONFIG_PROP_MAX_CYCLES = "aether.dependencyCollector.maxCycles"; + + protected static final int CONFIG_PROP_MAX_CYCLES_DEFAULT = 10; + + protected final Logger logger = LoggerFactory.getLogger( getClass() ); + + protected RemoteRepositoryManager remoteRepositoryManager; + + protected ArtifactDescriptorReader descriptorReader; + + protected VersionRangeResolver versionRangeResolver; + + public DependencyCollectorDelegate() + { + // enables default constructor + } + + protected DependencyCollectorDelegate( RemoteRepositoryManager remoteRepositoryManager, + ArtifactDescriptorReader artifactDescriptorReader, + VersionRangeResolver versionRangeResolver ) + { + setRemoteRepositoryManager( remoteRepositoryManager ); + setArtifactDescriptorReader( artifactDescriptorReader ); + setVersionRangeResolver( versionRangeResolver ); + } + + public void initService( ServiceLocator locator ) + { + setRemoteRepositoryManager( locator.getService( RemoteRepositoryManager.class ) ); + setArtifactDescriptorReader( locator.getService( ArtifactDescriptorReader.class ) ); + setVersionRangeResolver( locator.getService( VersionRangeResolver.class ) ); + } + + public DependencyCollector setRemoteRepositoryManager( RemoteRepositoryManager remoteRepositoryManager ) + { + this.remoteRepositoryManager = + requireNonNull( remoteRepositoryManager, "remote repository provider cannot be null" ); + return this; + } + + public DependencyCollector setArtifactDescriptorReader( ArtifactDescriptorReader artifactDescriptorReader ) + { + descriptorReader = requireNonNull( artifactDescriptorReader, "artifact descriptor reader cannot be null" ); + return this; + } + + public DependencyCollector setVersionRangeResolver( VersionRangeResolver versionRangeResolver ) + { + this.versionRangeResolver = + requireNonNull( versionRangeResolver, "version range resolver cannot be null" ); + return this; + } + } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java index 18d9341ed..6e9ac2f8a 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java @@ -72,13 +72,10 @@ import org.eclipse.aether.resolution.VersionRangeResolutionException; import org.eclipse.aether.resolution.VersionRangeResult; import org.eclipse.aether.spi.locator.Service; -import org.eclipse.aether.spi.locator.ServiceLocator; import org.eclipse.aether.util.ConfigUtils; import org.eclipse.aether.util.graph.manager.DependencyManagerUtils; import org.eclipse.aether.util.graph.transformer.TransformationContextKeys; import org.eclipse.aether.version.Version; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import static java.util.Objects.requireNonNull; import static org.eclipse.aether.internal.impl.collect.DefaultDependencyCycle.find; @@ -91,7 +88,7 @@ @Singleton @Named( BfDependencyCollector.NAME ) public class BfDependencyCollector - implements DependencyCollectorDelegate, Service + extends DependencyCollectorDelegate implements Service { public static final String NAME = "bf"; @@ -101,7 +98,7 @@ public class BfDependencyCollector * * @since 1.8.0 */ - public static final String CONFIG_PROP_USE_SKIP = "aether.dependencyCollector.useSkip"; + public static final String CONFIG_PROP_USE_SKIP = "aether.dependencyCollector.bf.useSkip"; /** * The default value for {@link #CONFIG_PROP_USE_SKIP}, {@code true}. @@ -110,22 +107,6 @@ public class BfDependencyCollector */ public static final boolean CONFIG_PROP_USE_SKIP_DEFAULT = true; - private static final String CONFIG_PROP_MAX_EXCEPTIONS = "aether.dependencyCollector.maxExceptions"; - - private static final int CONFIG_PROP_MAX_EXCEPTIONS_DEFAULT = 50; - - private static final String CONFIG_PROP_MAX_CYCLES = "aether.dependencyCollector.maxCycles"; - - private static final int CONFIG_PROP_MAX_CYCLES_DEFAULT = 10; - - private static final Logger LOGGER = LoggerFactory.getLogger( BfDependencyCollector.class ); - - private RemoteRepositoryManager remoteRepositoryManager; - - private ArtifactDescriptorReader descriptorReader; - - private VersionRangeResolver versionRangeResolver; - public BfDependencyCollector() { // enables default constructor @@ -136,36 +117,7 @@ public BfDependencyCollector() ArtifactDescriptorReader artifactDescriptorReader, VersionRangeResolver versionRangeResolver ) { - setRemoteRepositoryManager( remoteRepositoryManager ); - setArtifactDescriptorReader( artifactDescriptorReader ); - setVersionRangeResolver( versionRangeResolver ); - } - - public void initService( ServiceLocator locator ) - { - setRemoteRepositoryManager( locator.getService( RemoteRepositoryManager.class ) ); - setArtifactDescriptorReader( locator.getService( ArtifactDescriptorReader.class ) ); - setVersionRangeResolver( locator.getService( VersionRangeResolver.class ) ); - } - - public BfDependencyCollector setRemoteRepositoryManager( RemoteRepositoryManager remoteRepositoryManager ) - { - this.remoteRepositoryManager = - requireNonNull( remoteRepositoryManager, "remote repository provider cannot be null" ); - return this; - } - - public BfDependencyCollector setArtifactDescriptorReader( ArtifactDescriptorReader artifactDescriptorReader ) - { - descriptorReader = requireNonNull( artifactDescriptorReader, "artifact descriptor reader cannot be null" ); - return this; - } - - public BfDependencyCollector setVersionRangeResolver( VersionRangeResolver versionRangeResolver ) - { - this.versionRangeResolver = - requireNonNull( versionRangeResolver, "version range resolver cannot be null" ); - return this; + super( remoteRepositoryManager, artifactDescriptorReader, versionRangeResolver ); } @SuppressWarnings( "checkstyle:methodlength" ) @@ -181,7 +133,7 @@ public CollectResult collectDependencies( RepositorySystemSession session, Colle ); if ( useSkip ) { - LOGGER.debug( "Collector skip mode enabled" ); + logger.debug( "Collector skip mode enabled" ); } RequestTrace trace = RequestTrace.newChild( request.getTrace(), request ); @@ -339,7 +291,7 @@ public CollectResult collectDependencies( RepositorySystemSession session, Colle long time3 = System.nanoTime(); stats.put( "DefaultDependencyCollector.collectTime", time2 - time1 ); stats.put( "DefaultDependencyCollector.transformTime", time3 - time2 ); - LOGGER.debug( "Dependency collection stats {}", stats ); + logger.debug( "Dependency collection stats {}", stats ); if ( errorPath != null ) { diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java index 1630fb9ae..59e4eb3e5 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java @@ -71,13 +71,10 @@ import org.eclipse.aether.resolution.VersionRangeResolutionException; import org.eclipse.aether.resolution.VersionRangeResult; import org.eclipse.aether.spi.locator.Service; -import org.eclipse.aether.spi.locator.ServiceLocator; import org.eclipse.aether.util.ConfigUtils; import org.eclipse.aether.util.graph.manager.DependencyManagerUtils; import org.eclipse.aether.util.graph.transformer.TransformationContextKeys; import org.eclipse.aether.version.Version; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Depth-first {@link org.eclipse.aether.impl.DependencyCollector} (the "original" default). @@ -87,26 +84,10 @@ @Singleton @Named( DfDependencyCollector.NAME ) public class DfDependencyCollector - implements DependencyCollectorDelegate, Service + extends DependencyCollectorDelegate implements Service { public static final String NAME = "df"; - private static final String CONFIG_PROP_MAX_EXCEPTIONS = "aether.dependencyCollector.maxExceptions"; - - private static final int CONFIG_PROP_MAX_EXCEPTIONS_DEFAULT = 50; - - private static final String CONFIG_PROP_MAX_CYCLES = "aether.dependencyCollector.maxCycles"; - - private static final int CONFIG_PROP_MAX_CYCLES_DEFAULT = 10; - - private static final Logger LOGGER = LoggerFactory.getLogger( DfDependencyCollector.class ); - - private RemoteRepositoryManager remoteRepositoryManager; - - private ArtifactDescriptorReader descriptorReader; - - private VersionRangeResolver versionRangeResolver; - public DfDependencyCollector() { // enables default constructor @@ -117,36 +98,7 @@ public DfDependencyCollector() ArtifactDescriptorReader artifactDescriptorReader, VersionRangeResolver versionRangeResolver ) { - setRemoteRepositoryManager( remoteRepositoryManager ); - setArtifactDescriptorReader( artifactDescriptorReader ); - setVersionRangeResolver( versionRangeResolver ); - } - - public void initService( ServiceLocator locator ) - { - setRemoteRepositoryManager( locator.getService( RemoteRepositoryManager.class ) ); - setArtifactDescriptorReader( locator.getService( ArtifactDescriptorReader.class ) ); - setVersionRangeResolver( locator.getService( VersionRangeResolver.class ) ); - } - - public DfDependencyCollector setRemoteRepositoryManager( RemoteRepositoryManager remoteRepositoryManager ) - { - this.remoteRepositoryManager = - requireNonNull( remoteRepositoryManager, "remote repository provider cannot be null" ); - return this; - } - - public DfDependencyCollector setArtifactDescriptorReader( ArtifactDescriptorReader artifactDescriptorReader ) - { - descriptorReader = requireNonNull( artifactDescriptorReader, "artifact descriptor reader cannot be null" ); - return this; - } - - public DfDependencyCollector setVersionRangeResolver( VersionRangeResolver versionRangeResolver ) - { - this.versionRangeResolver = - requireNonNull( versionRangeResolver, "version range resolver cannot be null" ); - return this; + super( remoteRepositoryManager, artifactDescriptorReader, versionRangeResolver ); } @SuppressWarnings( "checkstyle:methodlength" ) @@ -295,7 +247,7 @@ public CollectResult collectDependencies( RepositorySystemSession session, Colle long time3 = System.nanoTime(); stats.put( "DefaultDependencyCollector.collectTime", time2 - time1 ); stats.put( "DefaultDependencyCollector.transformTime", time3 - time2 ); - LOGGER.debug( "Dependency collection stats {}", stats ); + logger.debug( "Dependency collection stats {}", stats ); if ( errorPath != null ) { From 50f7453457a2ebd6c49438b6f05b808c9503b981 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Tue, 5 Apr 2022 08:49:23 +0200 Subject: [PATCH 06/10] PR comments and more --- .../aether/impl/guice/AetherModule.java | 8 +-- .../collect/CachingArtifactTypeRegistry.java | 3 +- .../internal/impl/collect/DataPool.java | 3 +- .../DefaultDependencyCollectionContext.java | 2 +- .../collect/DefaultDependencyCollector.java | 2 +- .../impl/collect/DefaultDependencyCycle.java | 3 ++ ...tDependencyGraphTransformationContext.java | 5 ++ .../collect/DefaultVersionFilterContext.java | 10 +++- .../collect/DependencyCollectorDelegate.java | 4 +- .../internal/impl/collect/ObjectPool.java | 1 + .../collect/bf/BfDependencyCollector.java | 12 ++--- ....java => DependencyProcessingContext.java} | 25 ++++++---- .../bf/DependencyResolutionSkipper.java | 4 +- .../collect/df/DfDependencyCollector.java | 5 +- .../internal/impl/collect/df/NodeStack.java | 50 ++----------------- 15 files changed, 60 insertions(+), 77 deletions(-) rename maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/{BfProcessingContext.java => DependencyProcessingContext.java} (75%) diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java index e539d539e..93ef99ec3 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java @@ -228,10 +228,10 @@ Map dependencyCollectorDelegates( @Named( DfDependencyCollector.NAME ) DependencyCollectorDelegate df ) { - Map providedChecksumsSource = new HashMap<>(); - providedChecksumsSource.put( BfDependencyCollector.NAME, bf ); - providedChecksumsSource.put( DfDependencyCollector.NAME, df ); - return providedChecksumsSource; + Map dependencyCollectorDelegates = new HashMap<>(); + dependencyCollectorDelegates.put( BfDependencyCollector.NAME, bf ); + dependencyCollectorDelegates.put( DfDependencyCollector.NAME, df ); + return dependencyCollectorDelegates; } @Provides diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/CachingArtifactTypeRegistry.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/CachingArtifactTypeRegistry.java index bb03b142d..1a9a98f5b 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/CachingArtifactTypeRegistry.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/CachingArtifactTypeRegistry.java @@ -27,7 +27,8 @@ import org.eclipse.aether.artifact.ArtifactTypeRegistry; /** - * A short-lived artifact type registry that caches results from a presumedly slower type registry. + * A short-lived artifact type registry that caches results from a presumably slower type registry. + * Internal helper class for collector implementations. */ public class CachingArtifactTypeRegistry implements ArtifactTypeRegistry diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DataPool.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DataPool.java index d54a56347..04ebdf41d 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DataPool.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DataPool.java @@ -48,6 +48,7 @@ import org.eclipse.aether.version.VersionConstraint; /** + * Internal helper class for collector implementations. */ public final class DataPool { @@ -103,7 +104,7 @@ public DataPool( RepositorySystemSession session ) if ( descriptors == null ) { - descriptors = Collections.synchronizedMap( new WeakHashMap( 256 ) ); + descriptors = Collections.synchronizedMap( new WeakHashMap<>( 256 ) ); if ( cache != null ) { cache.put( session, DESCRIPTORS, descriptors ); diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectionContext.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectionContext.java index 0c66fa79c..117af8804 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectionContext.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollectionContext.java @@ -27,7 +27,7 @@ import org.eclipse.aether.graph.Dependency; /** - * @see DefaultDependencyCollector + * Internal helper class for collector implementations. */ public final class DefaultDependencyCollectionContext implements DependencyCollectionContext diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java index 7b479835a..f7f6ea265 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCollector.java @@ -40,7 +40,7 @@ import static java.util.Objects.requireNonNull; /** - * + * Default implementation of {@link DependencyCollector} that merely indirect to selected delegate. */ @Singleton @Named diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycle.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycle.java index 0a3ff2ba2..d417b70e2 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycle.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyCycle.java @@ -31,6 +31,7 @@ /** * Default implementation of {@link DependencyCycle}. + * Internal helper class for collector implementations. */ public final class DefaultDependencyCycle implements DependencyCycle @@ -59,11 +60,13 @@ public DefaultDependencyCycle( List nodes, int cycleEntry, Depen this.cycleEntry = cycleEntry; } + @Override public List getPrecedingDependencies() { return dependencies.subList( 0, cycleEntry ); } + @Override public List getCyclicDependencies() { return dependencies.subList( cycleEntry, dependencies.size() ); diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyGraphTransformationContext.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyGraphTransformationContext.java index 28ea74725..721eab5e2 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyGraphTransformationContext.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultDependencyGraphTransformationContext.java @@ -27,6 +27,8 @@ import org.eclipse.aether.collection.DependencyGraphTransformationContext; /** + * Default implementation of {@link DependencyGraphTransformationContext}. + * Internal helper class for collector implementations. */ public class DefaultDependencyGraphTransformationContext implements DependencyGraphTransformationContext @@ -42,16 +44,19 @@ public DefaultDependencyGraphTransformationContext( RepositorySystemSession sess this.map = new HashMap<>(); } + @Override public RepositorySystemSession getSession() { return session; } + @Override public Object get( Object key ) { return map.get( requireNonNull( key, "key cannot be null" ) ); } + @Override public Object put( Object key, Object value ) { requireNonNull( key, "key cannot be null" ); diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultVersionFilterContext.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultVersionFilterContext.java index 55b6fba42..97c16c63b 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultVersionFilterContext.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultVersionFilterContext.java @@ -34,7 +34,8 @@ import org.eclipse.aether.version.VersionConstraint; /** - * @see DefaultDependencyCollector + * Default implementation of {@link VersionFilter.VersionFilterContext}. + * Internal helper class for collector implementations. */ public final class DefaultVersionFilterContext implements VersionFilter.VersionFilterContext @@ -64,36 +65,43 @@ public List get() return new ArrayList<>( versions ); } + @Override public RepositorySystemSession getSession() { return session; } + @Override public Dependency getDependency() { return dependency; } + @Override public VersionConstraint getVersionConstraint() { return result.getVersionConstraint(); } + @Override public int getCount() { return versions.size(); } + @Override public ArtifactRepository getRepository( Version version ) { return result.getRepository( version ); } + @Override public List getRepositories() { return Collections.unmodifiableList( result.getRequest().getRepositories() ); } + @Override public Iterator iterator() { return versions.iterator(); diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java index 9d8a15605..24314d5ab 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java @@ -30,7 +30,7 @@ import static java.util.Objects.requireNonNull; /** - * The delegate to the actual implementation. + * Helper class for delegate implementations, they should subclass this class. * * @since 1.8.0 */ @@ -76,7 +76,7 @@ public void initService( ServiceLocator locator ) public DependencyCollector setRemoteRepositoryManager( RemoteRepositoryManager remoteRepositoryManager ) { this.remoteRepositoryManager = - requireNonNull( remoteRepositoryManager, "remote repository provider cannot be null" ); + requireNonNull( remoteRepositoryManager, "remote repository manager cannot be null" ); return this; } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/ObjectPool.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/ObjectPool.java index c4117eb02..0d5f37997 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/ObjectPool.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/ObjectPool.java @@ -27,6 +27,7 @@ /** * Pool of immutable object instances, used to avoid excessive memory consumption of (dirty) dependency graph which * tends to have many duplicate artifacts/dependencies. + * Internal helper class for collector implementations. */ class ObjectPool { diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java index 6e9ac2f8a..3bbd9f42f 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java @@ -255,7 +255,7 @@ public CollectResult collectDependencies( RepositorySystemSession session, Colle for ( Dependency dependency : dependencies ) { args.dependencyProcessingQueue.add( - new BfProcessingContext( rootDepSelector, rootDepManager, rootDepTraverser, + new DependencyProcessingContext( rootDepSelector, rootDepManager, rootDepTraverser, rootVerFilter, repositories, managedDependencies, parents, dependency ) ); } @@ -350,7 +350,7 @@ private static String getId( Artifact a ) } @SuppressWarnings( "checkstyle:parameternumber" ) - private void processDependency( Args args, Results results, BfProcessingContext context, + private void processDependency( Args args, Results results, DependencyProcessingContext context, List relocations, boolean disableVersionManagement ) { @@ -464,7 +464,7 @@ private void processDependency( Args args, Results results, BfProcessingContext } @SuppressWarnings( "checkstyle:parameternumber" ) - private void doRecurse( Args args, BfProcessingContext parentContext, + private void doRecurse( Args args, DependencyProcessingContext parentContext, ArtifactDescriptorResult descriptorResult, DefaultDependencyNode child ) { DefaultDependencyCollectionContext context = args.collectionContext; @@ -501,7 +501,7 @@ private void doRecurse( Args args, BfProcessingContext parentContext, for ( Dependency dependency : descriptorResult.getDependencies() ) { args.dependencyProcessingQueue.add( - new BfProcessingContext( childSelector, childManager, childTraverser, childFilter, + new DependencyProcessingContext( childSelector, childManager, childTraverser, childFilter, childRepos, descriptorResult.getManagedDependencies(), parents, dependency ) ); } args.pool.putChildren( key, child.getChildren() ); @@ -517,7 +517,7 @@ private void doRecurse( Args args, BfProcessingContext parentContext, private ArtifactDescriptorResult resolveCachedArtifactDescriptor( DataPool pool, ArtifactDescriptorRequest descriptorRequest, RepositorySystemSession session, - BfProcessingContext context, + DependencyProcessingContext context, Results results ) { Object key = pool.toKey( descriptorRequest ); @@ -686,7 +686,7 @@ static class Args final DataPool pool; - final Queue dependencyProcessingQueue = new ArrayDeque<>( 128 ); + final Queue dependencyProcessingQueue = new ArrayDeque<>( 128 ); final DefaultDependencyCollectionContext collectionContext; diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfProcessingContext.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/DependencyProcessingContext.java similarity index 75% rename from maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfProcessingContext.java rename to maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/DependencyProcessingContext.java index f18376f22..ee2e77204 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfProcessingContext.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/DependencyProcessingContext.java @@ -29,7 +29,12 @@ import org.eclipse.aether.graph.DependencyNode; import org.eclipse.aether.repository.RemoteRepository; -final class BfProcessingContext +/** + * Internal helper for {@link BfDependencyCollector}. + * + * @since 1.8.0 + */ +final class DependencyProcessingContext { final DependencySelector depSelector; final DependencyManager depManager; @@ -45,14 +50,14 @@ final class BfProcessingContext Dependency dependency; @SuppressWarnings( "checkstyle:parameternumber" ) - BfProcessingContext( DependencySelector depSelector, - DependencyManager depManager, - DependencyTraverser depTraverser, - VersionFilter verFilter, - List repositories, - List managedDependencies, - List parents, - Dependency dependency ) + DependencyProcessingContext( DependencySelector depSelector, + DependencyManager depManager, + DependencyTraverser depTraverser, + VersionFilter verFilter, + List repositories, + List managedDependencies, + List parents, + Dependency dependency ) { this.depSelector = depSelector; this.depManager = depManager; @@ -64,7 +69,7 @@ final class BfProcessingContext this.parents = parents; } - BfProcessingContext withDependency( Dependency dependency ) + DependencyProcessingContext withDependency( Dependency dependency ) { this.dependency = dependency; return this; diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/DependencyResolutionSkipper.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/DependencyResolutionSkipper.java index 119788168..b8b04456c 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/DependencyResolutionSkipper.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/DependencyResolutionSkipper.java @@ -33,10 +33,8 @@ /** * A skipper that determines whether to skip resolving given node during the dependency collection. + * Internal helper for {@link BfDependencyCollector}. * - * @noimplement This interface is not intended to be implemented by clients. - * @noextend This interface is not intended to be extended by clients. - * @provisional This type is provisional and can be changed, moved or removed without prior notice. * @since 1.8.0 */ abstract class DependencyResolutionSkipper diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java index 59e4eb3e5..768af649d 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java @@ -77,7 +77,8 @@ import org.eclipse.aether.version.Version; /** - * Depth-first {@link org.eclipse.aether.impl.DependencyCollector} (the "original" default). + * Depth-first {@link org.eclipse.aether.impl.DependencyCollector} (the "original" default). Originally + * this class was located a package higher (as "default" implementation). * * @since 1.8.0 */ @@ -379,7 +380,7 @@ private void processDependency( Args args, Results results, List= 0 ) { results.addCycle( args.nodes, cycleEntry, d ); diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/NodeStack.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/NodeStack.java index 2abdddcad..dafe7a2fe 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/NodeStack.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/NodeStack.java @@ -21,16 +21,18 @@ import java.util.ArrayList; -import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.graph.DependencyNode; /** - * @see DfDependencyCollector + * Internal helper for {@link DfDependencyCollector}. Originally (pre-1.8.0) this same class was located a + * package higher. + * + * @since 1.8.0 */ final class NodeStack { - @SuppressWarnings( {"unchecked", "checkstyle:magicnumber" } ) + @SuppressWarnings( {"checkstyle:magicnumber" } ) // CHECKSTYLE_OFF: MagicNumber ArrayList nodes = new ArrayList<>( 96 ); // CHECKSTYLE_ON: MagicNumber @@ -58,48 +60,6 @@ public void pop() nodes.remove( nodes.size() - 1 ); } - public int find( Artifact artifact ) - { - for ( int i = nodes.size() - 1; i >= 0; i-- ) - { - DependencyNode node = nodes.get( i ); - - Artifact a = node.getArtifact(); - if ( a == null ) - { - break; - } - - if ( !a.getArtifactId().equals( artifact.getArtifactId() ) ) - { - continue; - } - if ( !a.getGroupId().equals( artifact.getGroupId() ) ) - { - continue; - } - if ( !a.getExtension().equals( artifact.getExtension() ) ) - { - continue; - } - if ( !a.getClassifier().equals( artifact.getClassifier() ) ) - { - continue; - } - /* - * NOTE: While a:1 and a:2 are technically different artifacts, we want to consider the path a:2 -> b:2 -> - * a:1 a cycle in the current context. The artifacts themselves might not form a cycle but their producing - * projects surely do. Furthermore, conflict resolution will always have to consider a:1 a loser (otherwise - * its ancestor a:2 would get pruned and so would a:1) so there is no point in building the sub graph of - * a:1. - */ - - return i; - } - - return -1; - } - public int size() { return nodes.size(); From 4dcd9f6b2e6ca74f8c0554b9ceefe82c8e2d8757 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Fri, 8 Apr 2022 08:52:28 +0200 Subject: [PATCH 07/10] Add configuration entries --- src/site/markdown/configuration.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/site/markdown/configuration.md b/src/site/markdown/configuration.md index 6c3a20d0f..a668356a4 100644 --- a/src/site/markdown/configuration.md +++ b/src/site/markdown/configuration.md @@ -42,9 +42,10 @@ Option | Type | Description | Default Value | Supports Repo ID Suffix `aether.connector.smartChecksums` | boolean | Flag indicating that instead of comparing the external checksum fetched from the remote repo with the calculated one, it should try to extract the reference checksum from the actual artifact requests's response headers (several (strategies supported)[included-checksum-strategies.html]). This only works for transport-http transport. | `true` | no `aether.connector.userAgent` | String | The user agent that repository connectors should report to servers. | `"Aether"` | no `aether.connector.wagon.config` | Object | The configuration to use for the Wagon provider. | - | yes (must be used) -`aether.dependencyCollector.useSkip` | boolean | Flag controlling whether to skip resolving duplicate/conflicting nodes during the dependency collection process. | `true` | no `aether.dependencyCollector.maxCycles` | int | Only up to the given amount cyclic dependencies are emitted. | `10` | no `aether.dependencyCollector.maxExceptions` | int | Only exceptions up to the number given in this configuration property are emitted. Exceptions which exceed that number are swallowed. | `50` | no +`aether.dependencyCollector.impl` | String | The name of the dependency collector implementation to use: depth-first (original) named `df`, and breadth-first (new in 1.8) named `bf`. Both collectors produce equivalent results, but they may differ performance wise, depending on project being applied to. Experiment (and come back to us!) to figure out which one suits you the better. | `"df"` | no +`aether.dependencyCollector.bf.useSkip` | boolean | Flag controlling whether to skip resolving duplicate/conflicting nodes during the breadth-first (`bf`) dependency collection process. | `true` | no `aether.dependencyManager.verbose` | boolean | Flag controlling the verbose mode for dependency management. If enabled, the original attributes of a dependency before its update due to dependency managemnent will be recorded in the node's `DependencyNode#getData()` when building a dependency graph. | `false` | no `aether.enhancedLocalRepository.trackingFilename` | String | Filename of the file in which to track the remote repositories. | `"_remote.repositories"` | no `aether.interactive` | boolean | A flag indicating whether interaction with the user is allowed. | `false` | no From 49efbbedc9434232f7b64306eb75a822d2d4afef Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Fri, 8 Apr 2022 08:55:57 +0200 Subject: [PATCH 08/10] Add a hint --- src/site/markdown/configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/site/markdown/configuration.md b/src/site/markdown/configuration.md index a668356a4..496837888 100644 --- a/src/site/markdown/configuration.md +++ b/src/site/markdown/configuration.md @@ -44,7 +44,7 @@ Option | Type | Description | Default Value | Supports Repo ID Suffix `aether.connector.wagon.config` | Object | The configuration to use for the Wagon provider. | - | yes (must be used) `aether.dependencyCollector.maxCycles` | int | Only up to the given amount cyclic dependencies are emitted. | `10` | no `aether.dependencyCollector.maxExceptions` | int | Only exceptions up to the number given in this configuration property are emitted. Exceptions which exceed that number are swallowed. | `50` | no -`aether.dependencyCollector.impl` | String | The name of the dependency collector implementation to use: depth-first (original) named `df`, and breadth-first (new in 1.8) named `bf`. Both collectors produce equivalent results, but they may differ performance wise, depending on project being applied to. Experiment (and come back to us!) to figure out which one suits you the better. | `"df"` | no +`aether.dependencyCollector.impl` | String | The name of the dependency collector implementation to use: depth-first (original) named `df`, and breadth-first (new in 1.8) named `bf`. Both collectors produce equivalent results, but they may differ performance wise, depending on project being applied to. Our experience shows that existing `df` is well suited for smaller to medium size projects, while `bf` may perform better on huge projects with many dependencies. Experiment (and come back to us!) to figure out which one suits you the better. | `"df"` | no `aether.dependencyCollector.bf.useSkip` | boolean | Flag controlling whether to skip resolving duplicate/conflicting nodes during the breadth-first (`bf`) dependency collection process. | `true` | no `aether.dependencyManager.verbose` | boolean | Flag controlling the verbose mode for dependency management. If enabled, the original attributes of a dependency before its update due to dependency managemnent will be recorded in the node's `DependencyNode#getData()` when building a dependency graph. | `false` | no `aether.enhancedLocalRepository.trackingFilename` | String | Filename of the file in which to track the remote repositories. | `"_remote.repositories"` | no From c434e799f6f65bffa5355c368d76effb677f4340 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Fri, 8 Apr 2022 11:33:25 +0200 Subject: [PATCH 09/10] PR comments --- .../collect/DependencyCollectorDelegate.java | 2 +- .../collect/bf/BfDependencyCollector.java | 23 ++++++++++--------- .../bf/DependencyResolutionSkipper.java | 2 +- .../collect/df/DfDependencyCollector.java | 15 ++++++------ .../collect/bf/BfDependencyCollectorTest.java | 2 +- src/site/markdown/configuration.md | 4 ++-- 6 files changed, 25 insertions(+), 23 deletions(-) diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java index 24314d5ab..c7c2c985e 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java @@ -30,7 +30,7 @@ import static java.util.Objects.requireNonNull; /** - * Helper class for delegate implementations, they should subclass this class. + * Helper class for delegate implementations, they MUST subclass this class. * * @since 1.8.0 */ diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java index 3bbd9f42f..4beca7990 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java @@ -98,14 +98,14 @@ public class BfDependencyCollector * * @since 1.8.0 */ - public static final String CONFIG_PROP_USE_SKIP = "aether.dependencyCollector.bf.useSkip"; + public static final String CONFIG_PROP_SKIPPER = "aether.dependencyCollector.bf.skipper"; /** - * The default value for {@link #CONFIG_PROP_USE_SKIP}, {@code true}. + * The default value for {@link #CONFIG_PROP_SKIPPER}, {@code true}. * * @since 1.8.0 */ - public static final boolean CONFIG_PROP_USE_SKIP_DEFAULT = true; + public static final boolean CONFIG_PROP_SKIPPER_DEFAULT = true; public BfDependencyCollector() { @@ -129,7 +129,7 @@ public CollectResult collectDependencies( RepositorySystemSession session, Colle session = optimizeSession( session ); boolean useSkip = ConfigUtils.getBoolean( - session, CONFIG_PROP_USE_SKIP_DEFAULT, CONFIG_PROP_USE_SKIP + session, CONFIG_PROP_SKIPPER_DEFAULT, CONFIG_PROP_SKIPPER ); if ( useSkip ) { @@ -289,9 +289,12 @@ public CollectResult collectDependencies( RepositorySystemSession session, Colle } long time3 = System.nanoTime(); - stats.put( "DefaultDependencyCollector.collectTime", time2 - time1 ); - stats.put( "DefaultDependencyCollector.transformTime", time3 - time2 ); - logger.debug( "Dependency collection stats {}", stats ); + if ( logger.isDebugEnabled() ) + { + stats.put( "BfDependencyCollector.collectTime", time2 - time1 ); + stats.put( "BfDependencyCollector.transformTime", time3 - time2 ); + logger.debug( "Dependency collection stats {}", stats ); + } if ( errorPath != null ) { @@ -655,15 +658,13 @@ private static List filterVersions( Dependency dependency, Ve catch ( RepositoryException e ) { throw new VersionRangeResolutionException( rangeResult, - "Failed to filter versions for " + dependency.getArtifact() - + ": " + e.getMessage(), e ); + "Failed to filter versions for " + dependency.getArtifact(), e ); } versions = verContext.get(); if ( versions.isEmpty() ) { throw new VersionRangeResolutionException( rangeResult, - "No acceptable versions for " + dependency.getArtifact() - + ": " + rangeResult.getVersions() ); + "No acceptable versions for " + dependency.getArtifact() + ": " + rangeResult.getVersions() ); } } else diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/DependencyResolutionSkipper.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/DependencyResolutionSkipper.java index b8b04456c..b82a8799c 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/DependencyResolutionSkipper.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/DependencyResolutionSkipper.java @@ -50,7 +50,7 @@ abstract class DependencyResolutionSkipper abstract boolean skipResolution( DependencyNode node, List parents ); /** - * Cache the resolution result when a node is resolved by @See DependencyCollector after resolution. + * Cache the resolution result when a node is resolved by {@link BfDependencyCollector) after resolution. * * @param node Current node * @param parents All parent nodes of current node diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java index 768af649d..a310af3be 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java @@ -246,9 +246,12 @@ public CollectResult collectDependencies( RepositorySystemSession session, Colle } long time3 = System.nanoTime(); - stats.put( "DefaultDependencyCollector.collectTime", time2 - time1 ); - stats.put( "DefaultDependencyCollector.transformTime", time3 - time2 ); - logger.debug( "Dependency collection stats {}", stats ); + if ( logger.isDebugEnabled() ) + { + stats.put( "BfDependencyCollector.collectTime", time2 - time1 ); + stats.put( "BfDependencyCollector.transformTime", time3 - time2 ); + logger.debug( "Dependency collection stats {}", stats ); + } if ( errorPath != null ) { @@ -630,15 +633,13 @@ private static List filterVersions( Dependency dependency, Ve catch ( RepositoryException e ) { throw new VersionRangeResolutionException( rangeResult, - "Failed to filter versions for " + dependency.getArtifact() - + ": " + e.getMessage(), e ); + "Failed to filter versions for " + dependency.getArtifact(), e ); } versions = verContext.get(); if ( versions.isEmpty() ) { throw new VersionRangeResolutionException( rangeResult, - "No acceptable versions for " + dependency.getArtifact() - + ": " + rangeResult.getVersions() ); + "No acceptable versions for " + dependency.getArtifact() + ": " + rangeResult.getVersions() ); } } else diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollectorTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollectorTest.java index 3c4ea005a..c5f59abd0 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollectorTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollectorTest.java @@ -111,7 +111,7 @@ public void setup() public void setupCollector( boolean useSkip ) { session = TestUtils.newSession(); - session.setConfigProperty( BfDependencyCollector.CONFIG_PROP_USE_SKIP, useSkip ); + session.setConfigProperty( BfDependencyCollector.CONFIG_PROP_SKIPPER, useSkip ); collector = new BfDependencyCollector(); collector.setArtifactDescriptorReader( newReader( "" ) ); diff --git a/src/site/markdown/configuration.md b/src/site/markdown/configuration.md index 496837888..1524530a1 100644 --- a/src/site/markdown/configuration.md +++ b/src/site/markdown/configuration.md @@ -44,8 +44,8 @@ Option | Type | Description | Default Value | Supports Repo ID Suffix `aether.connector.wagon.config` | Object | The configuration to use for the Wagon provider. | - | yes (must be used) `aether.dependencyCollector.maxCycles` | int | Only up to the given amount cyclic dependencies are emitted. | `10` | no `aether.dependencyCollector.maxExceptions` | int | Only exceptions up to the number given in this configuration property are emitted. Exceptions which exceed that number are swallowed. | `50` | no -`aether.dependencyCollector.impl` | String | The name of the dependency collector implementation to use: depth-first (original) named `df`, and breadth-first (new in 1.8) named `bf`. Both collectors produce equivalent results, but they may differ performance wise, depending on project being applied to. Our experience shows that existing `df` is well suited for smaller to medium size projects, while `bf` may perform better on huge projects with many dependencies. Experiment (and come back to us!) to figure out which one suits you the better. | `"df"` | no -`aether.dependencyCollector.bf.useSkip` | boolean | Flag controlling whether to skip resolving duplicate/conflicting nodes during the breadth-first (`bf`) dependency collection process. | `true` | no +`aether.dependencyCollector.impl` | String | The name of the dependency collector implementation to use: depth-first (original) named `df`, and breadth-first (new in 1.8.0) named `bf`. Both collectors produce equivalent results, but they may differ performance wise, depending on project being applied to. Our experience shows that existing `df` is well suited for smaller to medium size projects, while `bf` may perform better on huge projects with many dependencies. Experiment (and come back to us!) to figure out which one suits you the better. | `"df"` | no +`aether.dependencyCollector.bf.skipper` | boolean | Flag controlling whether to skip resolving duplicate/conflicting nodes during the breadth-first (`bf`) dependency collection process. | `true` | no `aether.dependencyManager.verbose` | boolean | Flag controlling the verbose mode for dependency management. If enabled, the original attributes of a dependency before its update due to dependency managemnent will be recorded in the node's `DependencyNode#getData()` when building a dependency graph. | `false` | no `aether.enhancedLocalRepository.trackingFilename` | String | Filename of the file in which to track the remote repositories. | `"_remote.repositories"` | no `aether.interactive` | boolean | A flag indicating whether interaction with the user is allowed. | `false` | no From 362c33f3fd2d9bd7ad07fe61179fb80edad9cfc1 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Fri, 8 Apr 2022 12:20:36 +0200 Subject: [PATCH 10/10] Rename param --- .../internal/impl/collect/bf/BfDependencyCollectorTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollectorTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollectorTest.java index c5f59abd0..ea162f945 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollectorTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollectorTest.java @@ -108,10 +108,10 @@ public void setup() setupCollector( false ); } - public void setupCollector( boolean useSkip ) + public void setupCollector( boolean useSkipper ) { session = TestUtils.newSession(); - session.setConfigProperty( BfDependencyCollector.CONFIG_PROP_SKIPPER, useSkip ); + session.setConfigProperty( BfDependencyCollector.CONFIG_PROP_SKIPPER, useSkipper ); collector = new BfDependencyCollector(); collector.setArtifactDescriptorReader( newReader( "" ) );