If we ask software developers what Maven is, the majority will probably respond that Maven is a build tool. We can't say they are wrong, but this definition is not complete. If we want to be more precise, we should say that Maven is a project management tool that provides build and distribution functionalities, code generation, and communication features. Above all, Maven provides an advanced dependency management system that is able to retrieve transitive dependencies and download them from both local and remote repositories.
Maven comes with the convention over configuration philosophy. The origin of this philosophy resides in the idea that accepting conventions resulting from a set of past experiences leads to advantages such as saving time, reuse, and maintenance simplification. Maven pursues this philosophy through the use of defaults, which means that unnecessary configurations should be avoided; a project should just work.
While the use of defaults is a powerful concept, users might want to customize some behavior. Maven meets the users' needs by allowing the customization of almost all defaults.
In this chapter, we will:
Introduce Maven and explain its basic concepts
Present the example project used to show the concepts that we will treat
Start structuring the project, deepening some of the concepts introduced
In this paragraph, we will explore some of Maven's core concepts through a simple example. Maven defines project configurations through a Project Object Model (POM), which is stored in a file named
The following example of
pom.xml defines a simple project. Such a simple POM file is capable of compiling and building the project without the need to specify any additional information:
<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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <!-- Project coordinates --> <groupId>com.mycompany.projects</groupId> <artifactId>my-first-maven-project</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <name>My First Maven Project!</name> </project>
Inheritance: This concept simply means that everything that is not specified in a POM file is inherited from the upper-level POM. At the top level, Maven provides a parent POM defining all default values, which were mentioned earlier. According to this principle, multimodular projects are often structured with a root POM file defining common settings and a
pom.xmlfile related to each submodule to manage each module's peculiarities.
Overriding: This concept derives from the preceding one. All the values defined in the lower levels of the POM hierarchy override the definitions in the upper layers. As we can see in the preceding example POM file, only the project's coordinates have been defined, any other value is inherited from the parent POM. A project is uniquely identified into Maven repositories by its coordinates, which is composed of
version. Project coordinates are fundamental since they allow Maven to correctly manage modules and plugins.
Modularity: When we install Maven, we formally install only its core functionalities. Whenever we need some extra features, we can find it in some plugin. Plugins as well as software dependencies are downloaded from a set of configurable repositories.
Repository: Maven downloads a project's dependencies and plugins through repositories. Maven only distinguishes between two types of repositories: local and remote. The local repository is a folder inside the machine in which a project is being developed, acting as a cache with respect to the remote repositories. For what concerns remote repositories, Apache provides a central repository containing thousands of common dependencies, which is the default one. Maven does not rely on this specific repository, thus allowing users to define their own custom repositories.
This project aims to develop an application to track vehicles moving around the world and provides an integrated GUI for visualization and statistic calculations.
As we can see in the preceding figure, the project is composed of several modules that interact with each other and store their data in a shared database. All the modules composing the project follow a common naming convention. The first part of the name indicates the project. The second part is a descriptive name indicating the main functionality of the module. The final part indicates the packaging of each module. All the parts composing the name are separated by a dash.
The following list describes what these modules are in charge of:
transportation-acq-war: This is a web application module exposing the backend functionalities to an app across the world using the REST technology. This module receives the application requests, validates them, and invokes the
transportation-acq-ejbfunctionalities in order to perform its tasks.
In order to explain some advanced Maven features, we assume that the project is developed in an integrated environment. This environment consists of several entities, managing different phases of the software's lifecycle:
Source code repository: We assume that the code is available in an SVN repository, even though the kind of repository is not binding.
Bug-tracking tool: In order to avoid dependencies from specific products such as MantisBT or Jira, we assume to have a custom database to track bugs.
Custom-dependencies repository: This is proprietary software stored in the repository.
Integrated-build and versioning environment: This environment relies on Maven features to perform most of its work. Since, in this case, we must target a specific tool, we will assume to work with Hudson.
In spite of the fact that Maven is agnostic with respect to operating systems and IDE, in the course of this book, we assume to develop the software using Eclipse IDE with the M2E-Maven Integration for Eclipse plugin, m2e.
You can download Eclipse from https://www.eclipse.org/downloads/.
You can find all details about m2e at https://www.eclipse.org/m2e/.
In Maven, a multimodule project simply consists of a folder containing all submodule projects and a central POM file referencing these modules. This file is usually referred to as parent POM or aggregate POM. In this book, we will comply with this naming.
To start working, we simply need to open our IDE and create the project. Through the m2e plugin, Eclipse provides a wizard specific for Maven project creation.
The project creation starts as any other project. Open the new project window by navigating to File | New | Projectâ¦.
When the window opens, select the Maven Project option from the Maven folder. As we can see in the following screenshot, the last two steps consist of creating a simple project and filling in the form with the project's coordinates and packaging:
We finally have our Maven project. Our
pom.xml file will look a bit desolate, but it will soon grow up. In the following sections, we will start structuring this POM file:
<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>com.packt.examples</groupId> <artifactId>transportation-project</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>pom</packaging> <name>transportation-project</name> <description>The Transportation Project</description> </project>
A project packaging
pom does not produce any software package; it simply defines a POM file referencing a set of specified modules, and provides the common settings such as repositories, dependencies, and plugins.
This is the case of an aggregate POM, but it is possible to create POM projects with only specific settings; such projects might be used as a dependencies of other projects or modules that inherit the specified settings.
Actually, we created an empty project. In order to start structuring our project, we will start adding the project modules that we saw in the earlier sections. The easiest way to add a module is to exploit the m2e functionalities. Just right-click on the
pom.xml file and navigate to Maven | New Maven Module Project from the context menu. After this, we can fill in the modules, as shown in the following screenshot:
Now, our project will have the first module. We can iterate the same operation for the remaining modules, taking care to choose the right packaging for each module.
Once we stop adding modules, we will see that m2e created all the submodules. Each module that is stored as a folder into the project has its own
pom.xml file, which will specify its specific coordinates and settings. In order to distinguish between the POM file of the project and the POM files of its modules, we will call the
aggregate POM that we created earlier. The following screenshot shows the added modules and submodules:
As we might notice, this multimodular project follows the standard that we discussed in the Core concepts section.
While terminating the structuration of our project, we take the chance to deepen two more concepts, plugins and dependencies.
As we mentioned earlier, Maven's core functionalities do not cover all needs; most of them are implemented in external plugins. We can add plugins to our project by right-clicking on our
pom.xml file and navigating to Maven | Add Plugin from the context menu, as shown in the following screenshot. We can see that the Version field is not mandatory; if we don't specify its value, Maven will download the latest version.
In the following example, we add the maven-surefire-plugin to transportation project. This plugin is used during the test phase to execute the unit tests of the applications. It supports different unit-test frameworks such as JUnit and TestNG.
Since we want to use JUnit, we can simply add JUnit as a dependency of our project. We can add a new dependency the same way as we added a plugin before.
Of course, it is possible to add more plugins and dependencies by manually editing the POM file of each project. In order to add the Maven compiler plugin in our parent POM, we simply add the following tags as a child of the
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.7</source> <target>1.7</target> </configuration > </plugin>
<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>com.packt.examples</groupId> <artifactId>transportation-project</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>pom</packaging> <name>transportation-project</name> <description>The Transportation Project</description> <modules> <module>transportation-acq-ear</module> <module>transportation-acq-war</module> <module>transportation-acq-ejb</module> <module>transportation-reporting-ear</module> <module>transportation-reporting-war</module> <module>transportation-reporting-ejb</module> <module>transportation-common-jar</module> <module>transportation-statistics-batch-jar</module> </modules> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.8.1</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.17</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.5.1</version> <configuration> <source>1.7</source> <target>1.7</target> </configuration > </plugin> </plugins> </build> </project>
Downloading the example code
You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.
Before exploring the concepts we introduced in this chapter in more detail, it is important to focus on the best practice described in the following snippet. This practice concerns dependency management and will allow us to avoid some common problems related to this topic.
The aggregate POM should be the only one defining the dependency version. The modules composing the project should use the dependencies in an anonymous way (that is, without specifying their versions).
Archive modules such as EARs should be the only way to physically contain the libraries; all the submodules should consider their dependencies as provided.
In this chapter, we explored Maven's core concepts and described the structure of the sample project that we will follow across this book.
We also discussed the concepts of dependency and plugins in detail, and explained how to practically manage them.
In the following chapter, we will dive into our project and discuss the advanced use and customization of Maven plugins in detail.