Steps reference

Parent creation

Parent POM file is created with the following archetype

mvn archetype:generate -DgroupId=com.ezefarina.maven -DartifactId=maven-training-base -DarchetypeGroupId=org.codehaus.mojo.archetypes -DarchetypeArtifactId=pom-root -DinteractiveMode=false

Defined the following elements:

For full POM reference you can check the official specification

And here is a good list of available archetypes

Module aggregation

A child module is added, based on the following archetype

mvn archetype:generate -DgroupId=com.ezefarina.maven -DartifactId=maven-training-base-module -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

By running an artifact creation like this, standing over an existing pom location it will automatically add the new artifact as a child of it. In this case, the module added is a simple Java Main class with its default project structure. This default structure is defined in the archetype itself and it contains the following components:

Second module aggregation

Another child module is added, using a web application archetype

mvn archetype:generate -DgroupId=com.ezefarina.maven -DartifactId=maven-training-base-webapp -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false

This new module is added as well to the parent POM, but in this case the output structure is a bit different, as the archetype is different. We have an index.jsp, a web.xml, a resources folder, and some other elements that are common (or needed) for a really basic web application.

Centralized dependencies management in parent POM

By doing so, we ensure that the utilization of the project dependencies are common to all its children, making it easier to see, manage and update them. This can be achieved by defining section in the parent pom. This tag means that all that it contains are not dependencies itself that are strictly going to be used (besides they are potentially going to be), but they are there to be parametrized for its children to be used. We define here, that the JUnit version is 3.8.1. We will not specify the version in the version of the children as we want it to be handled by the parent only. On the other side, we are not defining the scope on the parent, as the usage of the artifact may be different for each module. It can be done, but it depends on the case.

For further info about dependencies management, check the official doc here

Multi-module inherited properties

By defining properties in the parent POM, they will be available for its childs. On the other side, if we define a property in a specific module it won't be available for its parent, as it only works downstream. We can do this by defining the section. Not necessarily have to be made in the parent, it just depends on the visibility you need for each of them.

Profiles to alter a lifecycle

Configuring different profiles allow us to define different behaviours based on which profile is being requested. We could export a coverage report only for development, configure a different set of URLs or usernames for production, just as an example. In this case, the development profile is enabled by default (it gets executed if you don't specify a profile), and a production role is off by default. The difference between them is that they will set the same property with a different value. The idea is to spit out the logs to the console on development, and to a file on production environment. So depending where are we going to send our build is which profile we will activate like this:

mvn install -Pproduction

For further reference about the Lifecycle, Phases and goals check this article

Fur further info about check this other one

Plugin basic configuration

You would need to configure a plugin when you need to attach a specific functionality to a specific phase. In this example, we are speficying which should be the name of the output WAR file on the webapp module. In this case the maven-war-plugin is the default plugin used for war packaging during package phase. In this case we are explicitly adding it to parametrize the final name this war will have (of course you are able to do a lot more than this, it's just a simple example). Each plugin has a default phase defined, that it will be attached to if you don't define a different one.

Centralized plugins management in parent POM

As well as you are able to centralize dependencies configuration on the parent POM in order to inherit these definitions and make them common to all the submodules, you can do just the same with the plugins. This will be more powerful than the dependencies inheritance, as a plugin has a lot of configurations that the dependencies doesn't. In this example we are moving this config to the parent POM, and only inheriting it into the WebApp module. This is done by defining the tag

This is the official doc about the plugin management functions

Controlled plugin configuration inheritance

While working with pluginManagement, you can centralize a lot of configurations, but there would be the case where for a specific module you don't want to inherit the configurations as is (maybe this module has a particularity). In this case, you have the option to append, merge or override the configuration. This is the case of this example. We are overriding the incoming plugin configuration from the parent POM by setting the attribute combine.self="override"