All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.maven.plugins.dependency.tree.VerboseDependencyGraphBuilder Maven / Gradle / Ivy

There is a newer version: 3.8.1
Show newest version
package org.apache.maven.plugins.dependency.tree;

/*
 * 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.apache.maven.model.DependencyManagement;
import org.apache.maven.model.Model;
import org.apache.maven.project.DefaultDependencyResolutionRequest;
import org.apache.maven.project.DependencyResolutionException;
import org.apache.maven.project.DependencyResolutionRequest;
import org.apache.maven.project.DependencyResolutionResult;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.ProjectBuildingRequest;
import org.apache.maven.project.ProjectDependenciesResolver;
import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
import org.apache.maven.shared.dependency.graph.DependencyGraphBuilderException;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.collection.DependencySelector;
import org.eclipse.aether.graph.DefaultDependencyNode;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.graph.DependencyNode;
import org.eclipse.aether.util.graph.selector.AndDependencySelector;
import org.eclipse.aether.util.graph.selector.ExclusionDependencySelector;
import org.eclipse.aether.util.graph.selector.ScopeDependencySelector;
import org.eclipse.aether.util.graph.transformer.ChainedDependencyGraphTransformer;
import org.eclipse.aether.util.graph.transformer.JavaDependencyContextRefiner;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * Builds the VerboseDependencyGraph
 */
class VerboseDependencyGraphBuilder
{
    private static final String PRE_MANAGED_SCOPE = "preManagedScope", PRE_MANAGED_VERSION = "preManagedVersion",
            MANAGED_SCOPE = "managedScope";

    public DependencyNode buildVerboseGraph( MavenProject project, ProjectDependenciesResolver resolver,
                                             RepositorySystemSession repositorySystemSession,
                                             Collection reactorProjects,
                                             ProjectBuildingRequest buildingRequest )
            throws DependencyGraphBuilderException
    {
        DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
        session.setLocalRepositoryManager( repositorySystemSession.getLocalRepositoryManager() );

        DependencySelector dependencySelector = new AndDependencySelector(
                // ScopeDependencySelector takes exclusions. 'Provided' scope is not here to avoid
                // false positive in LinkageChecker.
                new ScopeDependencySelector(), new ExclusionDependencySelector() );

        session.setDependencySelector( dependencySelector );
        session.setDependencyGraphTransformer(
                new ChainedDependencyGraphTransformer( new CycleBreakerGraphTransformer(), // Avoids StackOverflowError
                        new JavaDependencyContextRefiner() ) );
        session.setDependencyManager( null );

        DependencyResolutionRequest request = new DefaultDependencyResolutionRequest();
        request.setMavenProject( buildingRequest.getProject() );
        request.setRepositorySession( session ) ;
        DependencyNode rootNode;
        boolean reactor = false;

        try
        {
            rootNode = resolver.resolve( request ).getDependencyGraph();
        }
        catch ( DependencyResolutionException e )
        {
            // cannot properly resolve reactor dependencies with verbose RepositorySystemSession
            // this should be fixed in the future
            DependencyResolutionRequest reactorRequest = new DefaultDependencyResolutionRequest();
            reactorRequest.setMavenProject( buildingRequest.getProject() );
            reactorRequest.setRepositorySession( buildingRequest.getRepositorySession() ) ;
            try
            {
                rootNode = resolver.resolve( reactorRequest ).getDependencyGraph();
            }
            catch ( DependencyResolutionException exception )
            {
                if ( reactorProjects == null )
                {
                    throw new DependencyGraphBuilderException( "Could not resolve following dependencies: "
                            + exception.getResult().getUnresolvedDependencies(), exception );
                }
                reactor = true;
                // try collecting from reactor
                rootNode = collectDependenciesFromReactor( exception, reactorProjects ).getDependencyGraph();
                rootNode.setData( "ContainsModule", "True" );
                // rootNode.setArtifact( rootArtifact.setProperties( artifactProperties ) );
            }
        }

        // Don't want transitive test dependencies included in analysis
        DependencyNode prunedRoot = pruneTransitiveTestDependencies( rootNode, project );
        applyDependencyManagement( project, prunedRoot );
        if ( reactor )
        {
            prunedRoot.setData( "ContainsModule", "True" );
        }
        return prunedRoot;
    }

    private void applyDependencyManagement( MavenProject project, DependencyNode root )
    {
        Map dependencyManagementMap = createDependencyManagementMap(
                project.getDependencyManagement() );

        for ( DependencyNode child : root.getChildren() )
        {
            for ( DependencyNode nonTransitiveDependencyNode : child.getChildren() )
            {
                applyDependencyManagementDfs( dependencyManagementMap, nonTransitiveDependencyNode );
            }
        }
    }

