Monday, September 24, 2018

7 tools providing visibility for maven dependency tree

With maven pom.xml, you can have maven manage your project's dependency automatically. Whenever your project's code needs classes in a jar file, you can specify the dependency in your pom.xml, and give it a scope to specify whether this jars is needed for compile time, run time or test time. As long as the jars exist in maven repository, they will be downloaded into your local repository, which by default under ~/.m2/repository. Your project will include a set of jars which you specify in your pom.xml, they maps to the jar folders in ~/.m2/repository, and maven will include them in classpath. Those depended jars also has dependency specified in their own pom.xml. Maven goes one step further to download all the jars in the dependency chain, and have a set of rules to figure out which jar version takes precedence when it is declared in multiple places -- for example, when a jar version is declared in both parent pom and child pom, child pom takes precedence.

Enough theory about maven, here are 7 tools help you get visibility of maven dependency tree.
The example maven project used below can be checkout with "git clone https://github.com/GoogleCloudPlatform/appengine-gcs-client.git"

1. locate the dependency jar file. The groupId + artifactId + version in your pom.xml file is mapping to the corresponding directory. for example <groupId>org.codehaus.mojo</groupId> + <artifactId>versions-maven-plugin</artifactId> + <version>2.2</version> in the following example maps to ~/.m2/repository/org/codehaus/mojo/versions-maven-plugin/2.2/



>grep groupId pom.xml -A 2
    <groupId>com.google.appengine.tools</groupId>
    <artifactId>appengine-gcs-client-example</artifactId>

--
            <groupId>com.google.appengine</groupId>
            <artifactId>appengine-api-1.0-sdk</artifactId>
            <version>${appengine.version}</version>
--
            <groupId>com.google.appengine.tools</groupId>
            <artifactId>appengine-gcs-client</artifactId>
            <version>0.6</version>
--
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
--
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
--
            <groupId>com.google.appengine</groupId>
            <artifactId>appengine-api-stubs</artifactId>
            <version>${appengine.version}</version>
--
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>versions-maven-plugin</artifactId>
                <version>2.2</version>
--
                <groupId>org.apache.maven.plugins</groupId>
                <version>3.5.1</version>
                <artifactId>maven-compiler-plugin</artifactId>
--
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.6</version>
--
                <groupId>com.google.appengine</groupId>
                <artifactId>appengine-maven-plugin</artifactId>
                <version>${appengine.version}</version>
--
              <groupId>com.google.appengine</groupId>
              <artifactId>gcloud-maven-plugin</artifactId>
              <version>${gcloud.plugin.version}</version>
>ls ~/.m2/repository/org/codehaus/mojo/versions-maven-plugin/2.2/
_remote.repositories versions-maven-plugin-2.2.jar versions-maven-plugin-2.2.pom
m2e-lastUpdated.properties versions-maven-plugin-2.2.jar.sha1 versions-maven-plugin-2.2.pom.sha1
>      

In eclipse, you can hold command key and click a dependency in your pom.xml to step into the dependency jar's pom.xml file which located in a folder under ~/.m2/repository.

2. display dependency tree. The command "mvn dependency:tree" will display the dependency hierarchy in a tree structure. For example, com.google.oauth-client:google-oauth-client:jar:1.21.0:compile is an indirect dependency introduced by com.google.appengine.tools:appengine-gcs-client:jar:0.6:compile in your pom.xml.


