Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

View dependency tree of dependencyManagement

We all understand that dependency tree is crucial for resolving transitive dependency conflicts. The same is true for dependencyManagement, but I can't find a way to print the dependency tree for it in a similar fashion as dependencies.

Is there a plugin or something that can help?

Maven version: 3.2.3

Edit

For people thinking this question is duplicating the other question, consider:

  1. the other question is about plugin management with dependency management.

  2. the other question says nothing about generating a dependency tree.

like image 380
stackoverflower Avatar asked Feb 25 '16 14:02

stackoverflower


1 Answers

I couldn't find any plugins that prints the dependency tree for the dependency management section.

But, you can write your own MOJO for that. All of the code below was written and tested with Maven 3.3.9, it would be fairly easy to adapt it to your current Maven version by modifying the dependencies of the new MOJO.

The following is the POM of the Maven plugin:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>sample.plugin</groupId>
    <artifactId>test-maven-plugin</artifactId>
    <version>1.0.0</version>
    <packaging>maven-plugin</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.apache.maven</groupId>
            <artifactId>maven-plugin-api</artifactId>
            <version>3.3.9</version>
        </dependency>
        <dependency>
            <groupId>org.apache.maven.plugin-tools</groupId>
            <artifactId>maven-plugin-annotations</artifactId>
            <version>3.4</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.maven</groupId>
            <artifactId>maven-core</artifactId>
            <version>3.3.9</version>
        </dependency>
    </dependencies>
</project>

and the MOJO itself would be:

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.DefaultArtifact;
import org.apache.maven.artifact.handler.ArtifactHandler;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.building.ModelBuildingRequest;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.DefaultProjectBuildingRequest;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.ProjectBuilder;
import org.apache.maven.project.ProjectBuildingException;
import org.apache.maven.project.ProjectBuildingRequest;
import org.eclipse.aether.transfer.ArtifactNotFoundException;

@Mojo(name = "foo", requiresDependencyResolution = ResolutionScope.TEST)
public class MyMojo extends AbstractMojo {

    @Parameter(defaultValue = "${project}", readonly = true, required = true)
    private MavenProject project;

    @Parameter(defaultValue = "${session}", readonly = true, required = true)
    private MavenSession session;

    @Component
    private ArtifactHandler artifactHandler;

    @Component
    private ProjectBuilder projectBuilder;

    public void execute() throws MojoExecutionException, MojoFailureException {
        Set<Artifact> visitedArtifacts = new HashSet<Artifact>();
        for (Dependency dependency : project.getDependencyManagement().getDependencies()) {
            printDependencyTree(toArtifact(dependency), "", visitedArtifacts);
        }
    }

    private void printDependencyTree(Artifact artifact, String level, Set<Artifact> visitedArtifacts) throws MojoExecutionException {
        getLog().info(level + "+ " + artifact);
        for (Dependency transitive : getTransitiveDependencies(artifact)) {
            Artifact transitiveArtifact = toArtifact(transitive);
            if (!visitedArtifacts.contains(transitiveArtifact)) {
                visitedArtifacts.add(transitiveArtifact);
                printDependencyTree(transitiveArtifact, level + "  ", visitedArtifacts);
            }
        }
    }

    private List<Dependency> getTransitiveDependencies(Artifact artifact) throws MojoExecutionException {
        try {
            ProjectBuildingRequest buildingRequest = new DefaultProjectBuildingRequest(session.getProjectBuildingRequest());
            buildingRequest.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL);
            buildingRequest.setProject(null);
            MavenProject mavenProject = projectBuilder.build(artifact, buildingRequest).getProject();
            return mavenProject.getDependencies();
        } catch (ProjectBuildingException e) {
            if (e.getCause() != null && e.getCause().getCause() instanceof ArtifactNotFoundException) {
                //ignore
                return new ArrayList<Dependency>();
            }
            throw new MojoExecutionException("Error while building project", e);
        }
    }

    private Artifact toArtifact(Dependency dependency) {
        return new DefaultArtifact(dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion(), dependency.getScope(), dependency.getType(), dependency.getClassifier(), artifactHandler);
    }

}

