Maven Notes

Maven is a build and dependency management tool for Java applications.

Maven Terminologies

Project Object Model (pom.xml) file

  • Contains all of the details about the current project including, configurations, plugins, dependencies, goals. It also describes build process of the project.
  • pom.xml file also describes the co-ordinate of the project (artifact) so that it can be addressed remotely by maven.

Repositories

  • Stores build artifacts and dependencies for Maven projects, there are two kind of repositories:
    • Local Repository: Local repository resides inside user’s computer (inside maven’s installed directory). Maven saves dependencies and build artifacts in local repository as cache so that those can be used in other local maven projects.
    • Remote repository: These repositories are present remotely.  repo.maven.apache.org is one of the remote maven repository.
      • By default, Maven uses its central repository (located at https://repo.maven.apache.org/maven2) to serve the dependencies.
      • An alternative remote repository can be used by maven by specifying repository details in the <repositories> tag in pom.xml file

Build Lifecycle

  • Build lifecycles are series of phases which help building the project. Maven provide three built-in build lifecycles: default, clean and site
  • As an example, the default lifecycle comprise of following phases: (source)
    • validate – validate the project is correct and all necessary information is available
    • compile – compile the source code of the project
    • test – test the compiled source code using a suitable unit testing framework. These tests should not require the code be packaged or deployed
    • package – take the compiled code and package it in its distributable format, such as a JAR.
    • verify – run any checks on results of integration tests to ensure quality criteria are met
    • install – install the package into the local repository, for use as a dependency in other projects locally
    • deploy – done in the build environment, copies the final package to the remote repository for sharing with other developers and projects.
  • Any of the phase mentioned above can be directly executed through command line using mvn [phase-name]
  • An another commonly used phase is clean phase from the clean lifecycle. calling mvn clean executes the pre-clean and clean phase. which results in cleaning the files that were generated by previous build.
  • A full list of build-in maven phases can be seen here.

Note: executing a phase executes all of the previous phases of that build lifecycle automatically.

Following is an example image where install phase (of default lifecycle) is called using command line

maven install command line
  • Goals and Plugins
    • Goals and Plugins are fundamental part of Maven’s core framework.
    • Goals are specific tasks/actions which help in building process. Goals are bound to phases of build lifecycle with the help of POM file configurations (or build-in configurations)
    • A Plugin is a group of goals. As an example the Compiler Plugin compiles the source code. The compiler plugin has two goals: compile to compile main source files, and testCompile to compile test source files.
    • Note that individual goals of same plugin can be bound to different phases.

Maven CLI structure

mvn [options] [<goal(s)>] [<phase(s)>]

example:

  • Execute install phase of default life-cycle:
    • mvn install
  • Execute install phase using myProfile profile defined in pom.xml
    • mvn -P myProfile install
  • Execute generate goal of archetype plugin:
    • mvn archetype:generate

Maven File Structure

Following are most commonly used directories in maven project. Full list of directories/files can be seen here.

  • src: Stores java source
    • main
      • java: Store java application source code
        • package: root package
      • resources: Stores resources used by application. (ex: configuration/properties files used by application)
    • test
      • java: Store source code used during test phase
      • resources: Store resources used during test phase
  • target: This is an output folder. It contains all of the built output files.
  • pom.xml: Contains all of the information and configurations of maven project.

If a non-default structure of files and directories has to be used, information about the structure should be provided in the <build> tag of pom.xml file

<build>
    <sourceDirectory>mySrc</sourceDirectory>
    <directory>myTarget</directory>
</build>

POM inheritance

Much like java class inheritance, POM inheritance mostly follows similar concepts.

  • A POM file can inherit another POM file, inheriting all of the properties/configurations defined in the parent POM file to the children POM file.
  • All POM files (including the root POM file) inherit a super POM file. The super POM file is present in Maven’s installation directory. All of the default configurations (e.g.. The file structure, default remote repository, default package type etc.) are present in that super POM file.
  • mvn help:effective-pom command can be used to see the final interpreted pom resulting by all of the inherited configurations from parent (including global) pom files.
  • Any configuration specified in child pom file overrides same configuration specified in parent pom file.
  • To make a pom file inherit from parent pom file, specify artifactId, groupId, version inside the <parent> tag. Note that the parent pom file should be present in the maven repository before using its coordinates to inherit in child pom.xml
<parent>
    <groupId>com.khansajid</groupId>
    <artifactId>maven-examples-proj</artifactId>
    <version>1.0</version>
</parent>

Profiles

Maven allows creation of different profiles within the pom xml file. A profile is a group of elements/configurations which override the default elements/configurations defined in pom file. A profile contains <id> tag for identification of the profile. A pom file can contain multiple profiles.

    <profiles>
        <!-- development profile -->
        <profile>
            <id>development</id>
            <build>
                <!-- save the built project to targetDev folder -->
                <directory>targetDev</directory>
            </build>
        </profile>
        
        <!-- production profile -->
        <profile>
            <id>production</id>
            <build>
                <!-- save the built project to targetProd folder -->
                <directory>targetProd</directory>
            </build>
        </profile>
    </profiles>

To run a maven phase/goal with configuration defined in a specific profile, use mvn -P [profile-id (comma delimited for multiple profiles)] [phase-name/goal]

To automatically use a specific profile based on some defined environment property (or based on some other defined means), the <activation> tag is used. Whenever the condition defined in <activation> tag is matched, that profile gets activated and therefore is used by maven to run phase/goals. Multiple profiles can be activated during a phase based on the matched conditions.

As an example, in following pom file, the development profile is activated/used whenever the USE_PROFILE environment variable is set to DEV in the system

<profile>
    <id>development</id>
    <activation>
        <property>
            <name>env.USE_PROFILE</name>
            <value>DEV</value>
        </property>
    </activation>
    <build>
        <directory>targetDev</directory>
    </build>
</profile>

Other available conditions that can be used in <activation> tag are:

  • <activeByDefault> : If used, the profile is activated/used by default.
  • <property> : Activate/use the profile based on value of specified system variable.
  • <file>: Activate/use the profile based on presence of specified file.
  • <jdk>: Activate/use the profile based on jdk version.
  • <os>: Activate/use the profile based on operating system.

Dependencies

Coordinates of dependency libraries can be specified in pom.xml inside <dependencies> tag. The Maven fetches the dependencies from remote repository if those are not already present in the local repository. Transient dependencies (dependencies of dependencies) are also automatically downloaded by Maven.

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>2.0.13</version>
</dependency>

Dependency Scope

Scope of a dependency is specified using <scope> tag. It is used to determine when a dependency should be included in class path. Some important dependency scopes are:

  • compile: This scope is used by default when not specified. This scope ensures that the dependency is included in every class path. This scope tells maven that the dependency is required to build, test as well as run the project.
  • test: This scope tells maven that the dependency is required only for testing of the project.
  • provided: This scope indicates that the dependency is expected to be provided by the container or system environment during runtime. This means Maven won’t include the JAR file containing the dependency’s classes in the project’s final build

Example: In following pom.xml file, junit dependency is included using test scope

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    <scope>test</scope>
</dependency>

Optional Dependencies and Dependency Exclusion

Explained with the help of following project structure:

Project-A
   -> Project-B
        -> Project-D
              -> Project-E
              -> Project-F
   -> Project C

In above project structure, the project A has dependency on Project B and C (All dependencies defined with the help of POM.XML). Project B depends on D, and then D further depends on Project E and F.

Suppose, Project D adds some minor features to project B, however project B can work even if Project D is not present.

Let’s say That project A do not require the minor feature provided by Project D, however since project B has defined dependency on Project D, Maven will download the Project D (and all it’s sub-dependencies) even for project A.

This can be avoided in two ways:

Optional Dependency

One way is to specify Project D as an optional dependency in Project B’s POM.XML file.

<!-- Project_B/pom.xml -->
<dependencies>
  <dependency>
    <groupId>Project_D</groupId>
    <artifactId>Project-D</artifactId>
    <optional>true</optional>
  </dependency>
</dependencies>

In That way, all of the projects which have dependency on Project B, will download Project D only if the dependency is included explicitly.

<!-- Project_A/pom.xml -->
<dependencies>
  
  <dependency>
    <groupId>Project_B</groupId> <!-- Including B alone would not alo download D -->
    <artifactId>Project-B</artifactId>
  </dependency>
  
  <dependency>
    <groupId>Project_D</groupId> <!-- Have to include D explicitly -->
    <artifactId>Project-D</artifactId>
  </dependency>
</dependencies>

Dependency Exclusion

Let’s say, the optional tag was not present on Project D in the Project B’s pom.xml, in that case, Project A can still exclude Project D from getting downloaded by specifying it in exclusion tag:

<!-- Project_A/pom.xml -->
<dependencies>
  
  <dependency>
    <groupId>Project_B</groupId>
    <artifactId>Project-B</artifactId>
    <exclusions>
      <exclusion>
        <groupId>Project_D</groupId> <!-- Project D will not be included -->
        <artifactId>Project-D</artifactId>
      </exclusion>
    </exclusions>
  </dependency>
</dependencies>

Common Maven Commands

Note: Running a phase of a lifecycle automatically runs all it’s preceding phases of the same lifecycle.

Marked in red are phases, and marked in blue are plugin:goal

CommandDescription
mvn cleanclean up previous build (target directory)
mvn compileCompile all java source code present in source directory into java class files.
mvn test-compileCompile test source codes into the test destination directory.
mvn packageThis takes the compiled code and packages those in a distributable format (For eg. jar format).
mvn installSave the package (created in package phase) to the local repository so that our project can be used as dependency (or for POM inheritance) for other projects locally
mvn deployDone in the build environment, copies the final package to the remote repository for sharing with other developers and projects.
Note: Simply calling this phase, wouldn’t deploy the artifact to remote repository. The location of the remote repository has to be provided in pom.xml file in order to tell maven where to upload the artifact (see more details below)
mvn help:describe -Dcmd=[phase name]
mvn help:describe -Dplugin=[plugin name]
Get information about specified phase or plugin
mvn dependency:copy-dependenciesDownload all dependencies from remote repository to local repository
mvn help:effective-pomShow the effective pom.xml which is used by maven after resolving all of the pom inheritance and active profiles.

Deploying artifact to remote repository

The details of remote repository can be specified in <distributionManagement> tag inside pom.xml file. Calling mvn deploy then uploads the artifact to specified remote repository.

<distributionManagement>
    <repository>
        <!-- id and name can be anything chosen by dev -->
        <id>myRepositoryId</id>
        <name>myRepositoryName</name>
        <url>https://example.com/url-of-my-repository</url>
    </repository>
</distributionManagement>

Combining multiple Modules (Multi Module project)

To combine multiple modules, a base module can be created which contains all of the modules as it’s submodule (specified using pom.xml of base module). Following is the file structure of a multi module project. Note that in the following example screenshot, a root folder directly contains base module as well as multiple sub modules, however, it is also possible to create multiple sub modules inside the base module folder.

The sub modules (module1 and module2) are nothing different but a normal maven projects. The sub modules are combined together with the help of base-maven-module using the pom.xml file present in base-maven-module. Following are the important configurations of pom.xml of base module:

...
    <packaging>POM</packaging>
    <modules>
        <!-- relative path of sub modules -->
        <module>../module1</module>
        <module>../module2</module>
    </modules>
...

Executing a maven command on the base module directory executes the command for each sub modules as well. As an example, calling mvn package creates the package files for both sub modules and places the package files in the target folder of each of the sub modules.

Plugin Configuration/Parameters

List out all of the configurable parameters of the plugin can be seen either at plugin’s documentation page, or by using mvn help:describe -Dcmd=[plugin:goal] -Ddetail command

Parameters of plugin can be changed using either mvn [plugin:goal] -D[property]=[value], or using pom.xml file as shown in example code below

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<build>
  <plugins>
    <plugin>
      <groupId>groupId.of.plugin</groupId>
      <artifactId>artifactId-of-plugin</artifactId>
      <version>1.0</version>
      <configuration>
        <parameterName>value</parameterName>
      </configuration>
    </plugin>
  </plugins>
</build>

Binding plugin goals to phases

Following is an example code to bind a plugin goal to a phase

<build>
    <plugins>
        <plugin>
        <groupId>plugin.group.id</groupId>
        <artifactId>plugin-artifact-id</artifactId>
        <version>1.0</version>
        <executions>
          <execution>
            <id>my-execution-identifier</id>
            <phase>phase-name</phase>
            <configuration>
              <url>http://www.foo.com/query</url>
              <timeout>10</timeout>
              <options>
                <option>one</option>
                <option>two</option>
                <option>three</option>
              </options>
            </configuration>
            <goals>
              <goal>plugin-goal</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
 </build>

Archetype Plugin

Archetype is project templating plugin, common use of this plugin is to quickly setup project structure using mvn archetype:generate command. The command can be used to setup project based on already existing templates, as well as used to setup project based on provided project details through CLI interface (just like npm init in node.js)

As an example, archetype can be used to setup complex project configurations based on variety of spring templates so that we don’t have to go through initial configuration setup for creating a spring application.

Archetype Plugin can also be used to save a custom maven project as archetype template so that It can be shared and used by multiple users across different devices.