    private void applyDependencyManagementDfs( Map dependencyManagementMap,
                                               DependencyNode node )
    {
        if ( dependencyManagementMap.containsKey( getDependencyManagementCoordinate( node.getArtifact() ) ) )
        {
            org.apache.maven.model.Dependency manager = dependencyManagementMap.get(
                    getDependencyManagementCoordinate( node.getArtifact() ) );
            Map artifactProperties = new HashMap<>();
            for ( Map.Entry entry : node.getArtifact().getProperties().entrySet() )
            {
                artifactProperties.put( entry.getKey(), entry.getValue() );
            }

            if ( !manager.getVersion().equals( node.getArtifact().getVersion() ) )
            {
                artifactProperties.put( PRE_MANAGED_VERSION, node.getArtifact().getVersion() );
                node.setArtifact( node.getArtifact().setVersion( manager.getVersion() ) );
            }

            String managerScope = Objects.toString( manager.getScope(), "compile" );
            Dependency dependency = node.getDependency();
            String dependencyScope = dependency.getScope();
            if ( !managerScope.equals( dependencyScope ) )
            {
                artifactProperties.put( PRE_MANAGED_SCOPE, dependencyScope );
                // be aware this does not actually change the node's scope, it may need to be fixed in the future
                artifactProperties.put( MANAGED_SCOPE, managerScope );
            }
            node.setArtifact( node.getArtifact().setProperties( artifactProperties ) );
            dependency.setArtifact( dependency.getArtifact().setProperties( artifactProperties ) );
        }
        for ( DependencyNode child : node.getChildren() )
        {
            applyDependencyManagementDfs( dependencyManagementMap, child );
        }
    }

    private static Map createDependencyManagementMap(
            DependencyManagement dependencyManagement )
    {
        Map dependencyManagementMap = new HashMap<>();
        if ( dependencyManagement == null )
        {
            return dependencyManagementMap;
        }
        for ( org.apache.maven.model.Dependency dependency : dependencyManagement.getDependencies() )
        {
            dependencyManagementMap.put( getDependencyManagementCoordinate( dependency ), dependency );
        }
        return dependencyManagementMap;
    }

    private static String getDependencyManagementCoordinate( org.apache.maven.model.Dependency dependency )
    {
        StringBuilder builder = new StringBuilder();
        builder.append( dependency.getGroupId() ).append( ":" ).append( dependency.getArtifactId() ).append( ":" )
                .append( dependency.getType() );
        if ( dependency.getClassifier() != null && !dependency.getClassifier().equals( "" ) )
        {
            builder.append( ":" ).append( dependency.getClassifier() );
        }
        return builder.toString();
    }

    private static String getDependencyManagementCoordinate( Artifact artifact )
    {
        StringBuilder builder = new StringBuilder();
        builder.append( artifact.getGroupId() ).append( ":" ).append( artifact.getArtifactId() ).append( ":" ).append(
                artifact.getExtension() );
        if ( artifact.getClassifier() != null && !artifact.getClassifier().equals( "" ) )
        {
            builder.append( ":" ).append( artifact.getClassifier() );
        }
        return builder.toString();
    }

    private Dependency getProjectDependency( MavenProject project )
    {
        Model model = project.getModel();

        return new Dependency( new DefaultArtifact( model.getGroupId(), model.getArtifactId(), model.getPackaging(),
                model.getVersion() ), "" );
    }

    private DependencyNode pruneTransitiveTestDependencies( DependencyNode rootNode, MavenProject project )
    {
        Set visitedNodes = new HashSet<>();
        DependencyNode newRoot = new DefaultDependencyNode( getProjectDependency( project ) );
        newRoot.setChildren( new ArrayList() );

        for ( int i = 0; i < rootNode.getChildren().size(); i++ )
        {
            DependencyNode childNode = rootNode.getChildren().get( i );
            newRoot.getChildren().add( childNode );

            pruneTransitiveTestDependenciesDfs( childNode, visitedNodes );
        }

        return newRoot;
    }

    private void pruneTransitiveTestDependenciesDfs( DependencyNode node, Set visitedNodes )
    {
        if ( !visitedNodes.contains( node ) )
        {
            visitedNodes.add( node );
            // iterator needed to avoid concurrentModificationException
            Iterator iterator = node.getChildren().iterator();
            while ( iterator.hasNext() )
            {
                DependencyNode child = iterator.next();
                if ( child.getDependency().getScope().equals( "test" ) )
                {
                    iterator.remove();
                }
                else
                {
                    pruneTransitiveTestDependenciesDfs( child, visitedNodes );
                }
            }
        }
    }

    private DependencyResolutionResult collectDependenciesFromReactor( DependencyResolutionException e,
                                                                       Collection reactorProjects )
            throws DependencyGraphBuilderException
    {
        DependencyResolutionResult result = e.getResult();

        List reactorDeps = getReactorDependencies( reactorProjects, result.getUnresolvedDependencies() );
        result.getUnresolvedDependencies().removeAll( reactorDeps );
        result.getResolvedDependencies().addAll( reactorDeps );

        if ( !result.getUnresolvedDependencies().isEmpty() )
        {
            throw new DependencyGraphBuilderException( "Could not resolve nor collect following dependencies: "
                    + result.getUnresolvedDependencies(), e );
        }

        return result;
    }

    private List getReactorDependencies( Collection reactorProjects, List dependencies )
    {
        Set reactorProjectsIds = new HashSet();
        for ( MavenProject project : reactorProjects )
        {
            reactorProjectsIds.add( new ArtifactKey( project ) );
        }

        List reactorDeps = new ArrayList();
        for ( Object untypedDependency : dependencies )
        {
            Dependency dependency = (Dependency) untypedDependency;
            org.eclipse.aether.artifact.Artifact depArtifact = dependency.getArtifact();

            ArtifactKey key =
                    new ArtifactKey( depArtifact.getGroupId(), depArtifact.getArtifactId(), depArtifact.getVersion() );

            if ( reactorProjectsIds.contains( key ) )
            {
                reactorDeps.add( dependency );
            }
        }

        return reactorDeps;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy