Will Maven pick the latest dependency out of two? - java

This question may have an answer already but I couldn't find a satisfying one. I just updated jersey jars to the latest one. I get jersey dependencies form the Parent project which also contains another project as a dependency which uses the lower version of jersey. I am not updating the jersey jars in this project. After updating the jars I see two versions of jersey client one of the them is the latest and the other one is from the project which uses the lower version.Scope is compile. My app is running fine but on the lib folder I see two version of jersey client 1.17 and 2.25.1. Will this cause any issue or Maven will always use the 2.25.1?
Thanks

Maven will always take the dependency which is the nearest to the root of your dependency tree. If two different versions are on the same level, the first one which is declared will be taken. Lets take the following example:
pom.xml
|--A:1.0
|--B:1.0
| |--A:2.0
| |--C:1.0
| |--D:2.0
|--C:2.0
|--D:1.0
Here, the libraries A:1.0, B:1.0, C:2.0 and D:2.0 are resolved. While the versions of A, B and C are taken because being nearest to the root, the version of D is taken because it was declared first.

Related

Maven - Different versions of same library automatically considered for 2 different projects in a same Eclipse/STS workspace

I wanted to use same version of log4j2 for all my projects.
So I manually deleted all other versions of log4j2 from ".m2" folder and only kept log4j2 2.17.0 version of the library.
I also observed that, if I've kept any projects open which uses different version of log4j, then eclipse will trigger maven to automatically download them. Hence, I closed all other projects and only kept 2 projects in "Open" mode, where both of them are inside same workspace.
Now when I checked the "Dependency hierarchy" of both the projects, I'm seeing the below difference.
4.a. Project 1 is using "2.13.2" version of log4j2. But since I've manually mentioned in the pom.xml to use "2.17.0" version, "2.13.2" has been overridden.
4.b. Project 2 is using "2.7" version of log4j2. But since I've manually mentioned in the pom.xml to use "2.17.0" version, "2.7" has been overridden.
So, if I remove the "2.17.0" version from pom.xml, then Project 1 will fall back to "2.13" and Project 2 will fall back to "2.7".
Based on this observation, I have 2 questions.
Why "2.17.0" is not being used by both the projects by default?
And what is causing them to use 2 different versions of log4j2 in 2 different projects. I mean to ask, at least both of them could have used "2.13" OR both of them could have used "2.7" right?

Wrong artifact being taken during runtime

Let me format the question a little bit :)
The problem is
In the runtime the specific value of enum cannot be resolved (it was introduced in 1.2 version of dependency included in project's pom.xml - this version of the library is called common-1.2.jar)
Probably caused by
Wrong dependency being taken during runtime.
Description:
In the WEB-INF/lib directory of generated war file there are three versions of common library (common-1.2.jar, common-1.1.jar, common-1.0.jar) and it looks that during runtime the older one is used. I checked it by calling values() method on my enum: there is no new - added in the 1.2 version - value there.
I executed mvn dependency:tree and there is only one common library there - common-1.2.jar. But - what can be observer - the wrong one is being used.
Question
Is there a way to find out what causes the project to use the older version during runtime?
use eclipse EE +maven plugin to trace the dependency
In eclipse EE, it has installed maven plugin. So just click the pom.xml, in editor window bottom, choose "Dependency Hierarchy". In the right side, choose commons-1.1.jar or commons-1.0.jar, in the left side "Dependency Hierarchy", you will see which jar imported the old jar.
In pom.xml, find the artifact, and add
<excludes><exclude><artifactId>commons</artifactId><version>1.0 or 1.1</version></exclude></excludes> .
Make sure in the right side, it should not exist any commons-1.0.jar or commons-1.1.jar. So your issue will be solved.

Why is it impossible to include multiple versions of the same dependency in Maven?

Imagine a are writing an application that has 2 dependencies; lets call them dependency A and dependency B. You have no control over either of those dependencies; they are not your libraries, but your application depends on them none-the-less. It turns out that dependency A has a dependency on dependency X (version 1.0); and B has a dependency on X (version 2.0).
According to the Maven documentation, Dependency mediation will be used to decide which dependency to use:
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. Note that if
two dependency versions are at the same depth in the dependency tree,
until Maven 2.0.8 it was not defined which one would win, but since
Maven 2.0.9 it's the order in the declaration that counts: the first
declaration wins.
So, presuming we declare dependency A first, then X version 1.0 will be used in our application. This means dependency B will be using X version 1.0 at runtime, where as was compiled against X version 2.0. If B is using some 2.0 only features, then we will get a run time error (NoSuchMethodError, ClassNotFoundException etc.), that's not good.
So to "fix" this, we can exclude dependency X from dependency A, so that X version 2.0 is used instead. But oh no! X version 2.0 isn't backwards compatible, so we end up getting run time errors from A instead.
As far as I can tell, there is no way to fix this properly with Maven. All you can do is hope to get hold of the source code of one of the libraries and fix it your self. Is that correct?
Why is it not possible for both versions of the X to be packaged into my application, such that A uses X version 1.0, B uses X version 2.0, and the version of X available in my application is whatever Maven chooses through dependency mediation. Is it a limitation of Java, or a limitation of Maven? Is this situation common? Are there any other possible solutions? Should all libraries guarantee backwards compatibility to avoid this problem?
Two classes with the same name cannot be loaded into one class loader. If Maven allowed you to have multiple versions of the same artifact, this would undoubtedly occur. So this is a Java problem that Maven is accommodating.
I'm not sure there's a catch-all solution to this. If you controlled A or B, you could use shading to rename your usage of X to something that wouldn't clash, as described in What is the maven-shade-plugin used for, and why would you want to relocate java packages?.
In general one has to hope that backwards compatibility is maintained between the libraries and test the resulting application well to ensure there are no problems. Obviously you would want to configure Maven to include x-2.0 rather than x-1.0, either by listing B first or by explicitly listing X in your POM.
A word of warning: most version naming schemes allow breaking changes between 1.x.x and 2.x.x, so you may encounter problems. Some Maven projects (such as Apache Commons Lang) will use a new artifact ID and package structure when they change major version in order to avoid these conflicts.

Provided dependency in library, using previous version in project

If a library declares provided dependency on eg servlet-api using v3.0.1; would it be possible for users to use version 2.5 for their library, that will be used on third party web application?
In other words:
mylib (srv 3.0.1) <-- some_framework(srv 2.5) <-- user_webapp (tomcat 6 or 7)
Moreover: v2.5 is declared as javax.servlet:servlet-api:2.5 and version v3.0.1 is declared as javax.servlet:javax.servlet-api:3.0.1, so there is a difference.
Would it be a problem for some_framework to specify different servlet-api dependency (eg 2.5) than defined as provided in mylib (eg 3.0)? I assume that since scope is provided (and available only in compile time), dependency tools (mvn, gradle...) will not download it in some_framework, and they have (and are allowed) to declare dependency manually.
(yeah, i am aware of differences between 3.0 and 2.5, and that is not a question. I also assume everything compiles correctly, etc. I am just interested how maven would compile some_framework, on which dependency: 2.5 or 3?).
Let me answer my own question, as [SO] never helped with tricky ones:)
I created simple maven project (that will act as some_framework). It depends on jodd-servlet (mylib), that has servlets 3.0.1 listed as provided dependency. When I run
mvn dependency:resolve
i do not see servlets in the list of dependencies. Now, if current project (i.e. some_framework) lists servlets v2.5 as dependency; and then if I run the same command again, I see only dependency on 2.5.
Therefore, we may say that provided dependencies are not transparent or exported.

How do I properly setup a multi-module Maven project with sliding release cycles

