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?
Related
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.
We have a case in our dependency tree where we have two version of the same jar coming in from different sources, the version we want to use is lower in the tree, so maven is picking the other version using the nearest wins method.
To resolve this, We could either exclude this incorrect version from its source dependency, or directly add that version as another dependency in our pom.
I preferred the latter since it is quicker. But I am not sure if that is the best way of doing it, or are their any drawbacks of adding a dependency. So, is adding an exclusion better than what I did? In what cases and how?
I would suggest to explicitly define your dependency in order to make it clear which version you are expecting through transitive dependencies.
Although you would normally declare only dependencies directly referenced by your code (i.e. you use import statements in Java to include classes from these dependencies), it is also recommended to have control over your dependencies resolution via explicit declaration. Explicit declaration will have priority over Maven dependencies mediation.
Exclusions make your build harder to maintain and potentially you would need to replicate the same exclusion over and over if, for instance, the same dependency would be required transitively via other dependencies and every time with a different version (not the one you want). Moreover, exclusions can have undesired effects as it may take precedence when it is not expected.
Declaring it as part of your dependencies makes it clear, centralized and easier to update/maintain in the future.
Update: An even better approach is to use the dependenciesManagement section, which also takes priority over Maven dependencies mediation, as stated in the official documentation, here
dependency management takes precedence over dependency mediation for transitive dependencies
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.
I'm trying to determine an approach to the following situation:
There are 3 Maven artifacts: A, B, and C.
B depends on A. (i.e. it uses some of A's code)
C depends on both A and B (i.e. it uses some of A's code and B's code).
Assume I want to use the same version of A for both B and C.
What approach should be used?
1) Declare A as a dependency in C's pom.xml.
Pro: It's clear to the developer that C depends on A.
Con: If A's version changes, it needs to be updated in multiple places. (both B and C)
2) Don't declare A as a dependency in C's pom.xml.
Pro/Con: Opposite of option 1.
I think you should have all direct dependencies declared in your pom. Transitive dependencies are just a convenience for automagically resolving your dependencies dependencies.
If you change a version of a direct dependency, the transitive dependencies will likely change along with it, and thus potentially breaking the module. The module should build as an independent unit and thus should have well defined dependencies that will not break due to external changes.
I disagree that this violates the DRY principal, as maven defines things within the confines of a single project and its pom. And within this scope there is no repetition.
Update:
The reliance on transitive dependencies existing makes the project frail on it's own, and may also lead to more complex issues like when to include it.
For example, if C has a compile dependency on A, but a runtime dependency on B, then you now have to either add the dependency (since it is no longer in your build path) or declare B as compile even though it isn't. There is a lot to be said for clarity. Explicitly define what your dependencies are and what their scope is, and expect your dependencies to do the same. For the most part, your dependency is a black box, until it causes problems and you have to open it.
1) Declare A as a dependency in C's pom.xml.
The dependency is readableDependency is flexible. If you want to remove A's dependency from B, you do not need to think of the projects that dependend on B.As suggested in other answer, that it is a good practice to write down direct dependencies in pom.xml and let maven handle it.
2) Don't declare A as a dependency in C's pom.xml.
Mostly, no developer going to see pom.xml. And if they want they can see it by using mvn dependency:tree and it will show transitive dependency.There will be single point of change when a new version of A is released. If you define dependency at more than one place, you may forget to update all the places. In that case, Maven automatically uses the latest one. But, it does sting sometimes. Some people prefer this because, mostly, this type of dependency is common knowledge (e.g. MyWebApp -> MyWebAppLib -> MySharedLib and MyWebApp -> MySharedLib) and they want to avoid added step of updating versions at multiple places on each release.
I have written down pros-and-cons, you should evaluate what suits you the best yourself.
Edit#1: tsk! I have switched my comments.
Edit#2: updated the answer after a discussion done on this answer.
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?