Maven - dependency management in parent and import scope - java

The setup is the following:
I am using Maven and I would like to use as a parent POM some artifact that has a number of dependencies in the dependencyManagement section. But I would also like to have a dependencyManagement section in my POM as well, because my artifact will be the parent pom of some other submodules.
Example:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.mycompany</groupId>
<artifactId>parent-pom</artifactId>
<version>1.0</version>
</parent>
<groupId>com.mycompany</groupId>
<artifactId>my-artifact</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>sub-module-01</module>
<module>sub-module-02</module>
</modules>
<packaging>pom</packaging>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>1.2.3.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
Let's say that both these parent poms define a version for some dependency (let's say the version for jackson), but each has a different version.
When I define that dependency in one of my submodules, which version will be resolved?
I am not sure how to search for this situation on google.

Typically, Maven resolves using the closest version declaration.
My best guess would be that the one from the import in the dependency management section would be preferred, since it is closer to the location where the dependency is used:
dependency -> imported dependency in parent pom = 2 hops
dependency -> parent pom -> dependencyManagement in parent pom = 3 hops
But really, just try it ;)

Let us according to the different case:
Firstly, The simplest case is NO pom dependency import tag in any pom's dependencyManagement section, everything is clear and the dependency declaration will make a really affect with the closest dependency and properties such as exclude will inherit from parent pom and the exclusion with be merged .
Secondly, if the there is a direct declaration in any of each pom's DM section, this declaration will be used for this jar . if not, the closest import dependency declaration if exists will be used to the jar/pom's version. Here the closest mean is the distance to the pom and super pom or ,super super pom. But Not The transitivity distance!

I think it will be the version from import. Because the import
DependencyManagement Just do a replace of dependencies. so Parent dependency will be overwritten by the import dependency. I do not verify this. Just a guess.
I Just do an experiment to check this situation.
Firstly. I define a dependency of netty-handler.
In my maven repository netty-handler version contain:
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-handler</artifactId>
<version>4.1.8.Final</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-handler</artifactId>
<version>4.1.9.Final</version>
</dependency>
I create one test_parent pom project with this content in pom.xml
<groupId>dm-test</groupId>
<artifactId>parent-test-pom</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<dependencyManagement>
<dependencies>
<!-- define a dependency in super parent pom with version 4.1.8.Final -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-handler</artifactId>
<version>4.1.8.Final</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
and then create a pom project for import:
<groupId>dm-test</groupId>
<artifactId>import-pom</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<dependencyManagement>
<dependencies>
<!-- define a dependency in super parent pom with version 4.1.9.Final -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-handler</artifactId>
<version>4.1.9.Final</version>
</dependency>
</dependencies>
</dependencyManagement>
Finally, I create a war project with the parent_pom and import_pom, like this:
<groupId>dm-test</groupId>
<artifactId>test-project</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<parent>
<groupId>dm-test</groupId>
<artifactId>parent-test-pom</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>dm-test</groupId>
<artifactId>import-pom</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-handler</artifactId>
</dependency>
</dependencies>
finally, We can see the dependency jar version is 4.1.8.Final
So, My guess is wrong. sorry about that.

Related

Whats the difference between project and BOM dependencies?