I'm trying to work out the best way to setup our multi-module Apache Maven project in a way that allows for disparate release cycles of modules, and doesn't introduce dependency issues when debugging the project.
We currently have a setup along the lines of:
bigsystem#1.2
parent-1.1-SNAPSHOT
module a#1.4-SNAPSHOT
parented by parent#1.1-SNAPSHOT
module b#1.3-SNAPSHOT
parented by parent#1.1-SNAPSHOT
depends on a#1.1
module c#1.1-SNAPSHOT
parented by parent#1.1-SNAPSHOT
depends on a#1.2
depends on b#1.1
The dependencies declared in modules b and c contain the minimum version required to compile the module, which isn't necessarily the current version of the module, or the version of the module being deployed.
From a build perspective this works well, each module can be released/updated as needed, however when trying to debug the deployed application under IntelliJ IDEA (versions 8 and 9 EAPs) having opened the top level pom, IDEA decides that since we declared a dependency on a#1.2, that anytime we step into one of a's classes, it should open it from a-1.2-sources.jar rather than the current a#1.4 sources in the project. This is further confused by the fact that stepping into any of b's classes takes us to b#1.1 rather than b#1.3.
My initial attempt to work around this was to declare the version numbers in the parent pom's dependencyManagement section and just have the sub-modules inherit the version. This worked to the degree of solving the IDEA debug issue as the dependencyManagement section can point everyone to the current -SNAPSHOT versions.
This unfortunately causes a problem when doing a maven release due to having to release the parent pom before releasing the module, but as the parent may refer to multiple in-development -SNAPSHOTS it can't be released and we end up adding version references back to the modules pom to satisfy the release.
It would seem that using maven's dependencyManagement section would only really work well if we were releasing ALL bundles at the same time, regardless of if they changed, but as we're wanting to manage releases of each sub module only when needed this model doesn't seem to fit.
I have a suspicion I'm missing something, and that a combination of dependencyManagement and version ranges might satisfy out requirements although I've yet to see version ranges work properly.
Is there a better way? A proper way?
I would recommend not making them modules, but make their POMs independent. That way you do not have to worry about trying to satisfy parent POM dependencies. Since they are released independently, they really should have independent project object models. Think of Apache Commons as a template.
I think the problem with IDEA arises because you are using the root POM in your source structure to do two things that are usually mutually exclusive in Maven. You are first using the POM as a location to store common configuration information for unrelated (from a build perspective) Maven projects. Secondly you are using the POM as an aggregator for your build. You can do each of these without doing the other.
Like Rob said, remove your module a, b, etc. projects from the modules section of your parent POM. Secondly, move your parent POM down into its own directory as it is really a sibling of the other modules with respect to your build and release process. The way you have it now, it is more of a parent/aggregator.
The way you have it now also doesn't lend itself to tagging and releasing each module individually as a tag of your parent POM would likely needlessly include all of the module sub-folders.
Your file structure would look like:
parent
pom.xml
module a
pom.xml
module X
pom.xml
As for the thing you are missing, dependencyManagement isn't really well suited to manage versions for intra-project dependencies. That is dependencies between modules within an aggregated build. It is more well suited for declaring global versions for external dependencies.
The final/working solution we ended up using was fairly similar to what we started with. The actual project structure remains the same:
bigsystem#1.2
parent-1.1-SNAPSHOT
module a#1.4-SNAPSHOT
o parented by parent#1.1-SNAPSHOT
module b#1.3-SNAPSHOT
o parented by parent#1.1-SNAPSHOT
o depends on a#1.1
module c#1.1-SNAPSHOT
o parented by parent#1.1-SNAPSHOT
o depends on a#1.2
o depends on b#1.1
distribution a#1.2-SNAPSHOP
However the main differences are that:
parent module does not include any versions of project artifacts
individual modules fully declare their project dependencies and specify a version range, i.e. [1.0.0,1.1.0)
all modules start there version number cycles from .1, i.e 1.0.1-SNAPSHOT, this allows the version range to satisfied by initial snapshots (1.0.0-SNAPSHOT is earlier than 1.0.0 final, so not included).
distribution pom (not initially shown in question) identifies the exact version to be deployed/included in a specific release.
delete all project -SNAPSHOTS from local maven repository when releasing so that ranges pickup releases only ( or use -Dmaven.repo.local=/tmp/sometemprepo for a fresh local repo)
This makes each module more standalone and gives us the freedom to release and deploy new versions of our project artifacts with minimal fuss.
They certainly seem like separate modules. What benefits are you gaining by smashing them together if they have different dependencies, even within the multi-module project?

Categories