IMPORTANT:
Some of the content here is a personal summary/abbreviation of contents on the Offical Apache Maven Guide. Feel free to refer to the official site if you think some of the sections written here are not clear.
Apache Maven Introduction
- *Q: What is Apache Maven used for? *
In a nutshell Maven is an attempt to apply patterns to a project’s build infrastructure in order to promote comprehension and productivity by providing a clear path in the use of best practices. Maven is essentially a project management and comprehension tool and as such provides a way to help with managing:
- Builds
- Documentation
- Reporting
- Dependencies
- SCMs
- Releases
- Distribution
Maven can provide benefits for your build process by employing standard conventions and practices to accelerate your development cycle while at the same time helping you achieve a higher rate of success.
Installing Maven
Please follow Apache Maven Guide
Creating a Project (QuickStart)
First you might want to create a workspace folder where all your Maven project resides. Then, you could navigate to that folder’s directory and run:
1 | mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4 -DinteractiveMode=false |
where the -DartifactId=my-app
would be the new folder with name my-app
under that directory you created.
Note:
- If you have just installed Maven, it may take a while on the first run.
After it completes the setup, you can switch to that folder my-app
and look at what’s created by Maven:
1 | cd my-app |
Under this directory you will notice the following standard project structure.
1 | my-app |
where:
src/main/java
directory contains the project source codesrc/test/java
directory contains the test source codepom.xml
file is the project’s Project Object Model, or POM.
Configuring for Java 9 or Later
By default your version of Maven might use an old version of the maven-compiler-plugin
that is not compatible with Java 9 or later versions. To target Java 9 or later, you should at least use version 3.6.0
of the maven-compiler-plugin
and set the maven.compiler.release
property to the Java release you are targetting (e.g. 9, 10, 11, 12, etc.).
In the following example, we have configured our Maven project to use version 3.8.1
of maven-compiler-plugin and target Java 11. You should be able to edit it in the pom.xml
file.
1 | <properties> |
Build the Project
Maven is based around the central concept of a build lifecycle, which consists of a number of phases. (Each of these build lifecycles is defined by a different list of build phases, wherein a build phase represents a stage in the lifecycle.)
For example, the default lifecycle comprises of the following phases (for a complete list of the lifecycle phases, refer to the Lifecycle Reference):
validate
- validate the project is correct and all necessary information is availablecompile
- compile the source code of the projecttest
- test the compiled source code using a suitable unit testing framework. These tests should not require the code be packaged or deployedpackage
- take the compiled code and package it in its distributable format, such as aJAR
.integration-test
: process and deploy the package if necessary into an environment where integration tests can be runverify
- run any checks on results of integration tests to ensure quality criteria are metinstall
- install the package into the local repository, for use as a dependency in other projects locallydeploy
- done in the build environment, copies the final package to the remote repository for sharing with other developers and projects.
At this point, since you haven’t added anthing in the folder, you could directly do
1 | mvn package |
which, for Maven, when a phase is given, it will execute every phase in the sequence up to and including the one defined. For example, if we execute the compile
phase, the phases that actually get executed are:
validate
- generate-sources
- process-sources
- generate-resources
- process-resources
compile
In the case where we executed mvn package
, the command line will print out various actions, and end with the following:
1 | ... |
Note:
- Unlike the first command executed (
archetype:generate
) you may notice the second is simply a single word -package
. The difference is that the first one is a goal (i.e. the prefixarchetype
is the plugin that provides the goalarchetype:generate
. Maven is - at its heart - a plugin execution framework; all work is done by plugins), where as the second one is a phase.- After you run the
mvn package
, you will see that it created a new foldertarget
, in which there will be a packagedJAR
file that is the snap-shot of the project (in this case, MAVEN starts with aHello World
java file)
Now, since you see that in the standard lifecycle, package
already included compile
phase, it means we could test the newly compiled and packaged JAR
:
1 | java -cp target/my-app-1.0-SNAPSHOT.jar com.mycompany.app.App |
where
-cp
, or CLASSPATH (where the JVM will look for the .class file. In this case, the file is packaged in the my-app-1.0-SNAPSHOT.jar), is used as an option to the Java command that specifies the location of classes and packages which are defined by the user, for example, a list of directories, JAR archives and ZIP archives to search for class files.com.mycompany.app.App
is the java file that is created by MAVEN and packaged intotarget/my-app-1.0-SNAPSHOT.jar
Running Maven Tools
Although hardly a comprehensive list, these are the most common default lifecycle phases executed, also shown in the section Build the Project
validate
compile
test
package
integration-test
verify
install
deploy
There are two other Maven lifecycles of note beyond the default list above. They are:
clean
: cleans up artifacts created by prior buildssite
: generates site documentation for this project
Phases are actually mapped to underlying goals (A build phase is made up of a set of goals. Maven goals represent a specific task that contributes to the building and managing of a project). The specific goals executed per phase is dependent upon the packaging type of the project. For example, package executes jar:jar
if the project type is a JAR, and war:war
if the project type is - you guessed it - a WAR.
An interesting thing to note is that phases and goals may be executed in sequence.
1 | mvn clean dependency:copy-dependencies package |
This command will clean the project, copy dependencies, and package the project (executing all phases up to package
, of course).
Generating a Site
This site will contain information about your project, organized by MAVEN:
1 | mvn site |
This phase generates a site based upon information on the project’s pom
. You can look at the documentation generated under target/site
.
Creating a Project
Different from the quickstart
you could use, you could also create a standard project by:
1 | mvn -B archetype:generate \ |
Then, in the directory you created, you could look at the POM
file:
1 | <project xmlns="http://maven.apache.org/POM/4.0.0" |
The POM
is the basic unit of work in Maven. This is important to remember because Maven is inherently project-centric in that everything revolves around the notion of a project. In short, the POM
contains every important piece of information about your project and is essentially one-stop-shopping for finding anything related to your project.
This is a very simple POM but still displays the key elements every POM contains, so let’s walk through each of them to familiarize you with the POM essentials:
- project This is the top-level element in all Maven
pom.xml
files. - modelVersion This element indicates what version of the object model this POM is using. The version of the model itself changes very infrequently but it is mandatory in order to ensure stability of use if and when the Maven developers deem it necessary to change the model.
- groupId This element indicates the unique identifier of the organization or group that created the project. The groupId is one of the key identifiers of a project and is typically based on the fully qualified domain name of your organization. For example
org.apache.maven.plugins
is the designated groupId for all Maven plugins. - artifactId This element indicates the unique base name of the primary artifact being generated by this project. The primary artifact for a project is typically a JAR file. Secondary artifacts like source bundles also use the artifactId as part of their final name. A typical artifact produced by Maven would have the form
<artifactId>-<version>.<extension>
(for example,my-app-1.0.jar
). - packaging This element indicates the package type to be used by this artifact (e.g.
JAR
,WAR
,EAR
, etc.). This not only means if the artifact produced is JAR, WAR, or EAR but can also indicate a specific lifecycle to use as part of the build process. (The lifecycle is a topic we will deal with further on in the guide. For now, just keep in mind that the indicated packaging of a project can play a part in customizing the build lifecycle.) The default value for the packaging element isJAR
so you do not have to specify this for most projects. - version This element indicates the version of the artifact (your end product) generated by the project. Maven goes a long way to help you with version management and you will often see the SNAPSHOT designator in a version, which indicates that a project is in a state of development. We will discuss the use of snapshots and how they work further on in this guide.
- name This element indicates the display name used for the project (by default it will be the same name as artifactId). This is often used in Maven’s generated documentation.
- url This element indicates where the project’s site can be found. This is often used in Maven’s generated documentation.
- description This element provides a basic description of your project. This is often used in Maven’s generated documentation.
Note:
- The value of the
version
tag in the pom.xml file shown below has the suffix:-SNAPSHOT
. The SNAPSHOT value refers to the ‘latest’ code along adevelopment
branch, and provides no guarantee the code is stable or unchanging. Conversely, the code in a'release'
version (any version value without the suffix SNAPSHOT) is unchanging. In other words, a SNAPSHOT version is the ‘development’ version before the final ‘release’ version. The SNAPSHOT is “older” than its release.
For more complete understanding of a POM visit Apache Maven POM Guide
After the archetype generation of your first project you will also notice the same source tree structure you had above in the quickstart project created:
1 | my-app |
Compiling Project Source Code
Change to the directory where pom.xml
is created by archetype:generate
(this will be the default directory we are using for MAVEN commands) and execute the following command to compile your application sources:
1 | mvn compile |
Upon executing this command you should see output like the following:
1 | [INFO] ---------------------------------------------------------------------------- |
The first time you execute this (or any other) command, Maven will need to download all the plugins and related dependencies it needs to fulfill the command. This can take quite a while. If you execute the command again, Maven will now have what it needs, so it won’t need to download anything new and will be able to execute the command much more quickly.
Note:
- If you ran into error running this command, it might be cause because the code is not for the default compiler used by MAVEN. You can specify it similarly in the section [Configuing for Java 9 or Later](#
re-Java-9), by adding those before the root element ends:As you can see from the output, the compiled classes were placed in
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 <dependencies>
...
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>13</source>
<target>13</target>
</configuration>
</plugin>
</plugins>
</build>${basedir}/target/classes
, which is another standard convention employed by Maven.
Testing Project Source Code
Now you’re successfully compiling your application’s sources and now you’ve got some unit tests that you want to compile and execute (because every programmer always writes and executes their unit tests nudge nudge wink wink).
Execute the following command:
1 | mvn test |
Note:
- Maven downloads more dependencies this time. These are the dependencies and plugins necessary for executing the tests (it already has the dependencies it needs for compiling and won’t download them again).
- Before compiling and executing the tests Maven compiles the main code (in case there are some changes. In this case all these classes are up to date because we haven’t changed anything since we compiled last).
If you simply want to compile your test sources (but not execute the tests), you can execute the following:
1 |
|
Packaging into a Jar File
Making a JAR file is straight forward enough and can be accomplished by executing the following command:
1 | mvn package |
If you take a look at the POM
for your project you will notice the packaging element is set to jar
. This is how Maven knows to produce a JAR
file from the above command.
The generated file will be in ${basedir}/target directory
and you will see the generated JAR
file there as well.
Installing into Local Repository
If you want to install the artifact you’ve generated (the JAR file) in your local repository (${user.home}/.m2/repository
; for Windows,C:\Users\<Username>\.m2\repository
is the default location). For more information on repositories you can refer to Apache Maven Introduction to Repositories but let’s move on to installing our artifact! To do so execute the following command:
1 | mvn install |
Upon executing this command you should see the following output:
Upon executing this command you should see the following output:
1 | [INFO] ---------------------------------------------------------------------------- |
Note that the surefire
plugin (which executes the test) looks for tests contained in files with a particular naming convention. By default the tests included are:
**/*Test.java
**/Test*.java
**/*TestCase.java
And in the end you can find your files in C:\Users\<Username>\.m2\repository\com\mycompany\app\
(for Windows)
Other Useful Phases in Maven
One of the highly prized features of Maven is: without any work on your part this POM has enough information to generate a web site for your project! You will most likely want to customize your Maven site but if you’re pressed for time all you need to do to provide basic information about your project is execute the following command:
1 | mvn site |
There are plenty of other standalone goals that can be executed as well (again only with this POM
file), for example:
1 | mvn clean |
This will remove the target directory with all the build data before starting so that it is fresh.
Using Plugins in Maven
Whenever you want to customise the build for a Maven project, this is done by adding or reconfiguring plugins.
For this example, we will configure the Java compiler to allow JDK 5.0 sources (we have in fact done this already when we configure for Java 9.0 or later). This is as simple as adding this to your POM
:
1 | ... |
You’ll notice that all plugins in Maven look much like a dependency - and in some ways they are. This plugin will be automatically downloaded and used - including a specific version if you request it (the default is to use the latest available).
To find out what configuration is available for a plugin, you can see the Apache Maven Plugins List and navigate to the plugin and goal you are using. For general information about how to configure the available parameters of a plugin, have a look at the Apache Maven Guide to Configuring Plugins.
Adding Files/Resources to My Jar
Another common use case that can be satisfied which requires no changes to the POM that we have above is packaging resources in the JAR file. For this common task, Maven again relies on the Standard Directory Layout.
You see below in our example we have added the directory ${basedir}/src/main/resources
into which we place any resources/files we wish to package in our JAR. The simple rule employed by Maven is this: any directories or files placed within the ${basedir}/src/main/resources
directory are packaged in your JAR with the exact same structure starting at the base of the JAR
1 | my-app |
Generates:
1 | |-- META-INF |
But if you had a customized:
1 | my-app |
Generates:
1 | |-- META-INF |
You see other files there like META-INF/MANIFEST.MF
as well as a pom.xml
and pom.properties
file generated as well. These come standard with generation of a JAR
in Maven. You can create your own manifest if you choose, but Maven will generate one by default if you don’t. The pom.xml
and pom.properties
files are packaged up in the JAR so that each artifact produced by Maven is self-describing and also allows you to utilize the metadata in your own application if the need arises. One simple use might be to retrieve the version of your application.
Note:
- To add resources to the
classpath
for your unit tests, you follow the same pattern as you do for adding resources to the JAR except the directory you place resources in is${basedir}/src/test/resources
.
Resource Filtering in Maven
This is useful if sometimes a resource file will need to contain a value that can only be supplied at build time. Then, you can use Maven to perform variable replacement on project resources. When resource filtering is activated, Maven will scan resources
for property references surrounded by ${<property name>}
. When it finds these references it will replace them with the appropriate value.
To enable resource filtering, you need to set filtering
to true
in your pom.xml
, and add the directory src/main/resources
which is the default location MAVEN will look into when it compiles (since we overwrote the value to true
, this has to be re-written again):
1 | <project xmlns="http://maven.apache.org/POM/4.0.0" |
Now, to reference a property defined in your pom.xml, the property name uses the names of the XML elements that define the value, with “pom” being allowed as an alias for the project (root) element. So ${project.name}
refers to the name of the project, ${project.version}
refers to the version of the project, ${project.build.finalName}
refers to the final name of the file created when the built project is packaged, etc.
Similarly, values in the user’s settings.xml
can be referenced using property names beginning with “settings” (for example, {settings.localRepository}
refers to the path of the user’s local repository).
For example, let’s add a couple of properties to the application.properties
file (which we put in the src/main/resources
directory) whose values will be supplied when the resource is filtered:
1 | # application.properties |
With that in place, you can execute the following command (process-resources is the build lifecycle phase where the resources are copied and filtered):
1 | mvn process-resources |
and the application.properties
file under target/classes
(and will eventually go into the jar) looks like this:
1 | # application.properties |
With that in place, you can execute the following command (process-resources is the build lifecycle phase where the resources are copied and filtered):
1 | mvn process-resources |
and the application.properties
file under target/classes/META-INF
(and will eventually go into the jar) looks like this:
1 | # application.properties |
Filtering External Files
To reference a property defined in an external file, all you need to do is add a reference to this external file in your pom.xml
.
First, let’s create our external properties file and call it src/main/filters/filter.properties
:
1 | # filter.properties |
Next, we’ll add a reference to this new file in the pom.xml
:
1 | <project xmlns="http://maven.apache.org/POM/4.0.0" |
Then, if we add a reference to this property in the application.properties
file:
1 | # application.properties |
the next execution of the mvn process-resources
command will put our new property value into application.properties
.
Note:
- The file
filter.property
created in thesrc/main/filters
will not get outputted by MAVEN to thetarget
folder. This is because, it is not in the default location tree that MAVEN knows, so MAVEN will not compile it and include in your project.
Using External Dependency
You have, in fact, been using an external dependency all this time, but here we’ll talk about how this works in a bit more detail. For a more thorough introduction, please refer to our Introduction to Apache Maven Dependency Mechanism.
The dependencies section of the pom.xml
lists all of the external dependencies that our project needs in order to build (whether it needs that dependency at compile time, test time, run time, or whatever). Right now, our project is depending on JUnit
only (I took out all of the resource filtering stuff for clarity):
1 | <project xmlns="http://maven.apache.org/POM/4.0.0" |
For each external dependency, you’ll need to define at least 4 things: groupId
, artifactId
, version
, and scope
.
- The
groupId
,artifactId
, andversio
n are the same as those given in thepom.xml
of the project that built that dependency. - The
scope
element indicates how your project uses that dependency, and can be values likecompile
,test
, andruntime
.
Note:
- Where does Maven reference the dependency from? Maven looks in your local repository (
${user.home}/.m2/repository
is the default location) to find all dependencies.
This means that we could use code/projects made by others!
For example:
In a previous section, we installed the artifact from our project (my-app-1.0-SNAPSHOT.jar
) into the local repository. Once it’s installed there, another project can reference that jar as a dependency simply by adding the dependency information to its pom.xml
:
1 | <project xmlns="http://maven.apache.org/POM/4.0.0" |
Note:
- If a remote repo is needed, you could use the mirror in MAVEN to setup the dependency correctly. By default, the remote repository Maven uses can be found (and browsed) at https://repo.maven.apache.org/maven2/. You can also set up your own remote repository (maybe a central repository for your company) to use instead of or in addition to the default remote repository. For more information on repositories you can refer to the Apache Maven Introduction to Repositories.
Now, when we compile the project (mvn compile
), we’ll see Maven download the my-app
dependency for us.
Deploying Jar into a Remote Repository
For deploying jars to an external repository, you have to configure the repository url
in the pom.xml
and the authentication information for connectiong to the repository in the settings.xml
.
There are two locations where a settings.xml
file may live:
- The Maven install:
${maven.home}/conf/settings.xml
- A user’s install:
${user.home}/.m2/settings.xml
The former settings.xml
are also called global settings, the latter settings.xml
are referred to as user settings. If both files exists, their contents gets merged, with the user-specific settings.xml being dominant.
Note:
settings.xml
is a configuration file that should not be bundled to any specific project whereas thepom.xml
can be distributed.
Now, going back to the exmple, we can use scp and username/password authentication:
1 | # pom.xml |
and then in settings.xml
1 | # settings.xml |
Note:
- If you are connecting to an openssh ssh server which has the parameter
"PasswordAuthentication"
set to"no"
in thesshd_confing
, you will have to type your password each time for username/password authentication (although you can log in using another ssh client by typing in the username and password). You might want to switch to public key authentication in this case.
Creating Documentation with a Site
To get you jump started with Maven’s documentation system you can use the archetype
mechanism to generate a site for your existing project using the following command:
1 | mvn archetype:generate \ |
here we used the additional line: -Darchetype ArtifactIds=maven-archetype-site
, which is an archetype to generate a sample Maven site.
Now head on over to the Apache Maven Guide to creating a site to learn how to create the documentation for your project.
Creating Projects of Other Types
For example, back in the base directory we can create a simple web application
:
1 | mvn archetype:generate \ |
Here we used -DarchetypeArtifactId=maven-archetype-webapp
to use an archetype to generate a sample Maven Webapp project. (For the available archetypes, see https://maven.apache.org/guides/introduction/introduction-to-archetypes.html)
Creating Multiple Projects at Once
The basic idea is as follows:
- First, you could use Maven to create two (or more) projects.
- Then, you need to create a
pom.xml
file that is above the two folder - After that, you need to configure that
pom.xml
file to control the two (or more) project folders- if you need to add dependencies between the two (or more) sub-projects, you can also add them in the manner like the section Using External Dependency
- Finally, configure the
parent
for the two (or more) sub-projects
For example, if you created two projects with name my-app
and my-webapp
, you should have place your pom.xml
above the two folders like this:
1 | +- pom.xml |
And in the outer pom.xml
, you should have the following information:
1 | <project xmlns="http://maven.apache.org/POM/4.0.0" |
where
<artifactId>app</artifactId>
will be the entire project name which includes your two subprojects.- the two (or more) sub-projects you included will be inside the
<modules></modules>
section
Note:
- The parent
POM
created (calledapp
), has apackaging
ofpom
and a list ofmodules
defined. This tells Maven to run all operations over the set of projects instead of just the current one
Finally, we need the following parent
information to the pom.xml
of the sub-projects like this:
1 | <project xmlns="http://maven.apache.org/POM/4.0.0" |
where
<artifactId>app</artifactId>
would be same as theartifactId
in the outerpom.xml
file you manually created.
Note:
- The
parent
definition ensures that thePOM
can always be located even if the project is distributed separately from its parent by looking it up in the repository.
Now, if you try to run from the top level, the outer directory where the outer pom.xml
reside, the following command:
1 | mvn verify |
Then you will have successfully find both of your projects compiled and packed in their respective target
folder.
Build Lifecycle Basics
Maven is based around the central concept of a build lifecycle. What this means is that the process for building and distributing a particular artifact (project) is clearly defined.
There are three built-in build lifecycles: default
, clean
and site
. The default
lifecycle handles your project deployment, the clean
lifecycle handles project cleaning, while the site
lifecycle handles the creation of your project’s site documentation.
Then, you need to understand that a build Lifecycle
is made up of Phases
, and a Phase
is made up of Goals
.
Phases in a Build Lifecycle
Each of these build lifecycles is defined by a different list of build phases, wherein a build phase represents a stage in the lifecycle.
For example, the default
lifecycle comprises of the following phases
(these are not all the phases, only some are picked to illustrate the general concept behind phases
and lifecycles
):
validate
- validate the project is correct and all necessary information is availablecompile
- compile the source code of the projecttest
- test the compiled source code using a suitable unit testing framework. These tests should not require the code be packaged or deployedpackage
- 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 metinstall
- install the package into the local repository, for use as a dependency in other projects locallydeploy
- done in the build environment, copies the final package to the remote repository for sharing with other developers and projects.
Given the default lifecycle
phases above, this means that when the default
lifecycle is used, Maven will first validate the project, then will try to compile the sources, run those against the tests, package the binaries (e.g. jar), run integration tests against that package, verify the integration tests, install the verified package to the local repository, then deploy the installed package to a remote repository.
Executing Those Phases
You should first decide/select the phase
that matches your outcome, and then run the command. If you want your jar
to be made from your project, run mvn package
. If you want to run the unit tests, run mvn test
.
If you are uncertain what you want, the preferred phase to call is:
1 | mvn verify |
This command executes each default
lifecycle phase
in order (validate
, compile
, package
, etc.), up to and including, finally, executing verify
.
“Special” Phases
The phases named with hyphenated-words (pre-, post-, or process-*) are not usually directly called from the command line. These phases sequence the build, producing intermediate results that are not useful outside the build. In the case of invoking integration-test
, the environment may be left in a hanging state.
For example:
Failsafe and code coverage plugins bind goals to integration-test
and verify
phases. But if integration-test
were to be called from the command line, no reports are generated. If verify
is run, the net result is test and coverage reports are available after the verify
phase.
In fact, the worse for executing integration-test
is that the integration test container environment is left in a hanging state; the Tomcat webserver or Docker instance is left running, and Maven may not even terminate by itself.
Goals in a Phase
A build phase is responsible for a specific step in the build lifecycle, the manner in which it carries out those responsibilities may vary: it depends on the specific goals
(plugins) that are attached to the phase
.
A plugin/goal
represents a specific task (finer than a build phase) which contributes to the building and managing of a project. It may be bound to zero or more build phases. A goal not bound to any build phase could be executed outside of the build lifecycle by direct invocation.
The order of execution depends on the order in which the goal(s)
and the build phase(s)
are invoked. For example, consider the command below. The clean
and package
arguments are build phases, while the dependency:copy-dependencies
is a goal
(the goal copy-dependencies
of a plugin depedency
/"maven-dependency-plugin"
).
1 | mvn clean dependency:copy-dependencies package |
If this were to be executed, the clean
phase will be executed first (meaning it will run all preceding phases of the clean
lifecycle, plus the clean
phase itself), and then the dependency:copy-dependencies
goal, before finally executing the package
phase (and all its preceding build phases of the default
lifecycle).
Note:
- A phase can also have no goals bounded. If so, no action will be executed during that phase.
Customizing your Build Lifecycle
The first, and most common way, is to set the packaging
for your project via the element <packaging>
in your POM.xml
file. Some of the valid packaging values are jar
, war
, ear
and pom
. If no packaging value has been specified, it will default to jar
For example, in the section where you made multiple projects and had a parent pom.xml
file, it looked like:
1 | <project xmlns="http://maven.apache.org/POM/4.0.0" |
where
pom
for a packaging value means a project that is purely metadata, and it only binds goals to theinstall
anddeploy
phases, which means during all the other phases there will be no action/execution.
By default, if you have a jar
as the packaging value, you will have the following goals
bounded to the following phases
Phase | plugin:goal |
---|---|
process-resources | resources:resources |
compile | compiler:compile |
process-test-resources | resources:testResources |
test-compile | compiler:testCompile |
test | surefire:test |
package | jar:jar |
install | install:install |
deploy | deploy:deploy |
Customizing your Build Lifecycle by Plugins
The second way to add goals
to phases
is to configure plugins in your project. Plugins are artifacts that provide goals
to Maven. This means that a plugin may have one or more goals
wherein each goal represents a capability of that plugin.
For example, the Compiler
plugin has two goals: compile
and testCompile
. The former compiles the source code of your main code, while the latter compiles the source code of your test code.
Note:
- Perhaps it is clear already, but to make sure: adding the plugin on its own is not enough information - you must also specify the
goals
you want to run from that plugin.- As you will see in the later sections, plugins can contain information that indicates which lifecycle phase to bind a goal to. If more than one goal is bound to a particular
phase
, the order used is that those from thepackaging
are executed first, followed by those configured in thePOM
. However, you can use the<executions>
element to gain more control over the order of particular goals.
For example, the Modello plugin binds by default its goal modello:java
to thegenerate-sources
phase (Note: The modello:java
goal generates Java source codes). So to use the Modello plugin and have it generate sources from a model and incorporate that into the build, you would add the following to your POM
in the <plugins>
section of <build>
, and it will be executed in the generate-sources
phase if you do not explicitly call it by itself:
1 | ... |
You might be wondering why that <executions>
element is there. That is so that you can run the same goal multiple times with different configuration if needed.
Now, in the case of modello:java
, it only makes sense in the generate-sources
phase. But some goals can be used in more than one phase, and there may not be a sensible default. For those, you can specify the phase
yourself.
For example, let’s say you have a goal display:time
(that echos the current time to the commandline), and you want it to run in the process-test-resources
phase to indicate when the tests were started. This would be configured like so:
1 | ... |
Built-in Lifecycle and Phase Bindings
You can see the default bindings for the lifecycle clean
, default
, and site
here
Built-in Phase and Goal Bindings
Some phase
s have goal
s bound to them by default. You can see the default bindings for each phase
in the three lifecycles clean
, default
, site
here
POM Basics
A Project Object Model or POM
is the fundamental unit of work in Maven. It is an XML
file that contains information about the project and configuration details used by Maven to build the project. When executing a task or goal, Maven looks for the POM
in the current directory. It reads the POM
, gets the needed configuration information, then executes the goal.
For example, some of the configuration that can be specified in the POM
are the project dependencies
, the plugins
or goals
that can be executed, the build profiles, project version, description, developers, mailing lists and so on.
Super POM
The Super POM
is Maven’s default POM
. All POMs
extend the Super POM
unless explicitly set, meaning the configuration specified in the Super POM
is inherited by the POMs you created for your projects. The snippet below is the Super POM for Maven 3.5.4.
1 | <project> |
Here, we can see some default settings that the SUPER POM
has:
- the build target directory set to
<directory>${project.basedir}/target</directory>
- the build source directory set to
<sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
- the central repository set to
<url>https://repo.maven.apache.org/maven2</url>
Minimal POM
The minimum requirement for a POM
are the following:
- project root
- modelVersion - should be set to 4.0.0
- groupId - the id of the project’s group.
- artifactId - the id of the artifact (project)
- version - the version of the artifact under the specified group
For example:
1 | <project> |
This is because a needs project’s fully qualified artifact name, which will be in the form of <groupId>:<artifactId>:<version>
. As for the example above, its fully qualified artifact name is "com.mycompany.app:my-app:1"
.
Also, as mentioned in the previous section, if the configuration details are not specified, Maven will use their defaults. One of these default values is the packaging type. Every Maven project has a packaging type. If it is not specified in the POM
, then the default value “jar
“ would be used.
Project/POM Inheritance
Elements in the POM
that are merged when you inherit another parent POM
are the following:
- dependencies
- developers and contributors
- plugin lists (including reports)
- plugin executions with matching ids
- plugin configuration
- resources
The Super POM
is one example of a parent POM
that is inherited by default, however you can also introduce your own parent POM
s by specifying the parent
element in the POM
, as demonstrated in the following examples.
If we have everything ordered nicely in the following structure, where the parent
POM
sits right above the children:1
2
3|-- childProject
| `-- pom.xml
`-- pom.xml1
For example, if the **child artifact is `com.mycompany.app:childProject:1`**,
4.0.0 com.mycompany.app childProject 1 1
2
And the **parent artifact is `com.mycompany.app:parentProject:1`**, then, we could easily **configure the child `POM`** by adding the `<parent>` element:com.mycompany.app parentProject 1 4.0.0 com.mycompany.app childProject 1 . |-- childProject | `-- pom.xml `-- parentProject `-- pom.xml1
2. If we did not have everything in place, and had the following structure:
1
2
3Then we just need to **add an additional `relativePath` element** to specify where to find tha parent `POM`, since we did not have the `parent` element at the default structure.
So we would **configure the child `POM`** by:com.mycompany.app parentProject 1 ../parent/pom.xml 4.0.0
com.mycompany.app
childProject
1 1
2
However, in both cases, since the **child `POM` knows to inherit** from its parent `POM`, we could further **simplify the child `POM` to**:com.mycompany.app parentProject 1 (../parent/pom.xml )4.0.0 childProject . |-- my-module | `-- pom.xml `-- pom.xml1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18So that the **child project will have the same `groupID` and `version` as the parent, as the information would be inherited**.
> Note:
> - [ ] This is different from the example in the section [Creating Multiple Projects at Once](#Creating-Multiple-Projects-at-Once) section, because we **did not have the `<packaging>pom</packaging>`**. The difference is that, in this case (without specifying `packaging`) the **`parentProject` is/will be used by its child projects** (like Java Inheritance), but in the other case, the **parent uses the children projects** (like Java Composition) [see below](#Project/POM-Aggregation)
> - [ ] If you want to **compile a specific children project**, you might need to **change the parent `POM` to `<packaging>pom</packaging>`**. If you need to access the source codes in the parent directory, you should add the dependencies.
<a name="Project/POM-Aggregatio"></a>
## Project/POM Aggregation
Project Aggregation is similar to Project Inheritance, but there is an essential difference:
- Project Inheritance would have `childrenProjects` (`modules`) using/inheriting the `parentProject`(`parent`)
- Project Aggregation would have `parentProjects` (`parent`) using/composed of the `childProjects` (`modules`)
So, instead of specifying the parent `POM` from the `module`, it **specifies the `modules` in the parent `POM`**. By doing so, the parent project now knows its `modules`, and **if a Maven command is invoked against the parent project**, **that Maven command will then be executed to the parent's `modules` (its `childProjects`)as well**. To do Project Aggregation, you must do the following:
- **Configure the `parent` POMs** `packaging` to the value "`pom`".
- **Specify in the parent POM** the directories of its modules (children POMs).
1. If the directory structure follows that of the default structure:1
Then we need to add the `<packaging>` element and the `<modules>` element in the **parent `POM`**:
4.0.0 com.mycompany.app parent 1 pom my-module . |-- my-module | `-- pom.xml `-- parent `-- pom.xml1
2
3Now, whenever a Maven command processes `com.mycompany.app:parent:1`, that same Maven command would be ran against `com.mycompany.app:my-module:1` as well. Furthermore, some commands (goals specifically) handle project aggregation differently.
2. If we have the directory structure to the following:1
2
Then we need to specify the **relative path to the module** in the parent `POM`.4.0.0 com.mycompany.app parent 1 pom ../my-module 1
2
3
4
5
6
7
8
9
10
## Combining Project Inheritance and Project Aggregation
To achieve this, you just edit both child and parent `POM` in the same way introduced above:
- Specify in every child POM who their parent POM is. (Project Inheritance)
- Change the parent POMs packaging to the value "pom" . (Project Aggregation)
- Specify in the parent POM the directories of its modules. (Project Aggregation)
For a simple example:
`com.mycompany.app:paernt:1`'s POM4.0.0 com.mycompany.app parent 1 pom ../my-module 1
2
`com.mycompany.app:my-module:1`'s POMcom.mycompany.app parent 1 ../parent/pom.xml 4.0.0 my-module 1
2
3
4
5
6
## Variables in `POM`
One of the practices that Maven encourages is don't repeat yourself. However, there are circumstances where you will need to use the same value in several different locations. To assist in ensuring the value is only specified once, Maven allows you to **use both your own and pre-defined variables in the `POM`**.
For example, to access the `project.version` variable (this is pre-defined), you would reference it like so:${project.version} 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15> Note:
>
> - [ ] If you get an error, you might need to make the variable by yourself in the parent `pom`. To do this, [see below](#own-variable)
- Some of the **pre-defined variables** are:
- **Project Model Variables**:
Any field of the model that is a single value element can be referenced as a variable. For example, `${project.groupId}`, `${project.version}`, `${project.build.sourceDirectory}` and so on. Refer to the POM reference to see a full list of properties.
- **Special Variables**
- `project.basedir` The directory that the current project resides in.
- `project.baseUri` The directory that the current project resides in, represented as an URI. Since Maven 2.1.0
- `maven.build.timestamp` The timestamp that denotes the start of the build (UTC). Since Maven 2.1.0-M1
<a name="own-variable"></a>
- You can also use your **own variable** if you define it in the `<properties>` section:... 3.0 org.apache.maven maven-artifact ${mavenVersion} org.apache.maven maven-core ${mavenVersion} mvn help:active-profiles1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
## Profiles
In short, Maven profiles can be used to create **customized build configurations**.
Profiles are specified using the element `<profile>` in the `POM `itself (in the extra section `<profiles>`), and can be **triggered in many different ways**. They modify the POM at build time, and are meant to be used in complementary sets to give equivalent-but-different parameters for a set of target environments (providing, for example, the path of the appserver root in the development, testing, and production environments). As such, profiles can easily lead to differing build results from different members of your team.
Profiles (`<profiles>`) can be **specified in any of the four file locations**:
- Per Project
- Defined in the POM itself (`pom.xml`).
- Per User
- Defined in the Maven-settings (`%USER_HOME%/.m2/settings.xml`).
- Global
- Defined in the global Maven-settings (`${maven.home}/conf/settings.xml`).
- Profile descriptor (**unsupported in Maven 3.0 and above**)
- a descriptor located in project basedir (profiles.xml)
## Viewing Active Profiles
To see which profiles are already active, you can use the `help` plugin from MAVEN:Active Profiles for Project 'com.mycompany.app:my-app:pom:1.0-SNAPSHOT':1
2
and by default, nothing is actived so you will see nothing:
The following profiles are active:
1 | If we want to **automatically show the active profiles** when we are doing, for example, the `compile` phase, we can use the similar approach shown in the section [Customizing your Build Lifecycle by Plugins](#Customizing-your-Build Lifecycle-by-Plugins) by **adding the `help` plugin and the `goal`** in our phase `compile`: |
1 |
|
<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>
<echo>Using app.test.properties</echo>
<copy file="src/main/resources/app.test.properties"
tofile="${project.build.outputDirectory}/env.properties"/>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
1 | Another simpler example could be: |
1 | (This will skip the `test` phase in the artifact if you activate it.) |
1 | This will execute all phases up to and until `package`, except **skipping the `test` phase**, as **specified by our `no-test` profile**. |
1 |
|
<properties>
<maven.test.skip>true</maven.test.skip>
</properties>
1 | This will activate the profile `no-test` everytime when the project is run. |
1 | So that profiles listed in the `<activeProfiles>` tag would be **activated by default every time that user runs a project**. |
1 |
|
1 |
|
1 |
|
1 |
|
1 | - Triggered by a missing/present file |
1 |
|
1 | Or, you could use the [Dependency Management](#Dependency-Management) in the section below. |
1 | at this point, if you have the following Project B configuration: |
1 | Then version of artifact `a` and `c` would be `1.2` and `1.0`, respectively. |
1 | Then you would have version of `1.0` for both `a` and `c`. Also, the `scope` of `a` would become `runtime` |
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-b</artifactId>
<version>1.0</version>
<type>bar</type>
<scope>runtime</scope>
</dependency>
1 | and |
1 | We see that these two example `POM`s **share a common dependency (`group-a` artifact `artifact-b`)** and each has one non-trivial dependency. This **information can be put in the parent `POM`** like this: |
<exclusions>
<exclusion>
<groupId>group-c</groupId>
<artifactId>excluded-artifact</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>group-c</groupId>
<artifactId>artifact-b</artifactId>
<version>1.0</version>
<type>war</type>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-b</artifactId>
<version>1.0</version>
<type>bar</type>
<scope>runtime</scope>
</dependency>
</dependencies>
1 | Now, if the two children inherits the parent, we could simplify the children `POM`s: |
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-b</artifactId>
<!-- This is not a jar dependency, so we must specify type. -->
<type>bar</type>
</dependency>
1 | and |
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-b</artifactId>
<!-- This is not a jar dependency, so we must specify type. -->
<type>bar</type>
</dependency>
1 |
|
<dependency>
<groupId>test</groupId>
<artifactId>b</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
1 |
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.sample</groupId>
<artifactId>project1</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencyManagement>
<dependencies>
<!-- Imports the bill-of-materials POM. -->
<dependency>
<groupId>maven</groupId>
<artifactId>X</artifactId>
<version>1.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Add a dependency from the BOM POM.
Note: no version or scope is required, it's inherited from the BOM POM! -->
<dependency>
<groupId>test</groupId>
<artifactId>a</artifactId>
</dependency>
</dependencies>
</project>