I've noticed that fabric8.io for Kubernetes client has two dependencies ending with project and BOM.
The only difference I've noticed is that it first has a distributed version. Also according to apache guides, bom usually used as a parent for projects.
Are there any other uses/differences? Which dependency should I use with Spring Boot?
A BOM project can be either used as a parent for your Maven module or imported as a BOM dependency which allows you to import dependencies from that BOM. A really good article on this matter can be found here.
Why is a BOM important? Since you've added a Spring tag to your question, let's say you want to use a certain Spring version and component_1 works fine with component_2 as long as they have the same version. As a library developer, you would have a versioned BOM which contains component_1 and component_2 and in your project, you would need to import the BOM with the version you need and the components you need without the version, as it will be inherited from your imported BOM/parent. This is exactly what Spring does.
In case the link above won't work in the future, here is the basic workflow with BOMs.
// BOM project
<project ...>
<modelVersion>4.0.0</modelVersion>
<groupId>baeldung</groupId>
<artifactId>Baeldung-BOM</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>BaelDung-BOM</name>
<description>parent pom</description>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>test</groupId>
<artifactId>a</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>test</groupId>
<artifactId>b</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>test</groupId>
<artifactId>c</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
// importing the BOM in your project
<project ...>
<modelVersion>4.0.0</modelVersion>
<groupId>baeldung</groupId>
<artifactId>Test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Test</name>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>baeldung</groupId>
<artifactId>Baeldung-BOM</artifactId>
<version>0.0.1-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<dependency>
<groupId>test</groupId>
<artifactId>b</artifactId>
<!-- version and scope omitted, inherited from the BOM, 1.0 and compile (you can override them here, but that defeats the purpose) -->
</dependency>
</dependencies>
</project>
Note that importing a BOM does NOT add all of its dependencies specified in the dependencyManagement section, unless you add them in the dependencies section of your project. It's like a product catalog, it shows you what the BOM is offering you.
Here is the Spring Boot 2.3.0 dependencies pom.xml, with dependencyManagement section to see how a real world BOM looks like (or just parent, if you want).
If you ever wanted to use Spring 6, Hibernate 5 and JUnit 5 & Assertion lib friends, assuming that all of them provide a BOM, you could include those 3 BOMs and every time you need to upgrade Spring version for your project, all you'd need is an update to the imported Spring BOM's version.

`'dependencies.dependency.version'` is missing in multi module inherited project though we use <dependencymanagement>

I have a common dependency that needs to be used across different poms.Here for ex spring-ws-core is what I need to use on Pom A, Pom B etc .Pom A will inherit properly but when I remove the version in Pom B it is throwing issue.
Common
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-ws-core</artifactId>
<version>5.0 RELEASE</version>
</dependency>
Pom A
<dependecyManagement>
<dependencies>
<dependency>
<groupId>Common</groupId>
<artifactId>Common</artifactId>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependecyManagement>
<dependencies>
Note:I am not using version since it is inheriting from
<dependencymangement > centralized pom
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-ws-core</artifactId>
<dependency>
</dependencies>
Pom B:
**Parent will be Pom A **
<parent>
<groupId>Pom A</groupId>
<artifactId>Pom A</artifactId>
<version>someversion</version>
<relativePath>pom A.xml</relativePath>
</parent>
This doesn't have <dependencymanagement> since it should be inherited from Pom A since Pom A has <dependecymanagement>
Now my issue if I add any dependency without version in Pom B it is throwing 'dependencies.dependency.version' is missing.How to inherit to Pom B from common is what I am looking for .If I add <dependencymanagement> in Pom B it is not working as expected .
Anyone can shed some light would be appreciated.

Maven: How a project can be able to reference a class from a secondary previous dependency

