Maven Overview

Maven - Relationship between Lifecycle, Phases and Goals

Introduction

Maven is a build tool. A build tool automates processes related to building software. This can include
  • compilation of the source-code
  • (triggering) auto-generating (parts of) the code, e.g.with css
  • generation of documentation
  • packaging of the compiled-code
  • generation of containers (e.g. a docker-container)
  • deploying packages or containers on a remote server

Installing Maven is straight-forward, as describe on the Maven project webpage. If you run into problems, remember that stack overflow is your friend.

This overview doesn’t replace a Maven tutorial or the Maven documentation. The aim is to equip you with the basics, so you gain a better understanding of what you are doing when you are working hands-on with Maven (like in the next blog posts).

The Basics

Maven uses so-called POMs (Project Object Model) for its configuration. These POMs tell Maven how to build the software. A POM is often written in XML, but also other dialects can be used, such as YAML. I recently use YAML for POMs as they are easier to read. However, as XML is still more common, I will use XML in this introduction.

<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>...</groupId>
    <artifactId>...</artifactId>
    <version>...</version>
    <packaging>...</packaging>
    <dependencies>...</dependencies>
    <parent>...</parent>
    <dependencyManagement>...</dependencyManagement>
    <modules>...</modules>
    <properties>...</properties>

    <build>...</build>
    <reporting>...</reporting>

    <repositories>...</repositories>
    <pluginRepositories>...</pluginRepositories>
    <profiles>...</profiles>
</project>

Maven reads and executes the POM in the following sequence

  • Download the dependencies into a local repository (if not present)
  • Execute the build lifecycles, phases and/or goals (in relation to the desired profile)
  • Execute plugins

You start the build process with Maven using

mvn [build-lifecyle||build-phase||build-goal]

The build lifecycle consists of build phases; the build phases consist of build goals. These are always executed in sequential order. If a build phase is executed, all previous build phases are executed, too.

Before Maven executes the command it resolves the dependencies of the used libraries (jar-files in Java) recursively, i.e. you specify lib11.jar in the POM, which references,lib22.jar which in turn references lib33.jar. This is a significant advantage of Maven, as it resolves these dependencies fully automatically. However, in large projects, this process can take a quite long time. Hence Maven maintains a local repository (which is stored on your computer), where it stores the jars once downloaded.

If you work with different environments with different configurations (e.g. local, integration, pre-production, production), you can use different build profiles.

You can have more than one POM. POMs are organised in a tree structure. All Maven POMs inherit from a Super-POM. The child POMs get to override the configuration of the parent-POM.

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.4.3.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

If you have done builds with shell-scripts or ant, you were able to use low-level commands (copy or rename files, …). You have described how to build something. In Maven you define what to build (using the above build lifecycles, phases and goals).

Maven Directory Structure

Out-of-the-box Maven uses the following standard directory-structure for a Java-project (generated with mvn install)

[project-name]
    ├───pom.xml
    ├───README.txt
    ├───NOTICE.txt
    ├───LICENSE.txt
    └───src
        ├───main
        │   ├───java
        │   ├───resources
        │   ├───filters
        │   └───webapp
        ├───test
        │   ├───java
        │   ├───resources
        │   └───filters
        ├───it
        ├───site
        └───assembly

Maven Dependencies

One of the key features of Maven is dependency management of libraries you use in your project. Before Maven you had to care about what jar-files in which version you had to include. Maven does a large part of the work here and resolves the dependencies between the jar-files automatically.

You define your dependencies in the <dependency>-element of the POM. I recommend copying the definitions directly from the Maven Central Repository, which include groupId, artifactId and version. Those three parameters also define the directory structure of your local repository~/.m2/repository, in the example below: ~/.m2/repository/junit/junit/4.12

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.1.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>

The jar-file of your project will also be a part of the local repository. Hence, on top of your pom.xml you will also define groupId, artifactId and a version amongst other parameters.

<?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">

    <groupId>com.vividbreeze.grammardrill</groupId>
    <artifactId>grammardrill</artifactId>
    <version>0.1</version>
    <packaging>jar</packaging>

    <name>grammardrill</name>
    <description>Grammar Drill Application</description>

    <parent>
        ...

Maven Repositories

Maven repositories are jar-files that contain meta-data in the form of a POM. This POM-files often include their dependencies, amongst other information. So quite often, dependencies are downloaded recursively into your local repository by Maven.

Maven Repository Structure
Maven Repository Structure

