Reputation: 4872
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?
Upvotes: 2
Views: 1240
Reputation: 69399
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.
Upvotes: 2