I am working with Maven and creating my own modules.
I have the 01CentralDomain project
with the simple com.manuel.jordan.domain package with three #Entities
one of them is for example Product
Part of its pom.xml is:
<modelVersion>4.0.0</modelVersion>
<groupId>com.manuel.jordan.centraldomain</groupId>
<artifactId>central-domain</artifactId>
<packaging>jar</packaging>
<name>01CentralDomain</name>
<version>1.0.1</version>
<url>https://github.com/manueljordan/</url>
I have the other 02CentralDomain project
again with the same simple com.manuel.jordan.domain package with two new #Entities
one of them is for example User
Consider this how an extension of the first project
Part of its pom.xml is:
<modelVersion>4.0.0</modelVersion>
<groupId>com.manuel.jordan.centraldomain</groupId>
<artifactId>central-domain</artifactId>
<packaging>jar</packaging>
<name>02CentralDomain</name>
<version>1.0.2</version>
<url>https://github.com/manueljordan/</url>
...
<dependencies>
<dependency>
<groupId>com.manuel.jordan.centraldomain</groupId>
<artifactId>central-domain</artifactId>
<version>1.0.1</version>
</dependency>
</dependencies>
Therefore practically my second project has access to the first project, it about to be able to reference the three entities, like Product, we can assume my domain package now has a total of 5 entities, 3 from the first + 2 for the second.
Now I have a third project (02CentralExecution)
Part of its pom.xml is:
<modelVersion>4.0.0</modelVersion>
<groupId>com.manuel.jordan.centralexecution</groupId>
<artifactId>central-execution</artifactId>
<packaging>jar</packaging>
<name>02CentralExecution</name>
<version>1.0.2</version>
<url>https://github.com/manueljordan/</url>
...
<dependencies>
<dependency>
<groupId>com.manuel.jordan.centraldomain</groupId>
<artifactId>central-domain</artifactId>
<version>1.0.2</version>
</dependency>
</dependencies>
Theoretically the third project has access to the second project source code and therefore for the first project source code too.
It does not work, the third project only can access the second project source code.
I can use the User class but not the Product class.
Even if I declare both
<dependencies>
<dependency>
<groupId>com.manuel.jordan.centraldomain</groupId>
<artifactId>central-domain</artifactId>
<version>1.0.1</version>
</dependency>
<dependency>
<groupId>com.manuel.jordan.centraldomain</groupId>
<artifactId>central-domain</artifactId>
<version>1.0.2</version>
</dependency>
</dependencies>
It does not work.
How I can get working this?
You should use different artifactId's. Maven considers (roughly) groupId:artifactId as the "primary key" for your module, and if you depend on two different versions of module XXX:YYY then it will just include the "most appropiate one", which is usually the one with the higher version.
Furthermore, it turns out that two different modules is what you actually want, as you do not really have two versions of the same module, but a module X that depends on a different one Y, and a third module Z that depends on both X and Y (direct dependency on X, and indirect dependency on Y through X).
So if you have:
01CentralDomain pom.xml:
<modelVersion>4.0.0</modelVersion>
<groupId>com.manuel.jordan.centraldomain</groupId>
<artifactId>central-domain-01</artifactId><!-- note renamed artifactId -->
<packaging>jar</packaging>
<name>01CentralDomain</name>
<version>1.0.1</version>
<url>https://github.com/manueljordan/</url>
02CentralDomain pom.xml:
<modelVersion>4.0.0</modelVersion>
<groupId>com.manuel.jordan.centraldomain</groupId>
<artifactId>central-domain-02</artifactId><!-- note renamed artifactId -->
<packaging>jar</packaging>
<name>02CentralExecution</name>
<version>1.0.1</version><!-- Not a higher version, but a different module!!! -->
<url>https://github.com/manueljordan/</url>
...
<dependencies>
<dependency>
<groupId>com.manuel.jordan.centraldomain</groupId>
<artifactId>central-domain-01</artifactId>
<version>1.0.1</version>
</dependency>
</dependencies>
02CentralExecution pom.xml:
<modelVersion>4.0.0</modelVersion>
<groupId>com.manuel.jordan.centralexecution</groupId>
<artifactId>central-execution</artifactId>
<packaging>jar</packaging>
<name>02CentralExecution</name>
<version>1.0.1</version><!-- Version 1.0.2 makes no sense now, just use 1.0.1 -->
<url>https://github.com/manueljordan/</url>
...
<dependencies>
<dependency>
<groupId>com.manuel.jordan.centraldomain</groupId>
<artifactId>central-domain-02</artifactId>
<version>1.0.1</version>
</dependency>
</dependencies>
Then everything should "just work"

Maven include parent classes

