[MRESOLVER-33] New class 'DefaultDependencyManager' managing dependencies on all levels supporting transitive dependency management.
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/DefaultDependencyCollectorTest.java
index 232cf9f..88ced9f 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/DefaultDependencyCollectorTest.java
@@ -65,6 +65,7 @@
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.selector.ScopeDependencySelector;
@@ -511,6 +512,35 @@
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 )
{
diff --git a/maven-resolver-impl/src/test/resources/artifact-descriptions/managed/default-management-tree.txt b/maven-resolver-impl/src/test/resources/artifact-descriptions/managed/default-management-tree.txt
new file mode 100644
index 0000000..d0a1946
--- /dev/null
+++ b/maven-resolver-impl/src/test/resources/artifact-descriptions/managed/default-management-tree.txt
@@ -0,0 +1,6 @@
+gid:root:ext:ver compile
++- gid:direct:ext:managed-by-dominant-request compile
+ +- gid:transitive-1:ext:managed-by-root compile
+ +- gid:transitive-2:ext:managed-by-direct compile
+ +- gid:transitive-3:ext:managed-by-transitive-1 compile
+ +- gid:transitive-4:ext:managed-by-transitive-2 compile
diff --git a/maven-resolver-impl/src/test/resources/artifact-descriptions/managed/gid_direct_managed-by-dominant-request.ini b/maven-resolver-impl/src/test/resources/artifact-descriptions/managed/gid_direct_managed-by-dominant-request.ini
new file mode 100644
index 0000000..94ba9fb
--- /dev/null
+++ b/maven-resolver-impl/src/test/resources/artifact-descriptions/managed/gid_direct_managed-by-dominant-request.ini
@@ -0,0 +1,5 @@
+[dependencies]
+gid:transitive-1:ext:ver
+[manageddependencies]
+gid:transitive-1:ext:must-retain-core-management
+gid:transitive-2:ext:managed-by-direct
\ No newline at end of file
diff --git a/maven-resolver-impl/src/test/resources/artifact-descriptions/managed/gid_root_ver.ini b/maven-resolver-impl/src/test/resources/artifact-descriptions/managed/gid_root_ver.ini
index 15db99e..5ac544a 100644
--- a/maven-resolver-impl/src/test/resources/artifact-descriptions/managed/gid_root_ver.ini
+++ b/maven-resolver-impl/src/test/resources/artifact-descriptions/managed/gid_root_ver.ini
@@ -1,5 +1,5 @@
[dependencies]
gid:direct:ext:ver
[manageddependencies]
-gid:direct:ext:must-retain-core-management
+gid:direct:ext:must-be-ignored-for-maven-2-and-3-compat
gid:transitive-1:ext:managed-by-root
\ No newline at end of file
diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/DefaultDependencyManager.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/DefaultDependencyManager.java
new file mode 100644
index 0000000..ab0efcf
--- /dev/null
+++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/DefaultDependencyManager.java
@@ -0,0 +1,313 @@
+package org.eclipse.aether.util.graph.manager;
+
+/*
+ * 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.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Objects;
+
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.artifact.ArtifactProperties;
+import org.eclipse.aether.collection.DependencyCollectionContext;
+import org.eclipse.aether.collection.DependencyManagement;
+import org.eclipse.aether.collection.DependencyManager;
+import org.eclipse.aether.graph.Dependency;
+import org.eclipse.aether.graph.Exclusion;
+import org.eclipse.aether.util.artifact.JavaScopes;
+
+/**
+ * A dependency manager managing dependencies on all levels supporting transitive dependency management.
+ * <p>
+ * <b>Note:</b>Unlike the {@code ClassicDependencyManager} and the {@code TransitiveDependencyManager} this
+ * implementation applies management also on the first level. This is considered the resolver's default behaviour.
+ * It ignores all management overrides supported by the {@code MavenModelBuilder}.
+ * </p>
+ *
+ * @author Christian Schulte
+ * @since 1.4.0
+ */
+public final class DefaultDependencyManager
+ implements DependencyManager
+{
+
+ private final Map<Object, String> managedVersions;
+
+ private final Map<Object, String> managedScopes;
+
+ private final Map<Object, Boolean> managedOptionals;
+
+ private final Map<Object, String> managedLocalPaths;
+
+ private final Map<Object, Collection<Exclusion>> managedExclusions;
+
+ private int hashCode;
+
+ /**
+ * Creates a new dependency manager without any management information.
+ */
+ public DefaultDependencyManager()
+ {
+ this( Collections.<Object, String>emptyMap(), Collections.<Object, String>emptyMap(),
+ Collections.<Object, Boolean>emptyMap(), Collections.<Object, String>emptyMap(),
+ Collections.<Object, Collection<Exclusion>>emptyMap() );
+ }
+
+ private DefaultDependencyManager( final Map<Object, String> managedVersions,
+ final Map<Object, String> managedScopes,
+ final Map<Object, Boolean> managedOptionals,
+ final Map<Object, String> managedLocalPaths,
+ final Map<Object, Collection<Exclusion>> managedExclusions )
+ {
+ super();
+ this.managedVersions = managedVersions;
+ this.managedScopes = managedScopes;
+ this.managedOptionals = managedOptionals;
+ this.managedLocalPaths = managedLocalPaths;
+ this.managedExclusions = managedExclusions;
+ }
+
+ public DependencyManager deriveChildManager( final DependencyCollectionContext context )
+ {
+ Map<Object, String> versions = this.managedVersions;
+ Map<Object, String> scopes = this.managedScopes;
+ Map<Object, Boolean> optionals = this.managedOptionals;
+ Map<Object, String> localPaths = this.managedLocalPaths;
+ Map<Object, Collection<Exclusion>> exclusions = this.managedExclusions;
+
+ for ( Dependency managedDependency : context.getManagedDependencies() )
+ {
+ Artifact artifact = managedDependency.getArtifact();
+ Object key = getKey( artifact );
+
+ String version = artifact.getVersion();
+ if ( version.length() > 0 && !versions.containsKey( key ) )
+ {
+ if ( versions == this.managedVersions )
+ {
+ versions = new HashMap<>( this.managedVersions );
+ }
+ versions.put( key, version );
+ }
+
+ String scope = managedDependency.getScope();
+ if ( scope.length() > 0 && !scopes.containsKey( key ) )
+ {
+ if ( scopes == this.managedScopes )
+ {
+ scopes = new HashMap<>( this.managedScopes );
+ }
+ scopes.put( key, scope );
+ }
+
+ Boolean optional = managedDependency.getOptional();
+ if ( optional != null && !optionals.containsKey( key ) )
+ {
+ if ( optionals == this.managedOptionals )
+ {
+ optionals = new HashMap<>( this.managedOptionals );
+ }
+ optionals.put( key, optional );
+ }
+
+ String localPath = managedDependency.getArtifact().getProperty( ArtifactProperties.LOCAL_PATH, null );
+ if ( localPath != null && !localPaths.containsKey( key ) )
+ {
+ if ( localPaths == this.managedLocalPaths )
+ {
+ localPaths = new HashMap<>( this.managedLocalPaths );
+ }
+ localPaths.put( key, localPath );
+ }
+
+ if ( !managedDependency.getExclusions().isEmpty() )
+ {
+ if ( exclusions == this.managedExclusions )
+ {
+ exclusions = new HashMap<>( this.managedExclusions );
+ }
+ Collection<Exclusion> managed = exclusions.get( key );
+ if ( managed == null )
+ {
+ managed = new LinkedHashSet<>();
+ exclusions.put( key, managed );
+ }
+ managed.addAll( managedDependency.getExclusions() );
+ }
+ }
+
+ return new DefaultDependencyManager( versions, scopes, optionals, localPaths, exclusions );
+ }
+
+ public DependencyManagement manageDependency( Dependency dependency )
+ {
+ DependencyManagement management = null;
+
+ Object key = getKey( dependency.getArtifact() );
+
+ String version = managedVersions.get( key );
+ if ( version != null )
+ {
+ if ( management == null )
+ {
+ management = new DependencyManagement();
+ }
+ management.setVersion( version );
+ }
+
+ String scope = managedScopes.get( key );
+ if ( scope != null )
+ {
+ if ( management == null )
+ {
+ management = new DependencyManagement();
+ }
+ management.setScope( scope );
+
+ if ( !JavaScopes.SYSTEM.equals( scope )
+ && dependency.getArtifact().getProperty( ArtifactProperties.LOCAL_PATH, null ) != null )
+ {
+ Map<String, String> properties =
+ new HashMap<>( dependency.getArtifact().getProperties() );
+
+ properties.remove( ArtifactProperties.LOCAL_PATH );
+ management.setProperties( properties );
+ }
+ }
+
+ if ( ( scope != null && JavaScopes.SYSTEM.equals( scope ) )
+ || ( scope == null && JavaScopes.SYSTEM.equals( dependency.getScope() ) ) )
+ {
+ String localPath = managedLocalPaths.get( key );
+ if ( localPath != null )
+ {
+ if ( management == null )
+ {
+ management = new DependencyManagement();
+ }
+
+ Map<String, String> properties =
+ new HashMap<>( dependency.getArtifact().getProperties() );
+
+ properties.put( ArtifactProperties.LOCAL_PATH, localPath );
+ management.setProperties( properties );
+ }
+ }
+
+ Boolean optional = managedOptionals.get( key );
+ if ( optional != null )
+ {
+ if ( management == null )
+ {
+ management = new DependencyManagement();
+ }
+ management.setOptional( optional );
+ }
+
+ Collection<Exclusion> exclusions = managedExclusions.get( key );
+ if ( exclusions != null )
+ {
+ if ( management == null )
+ {
+ management = new DependencyManagement();
+ }
+ Collection<Exclusion> result = new LinkedHashSet<>( dependency.getExclusions() );
+ result.addAll( exclusions );
+ management.setExclusions( result );
+ }
+
+ return management;
+ }
+
+ private Object getKey( Artifact a )
+ {
+ return new Key( a );
+ }
+
+ @Override
+ public boolean equals( final Object obj )
+ {
+ boolean equal = obj instanceof DefaultDependencyManager;
+
+ if ( equal )
+ {
+ final DefaultDependencyManager that = (DefaultDependencyManager) obj;
+ equal = Objects.equals( managedVersions, that.managedVersions )
+ && Objects.equals( managedScopes, that.managedScopes )
+ && Objects.equals( managedOptionals, that.managedOptionals )
+ && Objects.equals( managedExclusions, that.managedExclusions );
+
+ }
+
+ return equal;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ if ( hashCode == 0 )
+ {
+ hashCode = Objects.hash( managedVersions, managedScopes, managedOptionals, managedExclusions );
+ }
+ return hashCode;
+ }
+
+ static class Key
+ {
+
+ private final Artifact artifact;
+
+ private final int hashCode;
+
+ Key( final Artifact artifact )
+ {
+ this.artifact = artifact;
+ this.hashCode = Objects.hash( artifact.getGroupId(), artifact.getArtifactId() );
+ }
+
+ @Override
+ public boolean equals( final Object obj )
+ {
+ boolean equal = obj instanceof Key;
+
+ if ( equal )
+ {
+ final Key that = (Key) obj;
+ return Objects.equals( artifact.getArtifactId(), that.artifact.getArtifactId() )
+ && Objects.equals( artifact.getGroupId(), that.artifact.getGroupId() )
+ && Objects.equals( artifact.getExtension(), that.artifact.getExtension() )
+ && Objects.equals( artifact.getClassifier(), that.artifact.getClassifier() );
+
+ }
+
+ return equal;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return this.hashCode;
+ }
+
+ }
+
+}