Maven dependency convergence error (wrong version used) - java

I've got this strange problem and I have no clue what's the problem.
I have a multi-module java maven project with a structure like this:
+ A (parent)
+-+-B
| +--C
| +--D
I added a dependency in the parent pom (A):
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>1.8.5</version>
<exclusions>
<exclusion>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
</exclusion>
<exclusion>
<groupId>com.mchange</groupId>
<artifactId>mchange-commons-java</artifactId>
</exclusion>
</exclusions>
</dependency>
Note: Version 1.8.5 is necessary for other modules beside A
In modules C and D it is necessary to use version 2.3.0, so i override version 1.8.5 from parent pom A with this dependency:
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
<exclusions>
<exclusion>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
</exclusion>
<exclusion>
<groupId>com.mchange</groupId>
<artifactId>mchange-commons-java</artifactId>
</exclusion>
</exclusions>
</dependency>
I added this dependecy in the poms of module C and D. C is also a dependency of D. So when i try to build the project, i get following error:
[WARNING] Rule 1: org.apache.maven.plugins.enforcer.DependencyConvergence failed with message:
Failed while enforcing releasability the error(s) are [
Dependency convergence error for org.quartz-scheduler:quartz:1.8.5 paths to dependency are:
+-de.xxx.xxx.xxx:module-D:6.40.1-jat-SNAPSHOT
+-de.xxx.xxx.xxx:module-C:6.40.1-jat-SNAPSHOT
+-org.quartz-scheduler:quartz:1.8.5
and
+-de.xxx.xxx.xxx:module-D:6.40.1-jat-SNAPSHOT
+-org.quartz-scheduler:quartz:2.3.0
So somehow maven thinks the version of quartz of Module C is 1.8.5 but i explicitly set the version in the pom of module C to 2.3.0
also when i run mvn dependency:tree -Dverbose in the directory of module C, it seems correct:
[INFO] +- org.quartz-scheduler:quartz:jar:2.3.0:compile
Does anybody has any idea?

Module A (the parent) must have a packaging type of pom.
In general it is a bad idea to declare dependencies in parent poms because it forces all child modules to have these specific dependencies whether they are needed or not. It is the equivalent of declaring these dependencies in every child module.
When modules C and D come into play you then have the equivalent of duplicate dependency declarations with conflicting versions.
Instead, module A should use a <dependencyManagement> section to declare dependency versions without forcing every child module to actually be dependent upon them.
In other words:
<dependencyManagement>
<dependencies>
...
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>1.8.5</version>
<exclusions>
<exclusion>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
</exclusion>
<exclusion>
<groupId>com.mchange</groupId>
<artifactId>mchange-commons-java</artifactId>
</exclusion>
</exclusions>
</dependency>
...
</dependencies>
</dependencyManagement>
The quartz dependency declarations in modules C and D will then simply override the version specified in the parent A.
Other modules that are dependent upon the managed quartz library will still need to explicitly declare it:
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
</dependency>
where the version and exclusions will be picked up from the managed dependency declaration.

Check dependency hierarchy of module C and exclude the version 1.8.5 (with modern IDE like eclipse, this should be easy).
Or you may remove this dependency from parent pom, use it in dependency management, and in child module, indicate the version you'd like to use. Dependencies in parent pom will be inherited by all the children.

Related

Exclusions in dependency management using wildcard

I have a project A which creates 10 artifacts with same group id . For example generated artifacts from the project A will be -
<groupId>com.example.abc</groupId>
<artifactId>A1</artifactId>
<version>v1</version>
<groupId>com.example.abc</groupId>
<artifactId>A2</artifactId>
<version>v2</version>
Likewise from A1 to A10 and v1 to v10 . Group Id remains same.
The generated artifacts needs to be used in another project B but I need to exclude two dependencies which are common to all the ten artifacts generated by Project A.
I know I can add dependency management tag in Project B's pom.xml with explicit exclusions tag.
What I am looking for is a less verbose way of excluding those two dependencies ? I tried with
<dependencyManagement>
<dependency>
<groupId>com.example.abc</groupId>
<artifactId>*</artifactId>
<version>*</version>
<exclusions>
<exclusion>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencyManagement>
which is not working .
Is there any less verbose way ?
could you declare the mockito dependency as a one provide.
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>YOUR VERSION</version>
<scope>provided</scope>
</dependency>
Note: The dependency won't end up inside the build artifact but it's still available during tests
Take a look at here too.
Regards.

How do I resolve a dependency conflict in Maven?

I have a fairly large legacy project that I'm adding a component to. This component uses HtmlUnit. I can compile it ok with Maven but when I run it I get:
java.lang.NoSuchMethodError:
org.apache.http.conn.ssl.SSLConnectionSocketFactory.<init>
(Ljavax/net/ssl/SSLContext;[Ljava/lang/String;[Ljava/lang/String;Ljavax/net/ssl/HostnameVerifier;)
So it's missing the correct constructor. I think this is almost certainly a version conflict in httpclient but I'm not sure how to resolve it. Here's the relevant part of my pom.xml (note all the games I've been trying to play with exclusions and dependency management):
<dependencies>
<dependency>
<groupId>com.mycompany.mine</groupId>
<artifactId>my-base-project</artifactId>
<version>${project.version}</version>
<exclusions>
<exclusion>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.mycompany</groupId>
<artifactId>base-project</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.2</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.2</version>
</dependency>
</dependencies>
</dependencyManagement>
Any ideas?
Edit: it's been suggested that this question is a duplicate of this one, but it's not since the dependency type in this case is not war.
In order to identify conflicting dependecies, use mvn dependency:tree. I like to pipe it to a text file for ease of use:
mvn dependency:tree > tree.txt
Then, use your favorite text editor to look for multiple versions of a depedency.
Alternatively, if you are looking for a specific groupId or artifactId, use the -Dincludes flag:
mvn dependency:tree -Dincludes=<groupId>:<artifactId>:<version>:<packaging>
mvn dependency:tree -Dincludes=org.springframework <<< get all dependencies with by groupId
mvn dependency:tree -Dincludes=:spring-web <<< get all dependencies by artifactId
You might also want to add the -Dverbose flag here.
To resolve dependency conflicts, there are two ways:
1) Exclude the one you don't want
<depdency>
<groupId>some.stuff</groupId>
<artifactId>with.transitive.depdency</artifactId>
<exclusions>
<exclusion>
<groupId>something</groupId>
<artifactId>unwanted</artifactId>
<exclusion>
<exclusions>
<depdency>
With this way, you will have to exclude on every dependency that brings in a transitive one. For this reason I like the other one better.
2) Explicitly add the version you want
<dependency>
<groupId>something</groupId>
<artifactId>with.version.conflict</artifactId>
<version>what I want</version>
</dependency>
This will make sure that any transitive dependency will be swapped with this exact version. This might also lead to errors though, if some framework actually needs an older version. For using this strategy safely, your dependencies will need to be fairly close to the newest available version (or versions released at the same time).