I have a fairly simple maven-ized Java project, but am having trouble getting my head around it.
My parent module defines a lot of Java classes (and dependencies) that I expect to be useful for several child modules. One of the child modules is dedicated to deploying a web app, so it needs a few extra classes (the servlets) plus everything from the parent module.
The file structure looks like this
- parent
- src
- pom.xml
- child
- src
- pom.xml
My parent pom looks like this:
<project>
<groupId>my.group</groupId>
<artifactId>parent</artifactId>
<version>0.0.1</version>
<packaging>pom</packaging>
...
<modules>
<module>child</module>
</modules>
</project>
And the child looks like this:
<project>
<artifactId>child</artifactId>
<packaging>war</packaging>
<parent>
<artifactId>parent</artifactId>
<groupId>my.group</groupId>
<version>0.0.1</version>
</parent>
...
</project>
Is this all I need to have the child know about the classes and dependencies defined in parent? It doesn't seem to be: eclipse gives compile errors, and running mvn clean package from parent folder or child folder results "cannot find symbol" messages any time a class from parent is mentioned.
What am I doing wrong?
I'd change the structure of your project like this:
parent (pom)
core (jar, with all the classes that used to be in parent)
child (war, depends on core)
Parent:
<project>
<groupId>my.group</groupId>
<artifactId>parent</artifactId>
<version>0.0.1</version>
<packaging>pom</packaging>
<modules>
<module>core</module>
<module>child</module>
<!-- possibly more modules... -->
</modules>
</project>
Core:
<project>
<parent>
<groupId>my.group</groupId>
<artifactId>parent</artifactId>
<version>0.0.1</version>
</parent>
<artifactId>core</artifactId>
<packaging>jar</packaging>
</project>
Child:
<project>
<parent>
<groupId>my.group</groupId>
<artifactId>parent</artifactId>
<version>0.0.1</version>
</parent>
<artifactId>module1</artifactId>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>my.group</groupId>
<artifactId>core</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
Since your parent module's packaging is "pom", no jar file will be provided and no .class file from that will be accessible to other modules. Try changing it to "jar" and give it another try.
Although it's not a good practice to have a parent as "jar". I'd stick to the "pom" parent and probably create a new "core" or "common" child and make my "war" depend on it.
Try to add an internal dependency in child module pom, so it is familiar with its parent
<dependency>
<groupId>my.group</groupId>
<artifactId>parent</artifactId>
<version>0.0.1</version>
</dependency>
The first thing you should do is to define your dependencies within a dependencyManagmeent block in your parent like this:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-a</artifactId>
<version>1.0</version>
</dependency>
...
</dependencies>
</dependencyManagement>
and furthermore in your child you should say which dependency your child would like to use as dependency:
<dependencies>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-a</artifactId>
<!-- No version given. See dependencyManagement -->
</dependency>
...
</dependencies>

How to define organization specific parent pom

