How to remove duplicate dependencies from pom? - java

I have a multi-module project in maven where some of the modules depend on other modules. Now the modules that act as dependencies have some of the dependencies which are already listed in the dependent module's pom.
Is there a quick way to identify such duplicate dependencies and remove them from the dependent module's pom?

A project's dependency tree can be expanded to display dependency conflicts.
Use command
mvn dependency:tree -Dverbose=true
to identify such duplicate dependencies. It shows all duplicates and conflicts in the pom.
Use the <exclusions> tag under the <dependency> section of the pom to exclude such duplicate dependencies.
<dependencies>
<dependency>
<groupId>sample.ProjectA</groupId>
<artifactId>Project-A</artifactId>
<version>1.0</version>
<scope>compile</scope>
<exclusions>
<exclusion> <!-- declare the exclusion here -->
<groupId>sample.ProjectB</groupId>
<artifactId>Project-B</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>

If you are using eclipse as IDE then the duplicates can be seen in the dependency hierarchy of the concerned pom.xml. And using exclusions tag they can be ommitted.

You can use mvn depgraph:graph -Dincludes=ets.tkt -DshowDuplicates -Dscope:compile.
To use this plugin put this on your settings.xml
<settings>
. . .
<pluginGroups>
<pluginGroup>com.github.ferstl</pluginGroup>
</pluginGroups>
</settings>
When you run the previous console command, you can go to /target and you will find a .dot file. you can render this file using graphviz.
More info at https://github.com/ferstl/depgraph-maven-plugin

There is a JBoss tool to help with these issues: http://tattletale.jboss.org/
Unfortunately, it seems that is not under active development these days.

Related

Dependency of Type "POM"

I am trying to get an ESB system running using ServiceMix and ActiveMQ. But even before I get that far, I had a question about dependency types of POM. I got the maven dependency as:
<!-- https://mvnrepository.com/artifact/org.apache.servicemix/servicemix -->
<dependency>
<groupId>org.apache.servicemix</groupId>
<artifactId>servicemix</artifactId>
<version>7.0.1</version>
<type>pom</type>
</dependency>
Now when I run "clean install" on the the project in which I included this dependency, I don't see any of the activeMQ jars being copied in the classpath or available for compilation (I have copy-dependency written, so I can see what jar files are included). In this case, do I still have to explicitly mention the activeMQ dependency in my pom file? Like:
<!-- https://mvnrepository.com/artifact/org.apache.activemq/activemq-core -->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-core</artifactId>
</dependency>
Any guidance would be appreciated. This ServiceMix is frustrating with the lack of documentation.
If you put a dependency of type pom into your <dependencies>, Maven will use the content of the POM as transitive dependencies. So everything in that POM will become a part of the classpath unless it has something like test scope or its version is overridden by some other part of the POM.
Putting a POM into the <dependencyManagement> is a different thing. Note that scope import is only for <dependencyManagement>.

Optional system dependency causes "invalid, transitive dependencies (if any) will not be available"

