Project A depends on project B with runtime scope.
Project B depends on project C with compile scope.
If code in project A calls a function in project C, I would expect mvn compile of project A to fail, but it does not.
What is the expected behavior?
I am using maven 3.0.4 with maven-compiler-plugin 2.5.1 and maven-dependency-plugin 2.5.
The maven documentation gives a slightly confusing until you understand it table.
To work out what the scope is you need to follow the following path of entries...
Start from the ultimate parent project and merge all the <dependencies> list. Do the same for <dependencyManagement>. When merging, the groupId:artifactId:type:classifier is the key, child poms which have dependencies with the same key overwrite whatever values they specify.
Now you need to apply the <dependencyManagement> to the <dependencies> list to fill in any blanks.
At this point we now know the dependencies of the current project. The scopes in this list correspond to the row you select from the table.
For each dependency in the list, you repeat the above process to compute the dependencies effective dependency list (yes including ths step too) and then the scope of these dependencies correspond with the column selection in the table.
Once you have worked out the transitive dependencies, and their scopes via the table, for each dependency, you then apply the current module's <dependencyManagement> overriding scope, version and exclusions as defined by <dependencyManagement> and there you have the effective transitive dependency list.
There are other more subtle effects which are there to retain existing behaviour where bugs have essentially become features, and version ranges can further confuse things, but above shoud give a god general understanding of the problem space
Related
Our team has multiple projects; most projects are just libraries. Let's assume for simplicity that the libraries don't depend on each other and there is a single project that uses them, e.g.:
Project Main:
Project Lib-A:
X (3rd-party library)
Project Lib-B:
X (3rd-party library)
To avoid surprises in 'Main', we'd like to make sure that all our own projects are using the same versions of the 3rd-party libraries, so that e.g. both 'Lib-A' and 'Lib-B' are built and tested with the same version of library X.
To achieve this we use a parent pom with <dependencyManagement> section detailing the versions of all relevant 3rd-party libraries and also their transitive dependencies. This parent pom is inherited by all the projects, i.e. 'Main', 'Lib-A', and 'Lib-B' from the above example. Then each child pom would only use <dependency> without specifying any version. We also have maven enforcer plugin's dependencyConvergence rule to make sure we have not missed any library conflict in any of our projects.
The problem: increasing the version of X: a developer of 'Lib-A' increases a version of X from 1.0 to 2.0. So he changes X's version in the parent pom, increases the version of parent, releases the parent pom, and notifies guys from 'Main' that they should now use a new parent. The situation becomes like this:
Main - inherits from Parent:2.0 and depends on:
Lib-A:2.0 - inherits from Parent 2.0 and depends on X:2.0
Lib-B:1.0 - inherits from Parent 1.0 and depends on X:1.0
X:2.0 (taken from Parent:2.0 <dependencyManagement> section)
Everything, including 'Main', builds fine, 'maven enforcer plugin' does not detect any conflict because the version of X is clearly specified in the Parent:2.0 from which 'Main' inherits. So we release 'Main'.
Ooops.... Lib-B has never been built with X:2.0. It has great unit tests that would uncover the problem, but we never tried this. We forgot to update Lib-B, try it with X:2.0 and release it. Still 'Main' has been built without problems and maven enforcer plugin has never complained.
Question: we need maven to detect that there are dependencies that inherit from the same artifact but different major versions and fail the build.
In our case the build had to fail since 'Main' and 'Lib-A' inherit from Parent:2.0, but 'Lib-B' inherits from Parent:1.0.
My solution so far (a hack): in addition to inheriting, add an explicit dependency on the parent pom to all out projects (i.e. 'Main', 'Lib-A', and 'Lib-B'):
<dependency>
<artifactId>Parent</artifactId>
<type>pom</type>
<version>${project.parent.version}</version>
</dependency>
Then use <bannedDependencies> rule of maven enforcer plugin to ban other major Parent versions (we could also use its <dependencyConvergence/> rule if we want to fail even on minor Parent version conflicts).
Is there a less hacky and cumbersome way to fail on conflicting major versions of parent pom?May be our entire approach to managing maven dependencies is wrong, what is the recommended way then?
Update:
Tried writing my own rule for maven-enforcer-plugin as suggested by #JF Mayer and described here, before giving up. Reasons:
First, the parent pom information is not available from the dependencies, at least not from the nodes built by maven's DependencyGraphBuilder
OK, I've added my parent poms as explicit dependencies to the children and tried to use this DependencyGraphBuilder to detect dependencies on the parent with different major versions. No way! As could be seen with mvn dependency:tree that also uses this class, DependencyGraphBuilder does not provide all the dependencies so it can't be used to detect dependencies conflicts. That's why the <dependencyConvergence> maven enforcer rule is using a super-deprecated DefaultDependencyTreeBuilder that has been even deleted from the GitHub and everywhere else - not a good choice for a trouble-free custom solution.
For completeness, my own poor-man's solution:
Add an explicit dependency of type pom to the parent to every project so that maven-enforcer-plugin's <dependencyConvergence> rule would detect conflicting parent versions. No big deal with this one as we only add this section once and forget about it:
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>${project.parent.artifactId}</artifactId>
<version>${project.parent.version}</version>
<type>pom</type>
</dependency>
<dependencyConvergence> will fail a build on 'Main' even on 'increment' differences in the parent versions, e.g. 1.0.1 and 1.0.2. In this case, the developer of 'Main' can decide that it's OK to build it despite the parent version conflict, because it's insignificant (this was my original question). So he builds 'Main' with some special profile that excludes the dependency on the parent: mvn -P I-know-what-I-am-doing deploy.
I am not very happy with this solution because of the step 2 that requires the developers of 'Main' to build it with a special profile in case of a parent versions conflict. I'd prefer a solution that always fails on major parent version conflicts but ignores insignificant differences in the parent pom versions automatically, I just don't know how to achieve this.
I think what you are looking for is Reactor Module Convergence enforcer:
https://maven.apache.org/enforcer/enforcer-rules/reactorModuleConvergence.html
Its not the same as the dependency convergence rule.
Concerning the pom.xml Maven file:
Is there any specific rule to apply to the ordering of declared sections?
Does it have any importance or impact on the build?
Shall I follow any official convention?
Although in most of the cases it is irrevant whether one section is declared before another, readeabilty could be indeed impacted when choosing a weird layout (e.g. Maven coordinates at the end).
But that's not the most important point, because yes, ordering of certain elements can impact your build.
Order of plugin declarations
The order of plugin sections wihtin the build/plugins section may be important. Since Maven 3.0.3 (MNG-2258), different plugin executions attached to the same Maven phase will be invoked in their order of declaration in the pom.xml file, after any execution attached via default bindings. That is, ordering is important in this case, since it may affect the behavior of the build.
Order of dependency declarations
Additionally, also order of dependency declarations within the dependencies section may affect your build towards Dependency Mediation, that is, the first declared dependency wins in case of conflict against a transitive dependency. So, once again, ordering is important in certain cases.
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.
As a rule of thump: declare first the dependencies you directly reference in your code (that is, as import statements).
Order of module declaration
Although not relevant in most of the case, because (the) other important rules applies before, Maven will also respect the order of declaration of module element within the modules section during a multi-module build as last decision point. The reactor mechanism will in fact:
The following relationships are honoured when sorting projects:
a project dependency on another module in the build
a plugin declaration where the plugin is another modules in the build
a plugin dependency on another module in the build
a build extension declaration on another module in the build
the order declared in the element (if no other rule applies)
Note: bold added here.
Standard layout
Last but not least, although ordering is not important for other sections of the pom.xml file, good habit is to follow the official Maven recommendations:
The team has voted during the end of June 2008 to follow a specific POM convention to ordering POM elements.
As a simplified version, follow this order of declaration:
<project>
<modelVersion/>
<parent/>
<groupId/>
<artifactId/>
<version/>
<packaging/>
<properties/>
<dependencyManagement/>
<dependencies/>
<build/>
<reporting/>
<profiles/>
</project>
As a final note, the sortpom-maven-plugin can also be used to automatically apply this standard ordering, simply invoking the following on the concerned pom.xml file:
mvn com.github.ekryd.sortpom:sortpom-maven-plugin:2.5.0:sort \
-Dsort.keepBlankLines -Dsort.predefinedSortOrder=recommended_2008_06
Also note, the exceptions above are not handled, but documented by the plugin as special cases where indeed ordering may affect your build.
For further reading:
Official Maven doc: Plugin Bindings
Official Maven doc: Dependency Mediation
Official Maven doc: Maven Code Style And Code Conventions
Our project has migrated from log4j to log4j2. However, other projects, which our project depends on, are still using log4j.
If I want to exclude log4j with exclusions, I need to add more than 10 exclusions on a single pom.xml and it is not practical.
Question: is there any way to say, it does not matter, from where it comes, exclude log4j from my project. It is like the exact opposite of adding dependency.
This not possible at POM level, as stated by official documentation
Why exclusions are made on a per-dependency basis, rather than at the POM level
This is mainly done to be sure the dependency graph is predictable, and to keep inheritance effects from excluding a dependency that should not be excluded. If you get to the method of last resort and have to put in an exclusion, you should be absolutely certain which of your dependencies is bringing in that unwanted transitive dependency.
If you have control over the other projects you depend on, then the concerned dependency should be declared as optional.
<dependency>
<groupId>com.sample</groupId>
<artifactId>project</artifactId>
<version>1.0</version>
<optional>true</optional>
</dependency>
Optional doesn't affect the project itself (it will keep on having this dependency) but it will not be considered as transitive dependency by dependent projects (hence, you will have the choice to ignore it or to re-declare it, if needed).
As from official documentation
Optional dependencies - If project Y depends on project Z, the owner of project Y can mark project Z as an optional dependency, using the "optional" element. When project X depends on project Y, X will depend only on Y and not on Y's optional dependency Z. The owner of project X may then explicitly add a dependency on Z, at her option. (It may be helpful to think of optional dependencies as "excluded by default.").
Afterwards, if you really want to make sure that the concerned dependency is not brought in by any dependency transitively, you could configure your build to ban it (the build would fail whenever the concerned dependency appears) using the Maven Enforcer Plugin and its bannedDependencies rule.
I'm trying to use a number of the BOMs in the org.jboss.bom group to bring in the API stacks that are compatible with EAP 6.3.0. My understanding is, that's what they're for. But when I reference them (using 'provided' or even 'compile' scope), the dependencies don't become transitively available. Given that "compile" scope is used on the items inside the BOMs, Maven's documentation of the dependency mechanism seems to indicate that those items should be added to the classpath of my project. Yet I'm getting undefined symbols for the classes that should be brought in.
For example, in project P, I'm including org.jboss.bom.wfk:jboss-javaee-6.0-with-spring:2.4.0-redhat-2 with 'provided' scope, yet org.springframework.context.ApplicationContext is undefined in P.
This is all happening in JBoss Dev Studio 8.1.0.GA, if that makes a difference.
I figured out the solution myself by reading the Maven Dependency documentation in more detail.
Here's my take-away: you cannot depend on the Eclipse m2e plugin to see you through the Maven dependency weeds. Know when/how to use the <dependencyManagement> section of the POM (and when not to use it). Know in particular the specific invocation Maven needs when you want to use a BOM: import the BOM in a <dependencyManagement> section with <type> of pom and <scope> of import, and then in the "regular" <dependencies> section (not <dependencyManagement>) specifically call out the sub-artifacts you need from the POM, but omit the version. (It's all spelled out here.)
The intent of the BOM is not to allow you to mass-import dependencies by referencing only the BOM artifact; rather, it's to make sure the versions of dependencies are the right ones, as defined by the BOM.
Do not assume that Maven allows you to express things in logically/mathematically reduced terms. Find out how to please the beast, and do not rely on a wizard to figure this out for you. Read the Maven docs in detail, find out the recipes and follow them exactly.
We have a maven module that is set up as so:
a (parent)
-> b (submodule)
-> c (submodule)
-> d (submodule)
This list of submodules is set to grow as time goes on (to a list of 20 or so). We have another module that will include as dependencies all submodules of a. Is there a neat way of doing this, rather than having to manually keep the submodule list in sync with the dependencies list. I.e. is there any way of including a and all submodules as a dependency?
You have a few choices here:
No change. List all dependencies in each pom. However, if they have a common parent, you can use dependencyManagement in the parent pom to set the versions for the different dependencies. In the child poms, you do not need to list the version. See this section from Maven By Example. A downside of this approach is that you have to re-list the same dependencies over and over.
Create a parent pom which lists all shared dependencies. See an example here. A downside here is you are restricting all projects that want to take advantage of this to use a parent project when they may need to use another parent project for some reason.
Wait for Maven mixins, which last I heard were still not ready.
Rethink your design. Does it make sense for projects to depend on so many different modules and interfaces? To reduce coupling, you may want to create one interface that these new projects can use. Several open source multi-module projects, such as Apache Axis2, follow this pattern. One module contains your 20 dependencies and exposes an interface which the new modules can call. The new modules can just list that one main module as a dependency and all of the 20 dependencies are pulled in as transitive dependencies.
I think choice #4 is probably right, but I am not familiar enough with your situation.
Design considerations aside, this is easily done - simply include <type>pom</type> in your dependency pointing at the parent pom. E.g:
<dependency>
<groupId>my.group.id</groupId>
<artifactId>a</artifactId>
<version>0.0.1-SNAPSHOT</version>
<type>pom</type>
</dependency>
The only way dependencies get "automatically" included is via the transitive dependency mechanism, and it only works by pulling in dependencies of dependencies, so unless you have a pom that depends on all the submodules, no, you won't get them without listing them all out. A need to do this points to a flaw in your project design. If the modules are "all or none", then they're probably not separate modules.