I'm not quite sure of the best way to handle the following situation in my Maven project...
I have two direct dependencies A and B. Both depend on different versions of C. Dependency A is compatible with both versions of C, but dependency B is only compatible with it's declared version of C. I'd like to simply use whatever version of C that B declares.
I realize that I can exclude the dependency of C in dependency A, but then if I ever removed dependency B or if C was removed as a dependency of B...A would not work anymore.
I also realize that I can declare B's current version of C in my dependency management section, but then I would not seamlessly get the change if B ever changed it's version of C.
What is the prescribed way to handle this sort of situation?
The best way as recommended, although, slightly indirectly, by the maven docs is to use the dependency management feature. Quoting the doc,
A second, and very important use of the dependency management section
is to control the versions of artifacts used in transitive
dependencies.
I can understand that you are forced to update the version of C on your target project, say T, every time B uses a new version of C, but you also have to look at this from the T's POV - the target project T, ought to know and be able to control all the versions of the transitive dependencies it has.
Related
I was studying Maven's build system and it adds a lot of transitive dependencies because of its transitive dependency system (maven itself does not add dependencies but transitive dependency system does). I see issues with it like major version conflicts and unknown dependencies coming in.
I was thinking why is the system designed this way and why not take direct dependencies. My library does not need to depend on something which my dependency is using but not my library (I mean I understand why it needs to be included in the build list, my dependencies need to build using those, but why does it needs to cause major version conflict?). Am I missing something fundamental here? One thing that I can think of is that my library's build dependency list can grow to be very big because of all the direct dependencies I will need to take, but that does seem to be as big of a problem as problems with transitive dependency system.
I am new to build systems, so please don't be too harsh. I also tried to google this question but didn't find useful answers but please feel free to comment anything that I might have missed.
Thanks
If you need library A to run, and library A needs library B to run, and this needs C to run, it is very tedious to figure this out and add all the relevant dependencies to your project.
Before Maven and Gradle, many people worked that way and found out, that it is much easier to let a build tool figure out the transitive dependencies.
My library does not need to depend on something which my dependency is using but not my library [...]
This is your major misconception. There are two possibilities:
The direct dependency of your library exposes types from the transitive dependency in its public API. To use this public API you need to access these types, so you need the transitive dependencies during compile time.
The direct dependency of your library only uses its own dependency internally, but not in its public API. In this case, your library does not need to depend on the transitive dependency during compile time. But as soon as your library code runs (even in a test), it may use some functionality of its direct dependency that internally uses functionality of the transitive dependency, causing your library code to fail.
[...] I mean I understand why it needs to be included in the build list, my dependencies need to build using those [...]
There is no actual build list (or order) for external dependencies, because they are used when they are already built (the downloaded .jar files contain compiled .class files). But as I mentioned above, you will need the transitive dependencies either during compile time or during runtime (e.g. tests), so your build system (Maven or Gradle) will fetch them for you.
[...] but why does it needs to cause major version conflict?
#khmarbaise already explained in his comment, why and how version conflict between transitive dependencies may occur:
You are using two libs X and Y. Both of them using another lib (A) So X is using A in version 1.0.0 but Y is using A in version 2.0.0. In the end you can't have both on the classpath there must be done a decision for one version. So depending on how X,Y are implemented either X can fail while using A in V1.0.0 or Y can fail in using A in V1.0.0 or with V2.0.0 the same... This can happen if X or Y are being updated. This is also true for different version combinations like A in 1.0.0 and 1.1.0 (if compatibility is not 100%)
Context
I have one project (A) that depends on another (B), both using the Gradle build system. Project A usually declares a dependency on a specific version of B. When modifying project B, it's convenient to immediately see the effects on project A. Using includeBuild in settings.gradle or --include-build on the command line, the build of project A is reconfigured to include the local development version of project B instead of a published artifact (a "composite build"). However, this local copy of project B declares a development version number, not the stable release version requested by project A. To ensure that the included build of B satisfies the declared dependency in A, I have been changing A to request a version range like org.example:B:[v6, ) instead of org.example:B:v6.0. However, I see that the build succeeds even when A specifies nonsensical version numbers that do not correspond to B.
Question
How do versions affect this dependency substitution process?
Research
The documentation I have read (linked below) shows how to include a build, but does not mention version numbers.
https://docs.gradle.org/current/samples/sample_composite_builds_basics.html
https://docs.gradle.org/current/userguide/composite_builds.html#defining_composite_builds
Some hints may be present on this page covering "brute force" modifications of dependency resolution:
In composite builds, the rule that you have to match the exact
requested dependency attributes is not applied: when using composites,
Gradle will automatically match the requested attributes. In other
words, it is implicit that if you include another build, you are
substituting all variants of the substituted module with an equivalent
variant in the included build.
Although this deals with "variants" and their "attributes", which according to the dependency terminology documentation seem to be distinct from module versioning, the statement seems related. Can something similar be said about dependency module versions?
In my project I have module M1 it use library A. This library have dependency to library B.
And in another module M2 I want to use library B.
And of course I have "root" module which depend on all my modules so versions of library B should be the same across all projects.
So I added B to dependency of my M2 module but don't know how to specify it version because I need exactly same version as library A use.
If I specify current version of library B then when I will upgrade library A to new version I will forget to update version of B in my module.
Also I don't want to put A as dependency of M2 because I need only limited part of it's functionality.
What is best practices for my case?
The maven enforce plugin can break the build if the versions diverge so that yo do not forget to update: see https://maven.apache.org/enforcer/enforcer-rules/dependencyConvergence.html
There are several options some of them you exclude explicitely. Let me mention them for the sake of completeness anyway:
Include B as a dependency in M2.
Advantage: Cleanest solution with respect to adding only those functionality to M2 that's actually needed.
Disadvantage: Version of B in M2 has to be kept in sync with versin of B in A manually.
Include A as a dependency in M2.
Advantage: Central <DependencyManagement> can be used in your parent POM. Hence, Versions of B will always be the same in all sub-projects.
Disadvantage: Functionality of A not needed in M2 is added to M2.
Include M1 as a dependency in M2.
Advantage: M2 always uses the same version of B as A without having to deal with B as a dependency at all.
Disadvantage: Functionality of M1 and of A not needed in M2 is added to M2.
Use <dependency>/.../<exclusion> to exclude B from A in M1.
Advantage: Central <DependencyManagement> can be used in your parent POM. Hence, Versions of B will always be the same in all sub-projects.
Disadvantage: Version of B has to be kept in sync with version of B in A manually.
[Input is welcome in case there's anything else I haven't thought of.]
Try Maven Enforcer Plugin's Dependency Convergence mentioned by Assen Kolov in his answer. If it works for a mixture of direct and transitive dependencies, like in your case, as well, I'd use it in conjunction with 1.
I was exploring maven, currently using Ant. The application which we use here have different modules.
Now if I add dependencies to those how I can solve version conflict. Example module A has the dependecy of version1. while module B has the dependency of version2 now If someone need to work with both the module together than it will automatically resolve to version2 that is higher version.
Is there any internal behavior of maven or any command that I can use to resolve version conflicts?
Maven "solves" this problem by using the nearest declaration for transitives dependencies.
e.g.
A -> X -> externalLib 2.0
B -> externalLib 1.0
C -> A,B will result in using externalLib 1.0 since the declaration (over B) of 1.0 is nearer to C that over A and X
If they have the same distance the position within the POM file decides.
This is typically not the kind of "solution" you want. Since they might be incompatible it is better to choose a version manualle explicitely and declare this version in the pom of C
C -> externalLib 2.1
Note, if there is no version that is compatible to both X and B, there is no solution and Java does not support several versions of the same lib in one classloader. In this case you thould think of using OSGi or similar.
this link provides solution to your problem
http://maven.apache.org/guides/introduction/introduction-to-optional-and-excludes-dependencies.html
I'm having a problem reconciling building a project for use within an application server and for use as a stand-alone application.
To give an overall simplified context, say I have three Projects A, B, C.
Project A depends on Project B which depends on Project C.
Project C has a dependency X which is marked as provided since it was expected that it would be available as a JEE library within say an application server. i.e. jms.jar.
So if I perform an assembly build of Project A, I get all the transitive dependencies save for those marked as provided as expected.
Now I have a new deployment scenario where Project A needs to be used in a standalone environment i.e. outside an application server.
So now I need the jms jar to be a compile dependency. Does this mean that I should explicitly add a compile dependency for X in Project A? Doesn't this violate the Law of Demeter, i.e. don't talk to strangers, in the sense Project A shouldn't explicitly know about Project C but only about Project B?
This is a simple example but in reality I have multiple dependencies which have been marked as provided but are now need to be compile or runtime dependencies so they end up in the artifact produced by the maven assembly plugin.
Is this a fundamental problem with Maven or am I not using the tools correctly?
Thanks in advance for any guidance.
If you need your build to have variations in it for different scenarios, you need to use profiles and keep certain things (such as some of the dependencies) in the various profiles.
http://maven.apache.org/pom.html#Profiles
Different dependencies for different build profiles in maven
answers a similar question - but you can swap in the "release" and "debug" for "Project A" and "Project C"
Provided dependencies are a difficult subject. First of all: Provided dependencies are not transitive in the following sense: If your project C has a provided dependency on X, then A will not get the dependency. It is silently ignored. This fits with the following meaning of "provided" which I propose:
Only the artifacts that are actually deployed should mark dependencies as "provided". Libraries or other jars that are not individually deployed to a specific server should not have provided dependencies. Instead, they should declare their dependencies as compile dependencies. In your example: Project C should have a compile dependency on X. If project A knows that X is provided, it sets X to provided in "dependencyManagement". As project A should know the environment in which it runs it should decide what is provided and what is not. And "dependenyManagement" is the right place to declare this.
If your project A should be able to run within and without a given server, you probably need to make a lot of adjustments, even change the type from ear to jar. So you either use build profiles for this, which then have different dependencyManagement entries, or you split A into two projects which depend on some other project that contains the common elements.
If some given project C already has a provided dependency on X and you cannot change that, this is effectively the same as a missing dependency in C. This has to be repaired at some point, and this could be project A itself.