Maven pull an old version of a transitive dependency - java

I've written a library I've written that depends upon Guava version 20.0 (the last version that is compatible with JDK 1.7)
I've then written an application that uses that library and also uk.org.lidalia:slf4j-test:1.2.0 (with test scope). slf4j-test has a dependency on version 14.0.1 of Guava.
My problem is that my application is seeing two different versions of Guava as transitive dependencies and picks the OLDER version. Now when I call certain methods in my Library I get Class or Method not found exceptions.
I can (and have) excluded Guava as a dependency of slf4j-test, which fixes the problem for now, but the problem will return if I introduce another dependency which uses Guava.
So my question is: Why would Maven pick the older version of a dependency and how do I always make it pick the latest versions.

You cannot force Maven to always pick the latest version. But there are several things you can do:
If you declare the dependency in your own pom, this version always wins. So if you have a dependency on guava 20.0 in your pom, you will get exactly this.
If this is not suitable, you can put an entry into dependencyManagement which sets the version for guava. This will overwrite all transitively set versions (like the 14.0.1 in your example).
The Maven enforcer plugin has a rule that makes sure that all dependencies are evaluated to the latest version - but it does not pick that latest version, it only fails the build if it was not chosen. Then you need to choose it yourself by applying method (1) or (2) above.

Related

Use gradle dependency locking & version ranges in local library

I just started using dependency locking with version ranges (e.g. latest.release, 2.+, etc.) in one of my Java projects. To simplify, let’s say it has a structure like this:
:root:lib1
:root:project1
:root:project2
where lib1 is shared by the other 2 subprojects.
I started using dependency locking in :root:project1 and everything works as intended. :smiling_face_with_three_hearts:
Then I started using dependency locking with version ranges in :root:lib1. e.g. I would define a dependency like so: implementation software.amazon.awssdk:sqs:latest.release and then lock it to a version, say 1.0.0. This works fine with the first project since I generate a separate lockfile for that project, which constrains the version of the library to whatever value is in that file.
However, in project2, I have not yet switched to dependency locking, and I see that it’s pulling in transitive dependencies as version latest.release instead of 1.0.0 (the value in the lockfile for the library), which resolves to different version day to day.
project2 declares a dependency on lib1 using the project annotation
implementation project(':root:lib1')
Do I need to publish the lib1 jar and consume a specific version of it in order to get the locked transitive dependencies? Or is there a way to transitively bring the locked versions from lib1 instead of the latest.release value? Any help is much appreciated :slight_smile:
Currently using Gradle version 7.4.2

How does Gradle resolve library version conflict?

In a setup like this:
Project
-ModuleA
implementation lib:1.1
implementation project(:ModuleB)
-ModuleB
api lib:1.2
-ExternalJar1
-lib1.3
-ExternalJar2
-lib1.4
How does Gradle determine what lib version to select? As per this SO answer, version 1.4 will be selected because it's the highest version and we have not specified "any specific constraints for transitive dependencies resolution". If that's correct, wouldn't ModuleA code break if it's not expecting a newer version? How can I fix that?
Do external libraries (imported jars) live in a closed sandbox - neither exposing their dependencies to project's classpath, nor using project dependencies in their classpath?
Assuming version 1.3 and 1.4 are not compatible, I can't even force one version as suggested in this SO post, what option do I have to fix this setup?

Specify highest/lowest version for maven dependency

Using the following versioning logic
major.minor.build
is there any way to specify that my project depends on a specific major version, the highest minor and the lowest build number?
I need to do this because I'm using the maven-dependency-plugin to copy all the dependencies of our multiple WAR files into a common folder, so that it can be sent to the client the latest version, but at the same time keep the numbers of packages to a minimum.
No, it is not possible. When Maven encounters multiple matches for a version reference, it uses the highest matching version. Here you can find good explanation how to work with version ranges in maven.
I can suggest other solution for your original task:
Create new maven module.
Add all projects that you need as a dependencies to this module.
Use maven-dependency-plugin to copy dependencies of this module.
Maven guarantees that one and only one version of each (groupId:artifactId) is used. You can check resolved versions using mvn dependency:tree.

What about new versions of third party libraries?

I'm maintaining an open source java library which has itself some dependencies to third party libraries (e.g. commons-beanutils:commons-beanutils-1.8.3). During development I just added the most recent version of such libraries to my pom. Now I did some changes to my library and realized that those versions are no more recent.
I'm now wondering which strategy is best practice with such dependencies.
My feeling says run mvn versions:use-latest-releases test.
I would recommend using mvn versions:display-dependency-updates and updating relevant libraries by hand.
It is not necessary to always use the latest version of the library.
Usually, should update your dependencies if:
you need a feature of the new version (for major and minor releases)
the update resolves a known bug (that affects you)
the update fixes a security problem.
resolves an incompatibility with another dependency
Otherwise, consider staying on your current version.
You might, temporarily consider using version ranges commons-beanutils:commons-beanutils:[1.8.0,1.9.0) to always use the latest bugfix version. Note however, that this results in non-reproducable builds and must be changed before releasing your project.

Preferring the latest jar in a sea of dependencies (java)

I have a large Ivy project, and Ive noticed that my code, which run well in eclipse, cause a compile error when run in ant. I've narrowed the problem down to the following line :
FileUtils.write(...).
This line fails - during compilation --- the method is simply not found. Obviously, my code is dependant on apache's commons-io library. And its quite clear that the current commons-io has this method.
http://commons.apache.org/io/apidocs/org/apache/commons/io/FileUtils.html
So what gives ?
I am pretty sure this is related to my ivy.xml -> the eclipse compiler is luckily (or smartly) using the newest possible version of commons-io , whereas my ivy.xml is using an older version which lacks this method.
Most important of all to not here is that ant is clearly using a different version of this jar.
So - my question is :
1) How can I tell ant / ivy to preferentially compile my code with the latest versions of libraries i specify ? I'm assuming that some of the dependencies in my lib/ may depend on older versions of commons-io .....
Also :
2) In this context, any hints about what to worry about regarding the how the classloader deals with duplicates in a multi-jar-dependent project would also be helpful to me ...
Dependency Reporting
I would suggest that you first add the generation of an ivy dependency report into your build, using the report task.
An example of this task is included in the following answer:
What is the Ivy equivalent of Maven's versions:display-dependency-updates?
This will tell you what versions of what jars are being used. Normally, ivy will use the version you specify in the ivy.xml file, however, another module might depend on a more recent version. Ivy's default behaviour is to always favour the most recent version of a Maven module.
Retrieve the latest dependency
If you want ivy to always prefer the latest version of a particular library then declare the dependency as follows:
<dependency org="commons-io" name="commons-io" rev="latest.release"/>
Ivy has a feature called Fixed and Dynamic Revisions.
You can set the version/revision of any artifact to latest-status like
rev="latest.integration" --> for development released
rev="latest.release" --> for released versions
Ivy takes the version with the highest version(you have specified) and omits all libraries with lower versions, so that you only have one lib in the ivy classpath (have a look at the resolution report, run ant -v (verbose mode))., which avoids having duplicate jars with conflicting versions.
This might be worth checking out, maybe you just have an old version defined in one of your ivy files.
As to the second point:
The classloader takes the class, that happens to be first in the classpath(or the jar that is first in the classpath). So mixed versions of the same lib, could behave differently on any system, depending on how the classpath is constructed.

Categories

Resources