I have a main project and a commons project which has a dependency that is not in any repository and needs to be included using <scope>system</scope>.
Because the system dependency is defined using a relative url using a maven property ${project.basedir}, I made it <optional>true</optional> so that it doesn't bother other projects and so depending projects need to redefine this dependency with the correct path.
Commons pom.xml:
<dependency>
<groupId>thirdparty-group</groupId>
<artifactId>artifact</artifactId>
<version>1.4.2</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/dependency.jar</systemPath>
<optional>true</optional>
</dependency>
In main project's pom:
<dependency>
<groupId>my-group</groupId>
<artifactId>commons</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>thirdparty-group</groupId>
<artifactId>artifact</artifactId>
<version>1.4.2</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/dependency.jar</systemPath>
</dependency>
Obviously dependency.jar is in both the commons and main project.
The commons jar installs properly without a hitch. But when used in the main project however, the result is:
[WARNING] The POM for my-group:commons:jar:1.0-SNAPSHOT is invalid, transitive dependencies (if any) will not be available: 1 problem was encountered while building the effective model for my-group:commons:1.0-SNAPSHOT
[ERROR] 'dependencies.dependency.systemPath' for thirdparty-group:artifact:jar must specify an absolute path but is ${project.basedir}/lib/dependency.jar #
The build continues, but transitive runtime dependencies are now excluded breaking the application.
Why is maven complaining about a dependency from commons that is not even relevant to the main project (as it is optional, why is it even included as transitive dependency)?
How to work around this problem?
Putting the system dependency in the repo is not an option unfortunately.
Going by #PascalThivent's excellent answer, I worked around the issue by defining a local repo inside the commons project, and then changing the scope to compile with <optional>true</optional>.
In commons pom.xml:
<dependencies>
<dependency>
<groupId>thirdparty-group</groupId>
<artifactId>artifact</artifactId>
<version>0.0.1</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
</dependencies>
<repositories>
<repository>
<id>local-repo</id>
<url>file://${basedir}/lib/local-repo</url>
</repository>
</repositories>
To install the library:
mvn org.apache.maven.plugins:maven-install-plugin:2.3.1:install-file \
-Dfile=./lib/dependency.jar \
-DgroupId=thirdparty-group \
-DartifactId=artifact \
-Dversion=0.0.1 \
-Dpackaging=jar \
-DlocalRepositoryPath=.\lib\local-repo
I can just commit this to the VCS directly so other developers don't have to do this every time on checkout.
In the main project's pom.xml nothing changes. It can still use its own system dependency, or go this approach as well. Both approaches work for the WAR file that comes out there...

What is the difference between the GeoIP2 MaxMind pom and my local one?

I'm following this guide:
https://github.com/maxmind/GeoIP2-java
It says:
We recommend installing this package with Maven. To do this, add the dependency to your pom.xml:
<dependency>
<groupId>com.maxmind.geoip2</groupId>
<artifactId>geoip2</artifactId>
<version>2.2.0</version>
</dependency>
There is also pom.xml file in the Git repository of GeoIP2 which is much longer - what is the difference between them?
Cited from the official homepage:
Apache Maven is a software project management and comprehension tool. Based on the concept of a project object model (POM), Maven can manage a project's build, reporting and documentation from a central piece of information.
Think of the pom.xml as the heart of Maven. In the file you can specify dependencies (most typically jar files), and other information, such as how the project should be built. Without digging to deep into this, one of Maven's strengths is that it manages the dependencies of projects.
To answer your concrete question, GeoIP2 manages its dependencies using Maven. This section of its pom.xml defines them:
<dependencies>
<dependency>
<groupId>com.maxmind.db</groupId>
<artifactId>maxmind-db</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.http-client</groupId>
<artifactId>google-http-client</artifactId>
<version>1.20.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.3</version>
</dependency>
</dependencies>
By using Maven in your own project, you will only need to add the one dependency to GeoIP2. Maven will then search for the dependency in a repository, typically the Maven Central Repository if Maven isn't configured to use another. It will also automatically download all other needed dependencies (transitive dependencies), in this case it would be the dependencies listed above, plus any other dependencies those in turn depend on, and so on.
So, a short recap: Without a dependency management tool like Maven, you would need to manually make sure you have all the correct dependencies on the classpath. Maven fixes this for you.

How to add maven dependencies for mahout and hadoop?