Maven knows the following repositories, which are scanned through in this sequence

  • Local Repository, a directory on the computer Maven runs. It functions as a cache for all dependencies Maven downloads from the Central or Remote Repository. The Local repository is shared among different projects. Also, your project-jar can be stored in the Local Repository, so other projects can access it. The default location of your local repository is ~/.m2/repository (it can be changed in ~/.m2/settings.xml).
  • (Maven) Central Repository is used when Maven can’t find a dependency in your local repository. The Central Repository is provided by the Maven Community. You can go to their webpage and copy and paste the artefacts for standard build tools, including Maven (e.g. the Apache Commons Collection).
  • Remote Repository is an external server that contains dependencies. A remote repository is often used to share organisation-wide dependencies that are used by other projects within the organisation. You can specify external repositories either in the repositories section or in your profile-section in the POM or for all projects in ~/.m2/settings.xmlArtifactory is an example for a remote repository.
 <repositories>
    <repository>
        <id>spring-snapshots</id>
        <url>http://repo.spring.io/snapshot</url>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
        </repository>
        <repository>
            <id>spring-milestones</id>
        <url>http://repo.spring.io/milestone</url>
    </repository>
</repositories>

Maven Build Life Cycles, Phases and Goals

Maven - Relationship between Lifecycle, Phases and Goals
Maven – Relationship between Lifecycle, Phases and Goals (example)

Build Life Cycles

Maven provides three build-in build life cycles
  • default – project deployment (mvn)
  • clean – project cleaning (mvn clean)
  • site – site documentation (mvn site)

Build Phases

As mentioned above, a build lifecycle is made up of different build phases. The default-lifecycle executes the following build phases (amongst others) in sequential order:

  1. validate – validates the consistency of the project
  2. compile – compiles the source code
  3. test – runs the (local) tests
  4. package – packages the compiled and tested code
  5. verify – runs integration tests on the packaged software
  6. install – pushes the package to the local Maven repository
  7. deploy – deploys the final package to the remote repository
If you call mvn in the project directory (that contains the pom.xml) all the above steps will be executed in sequential order.  You can also tell Maven to execute a build phase (mvn [phase-name]). As mentioned above, in this case, Maven will also execute the phases before the given phase, e.g. when you call mvn test – validate and compile are executed before a test.

You can trigger two lifecycles, e.g.,mvn clean install which cleans the project first and then calls all the phases up to install.

(Build) Plugin Goals

Each of the phases above is carried out different steps, called (build) plugin goals. I plugin goal is a specific task in the build process. One plugin goal can be bound to none, one or more than one phase. You can call a goal that is not linked to a build phase by mvn dependency:copy-dependencies.

Maven Plugins

As mentioned in the previous chapter, goals are implemented via plugins. There are basically

  • build plugins are responsible for the build process (configurable in the <build>-element of the POM)
  • reporting plugins are responsible for the site generation (configurable in the <reporting>-element of the POM)

Each plugin (aka Mojo) has a groupId, artifactId and version and can contain its own configurations and dependencies. You can find a list of all available plugins supported by the Maven Project on their webpage. Of course, you can use plugins developed by third-parties or develop your own plugins. You can configure each plugin inside of the <configuration>-element of the <plugin>-element. You can connect plugin goals with a phase using the <phase>-element. The Plugin will be executed after the deploy goal has run. Since Maven 3.0.3 the plugins will be executed in the same order as they are listed in the POM.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-checkstyle-plugin</artifactId>
    <version>3.0.0</version>
    <executions>
        <execution>
            <configuration>
                <configLocation>config/checkstyle/checkstyle.xml</configLocation>
            </configuration>
            <goals>
                <goal>check</goal>
            </goals>
            <phase>validate</phase>
        </execution>
    </executions>
</plugin>

Maven Build Profiles

As mentioned above, Maven supports different profiles (build configurations). It is often used to build your software on different environments, e.g. local, integration, production or similar. You don’t need to repeat every single build-step in a profile, but instead override the elements that are different. You can also have global profiles or profiles per user (defined in~/.m2/settings.xml).

<profiles>
    <profile>
        <id>test</id>
        <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-antrun-plugin</artifactId>
                <version>1.1</version>
                <executions>
                    <execution>
                    <phase>test</phase>
                    <goals>
                        <goal>run</goal>
                    </goals>
                    <configuration>
                        <tasks>
                            ...
                        </tasks>
                    </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
        </build>
    </profile>
</profiles>

Inside the POM, profiles they are located in the profiles element and are referenced by their profile-name, e.g. mvn -P [profile-name] install You can also set the active profile in the settings.xml or you can set triggers inside the profile-element inside the activation-element.

Running Maven

Maven expects a list of Build Lifecycles, Phases and Goals that are executed in the specified order, such as

mvn clean dependency:copy-dependencies package

which first calls the build lifecycle clean, than the goal copy-dependencies and than the build-phase package.

Miscellaneous

Properties

Properties can be defined or set in the POM in XML with <[property-name]>[property value]</[property-name]>.

Custom properties can be defined at the beginning of the POM and are accessible inside the POM by using ${property-name}. You can also set global values that are valid inside the POM like in the example below. Properties can be also used to configure plugins.

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
</properties>

Remarks

This short introduction should have provided the basics of Maven, so you are able to read and maintain a POM and build your project. Maven is a strong build tool and hence there is much more to it. I will show hands-on examples throughout the next tutorials.

Chris