maven sub projects - should they have their own depedencies - maven

If a project has many sub-projects, and all the sub projects have a common parent pom.xml, shouldn't all the the dependencies be listed in parent pom.xml ?
What's the point of allowing sub project to have their own dependencies.. ? It only opens up the possibility, that one sub-project will use apache_xyzlibrary_1.0.jar and another sub project might use _2.0.jar ?
Note: All the maven sub projects combine to form a single webapp WAR.

It will be very inefficient to include all the projects dependency into every sub project as by doing that, you are effectively bring in unnecessary dependencies into every of the sub projects' build. And one other issue is if you have inter sub project dependency, i.e. sub project a depending on sub project b, then you can easily end up a cycle dependency which maven unable to resolve.
To keep the consistence of dependency versions across the project, maven's approach is to make use of the dependency management section in the project's parent pom. Hence, only set the version of every dependency in the parent pom's dependency management section. In the sub project's pom, only the group Id and artefact Id is stated. No version tag should be used in the sub project's pom unless that is needed (i.e. when a sub project required a particular version of a dependency that is different from the rest of the project)

If all the children declare the same dependency, it is a good idea to declare that dependency in the parent instead. You are right about this ensuring all projects use consistent versions of dependencies.
As for why did the Maven developers decide to allow dependencies to be declared in child projects if this makes it possible to use different versions of the same dependency? I imagine they were thinking the advantage of locally declared dependencies in projects, when they are different from sibling projects, was nicer to have than restrict the parent to declare all dependencies for all its children.
Think of it like variables declared at different scopes in a class. If you have a private variable in a method, you know that it is only used in the one method and you can change it as you see fit without needing to worry about your entire code base. If you have a private field in a class, you have to aware of all it's usages in that class but not in other files. If you have a public variable, well you get the idea. Dependencies are the same, if you have one declared for a single project, you can be certain it is only used/needed by the single project. Furthermore, it doesn't need to be present if you are building a different child project of the same parent.
As for you building a WAR, you are right, using different versions of the same dependency in different child projects will probably cause you grief with the final artifact. Just be aware that there are other project types and build cycles where have mixed version numbers between projects may not be a problem and is actually desired.
Remember too that maven parent pom files can also have parents. This allows for more tiers of a hierarchy than just 2 levels. I think it would be ridiculous if all children projects had to declare its dependencies in the top level parent project pom file.

Related

Should exclusions be handled in the child pom or parent pom?

I am commonly in this situation where I am working on a couple of projects that all depend on the same parent pom. We handle all of our dependency versions and exclusions in the dependencyManagement section of the parent pom. The child poms will specify the dependencies they need but they will use the version from the parent.
When we place an exclusion on a jar, it is generally because we are wanting to get a different version of that jar from somewhere else (typically a different jar we depend on).
However, I ran into a problem. Lets say you have two jars A and B in the parent that depend on different versions of jar C. Someone working on a child project may need both jars A and B and then might go into the parent, excluding jar C from jar B. This may work out fine for them. However, someone else who is working on a different child project may only depend on jar B and now their project might get a NoClassDefFoundError at runtime after they pick up the latest parent code due to losing jar C. Now, maybe a 3rd person who is later working on a different child project decides they need jar B (and not jar A) so they add jar B. If they don't pay careful attention, they may not realize that they don't have jar C which is needed by jar B. At runtime they might get a NoClassDefFoundError.
As you can see, any child project that needs both jar A and jar B are fine but any child project that needs only jar B needs to remember to add jar C to their pom too.
Is it better practice to only do exclusions in the child pom? Or should I continue trying to have exclusions in the parent pom and make sure to just look closely at the exclusions and add additional dependencies to the child pom as needed? Thanks!
Use dependencyManagement in the parent for versions only, not for exclusions (and not for scope). As you point out yourself, adding exclusions in the parent can lead to situations where other developers (and maybe even yourself at a later point in time) get confused about why they don't get the necessary and expected transitive dependencies.
Regarding:
When we place an exclusion on a jar, it is generally because we are
wanting to get a different version of that jar from somewhere else
(typically a different jar we depend on).
You can enforce the correct version for these transitive dependencies in your parent pom as well to avoid exclusions for the reason you give here. The downside is of course that you must remember to update those versions when you update the version of the dependent library. This is how we manage our project with >200 poms, and I must say it's far better than figuring out where to add excludes to hopefully get the correct version of all the dependencies.

Maven: can a child module of a multi-module parent project be used in another completely separate project?

