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

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.

Related

Maven dependency convergence error (wrong version used)

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.

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 dependency management being ignored at test time (works at runtime)

I have two dependencies which point to a conflicting version of javax.validation:validation-api. To resolve this, I added a dependencyManagement section with the latest version of the validation-api.
While this causes the app to build correct, and the app works at runtime, during tests, the build breaks at a line in which the validation api is referenced (via a #Valid annotation).
The conflicting dependencies are:
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-core</artifactId>
<version>0.9.2</version>
</dependency>
<dependency>
<groupId>com.google.gwt</groupId>
<artifactId>gwt-user</artifactId>
<version>2.8.0-beta1</version>
<scope>provided</scope>
</dependency>
I've added the following dependencyManagement:
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
This works at runtime and compile time, but fails in tests where I refer #Valid.
Would greatly appreciate any help, as this is driving me nuts.
A possible troubleshooting path is on the scope (provided) defined for gwt-user, which is the only library bringing in validation-api applying the dependencies management above.
Running the following first omitting dropwizard-core
mvn dependency:tree -Dincludes=javax.validation
The output would be:
[INFO] +- com.google.gwt:gwt-user:jar:2.8.0-beta1:provided
[INFO] | +- javax.validation:validation-api:jar:1.1.0.Final:compile
[INFO] | \- javax.validation:validation-api:jar:sources:1.0.0.GA:provided
Taking hence the desired 1.1.0.Final version instead of the transitive 1.0.0.GA one.
(Note the scopes above, the last token per dependency entry, they should all be on provided because its root, gwt-user is on provided scope. However, validation-api is on compile because of the dependency management entry).
While omitting gwt-user the output would be:
[INFO] com.sample:sample2:jar:0.0.1-SNAPSHOT
[INFO] \- io.dropwizard:dropwizard-core:jar:0.9.2:compile
[INFO] \- io.dropwizard:dropwizard-validation:jar:0.9.2:compile
[INFO] \- org.hibernate:hibernate-validator:jar:5.2.2.Final:compile
[INFO] \- javax.validation:validation-api:jar:1.1.0.Final:compile
Hence, indeed, both would bring it in, but the first was defined as provided scope while the second as default (compile) scope.
Moreover, the dependency management for validation-api has been defined for default (compile) scope, impacting how Maven is bringing it in via gwt-user (listed above, as compile, even though gwt-user is provided and all of its transitive dependencies would also be on provided, unless defined differently by dependencies management, like in this case).
Provided dependencies, per official Maven documentation are
available on the compilation and test classpath
Hence you are having during compilation and test two libraries in conjuction, validation-api and gwt-user, which would then have different coupling at runtime (gwt-user is expected to be provided by the runtime container).
If gwt-user must be in provided scope, I would suggest to adopt a finer governance and exclude validation-api directly from gwt-user. As such, validation-api will be brought in from dropwizard-core with the version required by the dependencies management.
<dependencyManagement>
<dependencies>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-core</artifactId>
<version>0.9.2</version>
</dependency>
<dependency>
<groupId>com.google.gwt</groupId>
<artifactId>gwt-user</artifactId>
<version>2.8.0-beta1</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
Update
From your comment below, you are also using the following as part of your dependencies management:
<dependency>
<groupId>com.google.gwt</groupId>
<artifactId>gwt</artifactId>
<version>2.8.0-beta1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
Which is then also specifying the following in its dependencies management:
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
<!-- Note: use classifier=sources rather than type=java-sources so they're added to the classpath -->
<classifier>sources</classifier>
</dependency>
While you managed to override the first, you didn't override the second and indeed it is still added to the classpath (check dependency tree output above, the 1.0.0.GA sources are still brought in, in provided scope. That's a further difference between compiler/test and runtime. At runtime, you don't have it as part of your classpath.
I would hence suggest to also add to your dependencies management its 1.1.0.Final version as overriden, so that it would be coherent with the first one.
You have to excluded the dependency from one or another like this :
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-core</artifactId>
<version>0.9.2</version>
</dependency>
<dependency>
<groupId>com.google.gwt</groupId>
<artifactId>gwt-user</artifactId>
<version>2.8.0-beta1</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</exclusion>
</exclusions>
</dependenc

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 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