This is a bit complicated but the main ingredients are:

  • We are injected the current Maven project, for which we resolve the dependencies in the dependency management section with project.getDependencyManagement().getDependencies().
  • For each dependency, we resolve its transitive dependencies by building a Maven project in memory with the ProjectBuilder API. This requires to transform our Dependency into an Artifact (this is made with the help of toArtifact), construct a ProjectBuildingRequest setting the project to null (I noticed that we also need to set the validation level to minimal so that the API does not fail on slightly non-conformant POMs) and finally calling projectBuilder.build to build the project. Having the project, we can then return its dependencies with mavenProject.getDependencies().
  • All of this is made inside a recursive method that will simply print the basic information of the artifacts. The recursiveness level is made by prepending two spaces each time we go down one level.
  • Also, since we could potentially revisit an artifact already encoutered (and thus end up in an infinite loop), I keep a set of the visited artifacts and end the tree when an already encoutered artifact is encoutered again.
  • The ArtifactNotFoundException exceptions, that could happen if we try to download an artifact that somehow wasn't found, are ignored.
  • The first time this is launched, the project builder API will download all dependencies from your configured repsitories, so it clutters up the logs. You will want to launch it a second time to better see the output.

As a sample, I tested this with the following dependency management section

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-annotations</artifactId>
            <version>3.4.0.GA</version>
        </dependency>
    </dependencies>
</dependencyManagement>

and the result was:

+ org.hibernate:hibernate-annotations:jar:3.4.0.GA
  + org.hibernate:ejb3-persistence:jar:1.0.2.GA:compile
  + org.hibernate:hibernate-commons-annotations:jar:3.1.0.GA:compile
    + org.slf4j:slf4j-api:jar:1.4.2:compile
      + junit:junit:jar:3.8.1:test
  + org.hibernate:hibernate-core:jar:3.3.0.SP1:compile
    + antlr:antlr:jar:2.7.6:compile
    + commons-collections:commons-collections:jar:3.1:compile
    + dom4j:dom4j:jar:1.6.1:compile
      + jaxme:jaxme-api:jar:0.3:compile
      + jaxen:jaxen:jar:1.1-beta-6:compile
        + dom4j:dom4j:jar:1.5.2:compile
          + jaxen:jaxen:jar:1.1-beta-4:compile
            + jdom:jdom:jar:b10:compile
            + xerces:xmlParserAPIs:jar:2.6.2:compile
            + xerces:xercesImpl:jar:2.6.2:compile
            + xom:xom:jar:1.0b3:compile
              + xerces:xmlParserAPIs:jar:2.6.1:compile
              + xerces:xercesImpl:jar:2.2.1:compile
              + com.ibm.icu:icu4j:jar:2.6.1:compile
              + xalan:xalan:jar:2.6.0:compile
                + xml-apis:xml-apis:jar:2.0.2:compile
                + xerces:xercesImpl:jar:2.6.0:compile
              + org.ccil.cowan.tagsoup:tagsoup:jar:0.9.7:compile
              + javax.servlet:servlet-api:jar:2.4:provided
          + msv:xsdlib:jar:20030807:compile
          + msv:relaxngDatatype:jar:20030807:compile
          + pull-parser:pull-parser:jar:2:compile
          + xpp3:xpp3:jar:1.1.3.3:compile
          + stax:stax-api:jar:1.0:compile
          + junitperf:junitperf:jar:1.8:test
          + stax:stax-ri:jar:1.0:test
          + xalan:xalan:jar:2.5.1:test
        + jdom:jdom:jar:1.0:compile
          + xml-apis:xml-apis:jar:1.0.b2:compile
          + jaxen:jaxen:jar:1.0-FCS:compile
          + saxpath:saxpath:jar:1.0-FCS:compile
          + xalan:xalan:jar:2.5.0:compile
    + javax.transaction:jta:jar:1.1:compile
    + javax.security:jaas:jar:1.0.01:provided
    + javax.security:jacc:jar:1.0:provided
    + ant:ant:jar:1.6.5:provided
      + xml-apis:xml-apis:jar:1.3.04:compile
    + javassist:javassist:jar:3.4.GA:compile
    + org.hibernate:hibernate-cglib-repack:jar:2.1_3:compile

To test this, you will of course need to bind that MOJO to a test Maven project, and you will need to install the MOJO before-hand.

  1. Run mvn clean install on the Maven plugin having the POM above
  2. Create a new test Maven project declaring that plugin.

The plugin configuration for the MOJO above can be:

<plugin>
    <groupId>sample.plugin</groupId>
    <artifactId>test-maven-plugin</artifactId>
    <version>1.0.0</version>
    <executions>
        <execution>
            <id>t</id>
            <goals>
                <goal>foo</goal>
            </goals>
            <phase><!-- something --></phase>
        </execution>
    </executions>
</plugin>
like image 195
Tunaki Avatar answered Nov 09 '22 11:11

Tunaki