Dependencies version conflict -> different slf4j versions but can't exclude wrong one - java

I've got almost same question like was asked here:
Maven + SLF4J: Version conflict when using two different dependencies that require two different SLF4J versions
(but unfortunately all answers didn't help our case)
Case:
I need to include firebase dependency
<dependency>
<groupId>com.google.firebase</groupId>
<artifactId>firebase-admin</artifactId>
<version>5.11.0</version>
</dependency>
Which depends on slf4j version 1.7.25.
Afterwards we implemented some integration test (using spring and junit) and now we're facing the clash
SLF4J: The requested version 1.5.6 by your slf4j binding is not compatible with [1.6, 1.7]
SLF4J: See http://www.slf4j.org/codes.html#version_mismatch for further details.
But when I run " mvn dependency:tree" I don't see any other dependency on slf4j. So it's clearly something "outside" of the project.
I'm also unable to just exclude the slf4j from firebase because it's mandatory and I'm unable to use it without it.
Is there any chance how to check where the dependency comes from or how to exclude the older version (in case that it's gonna work with the newer one)?

Important rule: Declaring a dependency on an artifact A, say version v, in a local project P overrides other declarations made by the dependencies of P on A. Your project will have A version v imported, regardless the version(s) declared by your other dependencies for A.
In your case, declare explicitly the dependencies you want for slf4j-api as well as the desired binding.
See also Introduction to the Dependency Mechanism which states:
Dependency mediation - this determines what version of a dependency
will be used when multiple versions of an artifact are encountered.
Currently, Maven 2.0 only supports using the "nearest definition"
which means that it will use the version of the closest dependency to
your project in the tree of dependencies. You can always guarantee a
version by declaring it explicitly in your project's POM

Related

how to choose between versions in build.gradle

if you have a build.gradle file with the line implementation 'org.springframework.boot:spring-boot-starter-web', how do you choose the version of the jar it downloads so that you get the latest one? I've seen a project where it is a 2.2.4 release, but in another project I've seen the same line with a 2.2.5 release.
Since you dropped the name Spring Boot, I assume the project has been generated Spring Initializr. A project generated with the Initializr has two plugins applied:
org.springframework.boot (Reference Documentation)
io.spring.dependency-management (Reference Documentation)
io.spring.dependency-management is Spring's opinionated way to provide Maven-like dependency management to Gradle builds. It allows to declare dependency versions once and then omit the version when declaring the actual dependency.
The org.springframework.boot plugin does the following:
When you apply the io.spring.dependency-management plugin, Spring Boot’s plugin will automatically import the spring-boot-dependencies bom from the version of Spring Boot that you are using. This provides a similar dependency management experience to the one that’s enjoyed by Maven users. For example, it allows you to omit version numbers when declaring dependencies that are managed in the bom. To make use of this functionality, simply declare dependencies in the usual way but omit the version number.
(From: Managing Dependencies)
What does that mean in practice?
When you generate a project for Spring Boot 2.1.14, your build.gradle will look similar to this:
plugins {
id 'org.springframework.boot' version '2.1.14.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
}
The org.springframework.boot plugin instructs the io.spring.dependency-management to apply the bill of materials (BOM) of Spring Boot 2.1.14. The BOM declares the following version for spring-boot-starter-web:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.14.RELEASE</version>
</dependency>
(From: Maven Central)
And this combination allows to declare the dependency to spring-boot-starter-web in the build.gradle without providing an actual version:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
}
If you would change the version of the org.springframework.boot Gradle plugin, then a different version that matches the Spring Boot versions would be applied.
You may ask, why this tremendous effort?
We take an opinionated view of the Spring platform and third-party libraries so you can get started with minimum fuss.
That's why.
One possible solution is to use lockfiles and a version of +, or a combination of major.minor.+ or major.+
implementation 'org.springframework.boot:spring-boot-starter-web:+'
For more information on dependency locking: https://docs.gradle.org/current/userguide/dependency_locking.html
Another approach, and one I'm quite pleased with where available, is using a bill of materials, which specifies versions for a lot of dependencies, by introducing constraints. So where a dependency is used, with no version specified, as in your example, it will get the version the BOM brings in. So for the dependency below, if it is present in the BOM, it will match
implementation 'org.springframework.boot:spring-boot-starter-web
You're also free to override versions manually, by still specifying the version, should you choose to. And a BOM is like any other dependency, so you can use a mixture of lockfiles and BOMs.
Here's gradle documentation on bill of materials: https://docs.gradle.org/current/userguide/platforms.html

How to depend on log4j in a Sonarqube plugin?

I am developing a SonarQube 5.6 plugin. This plugin depends on a library X (a third party library). Library X depends on Log4J. To resolve the dependency problem I am adding log4j dependency in pom.xml with provided scope as defined in SonarQube documentation. But at runtime I am getting class not found exceptions related to log4j.
When I change my third party library, so that it does not use log4j anymore (basically log4j related code is commented out), the problem is gone.
How should I add the log4j dependency in SonarQube or what should I do so that the problem with log4j is resolved in my SonarQube plugin? Or what is the best way to deal with such problem when container says it will provide the dependency but it is not?
The dependency is declared like this:
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.8.2</version>
</dependency>
You should exclude any log4j dependency, and instead rely on log4j-over-slf4j to redirect any log4j call to slf4j.
log4j-over-slf4j should be already provided in the runtime plugin classloader, so basically it should work out of the box. What kind of problem are you facing?

Maven: how to handle conflicting dependencies

I want (have to) to use two maven dependencies "Y" and "G" next to each other, while they share the same dependency with different versions.
By default behavior, the older version will be overwritten by the newer one. However this would ruin the performance.
How can I isolate the two dependencies and make sure they use their proper dependency?
By default behavior, the older version will be overwritten by the newer one.
This is not true. By default, Maven uses the nearest definition strategy when resolving conflicting versions of the same dependency. It does not necessarily take the new version. This means that the version declared by a POM at a higher level in the dependency tree overrides versions declared in lower level POMs. In case the two versions are on the same level, as is the case in your diagram, the one declared first is taken.
The best way to force a specific version of the dependency is to explicitly define a dependency in the parent POM inside a dependencyManagement section:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>groupId-z</groupId>
<artifactId>dependency-z</artifactId>
<version>1.0</version>
</dependency>
...
Then all of the child projects and their dependencies will automatically use the specified version.
For more details on dependency conflict resolution in Maven, see this page.
Assuming that "X" is one module which has dependencies on both Y and G, then the answer depends on how it is being run. For example: if X is a WAR containing Y and G, then you won't be able to have two different versions of the same dependency or you'll run into class loading issues.
Could you not therefore explicitly declare the correct version for Y and G in their own POMs for when they are used independently, but override it to the necessary shared version when they are being used side by side?

Is it possible to have a Maven library that inherits a dependency minor version from the enclosing project?

I want to build a library as a Maven project that depends on some Spring libraries (version 3).
I want this to be used in projects that are also using Spring 3 - but I don't want the versions to clash, otherwise we'll have both versions of the spring libraries on the classpath.
I want to get the minor version for my library pom.xml from the enclosing project.
My question is: Is it possible to have a Maven library that inherits a dependency minor version from the enclosing project?
I believe you are worrying about something that is not going to happen. Conflicting versions between different dependencies on the same artifact will be resolved by a process called dependency mediation, and Maven will not pull in multiple versions of the same artifact onto the same classpath. E.g., if you make your library your-group:your-library:1.0 depend on org.springframework:spring-context:3.2.4.RELEASE, and my project my-group:my-artifact:1.0 depends on both org.springframework:spring-context:3.1.4.RELEASE and your-group:your-library:1.0, then Maven will only pull version 3.1.4 of spring-context into my build, not both 3.1.4 and 3.2.4. If your library also depends spring-beans:3.2.4 and there happens to exist some incompatibility between spring-context:3.1.4 and spring-beans:3.2.4, then you can consider it the responsibility of my project to add spring-beans as a dependency and explicitly override its version to 3.1.4 in my-artifact's POM.
That being said, you can sort of accomplish what your question is directly asking by using version ranges:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>[3.0.0.RELEASE,3.2.16.RELEASE]</version>
</dependency>
This will effectively tell projects depending on your library that your library is okay with any existing 3.X version of spring-context, but version ranges have their own set of rules during dependency mediation that can possibly be unfriendly and obscure, and they won't link up between different artifacts either, so I would recommend just sticking with a regular version number in your case.
Not really, no.
Are you happy to declare variables in the parent pom for this purpose? If not, then you'll have to create variables in your project pom or in a new parent pom that inherits from the enclosing project. ${project.version} and ${project.parent.version} aren't built from other variables and you can't compose/decompose them; you would need to duplicate those values into other variables and build your version string from those variables.
And even when you do that, maven will complain about version not being a constant.
The normal pattern in this case is to completely ignore the parent version and maintain your project's version independently: just because your project uses Spring 3 doesn't mean that it shouldn't start at version 1. You can manually track the parent version if you want to. Since your project is not part of the parent project, the maven convention of omitting ${project.version} and inheriting it from the parent project is probably not appropriate.

slf4j-log4j12-1.7.2.jar unwanted dependency

I'm working in a multi module maven eap project. Previously it was implemented to use slf4j to use as logging framework. I changed it's logging configuration to use log4j2 as the underlying logging framework (still uses the slf4j). I referred to this document when I do so. But when I build the project and deploy it in jboss I get the following error.
Class path contains multiple SLF4J bindings, Found binding in xxx/lib/log4j-slf4j-impl-2.0.2.jar/org/slf4j/impl/StaticLoggerBinder.class and xxx/lib/slf4j-log4j12-1.7.2.jar/org/slf4j/impl/StaticLoggerBinder.class
P.S: I never add the slf4j-log4j12-1.7.2.jar dependency to pom.xml or in any of sub modules. I have no idea how that dependency is copied in to the lib folder.
Any comments guys ?
Well as it says it means you have indeed several slf4j bindings in your project.
You are right to deal with it now as it can become nasty and hide logs.
You should run
mvn dependency:tree
to see which of your modules adds the dependency to slf4j-log4j12-1.7.2.jar. It is probably that you have a transitive dependency to it.
You problem has in fact several solutions:
you can exclude slf4j-log4j explicitely
you can use the "provided" scope
you can use empty artifacts
I am referring to the following FAQ. It is about excluding commons-logging, but should be the same for you with slf4j-log4j.
When I ran into the same kind of problems, I found solution 2 to be the easiest to set and maintain. But solution 3 should work fine as well.

Categories