Maven cannot load dependency from another dependency in repository

I am new to Maven, I try to load tranquility using the following in pom.xml:
<dependency>
<groupId>io.druid</groupId>
<artifactId>tranquility-core_2.11</artifactId>
<version>0.7.0</version>
</dependency>
Then I get an error: Failure to find com.fasterxml.jackson-module-scala_2.11:jar:2.4.6.
I tried to search in search.maven.org, and find out that, inside the io.druid tranquility-core module, the dependency is:
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-scala_2.11</artifactId>
<version>2.4.6</version>
</dependency>
However, when I try to search Jackson-module-scala_2.11 in the central repository, there's no 2.4.6 version there, there's only 2.4.5 and 2.5.0. See the following link: http://search.maven.org/#search|gav|1|g%3A%22com.fasterxml.jackson.module%22%20AND%20a%3A%22jackson-module-scala_2.11%22
So is there a way for me to build it successfully even the module in central repository (in this case the tranquility module) makes mistake in referencing another module?
Thanks.
you can exclude the dependency in the io.druid tranquility, and dependency it by yourself with the exist version like this:
<dependency>
<groupId>io.druid</groupId>
<artifactId>tranquility-core_2.11</artifactId>
<version>0.7.0</version>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-scala_2.11</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-scala_2.11</artifactId>
<version>2.5.0</version>
</dependency>

How to exclude a dependency from parent's project in Maven?

