Maven Notes
Posted by Sajid Khan | Last updated on
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
andsite
- 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 theclean
lifecycle. callingmvn clean
executes thepre-clean
andclean
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
- 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, andtestCompile
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)
- java: Store java application source code
- test
- java: Store source code used during test phase
- resources: Store resources used during test phase
- main
- 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
Command | Description |
mvn clean | clean up previous build (target directory) |
mvn compile | Compile all java source code present in source directory into java class files. |
mvn test-compile | Compile test source codes into the test destination directory. |
mvn package | This takes the compiled code and packages those in a distributable format (For eg. jar format). |
mvn install | Save 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 deploy | Done 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-dependencies | Download all dependencies from remote repository to local repository |
mvn help:effective-pom | Show 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.