I am doing a project that has dependencies on some classes from the mahout and hadoop core jars. I was using javac with the classpath option to include them before, but someone suggested to me that I should use maven to build my project instead. However, I am not sure how to add the dependencies to these jar files which are located in my /usr/local directory.
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-core</artifactId>
<version>0.20.205.0</version> <!-- or whatever version -->
</dependency>
<dependency>
<groupId>org.apache.mahout</groupId>
<artifactId>mahout-core</artifactId>
<version>0.5</version>
</dependency>
Add this to your pom:
<dependencies>
<dependency>
<groupId>org.apache.mahout</groupId>
<artifactId>mahout-core</artifactId>
<version>0.5</version>
</dependency>
<dependency>
<groupId>some.group</groupId>
<artifactId>hadoop</artifactId>
<version>some.version</version>
</dependency>
</dependencies>
If you have a copy of the jar to be used for say the hadoop example above, execute this command:
mvn install:install-file -Dfile=/some/path/my-hadoop.jar -DgroupId=some.group -DartifactId=hadoop -Dversion=some.version -Dpackaging=jar
Have a look at the maven documentation, especially the part on dependency management. If you want to use Maven you should get to know the basics (one of which is dependency management).
Basially you define your project's dependencies in the <dependencies> section of your pom. Look up maven central (the most common online repository) for the dependencies you want or search for other online repositories that might contain them.
If you can't find them, add the dependencies you want anyways (think of a sensible group id, artifact id and version) and try to compile. Maven will complain about the dependencies missing and provide a basic command to put those dependencies into the local repository. Copy those commands and fill in the appropriate path to the jar file and maven will deploy that dependency in your local repository.
Note that you should first look for the dependencies in an online repository since otherwise you'd have to manually deploy each new version in your local repo.

Clean up Maven dependency management

In a big Maven 2 project it is nice to have the dependency management to make sure that only one version of a dependency is used in the whole system. That makes the system consistent.
But when I generate effective POMs I have no chance to see where the dependency versions came from. Likewise in a POM at the top of the hierarchy I have no idea where in the child POMs the defined versions of the dependency management section are really used.
So how do I keep the dependency management cleaned up? When I remove a dependency in one project, I always check in all other projects if it is still needed at all, so that I can also remove in from the dependency management at the top?
Also, how do I build up the dependency management, making sure it is not duplicated somewhere in the child POMs? When I add dependencies I always check all other projects to see if it possibly could be aggregated on top in the dependency management? Or would you just always move all dependency versions to the top from the beginning so they are always in only one place?
Thanks for any thoughts.
You could create one or more boms (bill of materials) for your project. These pom.xmls will declare all the dependencies used in your project within dependencyManagement section.
In each child pom, you would import these boms and use those dependencies that are required for the project.
In this way, dependency versions are managed centrally, while at the same time, each child pom uses only those dependencies that it needs.
See Importing Managed Dependencies
BOM project
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>my.group</groupId>
<artifactId>My-Project-Bom</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.7.0</version>
</dependency>
...
</dependencies>
</dependencyManagement>
</project>
Child project
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>my.group</groupId>
<artifactId>child1</artifactId>
<packaging>jar</packaging>
<name>Child1</name>
<version>1.0</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>my.group</groupId>
<artifactId>My-Project-BOM</artifactId>
<version>1.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
</dependency>
</dependencies>
...
</project>
maven dependency plugin has a few goals to help you get the dependency hierarchy.
mvn dependency:list
mvn dependency:tree
mvn dependency:analyze
If you are using eclipse, the m2eclipe plugin allows you to view the Dependency Hierarchy for you pom. This can be very useful when trying to determine where dependencies are brought into your project and where conflicts are occurring.
You should explicitly declare the dependencies in the projects in which they are used, unless it is being used in ALL of the projects. If, for example, Spring is used for all of your projects, then put that in the parent POM. If it is only used in some projects, declare it in each one and put a spring.version property in the parent which each child pom can use for its version.
Moving all dependencies to the parent removes the responsibility from each project to manage its own dependencies. I would consider this a misuse of maven as it makes things more difficult to maintain instead of easier. It now adds dependencies to projects that doesn't need them. Often the scope of a dependency is different for projects as well, and you cannot manage that unless you declare your dependencies locally.
You can get the POM to POM dependencies, and the code-references that cause them, using the Structure101 composition perspective. Create a new s101 project, type Maven, specify the root pom.xml file, finish (use defaults for the rest of the wizard), then select the composition perspective (2nd button down on the vertical toolbar top left of the UI) and you will see something like this:

Categories