EDIT: I have changed this question so that it is not simply a duplicate of Can maven projects have multiple parents? (the answers for which seems to assert that a module can't have 2 parents)
Q: Maven: can a child module of a multi-module parent project be used in another completely separate project ?
Consider a ChildCommon project that is an API module or common implementation library to be shared by many projects.
There are at least 2 possibilities:
Case1: If 2 parents are allowed (with Maven 3.3.3):
ParentA
|--ChildCommon
ParentB
|--ChildCommon
Case2: Instead of 2 parents, a completely separate project depends directly on the child module:
ParentA
|--ChildCommon
ProjectB dependsOn ChildCommon [EDIT: ANSWER IS, YES, THIS WAY WORKS]
And if so, if part of the Maven build spec for the ChildCommon module has been siphoned off (up) into ParentA, how does one ensure the correct Maven spec is used when it is used by ParentB or ProjectB (without having to repeat things from ParentA brute force) ?
[EDIT: Answer is that Maven handles that perfectly via the "externally" referenced child's parent POM.]
EDIT: Expanding: My aim is to have my POM Optimizing and Refactoring cake using a parent POM and eat it. I have a set of 6 core modules that work together to nicely achieve the modularisation I need to develop a core web app system:
CoreLib, CoreAll, CoreImp, CoreAPI, CoreEJB, CoreWeb
CoreWeb runs as a standalone web app, and can also be reused by other web apps using overlays.
I won't go into here why I need 6, but it's in fact elegant for my case. It is however wearisome having to sometimes clean and build each one. It is also error-prone repeating certain artifact versions in each pom.xml (my main concern).
A specific web app combo leverages (at least some of) the above core modules (including CoreWeb):
SpecAPI, SpecEJB, SpecWeb
But another applications (a non-web one) might leverage only a subset of the core ones such as CoreAll, CoreImp, and would not under any circumstances want coupling to, for example, CoreWeb.
If you have a child project/module ChildCommon with a parent POM project:
ParentA
|--ChildCommon
You MAY reference ChildCommon to reuse it in another project ProjectB without adverse side-effects such as cascading to all other children of ParentA.

How does Maven's Aggregation Model comply with its Dependency Mechanism?

My question is relatively simple. I do have a project that has a typical parent pom with a dependency management and a module with a pom that "normaly" inherits those dependencies.
If I explicitly name the parent inside the inherited pom, then the project builds successfully, but if I omit the parent information then the project fails to build.
One would expect maven to be able to build this project normally since the aggregation pom lists all the modules along with the dependency management.
Why is that not the case?
Because you are mixing inheritance with aggregation.
In Maven, inheritance means declaring a parent POM for this POM (with the <parent> tag element). By default, each POM inherits from the Super POM. This inheritance allows to factor out common dependencies inside the parent (like plugins version or dependency management).
On the other hand, aggregation is a synonym for a multi-module project. This means that an aggregator project declares sub-modules (with the <module> tag element).
Even if it's not commonly used, you could have a project that aggregates sub-modules for which their parent is not this project. It is very common to see parent projects that are also aggregator project but it's not a necessity. So the build fails in your case because you reached the situation of an aggregator project that is not a parent project anymore so the sub-modules do not inherit from its configuration.
Quoting from the Maven docs:
A POM project may be inherited from - but does not necessarily have - any modules that it aggregates. Conversely, a POM project may aggregate projects that do not inherit from it.
Further reading:
The Maven book: Multi-module vs. Inheritance (selected quote):
There is a difference between inheriting from a parent project and being managed by a multimodule project. A parent project is one that passes its values to its children. A multimodule project simply manages a group of other subprojects or modules. The multimodule relationship is defined from the topmost level downwards. When setting up a multimodule project, you are simply telling a project that its build should include the specified modules. Multimodule builds are to be used to group modules together in a single build. The parent-child relationship is defined from the leaf node upwards. The parent-child relationship deals more with the definition of a particular project. When you associate a child with its parent, you are telling Maven that a project’s POM is derived from another.
Maven documentaton: Project Inheritance vs Project Aggregation (selected quote):
If you have several Maven projects, and they all have similar configurations, you can refactor your projects by pulling out those similar configurations and making a parent project. Thus, all you have to do is to let your Maven projects inherit that parent project, and those configurations would then be applied to all of them.
And if you have a group of projects that are built or processed together, you can create a parent project and have that parent project declare those projects as its modules. By doing so, you'd only have to build the parent and the rest will follow.

How to handle two Maven submodules that share a code dependency

I have what I think is a fairly common setup. I have a project with two modules, each with its own pom.xml. Above that, I have a pom.xml for the project, which depends upon its submodules. The two submodules have a shared dependency, namely log4j. How should I deal with this dependency? Should I simply just have each submodule have log4j as a dependency, or should the higher level project module get involved, claiming it as a project dependency? If I have both submodules listing the dependency, will Maven be smart and only pull down log4j once, or will each submodule pull down its own private copy of log4j ? If the project module has the dependency, will the log4j package be available at the right time for the submodules? What would you do, or what have you done in this situation?
The best way to do this is with the dependencyManagement tag.
The dependency management section is a mechanism for centralizing dependency information. When you have a set of projects that inherits a common parent it's possible to put all information about the dependency in the common POM and have simpler references to the artifacts in the child POMs.
To summarize the effect, you put the tag in the parent pom and have the children refer to it. They don't refer to the version number though. The benefit is at any time you can update the version of log4j in the parent pom and all your child poms get the new version without modifying their poms.
You are able to set the dependency at the higher level project. This will cover the dependency for both modules of your project.
Source: This is what my team does in one of our projects.

Maven - include all submodules of a pom as dependencies in another module

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.

Resources