Maven transitive dependency - Commons Collection version 3 vs 4 - java

I have such configuration in pom.xml:
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
But unfortunately mvn dependency:tree shows me that commons-beanutils has dependency: commons-collections 3.x:
[INFO] +- commons-beanutils:commons-beanutils:jar:1.9.4:compile
[INFO] | \- commons-collections:commons-collections:jar:3.2.2:compile
[INFO] +- org.apache.commons:commons-collections4:jar:4.4:compile
I don't want Common Collection 3 in my code but it's possible that someone uses a class from this library by mistake (instead of Common Collective 4, which is preferred version).
As you see in dependency tree - I can't exclude the Common Collection 3, because it is used (can occur java.lang.NoClassDefFoundError).
Question:
How to protect my code base against pollution of Commons Collection 3 API?

You can use the dependency:analyze-only
https://maven.apache.org/plugins/maven-dependency-plugin/analyze-only-mojo.html
to check whether you use undeclared dependencies, i.e. you use a transitive dependency without explicitly declaring it.
If you don't want this in your build, but just check it occasionally, you can use dependency:analyze from the command line.

Related

Maven Dependency with Parent Pom Not Working Properly

I have the following maven structure.
Parent Pom
<dependencyManagement>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>2.27</version>
</dependency>
</dependencyManagement>
Service Pom
<parent>
<groupId>com.aliseeks.dependencies</groupId>
<artifactId>AliseeksLive</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
</dependency>
</dependencies>
Dependency Conflict:
[INFO] +- org.glassfish.jersey.core:jersey-client:jar:2.27:compile
[INFO] | +- org.glassfish.jersey.core:jersey-common:jar:2.25.1:compile
[INFO] | | +- org.glassfish.jersey.bundles.repackaged:jersey-guava:jar:2.25.1:compile
Why does Maven pull in JerseyCommon 2.25? JerseyClient 2.27 clearly depends on JerseyCommon 2.25? Is this because JerseyClient 2.27 POM has ${project.version} as a variable and its somehow getting messed up with Dependency Management?
Dependency Tree Dump
jersey-client 2.27 version depends on jersey-common 2.27 version as per following link:
https://mvnrepository.com/artifact/org.glassfish.jersey.core/jersey-client/2.27
But it is definitely pulling 2.25 version of jersey-common after resolving transitive dependencies and maven finds 2.25 version as the nearest child. That's reason it decides to pull 2.25 version.
For the reference that such type of dependency conflict issues can be easily investigated with the help of maven-enforcer-plugin. Following link further explains usage of this plugin with example:
https://dzone.com/articles/solving-dependency-conflicts-in-maven

Maven: test vs. transitive compile [duplicate]

This question already has answers here:
Maven dependency within dependency with different scope
(4 answers)
Closed 6 years ago.
Let's say I have a compile dependency on jar A which has a compile dependency on B. Furthermore, my tests have a dependency on B.
When I declare a "test" dependency on B, this seems to override the transitive compile dependency on B, so that I cannot compile my project anymore. If I leave out B, everything works, but it feels strange because I use a transitive dependency directly.
What would be a "Maven-like" solution for this?
EXAMPLE:
If I only have
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.1</version>
</dependency>
then my dependency:list is:
commons-codec:commons-codec:jar:1.2:compile
commons-httpclient:commons-httpclient:jar:3.1:compile
commons-logging:commons-logging:jar:1.0.4:compile
The tree is:
--- maven-dependency-plugin:2.8:tree (default-cli) # testcompile ---
de.continentale.spu:testcompile:jar:0.0.1-SNAPSHOT
\- commons-httpclient:commons-httpclient:jar:3.1:compile
+- commons-logging:commons-logging:jar:1.0.4:compile
\- commons-codec:commons-codec:jar:1.2:compile
If I use
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.0.4</version>
<scope>test</scope>
</dependency>
Then I get the dependency:list
commons-codec:commons-codec:jar:1.2:compile
commons-httpclient:commons-httpclient:jar:3.1:compile
commons-logging:commons-logging:jar:1.0.4:test
The tree is:
--- maven-dependency-plugin:2.8:tree (default-cli) # testcompile ---
de.continentale.spu:testcompile:jar:0.0.1-SNAPSHOT
+- commons-httpclient:commons-httpclient:jar:3.1:compile
| \- commons-codec:commons-codec:jar:1.2:compile
\- commons-logging:commons-logging:jar:1.0.4:test
So commons-logging changed its scope and is not available any more for compilation.
EDIT2: If I use anything from commons-logging in the src/main/java, then the first version compiles while the second does not. The additional test dependency "hides" the transitive compile dependency.
Have not come across such a situation but if I understood your problem which is the version of dependency B required by your tests and required by dependency A would be different. If the versions are same then you would not have face this issue.
In such case, you could use Dependency Scope feature provided by Maven. Let dependency A get the dependency B of its required version and declare a separate dependency B in your pom with a scope test. That means its usage will be restricted to tests only. You can know more here

