Thursday, February 20, 2020

Java: How to Remove Unused Dependencies from a Maven pom.xml

The Maven Dependency Plugin will help, especially the dependency:analyze goal:
dependency:analyze analyzes the dependencies of this project and determines which are: used and declared; used and undeclared; unused and declared.
Another thing that might help to do some cleanup is the Dependency Convergence report from the Maven Project Info Reports Plugin.
Let's use maven-depedency-plugin to find obvious (and easy) stuff to fix:

1. Duplicated Dependencies

mvn dependency:analyze-duplicate
Just remove what's duplicated and that's it.

2. Used and declared; used and undeclared; unused and declared

mvn dependency:analyze
This will take a while and may warn you about a lot of depedency errors.
You will be able to remove dependencies that are not used anymore and add dependencies that are directly used.
Beware: This might show some false-positives.

Bring the big guns

1. Multiple versions of the same depedency

This is the harder, as maven doesn't have a plugin to find them easily.
So, I used a little bit of bash trickery to do the job for me:
mvn dependency:list -Dsort=true |
  grep "^\[INFO\]    " |
  awk '{print $2}' |
  cut -f1-4 -d: |
  sort |
  uniq |
  cut -f1-3 -d: |
  uniq -c |
  grep -v '^ *1 '
Line-by-line, explained version:
mvn dependency:list -Dsort=true | # list all deps
  grep "^\[INFO\]    " |          # grep for the deps list only
  awk '{print $2}' |              # remove the INFO prefix
  cut -f1-4 -d: |                 # removes the dep scope
  sort |                          # sort (duh)
  uniq |                          # remove duplicates
  cut -f1-3 -d: |                 # removes the version
  uniq -c |                       # count line groups
  grep -v '^ *1 '                 # grep groups that repeat
You'll end up with a list like this:
2 com.foo:foo-modules-commons:jar
    3 com.fasterxml.jackson.core:jackson-annotations:jar
    3 com.fasterxml.jackson.core:jackson-core:jar
    2 com.fasterxml.jackson.core:jackson-databind:jar
    2 com.squareup.okio:okio:jar
    2 commons-collections:commons-collections:jar
    2 commons-fileupload:commons-fileupload:jar
    2 joda-time:joda-time:jar
    2 org.mapstruct:mapstruct:jar
Now, for each of these deps, you'll have to run:
mvn dependency:tree -Dincludes=DEP
as in
mvn dependency:tree -Dincludes=org.mapstruct:mapstruct
This will show you all the places this dependency is being used, so all you need to do is fix it.

2. Duplicated Classes

This is something that I have seen a lot in Apache Commons libraries.
As far as I know, the easiest way to find these problems is by using Jboss' Tattletale Maven Plugin.
While the plugin seems to be abandoned, it still works. Just add it to your parent pom and run it to get the reports.
These kind of problems may require manually removing .class files from jars, excluding dependencies or, sometimes, just ignoring some of them.

Preventive actions

Act when the everything went to shit already usually is harder than avoiding small mistakes.
So, my tips are:
  • Give more attention to it in code reviews;
  • Run maven-dependency-plugin and tattletale-maven in the build for every pull request to block problematic changes;
  • You can tweak the script to find dependencies with multiple versions to exit 1 in thoses cases, and just add it to the build too.
That's it. Hope it helps!

Reference:

Apache Maven Dependency Plugin


The dependency plugin provides the capability to manipulate artifacts. It can copy and/or unpack artifacts from local or remote repositories to a specified location.

Goals Overview

The Dependency plugin has several goals:
  • dependency:analyze analyzes the dependencies of this project and determines which are: used and declared; used and undeclared; unused and declared.
  • dependency:analyze-dep-mgt analyzes your projects dependencies and lists mismatches between resolved dependencies and those listed in your dependencyManagement section.
  • dependency:analyze-only is the same as analyze, but is meant to be bound in a pom. It does not fork the build and execute test-compile.
  • dependency:analyze-report analyzes the dependencies of this project and produces a report that summarises which are: used and declared; used and undeclared; unused and declared.
  • dependency:analyze-duplicate analyzes the <dependencies/> and <dependencyManagement/> tags in the pom.xml and determines the duplicate declared dependencies.
  • dependency:build-classpath tells Maven to output the path of the dependencies from the local repository in a classpath format to be used in java -cp. The classpath file may also be attached and installed/deployed along with the main artifact.
  • dependency:copy takes a list of artifacts defined in the plugin configuration section and copies them to a specified location, renaming them or stripping the version if desired. This goal can resolve the artifacts from remote repositories if they don't exist in either the local repository or the reactor.
  • dependency:copy-dependencies takes the list of project direct dependencies and optionally transitive dependencies and copies them to a specified location, stripping the version if desired. This goal can also be run from the command line.
  • dependency:display-ancestors displays all ancestor POMs of the project. This may be useful in a continuous integration system where you want to know all parent poms of the project. This goal can also be run from the command line.
  • dependency:get resolves a single artifact, eventually transitively, from a specified remote repository.
  • dependency:go-offline tells Maven to resolve everything this project is dependent on (dependencies, plugins, reports) in preparation for going offline.
  • dependency:list alias for resolve that lists the dependencies for this project.
  • dependency:list-repositories displays all project dependencies and then lists the repositories used.
  • dependency:properties set a property for each project dependency containing the to the artifact on the file system.
  • dependency:purge-local-repository tells Maven to clear dependency artifact files out of the local repository, and optionally re-resolve them.
  • dependency:resolve tells Maven to resolve all dependencies and displays the version. JAVA 9 NOTE: will display the module name when running with Java 9.
  • dependency:resolve-plugins tells Maven to resolve plugins and their dependencies.
  • dependency:sources tells Maven to resolve all dependencies and their source attachments, and displays the version.
  • dependency:tree displays the dependency tree for this project.
  • dependency:unpack like copy but unpacks.
  • dependency:unpack-dependencies like copy-dependencies but unpacks.

Usage

General instructions on how to use the Dependency Plugin can be found on the usage page. Some more specific use cases are described in the examples given below.
In case you still have questions regarding the plugin's usage, please have a look at the FAQ and feel free to contact the user mailing list. The posts to the mailing list are archived and could already contain the answer to your question as part of an older thread. Hence, it is also worth browsing/searching the mail archive.
If you feel like the plugin is missing a feature or has a defect, you can fill a feature request or bug report in our issue tracker. When creating a new issue, please provide a comprehensive description of your concern. Especially for fixing bugs it is crucial that the developers can reproduce your problem. For this reason, entire debug logs, POMs or most preferably little demo projects attached to the issue are very much appreciated. Of course, patches are welcome, too. Contributors can check out the project from our source repository and will find supplementary information in the guide to helping with Maven

Examples

The following examples show how to use the dependency plugin in more advanced use-cases:

Resources

Here is a link that provides more reference regarding dependencies (i.e. dependency management, transitive dependencies).

Reference :