You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by mi...@apache.org on 2019/06/08 11:31:31 UTC

[maven-resolver] 01/01: [MRESOLVER-10] New 'TransitiveDependencyManager' supporting transitive dependency management

This is an automated email from the ASF dual-hosted git repository.

michaelo pushed a commit to branch MRESOLVER-10
in repository https://gitbox.apache.org/repos/asf/maven-resolver.git

commit a08cb20e53f206ca720cb850ff392544b73268fb
Author: Christian Schulte <cs...@schulte.it>
AuthorDate: Sat Mar 11 22:38:00 2017 +0100

    [MRESOLVER-10] New 'TransitiveDependencyManager' supporting transitive dependency management
    
    This closes #35
---
 .../collect/DefaultDependencyCollectorTest.java    |  49 ++++
 .../managed/gid_direct_ver.ini                     |   5 +
 .../artifact-descriptions/managed/gid_root_ver.ini |   5 +
 .../managed/gid_transitive-1_managed-by-root.ini   |   5 +
 .../managed/gid_transitive-2_managed-by-direct.ini |   5 +
 .../gid_transitive-3_managed-by-transitive-1.ini   |   4 +
 .../gid_transitive-4_managed-by-transitive-2.ini   |   1 +
 .../managed/management-tree.txt                    |   6 +
 .../graph/manager/TransitiveDependencyManager.java | 307 +++++++++++++++++++++
 9 files changed, 387 insertions(+)

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 0c10469..232cf9f 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
@@ -21,6 +21,7 @@ package org.eclipse.aether.internal.impl.collect;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 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;
@@ -47,6 +48,7 @@ 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;
@@ -64,6 +66,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.DependencyManagerUtils;
+import org.eclipse.aether.util.graph.manager.TransitiveDependencyManager;
 import org.eclipse.aether.util.graph.selector.ScopeDependencySelector;
 import org.eclipse.aether.util.graph.version.HighestVersionFilter;
 import org.junit.Before;
@@ -481,6 +484,52 @@ public class DefaultDependencyCollectorTest
     }
 
     @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 ) );
