It most cases when we need maven, we need a parent child like structure. I personally like to start my projects with a parent bom file where I manage all my dependencies and plugins.
<?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>
<artifactId>commons</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>com.sergiuoltean.test</groupId>
<artifactId>bom</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath/>
</parent>
</project>
Now I need to put the version of the parent which is fine for this situation. But what happens when we need layering of our application. Given a 3 tier architecture we would have 3 maven modules(eg presentation, services and data access). Let’s assume this is a microservice and we need to version it.
Take for example the presentation module definition
<parent>
<groupId>com.sergiuoltean</groupId>
<artifactId>maven-parent</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>presentation</artifactId>
<version>${project.parent.version}</version>
We see there the parent version tag which is hardcoded. If we remove it maven will complain.
[ERROR] The build could not read 1 project -> [Help 1]
[ERROR]
[ERROR] The project com.sergiuoltean:presentation:${project.parent.version} (/Users/sergiuoltean/fun/maven-parent/presentation/pom.xml) has 1 error
[ERROR] 'parent.version' is missing. @ line 7, column 11
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/ProjectBuildingException
If we try to put an expression maven will complain
<parent>
<groupId>com.sergiuoltean</groupId>
<artifactId>maven-parent</artifactId>
<version>${prj.version}/version>
</parent>
<artifactId>presentation</artifactId>
<version>${project.parent.version}</version>
</parent>
[ERROR] The build could not read 1 project -> [Help 1]
[ERROR]
[ERROR] The project (/Users/sergiuoltean/fun/maven-parent/presentation/pom.xml) has 1 error
[ERROR] Non-parseable POM /Users/sergiuoltean/fun/maven-parent/presentation/pom.xml: end tag name </parent> must match start tag name <version> from line 10 (position: TEXT seen ...<version>${prj.version}/version>\n </parent>... @11:12) @ line 11, column 12 -> [Help 2]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/ProjectBuildingException
[ERROR] [Help 2] http://cwiki.apache.org/confluence/display/MAVEN/ModelParseException
If we keep it hardcoded the release will not be as straightforward as we would have liked. We need to go in each module and change the parent version when we prepare a new release. And on top of that there is room for mistakes(eg forget to change the parent to a module which means it may reference the previous parent). Now ideally during development the modules should have a snapshot version in which case maven release plugin may be of help.
There is also Maven CI Friendly Versions. Now we can use the ${revision} placeholder to get what we want. This means we write
<?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.sergiuoltean</groupId>
<artifactId>maven-parent</artifactId>
<version>${revision}</version>
<packaging>pom</packaging>
<properties>
<revision>1.0.0</revision>
</properties>
<modules>
<module>presentation</module>
<module>services</module>
<module>data-access</module>
</modules>
</project>
and the child module will change to
<?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>
<parent>
<groupId>com.sergiuoltean</groupId>
<artifactId>maven-parent</artifactId>
<version>${revision}</version>
</parent>
<artifactId>presentation</artifactId>
</project>
We can now control the version in a single place. But we are not done yet. When we deploy or install the modules we see that the placeholder is still there making the modules unusable. We also need the flatten-maven plugin to fix this.
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<version>${flatten.version}</version>
<configuration>
<updatePomFile>true</updatePomFile>
<flattenMode>resolveCiFriendliesOnly</flattenMode>
</configuration>
<executions>
<execution>
<id>flatten</id>
<phase>process-resources</phase>
<goals>
<goal>flatten</goal>
</goals>
</execution>
<execution>
<id>flatten.clean</id>
<phase>clean</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
</plugin>
When we check the m2 repo we see
<?xml version="1.0" encoding="UTF-8"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.sergiuoltean</groupId>
<artifactId>maven-parent</artifactId>
<version>1.0.0</version>
</parent>
<groupId>com.sergiuoltean</groupId>
<artifactId>presentation</artifactId>
<version>1.0.0</version>
</project>
Much easier now to control the version.
Thank you for the article. it is very helpful. I got the version settings of my multi module mvn project work the 1st time. just 1 last question. the definition of revision variable in my parent pom is a variable, and my child 1 module depends on my child 2 module. When building from parent dir, it works all fine if i give a real value to revision; if I leave the definition of revision in parent pom as an env variable, even if the variable is defined, I got complaints building child 1 module saying it cannot find child 2 module of the version ${env.my_version} 😦 Any way to fix that, if not passing additional value to mvn install?
LikeLike
Hello,
First of all I would not recommend to use env vars and maven. It’s always better to pass properties as arguments. Having said that I tried to put the revision property as env variable.
${env.MY}
Exported the variable
export MY=”1.2.3″
then run mvn clean install with no issues.
Hope this helps
LikeLike