>mvn dependency:tree
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building appengine-gcs-client-example 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ appengine-gcs-client-example ---
[INFO] com.google.appengine.tools:appengine-gcs-client-example:war:1.0-SNAPSHOT
[INFO] +- com.google.appengine:appengine-api-1.0-sdk:jar:1.9.36:compile
[INFO] +- com.google.appengine.tools:appengine-gcs-client:jar:0.6:compile
[INFO] |  +- com.google.guava:guava:jar:19.0-rc1:compile
[INFO] |  +- joda-time:joda-time:jar:2.3:compile
[INFO] |  +- com.google.apis:google-api-services-storage:jar:v1-rev68-1.21.0:compile
[INFO] |  |  \- com.google.api-client:google-api-client:jar:1.21.0:compile
[INFO] |  |     \- com.google.oauth-client:google-oauth-client:jar:1.21.0:compile
[INFO] |  +- com.google.api-client:google-api-client-appengine:jar:1.25.0:compile (version selected from constraint [1.19,2.0))
[INFO] |  |  +- com.google.oauth-client:google-oauth-client-appengine:jar:1.25.0:compile
[INFO] |  |  |  \- com.google.oauth-client:google-oauth-client-servlet:jar:1.25.0:compile
[INFO] |  |  |     \- com.google.http-client:google-http-client-jdo:jar:1.25.0:compile
[INFO] |  |  +- com.google.api-client:google-api-client-servlet:jar:1.25.0:compile
[INFO] |  |  |  \- javax.jdo:jdo2-api:jar:2.3-eb:compile
[INFO] |  |  |     \- javax.transaction:transaction-api:jar:1.1:compile
[INFO] |  |  \- com.google.http-client:google-http-client-appengine:jar:1.25.0:compile
[INFO] |  +- com.google.http-client:google-http-client:jar:1.25.0:compile (version selected from constraint [1.19.0,2.0))
[INFO] |  |  +- com.google.code.findbugs:jsr305:jar:3.0.2:compile
[INFO] |  |  +- org.apache.httpcomponents:httpclient:jar:4.5.5:compile
[INFO] |  |  |  +- org.apache.httpcomponents:httpcore:jar:4.4.9:compile
[INFO] |  |  |  +- commons-logging:commons-logging:jar:1.2:compile
[INFO] |  |  |  \- commons-codec:commons-codec:jar:1.10:compile
[INFO] |  |  \- com.google.j2objc:j2objc-annotations:jar:1.1:compile
[INFO] |  \- com.google.http-client:google-http-client-jackson2:jar:1.25.0:compile (version selected from constraint [1.19,2.0))
[INFO] |     \- com.fasterxml.jackson.core:jackson-core:jar:2.9.6:compile
[INFO] +- javax.servlet:servlet-api:jar:2.5:provided
[INFO] +- jstl:jstl:jar:1.2:compile
[INFO] \- com.google.appengine:appengine-api-stubs:jar:1.9.36:test
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.037 s
[INFO] Finished at: 2018-09-24T21:19:12-04:00
[INFO] Final Memory: 17M/226M
[INFO] ------------------------------------------------------------------------
>

In eclipse, it is equivalent to the "Effective POM" tab's output.


The eclipse even goes one step further to list all the jar dependency chains in "Dependency Hierarchy" tab. Those needed but overruled jar versions are marked as "omitted for conflict".


3. you can force maven to download the source code for your jar files if they are available in maven remote repository. "mvn dependency:sources".

You can do the same in eclipse by right click a project -> Maven -> Download Sources


4. Sometimes you get a dependent jar that almost meets your requirement except some small dissatisfaction. You can check out the source code from svn, git, etc, modify the code, then issue "mvn install". This command will publish an artifact into your local repository. Other project want to use them can reference it with a dependency entry in pom.xml. For example, run "mvn clean install" for the example will create an artifact under .m2/repository/com/google/appengine/tools/appengine-gcs-client-example/1.0-SNAPSHOT/appengine-gcs-client-example-1.0-SNAPSHOT.pom, you can reference this artifact with

      <dependency>
            <groupId>com.google.appengine.tools</groupId>
            <artifactId>appengine-gcs-client-example</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>

        </dependency>

in other projects' pom.xml.

You can do the same in eclipse, by right clicking the project, then selecting Run as, then clicking Maven Install. Notice due to eclipse bug,  org.codehaus.mojo:versions-maven-plugin can not be executed, I comment it out to get the maven install running. It won't hurt main functionality, this plugin as the name says, is a nice to have step during compile.


5. You can build maven project with command line, then import the built project into eclipse or have eclipse synchronize with the update. To import an maven project in eclipse, you click File -> Import -> expand maven folder, select "Existing Maven Projects" -> click Next -> browse to the pom.xml you wan to import. To update maven project in eclipse, whenever you built maven project with command line, you right click the project, select maven then click "update project...". 

6. You can inspect exactly what jars your eclipse project has in the classpath, these jars are introduced by maven plugin. 



7. Finally, you resolved the dependency issue by specifying a particular dependent jar version to override the maven chosen version or adding a list of <exclusions><exclusion></exclusion>...</exclusions>  under some <dependency></dependency> to force maven to reconsider the version. Problem now solved, you can update the project versions to a higher one to mark this fix. You can change multiple pom.xml file versions with command "mvn versions:set -DnewVersion=1.3.x-SNAPSHOT. The versions:set goal will automatically climb up local directories to find the aggregation root. It will automatically update explicitly referenced dependencies.


No comments:

Post a Comment

Why I stopped publishing blog posts as information provider

Now the AI can generate content. Does that mean the web publishing industry reaches the end? ChatGPT said: ChatGPT Not at all. While AI can ...