+    }
+
+    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
     {
diff --git a/maven-resolver-impl/src/test/resources/artifact-descriptions/managed/gid_direct_ver.ini b/maven-resolver-impl/src/test/resources/artifact-descriptions/managed/gid_direct_ver.ini
new file mode 100644
index 0000000..94ba9fb
--- /dev/null
+++ b/maven-resolver-impl/src/test/resources/artifact-descriptions/managed/gid_direct_ver.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
new file mode 100644
index 0000000..15db99e
--- /dev/null
+++ b/maven-resolver-impl/src/test/resources/artifact-descriptions/managed/gid_root_ver.ini
@@ -0,0 +1,5 @@
+[dependencies]
+gid:direct:ext:ver
+[manageddependencies]
+gid:direct:ext:must-retain-core-management
+gid:transitive-1:ext:managed-by-root
\ No newline at end of file
diff --git a/maven-resolver-impl/src/test/resources/artifact-descriptions/managed/gid_transitive-1_managed-by-root.ini b/maven-resolver-impl/src/test/resources/artifact-descriptions/managed/gid_transitive-1_managed-by-root.ini
new file mode 100644
index 0000000..8fd9f0d
--- /dev/null
+++ b/maven-resolver-impl/src/test/resources/artifact-descriptions/managed/gid_transitive-1_managed-by-root.ini
@@ -0,0 +1,5 @@
+[dependencies]
+gid:transitive-2:ext:ver
+[manageddependencies]
+gid:transitive-2:ext:must-retain-core-management
+gid:transitive-3:ext:managed-by-transitive-1
\ No newline at end of file
diff --git a/maven-resolver-impl/src/test/resources/artifact-descriptions/managed/gid_transitive-2_managed-by-direct.ini b/maven-resolver-impl/src/test/resources/artifact-descriptions/managed/gid_transitive-2_managed-by-direct.ini
new file mode 100644
index 0000000..96cc8f5
--- /dev/null
+++ b/maven-resolver-impl/src/test/resources/artifact-descriptions/managed/gid_transitive-2_managed-by-direct.ini
@@ -0,0 +1,5 @@
+[dependencies]
+gid:transitive-3:ext:ver
+[manageddependencies]
+gid:transitive-3:ext:must-retain-core-management
+gid:transitive-4:ext:managed-by-transitive-2
\ No newline at end of file
diff --git a/maven-resolver-impl/src/test/resources/artifact-descriptions/managed/gid_transitive-3_managed-by-transitive-1.ini b/maven-resolver-impl/src/test/resources/artifact-descriptions/managed/gid_transitive-3_managed-by-transitive-1.ini
new file mode 100644
index 0000000..2b8f3cf
--- /dev/null
+++ b/maven-resolver-impl/src/test/resources/artifact-descriptions/managed/gid_transitive-3_managed-by-transitive-1.ini
@@ -0,0 +1,4 @@
+[dependencies]
+gid:transitive-4:ext:ver
+[manageddependencies]
+gid:transitive-4:ext:must-retain-core-management
diff --git a/maven-resolver-impl/src/test/resources/artifact-descriptions/managed/gid_transitive-4_managed-by-transitive-2.ini b/maven-resolver-impl/src/test/resources/artifact-descriptions/managed/gid_transitive-4_managed-by-transitive-2.ini
new file mode 100644
index 0000000..61a252c
--- /dev/null
+++ b/maven-resolver-impl/src/test/resources/artifact-descriptions/managed/gid_transitive-4_managed-by-transitive-2.ini
@@ -0,0 +1 @@
+[dependencies]
diff --git a/maven-resolver-impl/src/test/resources/artifact-descriptions/managed/management-tree.txt b/maven-resolver-impl/src/test/resources/artifact-descriptions/managed/management-tree.txt
new file mode 100644
index 0000000..1371426
--- /dev/null
+++ b/maven-resolver-impl/src/test/resources/artifact-descriptions/managed/management-tree.txt
@@ -0,0 +1,6 @@
+gid:root:ext:ver compile
++- gid:direct:ext:ver 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-util/src/main/java/org/eclipse/aether/util/graph/manager/TransitiveDependencyManager.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/TransitiveDependencyManager.java
new file mode 100644
index 0000000..3536e42
--- /dev/null
+++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/TransitiveDependencyManager.java
@@ -0,0 +1,307 @@
+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 transitive dependencies supporting transitive dependency management.
+ *
+ * @author Christian Schulte
+ * @since 1.4.0
+ */
+public final class TransitiveDependencyManager
+    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 final int depth;
+
+    private int hashCode;
+
+    /**
+     * Creates a new dependency manager without any management information.
+     */
+    public TransitiveDependencyManager()
+    {
+        this( 0, Collections.<Object, String>emptyMap(), Collections.<Object, String>emptyMap(),
+              Collections.<Object, Boolean>emptyMap(), Collections.<Object, String>emptyMap(),
+              Collections.<Object, Collection<Exclusion>>emptyMap() );
+    }
+
+    private TransitiveDependencyManager( final int depth,
+                                         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.depth = depth;
+        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 = managedVersions;
+        Map<Object, String> scopes = managedScopes;
+        Map<Object, Boolean> optionals = managedOptionals;
+        Map<Object, String> localPaths = managedLocalPaths;
+        Map<Object, Collection<Exclusion>> exclusions = 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 == managedVersions )
+                {
+                    versions = new HashMap<>( 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 == managedOptionals )
+                {
+                    optionals = new HashMap<>( 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<>( managedLocalPaths );
+                }
+                localPaths.put( key, localPath );
+            }
+
+            if ( !managedDependency.getExclusions().isEmpty() )
+            {
+                if ( exclusions == managedExclusions )
+                {
+                    exclusions = new HashMap<>( managedExclusions );
+                }
+                Collection<Exclusion> managed = exclusions.get( key );
+                if ( managed == null )
+                {
+                    managed = new LinkedHashSet<>();
+                    exclusions.put( key, managed );
+                }
+                managed.addAll( managedDependency.getExclusions() );
+            }
+        }
+
+        return new TransitiveDependencyManager( depth + 1, versions, scopes, optionals, localPaths,
+                                                exclusions );
+
+    }
+
+    public DependencyManagement manageDependency( Dependency dependency )
+    {
+        DependencyManagement management = null;
+
+        Object key = getKey( dependency.getArtifact() );
+
+        if ( depth >= 2 )
+        {
+            String version = managedVersions.get( key );
+            if ( version != 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 ( ( 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 TransitiveDependencyManager;
+
+        if ( equal )
+        {
+            final TransitiveDependencyManager that = (TransitiveDependencyManager) obj;
+            return depth == that.depth
+                       && Objects.equals( managedVersions, that.managedVersions )
+                       && Objects.equals( managedScopes, that.managedScopes )
+                       && Objects.equals( managedOptionals, that.managedOptionals )
+                       && Objects.equals( managedExclusions, that.managedExclusions );
+        }
+
+        return false;
+    }
+
+    @Override
+    public int hashCode()
+    {
+        if ( hashCode == 0 )
+        {
+            hashCode = Objects.hash( depth, 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;
+        }
+
+    }
+
+}