How can I remove logback from a library's dependency while keeping SLF4J?

In my Vaadin project, I have a dependency on a certain library. This library uses slf4j for logging. In the library pom, logback slf4j binding is added as a runtime dependency.
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
<scope>runtime</scope>
</dependency>
In my application, I directly use log4j for logging. I want the logs added by the library to go in my log4j log.
For this, I added following to my pom to include slf4j log4j binding
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.12</version>
</dependency>
However, slf4j complains that it has found multiple bindings.
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/D:/program_files/apache-tomcat-8.0.24/temp/0-ROOT/WEB-INF/lib/logback-classic-1.0.13.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/D:/program_files/apache-tomcat-8.0.24/temp/0-ROOT/WEB-INF/lib/slf4j-log4j12-1.7.12.jar!/org/slf4j/impl/StaticLoggerBinder.class]
I checked the dependency tree of my application, which has following for its dependency on logback. (Following is the only dependency on logback)
[INFO] | +- com.mycompany.mylib:libname:jar:1.1.0-SNAPSHOT:compile
[INFO] | | +- org.slf4j:jcl-over-slf4j:jar:1.7.5:runtime
[INFO] | | +- ch.qos.logback:logback-classic:jar:1.0.13:runtime
[INFO] | | | \- ch.qos.logback:logback-core:jar:1.0.13:runtime
[INFO] | | +- ch.qos.logback:logback-access:jar:1.0.13:runtime
Also, when I checked inside WEB-INF\lib directory in my war file, I found following jars.
logback-access-1.0.13.jar
logback-classic-1.0.13.jar
logback-core-1.0.13.jar
Why did logback ended up in my lib directory? As I have heard, runtime dependencies should not come into libs directory.
How should I resolve this? The library is developed within my company and I can ask the library developers to remove the logback runtime dependencies if needed.
Option 1: They change their pom.
The easiest way to fix this would be to have the library developers in your own company mark logback as optional and require SLF4J as a compile dependency explicitly. This is the right, canonical way to do SLF4J in Maven. In other words:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
Option 2: You revise their dependencies in your pom.
If you want to fix it yourself, without going through them, then you can use the exclusions tag when declaring their dependency. In other words, in your pom, do:
<dependency>
<groupId>your.company</groupId>
<artifactId>libraryname</artifactId>
<version>${theirlibrary.version}</version>
<exclusions>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</exclusion>
</exclusions>
</dependency>
You asked if there's an reason to depend on Logback directly; generally there isn't, for a library author. Their pom configuration is probably just a minor oversight on their part. There are some reasons to depend on logback specifically, but they have to do with startup (stuff with JoranConfigurator or StatusPrinter, that sort of thing, which shouldn't come up with a library. Other reasons to call Logback classes directly include stuff like custom appenders, which, again, shouldn't come up in a library, only a deployed app.

Using Logback but Log4j started displaying WARN no Appenders

I am using logback for my logging and it has been working however; the other day I started getting a warning
log4j:WARN No appenders could be found for logger (org.apache.axis.i18n.ProjectResourceBundle).
log4j:WARN Please initialize the log4j system properly.
I am not using log4j nor have I ever with this project.
I have a logback.xml in my resources folder.
Any ideas on why this warning started to show up?
You must be using a library that does use log4j. Can you post anything more about your project?
You should probably just put log4j bridge on the classpath. Read more here:
http://www.slf4j.org/legacy.html
The jar you want to look into is log4j-over-slf4j. It will bridge log4j API to actually make calls to your implementation of slf4j API (in your case - logback).
If you are using Maven to build your project then it might be as simple as putting
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.7</version>
</dependency>
in dependencies.
Excluding a library (if needed) would be done in this fashion (this assumes we are talking about the transitive dependency from the jar you've mentioned):
<dependency>
<groupId>org.swift.common</groupId>
<artifactId>jira-soap</artifactId>
<version>4.4.0</version>
<exclusions>
<exclusion>
<groupId>...</groupId>
<artifactId>...</artifactId>
</exclusion>
</exclusions>
</dependency>
Took me some time to find out since the message was log4j:WARN No appenders could be found for logger
I tried to exclude log4j and I tried the log4j-over-slf4j.
Then I ran mvn dependency:tree and finally found out that mye commons-configuration actually was using commons-logging
[INFO] +- commons-configuration:commons-configuration:jar:1.9:compile
[INFO] | \- commons-logging:commons-logging:jar:1.1.1:compile
[INFO] +- ch.qos.logback:logback-classic:jar:1.0.13:compile
[INFO] | +- ch.qos.logback:logback-core:jar:1.0.13:compile
[INFO] | \- org.slf4j:slf4j-api:jar:1.7.5:compile
[INFO] +- org.slf4j:log4j-over-slf4j:jar:1.7.6:compile
[INFO] \- org.apache.commons:commons-lang3:jar:3.1:compile
This became the solution for me.
<!-- logging with logback (and slf4j)-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.13</version>
</dependency>
<!-- had a dep in commons-configuration -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.6</version>
</dependency>

Maven transitive dependecy breaks build

I have a following problem - I am trying to use apache commons-lang version 2.6 in my project (which is defined in the pom.xml) but due to transitive dependency maven always add version 3.2.1 which breaks my build.
Here is relevant part of pom.xml
<properties>
<commons-lang.version>2.6</commons-lang.version>
</properties>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>${commons-lang.version}</version>
</dependency>
and when I try to run mvn dependency:tree I can see the reason is transitive dependency of
+- org.seleniumhq.selenium:selenium-api:jar:2.26.0:compile (version managed from 2.26.0)
+- net.sourceforge.htmlunit:htmlunit:jar:2.10:compile
+- org.apache.commons:commons-lang3:jar:3.1:compile
So I can see the problem but I have really no idea how to fix it. Thanks for any suggestions:-)
Look into using an exclusion tag inside the dependency that's causing the problem. An exclusion tag tells maven that you don't want maven to bring in an indirect dependency and is used in just this situation.
For example, if one of my dependencies A brings in a version 1.0 of B but I want to use version 2.0 of B instead, I could do this:
<dependency>
<groupId>org.mycorp</groupId>
<artifactID>A</artifactID>
<version>4.0</version>
<exclusions>
<exclusion>
<groupId>org.mycorp</groupId>
<artifactId>B</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.mycorp</groupId>
<artifactID>B</artifactID>
<version>2.0</version>
</dependency>
The exclusion tag says to not bring in the indirect dependency (B 1.0) in this case. Note that you don't have to give the version. Once you've told maven not to automatically bring in that version of B, you follow with an explicit dependency that defines which version of B (version 2.0, in this case) you do want.
This particular dependency should not break your build, because Commons Lang 3 uses different package naming than Lang 2. Your code should use the classes from Lang2, while HTMLUnit will use the classes from Lang3, and both JARs can co-exist.
Perhaps you should describe how you think this breaks your build, with relevant extracts from the build output.
My collegue forgot to declare version in dependencyManagement in parent pom so that was it. Thank you for your suggestions!

Categories

Resources