You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by cs...@apache.org on 2022/04/04 15:27:22 UTC
[maven-resolver] branch MRESOLVER-248-collector-bf-df-coexists updated: Resurrect replaced DF as well
This is an automated email from the ASF dual-hosted git repository.
cstamas pushed a commit to branch MRESOLVER-248-collector-bf-df-coexists
in repository https://gitbox.apache.org/repos/asf/maven-resolver.git
The following commit(s) were added to refs/heads/MRESOLVER-248-collector-bf-df-coexists by this push:
new 9572e88b Resurrect replaced DF as well
9572e88b is described below
commit 9572e88b276df9acd960aed632bfcfe857539f0c
Author: Tamas Cservenak <ta...@cservenak.net>
AuthorDate: Mon Apr 4 17:26:58 2022 +0200
Resurrect replaced DF as well
Along with tests
---
.../eclipse/aether/impl/guice/AetherModule.java | 21 +-
.../impl/collect/DefaultDependencyCollector.java | 6 +-
.../impl/collect/bf/BfDependencyCollector.java | 3 +
.../DfDependencyCollector.java} | 272 ++++-----
.../impl/collect/df/DfDependencyCycle.java | 88 +++
.../aether/internal/impl/collect/df/NodeStack.java | 127 ++++
.../impl/collect/df/DfDependencyCollectorTest.java | 649 +++++++++++++++++++++
.../impl/collect/df/DfDependencyCycleTest.java | 44 ++
8 files changed, 1052 insertions(+), 158 deletions(-)
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 7d7f9f0b..e539d539 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.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.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 @@ public class AetherModule
.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 @@ public class AetherModule
@Provides
@Singleton
- Map<String, ProvidedChecksumsSource> provideChecksumSources(
- @Named( FileProvidedChecksumsSource.NAME ) ProvidedChecksumsSource fileProvidedChecksumSource
+ Map<String, DependencyCollectorDelegate> dependencyCollectorDelegates(
+ @Named( BfDependencyCollector.NAME ) DependencyCollectorDelegate bf,
+ @Named( DfDependencyCollector.NAME ) DependencyCollectorDelegate df
)
{
- Map<String, ProvidedChecksumsSource> providedChecksumsSource = new HashMap<>();
- providedChecksumsSource.put( FileProvidedChecksumsSource.NAME, fileProvidedChecksumSource );
+ Map<String, DependencyCollectorDelegate> providedChecksumsSource = new HashMap<>();
+ providedChecksumsSource.put( BfDependencyCollector.NAME, bf );
+ providedChecksumsSource.put( DfDependencyCollector.NAME, df );
return providedChecksumsSource;
}
@Provides
@Singleton
- Map<String, DependencyCollectorDelegate> dependencyCollectorDelegates(
- @Named( BfDependencyCollector.NAME ) DependencyCollectorDelegate bf
+ Map<String, ProvidedChecksumsSource> provideChecksumSources(
+ @Named( FileProvidedChecksumsSource.NAME ) ProvidedChecksumsSource fileProvidedChecksumSource
)
{
- Map<String, DependencyCollectorDelegate> providedChecksumsSource = new HashMap<>();
- providedChecksumsSource.put( BfDependencyCollector.NAME, bf );
+ Map<String, ProvidedChecksumsSource> 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 1c0b6e95..7b479835 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.CollectResult;
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<String, DependencyCollectorDelegate> delegates;
@@ -74,7 +75,10 @@ public class DefaultDependencyCollector
{
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 4836d635..837af528 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 java.util.Objects.requireNonNull;
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/bf/BfDependencyCollector.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java
similarity index 80%
copy from maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollector.java
copy to maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/df/DfDependencyCollector.java
index 4836d635..528fc908 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/df/DfDependencyCollector.java
@@ -1,4 +1,4 @@
-package org.eclipse.aether.internal.impl.collect.bf;
+package org.eclipse.aether.internal.impl.collect.df;
/*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -19,11 +19,6 @@ package org.eclipse.aether.internal.impl.collect.bf;
* 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;
@@ -32,7 +27,11 @@ 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 javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositoryException;
@@ -54,7 +53,6 @@ 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;
@@ -80,32 +78,17 @@ 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;
-
/**
+ * Depth-first {@link org.eclipse.aether.impl.DependencyCollector} (the "original" default).
+ *
+ * @since 1.8.0
*/
@Singleton
-@Named( BfDependencyCollector.NAME )
-public class BfDependencyCollector
+@Named( DfDependencyCollector.NAME )
+public class DfDependencyCollector
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;
+ public static final String NAME = "df";
private static final String CONFIG_PROP_MAX_EXCEPTIONS = "aether.dependencyCollector.maxExceptions";
@@ -115,7 +98,7 @@ public class BfDependencyCollector
private static final int CONFIG_PROP_MAX_CYCLES_DEFAULT = 10;
- private static final Logger LOGGER = LoggerFactory.getLogger( BfDependencyCollector.class );
+ private static final Logger LOGGER = LoggerFactory.getLogger( DfDependencyCollector.class );
private RemoteRepositoryManager remoteRepositoryManager;
@@ -123,13 +106,13 @@ public class BfDependencyCollector
private VersionRangeResolver versionRangeResolver;
- public BfDependencyCollector()
+ public DfDependencyCollector()
{
// enables default constructor
}
@Inject
- BfDependencyCollector( RemoteRepositoryManager remoteRepositoryManager,
+ DfDependencyCollector( RemoteRepositoryManager remoteRepositoryManager,
ArtifactDescriptorReader artifactDescriptorReader,
VersionRangeResolver versionRangeResolver )
{
@@ -145,20 +128,20 @@ public class BfDependencyCollector
setVersionRangeResolver( locator.getService( VersionRangeResolver.class ) );
}
- public BfDependencyCollector setRemoteRepositoryManager( RemoteRepositoryManager remoteRepositoryManager )
+ public DfDependencyCollector setRemoteRepositoryManager( RemoteRepositoryManager remoteRepositoryManager )
{
this.remoteRepositoryManager =
requireNonNull( remoteRepositoryManager, "remote repository provider cannot be null" );
return this;
}
- public BfDependencyCollector setArtifactDescriptorReader( ArtifactDescriptorReader artifactDescriptorReader )
+ public DfDependencyCollector setArtifactDescriptorReader( ArtifactDescriptorReader artifactDescriptorReader )
{
descriptorReader = requireNonNull( artifactDescriptorReader, "artifact descriptor reader cannot be null" );
return this;
}
- public BfDependencyCollector setVersionRangeResolver( VersionRangeResolver versionRangeResolver )
+ public DfDependencyCollector setVersionRangeResolver( VersionRangeResolver versionRangeResolver )
{
this.versionRangeResolver =
requireNonNull( versionRangeResolver, "version range resolver cannot be null" );
@@ -173,14 +156,6 @@ public class BfDependencyCollector
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 );
@@ -278,40 +253,23 @@ public class BfDependencyCollector
{
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, context, versionContext, request,
- useSkip ? new DefaultDependencyResolutionSkipper()
- : NeverDependencyResolutionSkipper.INSTANCE );
+ Args args = new Args( session, trace, pool, nodes, context, versionContext, request );
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<DependencyNode> 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 );
- }
+ 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 );
- args.skipper.report();
errorPath = results.errorPath;
}
@@ -395,73 +353,90 @@ public class BfDependencyCollector
}
@SuppressWarnings( "checkstyle:parameternumber" )
- private void processDependency( Args args, Results results, BfProcessingContext context,
+ private void process( final Args args, Results results, List<Dependency> dependencies,
+ List<RemoteRepository> 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<RemoteRepository> repositories,
+ DependencySelector depSelector, DependencyManager depManager,
+ DependencyTraverser depTraverser, VersionFilter verFilter, Dependency dependency )
+ {
+
+ List<Artifact> 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<RemoteRepository> repositories,
+ DependencySelector depSelector, DependencyManager depManager,
+ DependencyTraverser depTraverser, VersionFilter verFilter, Dependency dependency,
List<Artifact> relocations, boolean disableVersionManagement )
{
- if ( context.depSelector != null && !context.depSelector.selectDependency( context.dependency ) )
+ if ( depSelector != null && !depSelector.selectDependency( dependency ) )
{
return;
}
PremanagedDependency preManaged =
- PremanagedDependency.create( context.depManager, context.dependency, disableVersionManagement,
- args.premanagedState );
- Dependency dependency = preManaged.managedDependency;
+ PremanagedDependency.create( depManager, dependency, disableVersionManagement, args.premanagedState );
+ dependency = preManaged.managedDependency;
boolean noDescriptor = isLackingDescriptor( dependency.getArtifact() );
- boolean traverse =
- !noDescriptor && ( context.depTraverser == null || context.depTraverser.traverseDependency(
- dependency ) );
+ boolean traverse = !noDescriptor && ( depTraverser == null || depTraverser.traverseDependency( dependency ) );
List<? extends Version> versions;
VersionRangeResult rangeResult;
try
{
- VersionRangeRequest rangeRequest = createVersionRangeRequest( args, context.repositories, dependency );
+ VersionRangeRequest rangeRequest = createVersionRangeRequest( args, repositories, dependency );
rangeResult = cachedResolveRangeResult( rangeRequest, args.pool, args.session );
- versions = filterVersions( dependency, rangeResult, context.verFilter, args.versionContext );
+ versions = filterVersions( dependency, rangeResult, verFilter, args.versionContext );
}
catch ( VersionRangeResolutionException e )
{
- results.addException( dependency, e, context.parents );
+ results.addException( dependency, e, args.nodes );
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 );
+ ArtifactDescriptorRequest descriptorRequest = createArtifactDescriptorRequest( args, repositories, d );
final ArtifactDescriptorResult descriptorResult =
- noDescriptor
- ? new ArtifactDescriptorResult( descriptorRequest )
- : resolveCachedArtifactDescriptor( args.pool, descriptorRequest, args.session,
- context.withDependency( d ), results );
-
+ getArtifactDescriptorResult( args, results, noDescriptor, d, descriptorRequest );
if ( descriptorResult != null )
{
d = d.setArtifact( descriptorResult.getArtifact() );
- int cycleEntry = find( context.parents, d.getArtifact() );
+ DependencyNode node = args.nodes.top();
+
+ int cycleEntry = args.nodes.find( d.getArtifact() );
if ( cycleEntry >= 0 )
{
- results.addCycle( context.parents, cycleEntry, d );
- DependencyNode cycleNode = context.parents.get( cycleEntry );
+ 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 );
- context.getParent().getChildren().add( child );
+ createDependencyNode( relocations, preManaged, rangeResult, version, d, descriptorResult,
+ cycleNode );
+ node.getChildren().add( child );
continue;
}
}
@@ -472,8 +447,8 @@ public class BfDependencyCollector
originalArtifact.getGroupId().equals( d.getArtifact().getGroupId() )
&& originalArtifact.getArtifactId().equals( d.getArtifact().getArtifactId() );
- processDependency( args, results, context.withDependency( d ), descriptorResult.getRelocations(),
- disableVersionManagementSubsequently );
+ processDependency( args, results, repositories, depSelector, depManager, depTraverser, verFilter, d,
+ descriptorResult.getRelocations(), disableVersionManagementSubsequently );
return;
}
else
@@ -481,77 +456,69 @@ public class BfDependencyCollector
d = args.pool.intern( d.setArtifact( args.pool.intern( d.getArtifact() ) ) );
List<RemoteRepository> repos =
- getRemoteRepositories( rangeResult.getRepository( version ), context.repositories );
+ getRemoteRepositories( rangeResult.getRepository( version ), repositories );
DefaultDependencyNode child =
createDependencyNode( relocations, preManaged, rangeResult, version, d,
descriptorResult.getAliases(), repos, args.request.getRequestContext() );
- context.getParent().getChildren().add( child );
+ node.getChildren().add( child );
boolean recurse = traverse && !descriptorResult.getDependencies().isEmpty();
if ( recurse )
{
- doRecurse( args, context.withDependency( d ), descriptorResult, child );
+ doRecurse( args, results, repositories, depSelector, depManager, depTraverser, verFilter, d,
+ descriptorResult, child );
}
}
}
else
{
+ DependencyNode node = args.nodes.top();
List<RemoteRepository> repos =
- getRemoteRepositories( rangeResult.getRepository( version ), context.repositories );
+ getRemoteRepositories( rangeResult.getRepository( version ), repositories );
DefaultDependencyNode child =
createDependencyNode( relocations, preManaged, rangeResult, version, d, null, repos,
args.request.getRequestContext() );
- context.getParent().getChildren().add( child );
+ node.getChildren().add( child );
}
}
}
@SuppressWarnings( "checkstyle:parameternumber" )
- private void doRecurse( Args args, BfProcessingContext parentContext,
+ private void doRecurse( Args args, Results results, List<RemoteRepository> repositories,
+ DependencySelector depSelector, DependencyManager depManager,
+ DependencyTraverser depTraverser, VersionFilter verFilter, Dependency d,
ArtifactDescriptorResult descriptorResult, DefaultDependencyNode child )
{
DefaultDependencyCollectionContext context = args.collectionContext;
- context.set( parentContext.dependency, descriptorResult.getManagedDependencies() );
+ context.set( d, 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;
+ 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<RemoteRepository> childRepos =
- args.ignoreRepos
- ? parentContext.repositories
- : remoteRepositoryManager.aggregateRepositories( args.session, parentContext.repositories,
- descriptorResult.getRepositories(), true );
+ args.ignoreRepos
+ ? repositories
+ : remoteRepositoryManager.aggregateRepositories( args.session, repositories,
+ descriptorResult.getRepositories(), true );
Object key =
- args.pool.toKey( parentContext.dependency.getArtifact(), childRepos, childSelector, childManager,
- childTraverser, childFilter );
+ args.pool.toKey( d.getArtifact(), childRepos, childSelector, childManager, childTraverser, childFilter );
List<DependencyNode> children = args.pool.getChildren( key );
if ( children == null )
{
- boolean skipResolution = args.skipper.skipResolution( child, parentContext.parents );
- if ( !skipResolution )
- {
- List<DependencyNode> 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 );
- }
+ args.pool.putChildren( key, child.getChildren() );
+
+ args.nodes.push( child );
+
+ process( args, results, descriptorResult.getDependencies(), childRepos, childSelector, childManager,
+ childTraverser, childFilter );
+
+ args.nodes.pop();
}
else
{
@@ -559,11 +526,20 @@ public class BfDependencyCollector
}
}
+ 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,
- BfProcessingContext context,
- Results results )
+ RepositorySystemSession session, Dependency d,
+ Results results, Args args )
{
Object key = pool.toKey( descriptorRequest );
ArtifactDescriptorResult descriptorResult = pool.getDescriptor( key, descriptorRequest );
@@ -576,7 +552,7 @@ public class BfDependencyCollector
}
catch ( ArtifactDescriptorException e )
{
- results.addException( context.dependency, e, context.parents );
+ results.addException( d, e, args.nodes );
pool.putDescriptor( key, e );
return null;
}
@@ -731,7 +707,7 @@ public class BfDependencyCollector
final DataPool pool;
- final Queue<BfProcessingContext> dependencyProcessingQueue = new ArrayDeque<>( 128 );
+ final NodeStack nodes;
final DefaultDependencyCollectionContext collectionContext;
@@ -739,11 +715,9 @@ public class BfDependencyCollector
final CollectRequest request;
- final DependencyResolutionSkipper skipper;
-
- Args( RepositorySystemSession session, RequestTrace trace, DataPool pool,
+ Args( RepositorySystemSession session, RequestTrace trace, DataPool pool, NodeStack nodes,
DefaultDependencyCollectionContext collectionContext, DefaultVersionFilterContext versionContext,
- CollectRequest request, DependencyResolutionSkipper skipper )
+ CollectRequest request )
{
this.session = session;
this.request = request;
@@ -751,9 +725,9 @@ public class BfDependencyCollector
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;
- this.skipper = skipper;
}
}
@@ -779,7 +753,7 @@ public class BfDependencyCollector
maxCycles = ConfigUtils.getInteger( session, CONFIG_PROP_MAX_CYCLES_DEFAULT, CONFIG_PROP_MAX_CYCLES );
}
- public void addException( Dependency dependency, Exception e, List<DependencyNode> nodes )
+ public void addException( Dependency dependency, Exception e, NodeStack nodes )
{
if ( maxExceptions < 0 || result.getExceptions().size() < maxExceptions )
{
@@ -787,13 +761,13 @@ public class BfDependencyCollector
if ( errorPath == null )
{
StringBuilder buffer = new StringBuilder( 256 );
- for ( DependencyNode node : nodes )
+ for ( int i = 0; i < nodes.size(); i++ )
{
if ( buffer.length() > 0 )
{
buffer.append( " -> " );
}
- Dependency dep = node.getDependency();
+ Dependency dep = nodes.get( i ).getDependency();
if ( dep != null )
{
buffer.append( dep.getArtifact() );
@@ -809,11 +783,11 @@ public class BfDependencyCollector
}
}
- public void addCycle( List<DependencyNode> nodes, int cycleEntry, Dependency dependency )
+ public void addCycle( NodeStack nodes, int cycleEntry, Dependency dependency )
{
if ( maxCycles < 0 || result.getCycles().size() < maxCycles )
{
- result.addCycle( new BfDependencyCycle( nodes, cycleEntry, dependency ) );
+ result.addCycle( new DfDependencyCycle( nodes, cycleEntry, dependency ) );
}
}
@@ -935,4 +909,4 @@ public class BfDependencyCollector
}
-}
+}
\ 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 00000000..ff8d639d
--- /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<Dependency> 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<Dependency> getPrecedingDependencies()
+ {
+ return dependencies.subList( 0, cycleEntry );
+ }
+
+ public List<Dependency> 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 00000000..8266aefd
--- /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 00000000..b641bbd6
--- /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<DependencyNode>() );
+ }
+
+ private static void assertEqualSubtree( DependencyNode expected, DependencyNode actual,
+ LinkedList<DependencyNode> 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<DependencyNode> iterator1 = expected.getChildren().iterator();
+ Iterator<DependencyNode> 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<Dependency> 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<RemoteRepository> repos = new ArrayList<>();
+
+ collector.setArtifactDescriptorReader( new ArtifactDescriptorReader()
+ {
+ public ArtifactDescriptorResult readArtifactDescriptor( RepositorySystemSession session,
+ ArtifactDescriptorRequest request )
+ {
+ repos.addAll( request.getRepositories() );
+ return new ArtifactDescriptorResult( request );
+ }
+ } );
+
+ List<Dependency> 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<String, String> versions = new HashMap<>();
+
+ private Map<String, String> scopes = new HashMap<>();
+
+ private Map<String, Boolean> optionals = new HashMap<>();
+
+ private Map<String, String> paths = new HashMap<>();
+
+ private Map<String, Collection<Exclusion>> 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 00000000..d9708476
--- /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