For example, I have 2 Maven projects. One is "project-parent". The other is "project-child". Obviously, "project-child" is the sub project of "project-parent".
"project-parent" has a dependency of log4j. But I want to exclude it from the "project-child". Is there a way?
You might say I should move log4j from "project-parent" to "project-child". That is totally correct. But the assumption is I CANNOT modify "project-parent"'s POM.
Thanks in advance.
I think in Maven2 there is no way to achieve this, because this is what POM inheritance is for
. However there is one trick that I can think of:
Assume you have the right to upload artifact to your internal artifact repository. You may create an empty JAR, deploy it as log4j:log4j, with a obviously abnormal version (e.g. log4j:log4j:9999 ). Add such dependency in your project-child. Then it will override the dependency of parent to depends on a in-fact-empty JAR.
I don't know of a way of actually excluding a dependency, but you can exclude it from the target distribution, but it's a bit of a hack. You need to change the scope of the dependency to something that you can exclude in the final distribution.
So, say that my parent had a dependency on junit 4.8, in my pom you say:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8</version>
<scope>provided</scope>
</dependency>
So we're changing the scope to provided. For an explanation of how this works, see my answer to NoClassDefFoundError: org/junit/AfterClass during annotation processing. Unfortunately, this doesn't affect the build, but when you're copying the dependencies for the final distribution, you can use the excludeScope configuration element to not copy the dependency into the final distribution:
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-libs</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
<excludeScope>provided</excludeScope>
</configuration>
</execution>
I have met the same question just like you.
In my project, let call the parent pom is parent.pom. parent defined the log4j, slf4j like this:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j-api.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${slf4j-api.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j-log4j12.version}</version>
</dependency>
child project invoke some dependency in child.pom. But I don't want the log4j-1.2.x dependency and want to increase the version of slf4j.
So. I add the dependency of parent
<dependency>
<groupId>parent</groupId>
<artifactId>myartifactId</artifactId>
<version>${my parent version}</version>
</dependency>
and use exclusions to remove the log4j
<dependency>
<groupId>parent</groupId>
<artifactId>myartifactId</artifactId>
<version>${my parent version}</version>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
and explicitly add the slf4j and log4j2's dependency in child pom
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.6</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.3.4</version>
</dependency>
then use mvn dependency:tree to show the dependency list, still see the log4j
[INFO] +- org.apache.kafka:kafka_2.10:jar:0.8.2.0:compile
[INFO] | +- com.yammer.metrics:metrics-core:jar:2.2.0:compile
[INFO] | +- org.scala-lang:scala-library:jar:2.10.4:compile
[INFO] | +- org.apache.zookeeper:zookeeper:jar:3.4.6:compile
[INFO] | | +- org.slf4j:slf4j-log4j12:jar:1.7.5:compile
[INFO] | | +- log4j:log4j:jar:1.2.17:compile
well, let's add the exclusions on that dependency...remove this guy.
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>0.10.1.1</version>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
then run the command again to check the dependency list. OK! clear~
Hope that can help you :>
If I understand the question, what you need is something like the following. It pulls in a dependency and excludes that dependency from adding to its dependency list. Often this is used if you want to inject a newer version of a package instead of the one referenced in the other package.
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.15</version>
<exclusions>
<exclusion>
<groupId>com.sun.jmx</groupId>
<artifactId>jmxri</artifactId>
</exclusion>
...
</exclusions>
...
If you are instead talking about a <parent> relationship then I'm not sure there is a way to do this. Can you switch from being a <parent> to a <dependency>?
One hacky way to accomplish this is to specify the dependency in project-child but with 'test' scope (or whatever lightest-weight scope is available). This will "hide" the scope specified in project-parent so that it is available only to test code, and unavailable to non-test code at both compile and runtime.
I came across this bug-feature mostly by mistake. In my case, my project-child had a project-sibling with a 'compile' scope dependency, while project-parent had the same dependency specified (actually inherited from a grandparent) with 'provided' scope. project-child was an executable however that depended on project-sibling, and so a NoClassDefFoundError was thrown at runtime from project-sibling since project-child's runtime classpath was being used, which didn't include the 'provided' dependency. I fixed this by moving the 'compile' dependency from project-sibling to project-parent so that the 'compile' would "hide" the 'provided'.
I have managed to exclude a transient dependency from a parent pom. By re-importing it in <dependencymanagement> and then setting exclusion por the dependency I wanted out. like this eg.:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring-boot.version}</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
find out actual base package for your unwanted dependency
mvn dependency:tree
Analyse this output and find out actual base package for your unwanted dependency
now add dependency of base package again in child pom and use exclude tag
<dependency>
<groupId>base group id</groupId>
<artifactId>base artifact id</artifactId>
<version>version</version>
<exclusions>
<exclusion>
<groupId>your unwanted dependency group id</groupId>
<artifactId>your unwanted dependency artifact id</artifactId>
</exclusion>
</exclusions>
</dependency>
check again
mvn dependency:tree
unwanted dependency should be removed.

How to exclude cyclic deprecated dependencies in maven without killing the dependencies completely?

I have a case that I have the following cyclic dependencies in maven:
JAR A version 1.1 depends on JAR B version 1.0
JAR B version 1.1 depends on JAR A version 1.0
For some reason that I don't know, Maven brings all the 4 JARs: A 1.0, A 1.1, B 1.0 and B 1.1, which results in a classpath conflict.
This really sucks. I already ask the developers of both JARs to fix this, however I can't simply sit and wait for the day that they decide to fix this.
I tried this:
<dependency>
<groupId>groupA</groupId>
<artifactId>artifactA</artifactId>
<version>1.1</version>
<type>pom</type>
<scope>compile</scope>
<exclusions>
<exclusion>
<groupId>groupB</groupId>
<artifactId>artifactB</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>groupB</groupId>
<artifactId>artifactB</artifactId>
<version>1.1</version>
<type>pom</type>
<scope>compile</scope>
<exclusions>
<exclusion>
<groupId>groupA</groupId>
<artifactId>artifactA</artifactId>
</exclusion>
</exclusions>
</dependency>
The result is that maven excludes all of the JARs as if none dependency were added, and the project does not compiles because there are missing classes.
So, other than just asking both JARs developers to solve this, what can I do? How can I import both the new dependencies while leaving out both the old ones?
Pragmatic solution would be to redeclare the unwanted dependencies as provided, for example:
<dependency>
<groupId>groupA</groupId>
<artifactId>artifactA</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
I'm not particularly fond of using provided in such manner, since it leaves the dependency in the compile time and could lead to unwanted compile dependencies, but I see no other way in your case ;(.

Categories