Env: Maven 2.2.1
I have two java projects under svn (projectA, projectB). My maven structure is as follows..
For projectA
pom.xml (contains ProjectA parent pom definitions)
module moduleA
module moduleB
For projectB
pom.xml (contains ProjectB parent pom definitions)
module moduleC
module moduleD
projectA/pom.xml and projectB/pom.xml contain common definitions like junit, selenium, compiler, eclipse plug-ins which are common to both projects. (e.g. given below)
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.4</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency
How should I create / organize a organization specific project pom which includes such common definitions, so that individual projects don't have to re-create / maintain one. Can someone provide some snippets or projects which have already done this before?
EDIT1:
company/pom.xml
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany</groupId>
<artifactId>company</artifactId>
<packaging>pom</packaging>
<name>parent</name>
<version>1.0.0</version>
<build>
<defaultGoal>install</defaultGoal>
</build>
<dependencies>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.4</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
</dependencies>
projectA/pom.xml
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.mycompany</groupId>
<artifactId>company</artifactId>
<version>1.0.0</version>
</parent>
<groupId>com.mycompany</groupId>
<artifactId>projectA</artifactId>
<packaging>pom</packaging>
<name>projectA</name>
<version>1.0.0</version>
<modules>
<module>moduleA</module>
<build>
<defaultGoal>install</defaultGoal>
</build>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
projectA/moduleA/pom.xml
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.mycompany</groupId>
<artifactId>projectA</artifactId>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>com.mycompany</groupId>
<artifactId>moduleA</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>moduleA</name>
<build>
<finalName>moduleA</finalName>
<defaultGoal>install</defaultGoal>
</build>
<dependencies>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</dependency>
</dependencies>
Throws the following error:
Project ID: com.mycompany:moduleA
POM Location: c:\temp\maven\projectA\moduleA\pom.xml
Validation Messages:
[0] 'dependencies.dependency.version' is missing for commons-lang:comm
ons-lang:jar
[1] 'dependencies.dependency.version' is missing for javax.servlet:ser
vlet-api:jar
I would seriously reconsider adding dependencies into a "super" POM, this unnecessarily couples the projects (but may hint that if the projects aren't disparate then they should be merged anyway).
I think the last comment by #lexicore is poignant too, to expand on the OOP analogy it also feels like "mixing levels of abstraction".
Alex Gitelman provides the correct answer, you need to use dependencyManagement as shown here Dependency Scope
Maven3 is supposed to be supporting POM fragments see How to use Maven 3 mixins? which I've long been waiting for.
We have an organisation Über POM but this just contains:
<organization>
<name>...</name>
<url>...</url>
</organization>
<developers>
<developer>
<id>...<id>
<name>...</name>
<email>...</email>
<roles>
<role>...</role>
</roles>
</developer>
<distributionManagement>
...
</distributionManagement>
<repositories>
<!-- your proxy repo here -->
</repositories>
These are things that change very rarely (if we change our repository/distribution-management then all projects must change, if a developer leaves or joins we can update the project POMs at any time convenient).
Dependencies belong specifically to the module under consideration, just because two independent project happen to share dependencies now doesn't mean they always will. I completely understand the annoyance of having to copy 'n' paste reams of XML for each project (compiler plugin, reporting plugins, junit etc), but differing levels of activity in each project will surely mean they diverge at some point.
WRT cascade builds in Continuous Integration, if project A demands a change in the super dependencies POM, then all you other projects will be forced to rebuild - maybe fine if you've only 2 projects but even then did you checkout and build both before committing the change?
If it's only dependencies that you need to reuse, create another project with packaging pom and specify dependencies there. Let's call it OrgDependencies Then include it as dependency in your projectA and projectB. It will transitively pull all dependencies from OrgDependencies project.
In your example, in projectA, instead of
<parent>
<groupId>com.mycompany</groupId>
<artifactId>company</artifactId>
<version>1.0.0</version>
</parent>
Try putting
<dependencies>
<dependency>
<groupId>com.mycompany</groupId>
<artifactId>company</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
And remove dependencies to commons-lang etc from modules.
Update.
While my previous solution with transitive dependencies should work, actually what you need
is <dependencyManagement> section in your company wide pom.xml
That's where you define versions.
Note: Anything in dependencyManagement section is not really a dependency but just a descriptor that allows to specify version and exclude transitive dependencies (if necessary) in case normal dependencies section specifies that dependency. So you can put as many items in dependencyManagement as you want, it will not make all descendants dependent on them.
I tested it and it works:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany</groupId>
<artifactId>company</artifactId>
<packaging>pom</packaging>
<name>parent</name>
<version>1.0.0</version>
<build>
<defaultGoal>install</defaultGoal>
</build>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.4</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
In OS projects I am normally using 3+ levels of POMs:
"Company-wide POM" contains dev-wide definitions like distribution management, individual plugin versions etc. Very stable, normally has one-number version. Example: Sonatype OSS Parent.
"Project POM" contains project-wide definitions: Java compiler version, dependency management etc. Parent is company-wide POM. Example :JAXB2 Basics Project. Version is updated with each release.
"Module POMs" on different levels. List individual dependencies (versions of dependencies are inherited from the project POM), add "special" build steps. Example: JAXB Basics.
I saw a similar pattern an other OS projects (like Apache's) as well.
A few more comments:
You may also have the "department POM" or "product POM" depending on the company size and product organization.
Think of POM inheritance pretty much as of OOP inheritance. What would you put into which abstract class so that class hierarchy is stable but dynamic? For instance, it would not make sense to define versions of dependencies in the company-wide POM since versions change too often. On the contrary, defining distribution management in earch of the projects would hurt the DRY principle.

Categories