The goal of this article is to provide a good overview of Apache Maven. To answer to the question, “What is Maven?,” we need to cover several topics, but for starters, let’s say that Maven is a great tool that can sustain, support, and assist all stages involved in software development. Apache Maven is ready to serve you for creating a project from scratch, building, testing, reporting, documenting, guiding, ensuring code quality, transparent migration, project modularization, deployment, centralized remote repositories, and so forth, as a complex and comprehensive tool dedicated to software project management. Maven is an important “player” in Agile team collaboration.
Downloading and Installing Maven on Linux/Windows/Mac
Downloading Apache Maven is a very simple task that can be accomplished via the download page. Independently of your operating system, just pick up the desired archive (ZIP or TAR.GZ):
Figure 1: Download Apache Maven
For testing the examples from this article, we have downloaded Apache Maven 3.3.9.
The next step is also very simple. Extract the archive content in a convenient location on your computer and try to obtain a path without white-space characters (for example, in Windows, D:Maven (used in this article) or D:toolsMaven-3.3.9).
Ensure JDK Presence
To work properly, Apache Maven needs the Java Development Kit (JDK). For example, Apache Maven 3.3.9 requires JDK 1.7 or above to execute. In case that you don’t know if you have JDK installed, open a command prompt and type java-version. In Figure 2, you can see this command under Windows, but you can use it under Linux or Mac also:
Figure 2: Check JDK presence
If JDK is not installed, please follow these instructions. It is mandatory to successfully accomplish this step before going further. So, take your time and pay attention to this step.
Configure Environment Variables
To start using Maven, you need to set a few environment variables as follows: PATH, M2_HOME, and MAVEN_HOME. M2_HOME and MAVEN_HOME must point to the Maven installation folder (for example, D:Maven) and the PATH must point to the /binD:Mavenbin). This is pretty simple to achieve and depends on your OS, but here are some hints.
Configure PATH, MAVEN_HOME, and M2_HOME on Windows
On Windows, you can accomplish this via the Environment Variables wizard available in Control Panel |System and Security |System | Environment Variables.
Figure 3: Setting PATH, M2_HOME, and MAVEN_HOME in Windows
Configure PATH, MAVEN_HOME, and M2_HOME on Linux
On Linux, you need to export the PATH, MAVEN_HOME, and M2_HOME environment variables in the .bashrc file. This file can be found in the user’s home folder, which for me is /home/leonard/.bashrc:
export MAVEN_HOME=/home/leonard/Maven export M2_HOME=/home/leonard/Maven export PATH=${PATH}:${M2_HOME}/bin
Configure PATH, MAVEN_HOME, and M2_HOME on Mac
On a Mac, you need to export the PATH, MAVEN_HOME, and M2_HOME environment variables in the .bash_login:
export MAVEN_HOME=/usr/local/Maven export M2_HOME=/usr/local/Maven export PATH=${PATH}:${M2_HOME}/bin
Verify Maven Installation
After you download and install Maven, it is a good practice to ensure that Maven is ready to go. For this, independent of the OS, you can execute mvn -version in a command-line terminal window. In Figure 4, you can see this under Windows:
Figure 4: Verify Maven installation
So, if you see output like in Figure 4, Maven is working as expected. From this point forward, the presented examples will consider Windows as the OS.
Create/Generate a Maven Project
Maven is basically a suite of plug-ins that work together or separately (plug-in centric) to accomplish different kind of tasks. For example, Maven allows us to create a Java project from Maven archetypes (templates) or from a pom.xml file. Among Maven archetypes we have:
An archetype to generate a sample Maven project: maven-archetype-quickstart
An archetype to generate a simplified sample J2EE application: maven-archetype-j2ee-simple
An archetype to generate a sample Maven Web application project: maven-archetype-webapp
...
To create a new Maven project from an archetype, we can use the following command (replace archetype_artifactId with the artifactId of the desired archetype):
mvn archetype:generate -DarchetypeArtifactId=archetype_artifactId>
Or, if you also need to specify the groupId, use this:
mvn archetype:generate -DarchetypeGroupId=archetype_groupId -DarchetypeArtifactId=archetype_artifactId
We also can choose the desired archetype during project creation via the following command:
mvn archetype:generate
Create a Quickstart Java SE Project
Okay, so let’s try the mvn archetype:generate for creating (generating) a quickstart Java SE project. Navigate from the command line to a convenient location (where you want to store the project) and type the preceding command. At the beginning, you see something like in Figure 5:
Figure 5: Create first Maven project
The first thing you must specify is the archetype that you intend to use for this project. Before Maven prompts you to do that, you will see a very long list with the available archetypes (1500+). Each archetype is listed with a number (identifier), a name, and a short description meant to provide a hint about its purpose. By default, Maven will suggest that you choose the maven-archetype-quickstart archetype which is identified by the number 784. We have cropped the relevant part in Figure 6:
Figure 6: Choose the desired archetype
Because we want to go for maven-archetype-quickstart, simply press Enter. Furthermore, Maven will list the available versions for the selected archetype and suggest you to use the latest version. Again, you can just press Enter to go with the latest version:
Figure 7: Choose the archetype version
After several downloads, you will be prompted to provide the project “landmarks” (known as Maven coordinates) such as groupId, artifactId, version, and package:
- groupId: The unique hierarchical location of a project within a Maven repository. We use the local Maven repository that resides on your computer. Here, you can put something composed from your organization (domain) name and the project name (for example, org.mycompany.myproj). Further sub-modules can be derived from this name (for example, org.mycompany.myproj.subproj1). Following this naming convention, you can easily “navigate” through the project structure.
- artifactId: Represents the name of the project without version (for example, myproj). Typically, this is the name of the project as it is known by its users (for example, OmniFaces, PrimeFaces, and so on).
- version: Represents the version of the project. This can follow any version convention (for example, 1.0, 1.1, 1.2, and the like). Maven suggests using the 1.0-SNAPSHOT style.
- package: Indicates the package name where the sources will be stored.
So, for groupId, we have specified org.quickstart; for artifactId, we have specified quickstart; for version, we have specified 1.0; and for package, we have specified the same as for groupId:
Figure 8: Finalization of project creation
After we provide the Maven coordinates, the project is successfully created and is ready to go. In Figure 9, you can see what Maven has generated for us:
Figure 9: Maven-generated Java SE project
Create a Java EE 7 Starter Project for JBoss WildFly
Another common type of projects generated via Maven archetypes are Java Web/enterprise projects. This time, let’s create a Java EE 7 starter project for JBoss Wildfly by indicating the archetype groupId and artifactId right from command line (the code of this project is 1543):
Figure 10: Generate a Java Web project
Take your time to check the generated project. As you will see, it has a significant number of folders and files.
Compile and Test a Project
Compiling and testing the project are two of the main objectives that must be successfully accomplished to deliver a working software package. So, let’s see how we can do that via Maven.
Compile the Project
To compile a Maven project, we need to execute from a command-line terminal window the following command: mvn compile (by default, Maven supports several goals and compile is one of them; among the most popular we have: clean, package, deploy, test, and install). So, let’s compile our Java SE and web project; notice that we execute this command from the project folder:
Figure 11: Compile the Java SE project
The result of compilation (Java classes) can be seen in the /quickstart/target/classes folder. Now, let’s compile the Java EE 7 application:
Figure 12: Compile the Java Web project
The result of compilation (Java classes) can be seen in the /javaee7.quickstart/target/classes folder.
Test the Project
Another part of the default build lifecycle is represented by the capability of testing the project. To sustain a TDD (Test Driven Development) pattern, Maven provide the test feature via the mvn test command. Via this command, we will execute the tests located in the src/test folder. For our Java SE application, we have a single test named AppTest:
Figure 13: Testing the Java SE application
We should do the same for our Java EE 7 application. In this case, the test is also located in the src/test folder and it is named MemberRegistrationTest. But, this test is not as easy as for the Java SE app, so you must have some patience and read further. Later on, you will see this test also.
What Is POM (Project Object Model)?
Basically, the presence of a pom.xml file in a project is a clear indicator that this is a Maven project. Maven knows this file as the XML representation of the project. This file may contain a lot of information, such as metadata, dependencies, configurations, project organization, paths, profiles, and so forth. If you check, there are two projects; you can notice that both of them contain a pom.xml file in the root. Although the pom.xml file for the Java SE application is pretty short and simple, the pom.xml file for the Java EE 7 application is quite big and complex. So, let’s see the structure of a POM file:
<project ... > <modelVersion>4.0.0</modelVersion> <!-- The Basics --> <groupId>...</groupId> <artifactId>...</artifactId> <version>...</version> <packaging>...</packaging> <dependencies>...</dependencies> <parent>...</parent> <dependencyManagement>...</dependencyManagement> <modules>...</modules> <properties>...</properties> <!-- Build Settings --> <build>...</build> <reporting>...</reporting> <!-- More Project Information --> <name>...</name> <description>...</description> <url>...</url> <inceptionYear>...</inceptionYear> <licenses>...</licenses> <organization>...</organization> <developers>...</developers> <contributors>...</contributors> <!-- Environment Settings --> <issueManagement>...</issueManagement> <ciManagement>...</ciManagement> <mailingLists>...</mailingLists> <scm>...</scm> <prerequisites>...</prerequisites> <repositories>...</repositories> <pluginRepositories>...</pluginRepositories> <distributionManagement>...</distributionManagement> <profiles>...</profiles> </project>
Let’s have a few words about each section and let’s try to identify some of them in the pom.xml of the Java EE 7 application:
<!-- The Basics -->
In this part, we have the project coordinates, dependencies, dependency management, and inheritance details. Moreover, it may contain modules and some project-level properties. Take your time and try to dissect this part extracted from the Java EE 7 application pom.xml file; don’t hesitate to carefully read the comments because is a good way of learning by example:
... <groupId>org.javaee7.quickstart</groupId> <artifactId>javaee7.quickstart</artifactId> <version>1.0</version> <packaging>war</packaging> <name>WildFly Quickstarts: javaee7.quickstart</name> <description>A starter Java EE 7 webapp project for use on JBoss WildFly / WildFly, generated from the jboss-javaee6-webapp archetype</description> <properties> <!-- Explicitly declaring the source encoding eliminates the following message: --> <!-- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! --> <project.build.sourceEncoding> UTF-8 </project.build.sourceEncoding> <!-- JBoss dependency versions --> <version.wildfly.maven.plugin> 1.0.2.Final </version.wildfly.maven.plugin> <!-- Define the version of the JBoss BOMs we want to import to specify tested stacks. --> <version.jboss.bom>8.2.1.Final</version.jboss.bom> <!-- other plugin versions --> <version.compiler.plugin>3.1</version.compiler.plugin> <version.surefire.plugin>2.16</version.surefire.plugin> <version.war.plugin>2.5</version.war.plugin> <!-- maven-compiler-plugin --> <maven.compiler.target>1.7</maven.compiler.target> <maven.compiler.source>1.7</maven.compiler.source> </properties> <dependencyManagement> <dependencies> <!-- JBoss distributes a complete set of Java EE 7 APIs including a Bill of Materials (BOM). A BOM specifies the versions of a "stack" (or a collection) of artifacts. We use this here so that we always get the correct versions of artifacts. Here we use the jboss-javaee-7.0-with-tools stack (you can read this as the JBoss stack of the Java EE 7 APIs, with some extras tools for your project, such as Arquillian for testing) and the jboss-javaee-7.0-with-hibernate stack you can read this as the JBoss stack of the Java EE 7 APIs, with extras from the Hibernate family of projects) --> <dependency> <groupId>org.wildfly.bom</groupId> <artifactId>jboss-javaee-7.0-with-tools</artifactId> <version>${version.jboss.bom}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.wildfly.bom</groupId> <artifactId>jboss-javaee-7.0-with-hibernate</artifactId> <version>${version.jboss.bom}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- First declare the APIs we depend on and need for compilation. All of them are provided by JBoss WildFly --> <!-- Import the CDI API, we use provided scope as the API is included in JBoss WildFly --> <dependency> <groupId>javax.enterprise</groupId> <artifactId>cdi-api</artifactId> <scope>provided</scope> </dependency> <!-- Import the Common Annotations API (JSR-250), we use provided scope as the API is included in JBoss WildFly --> <dependency> <groupId>org.jboss.spec.javax.annotation</groupId> <artifactId>jboss-annotations-api_1.2_spec</artifactId> <scope>provided</scope> </dependency> <!-- Import the JAX-RS API, we use provided scope as the API is included in JBoss WildFly --> <dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>jaxrs-api</artifactId> <scope>provided</scope> </dependency> <!-- Import the JPA API, we use provided scope as the API is included in JBoss WildFly --> <dependency> <groupId>org.hibernate.javax.persistence</groupId> <artifactId>hibernate-jpa-2.1-api</artifactId> <scope>provided</scope> </dependency> <!-- Import the EJB API, we use provided scope as the API is included in JBoss WildFly --> <dependency> <groupId>org.jboss.spec.javax.ejb</groupId> <artifactId>jboss-ejb-api_3.2_spec</artifactId> <scope>provided</scope> </dependency> <!-- JSR-303 (Bean Validation) Implementation --> <!-- Provides portable constraints such as @Email --> <!-- Hibernate Validator is shipped in JBoss WildFly --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <scope>provided</scope> <exclusions> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> </exclusion> </exclusions> </dependency> <!-- Import the JSF API, we use provided scope as the API is included in JBoss WildFly --> <dependency> <groupId>org.jboss.spec.javax.faces</groupId> <artifactId>jboss-jsf-api_2.2_spec</artifactId> <scope>provided</scope> </dependency> <!-- Now we declare any tools needed --> <!-- Annotation processor to generate the JPA 2.0 metamodel classes for typesafe criteria queries --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-jpamodelgen</artifactId> <scope>provided</scope> </dependency> <!-- Annotation processor that raising compilation errors whenever constraint annotations are incorrectly used. --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator-annotation-processor</artifactId> <scope>provided</scope> </dependency> <!-- Needed for running tests (you may also use TestNG) --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> <!-- Optional, but highly recommended --> <!-- Arquillian allows you to test enterprise code such as EJBs and Transactional(JTA) JPA from JUnit/TestNG --> <dependency> <groupId>org.jboss.arquillian.junit</groupId> <artifactId>arquillian-junit-container</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.jboss.arquillian.protocol</groupId> <artifactId>arquillian-protocol-servlet</artifactId> <scope>test</scope> </dependency> </dependencies> ...
<!-- Build Settings -->
This section is reserved for build details as your project’s directory structure and managing plug-ins. For example, our pom.xml study case declares here the WildFly plug-in for deploying the application WAR to a local WildFly container:
... <build> <!-- Maven will append the version to the finalName (which is the name given to the generated war, and hence the context root) --> <finalName>${project.artifactId}</finalName> <plugins> <plugin> <artifactId>maven-war-plugin</artifactId> <version>${version.war.plugin}</version> <configuration> <!-- Java EE 7 doesn't require web.xml, Maven needs to catch up! --> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> <!-- The WildFly plugin deploys your war to a local WildFly container --> <!-- To use, run: mvn package wildfly:deploy --> <plugin> <groupId>org.wildfly.plugins</groupId> <artifactId>wildfly-maven-plugin</artifactId> <version>${version.wildfly.maven.plugin}</version> </plugin> </plugins> </build> ...
<!-- More Project Information -->
This section is useful for providing in-depth details about licenses, organization, developers, and contributors. In our case study, this section is like this:
... <url>http://wildfly.org</url> <licenses> <license> <name>Apache License, Version 2.0</name> <distribution>repo</distribution> <url> http://www.apache.org/licenses/LICENSE-2.0.html </url> </license> </licenses> ...
<!– Environment Settings –>
In this section, we have all information regarding the environment including issue management (defect tracking system), continuous integration management, mailing lists, SCM (Software Configuration Management), prerequisites, repositories, profiles, and so on. In our case study, we have several profiles:
... <profiles> <profile> <!-- The default profile skips all tests, though you can tune it to run just unit tests based on a custom pattern --> <!-- Seperate profiles are provided for running all tests, including Arquillian tests that execute in the specified container --> <id>default</id> <activation> <activeByDefault>true</activeByDefault> </activation> <build> <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>${version.surefire.plugin}</version> <configuration> <skip>true</skip> </configuration> </plugin> </plugins> </build> </profile> <profile> <!-- An optional Arquillian testing profile that executes tests in your WildFly instance --> <!-- This profile will start a new WildFly instance, and execute the test, shutting it down when done --> <!-- Run with: mvn clean test -Parq-wildfly-managed --> <id>arq-wildfly-managed</id> <dependencies> <dependency> <groupId>org.wildfly</groupId> <artifactId> wildfly-arquillian-container-managed </artifactId> <scope>test</scope> </dependency> </dependencies> </profile> <profile> <!-- An optional Arquillian testing profile that executes tests in a remote WildFly instance --> <!-- Run with: mvn clean test -Parq-wildfly-remote --> <id>arq-wildfly-remote</id> <dependencies> <dependency> <groupId>org.wildfly</groupId> <artifactId> wildfly-arquillian-container-remote </artifactId> <scope>test</scope> </dependency> </profile> <profile> <!-- When built in OpenShift the 'openshift' profile will be used when invoking mvn. --> <!-- Use this profile for any OpenShift specific customization your app will need. --> <!-- By default, that is to put the resulting archive into the 'deployments' folder. --> <!-- http://maven.apache.org/guides/mini/ guide-building-for-different-environments.html --> <id>openshift</id> <build> <plugins> <plugin> <artifactId>maven-war-plugin</artifactId> <version>${version.war.plugin}</version> <configuration> <outputDirectory> deployments </outputDirectory> <warName>ROOT</warName> </configuration> </plugin> </plugins> </build> </profile> </profiles> ...
A detailed POM explanation can be found here.
Build Lifecycles
There are three built-in build lifecycles: default, clean, and site.
Default Lifecycle
When we refer to a Maven build lifecycle, we refer to a suite of phases that Maven is taking into account to build a project. Even though phases such as compile, test, or deploy are pretty well-known, you have to know that Maven actually has eight “major” phases, as shown in Figure 14 (there is also a set of “minor” phases not listed here):
Figure 14: Maven build lifecycle
- validate: Validates that all project information is available and is correct
- compile: Compiles the project’s source code
- test: Runs unit tests inside the proper framework
- package: Packages the compiled code in its distribution format (for example, JAR, WAR, and the like)
- integration-test: Takes the package in the integration-test environment
- verify: Checks the package validity
- install: To re-use the package locally in other projects, Maven installs the package in the local repository
- deploy: The final package is installed in the remote repository (for example, Nexus, Artifactory, or even Maven Central) and is ready to be shared by other projects/developers.
Clean Lifecycle
The clean process is responsible for cleaning the project for files generated at compilation, additional files, and so forth. This is accomplished in three “minor” phases:
- pre-clean: Executes before project cleaning
- clean: Deletes all files from previous builds
- post-clean: Finalizes project cleaning
Site Lifecycle
The site process is able to generate and deploy the project’s site documentation. It has four “minor” phases:
- pre-site: Executes before generation of the site
- site: Generates the project’s site documentation
- post-site: Finalizes the site generation and gets ready for site deployment
- site-deploy: Deploys the site documentation to the specified server
Command Commands
Let’s see several common commands that involve the build lifecycle phases. In the section Compile and test a project, you already saw mvn compile and mvn test at work, so let’s see some more:
Clean
- mvn clean: Cleans the project
Figure 15: Cleaning the Java EE 7 project
Package
- mvn package: Packages the application
Figure 16: Packaging the Java EE 7 project into a WAR
Deploy
- mvn deploy: Deploys the application
Here, we must have a little discussion because this command is more tricky. If you try this command for the Java EE 7 application, you will receive an error like in Figure 17:
Figure 17: Trying to deploy the Java EE 7 application
To understand this idea, you must know a few additional things. First, we must distinguish between repository deployment and application server deployment. The default deploy phase of Maven is considering repository deployment, which means that you try to deploy your artifact (application) on a remote repository (release or snapshot) location as Nexus, Artifactory, or even Maven Central. Basically, we try to use the Maven Deploy Plugin, which has two goals (known as Mojos). The first one is deploy:deploy (the deploy phase of the build lifecycle is implemented via this Mojo), and the second one is deploy:deploy-file (not discussed here). Now, we will focus on the first Mojo, deploy:deploy. To work, this Mojo needs a <distributionManagement/> section in pom.xml. This section should supply at least a <repository/> defining the remote repository location for your artifact. When your repository also needs authentication, you need a <server/> entry in your settings.xml file (this file can be found in MAVEN_HOME/conf folder). So, our error message means that we never specified a remote repository.
Now, in terms of Java EE, by deploy, we understand application server deployment. This means that some application server (for example, WildFly, GlassFish, Tomcat, and so on) will be aware of our application and it will expose it for usage in a browser. So, asking Maven to deploy an application will not result in its application server deployment. To accomplish application server deployment, you need a special plugin, such as the WildFly Maven Plugin. This plugin comes with several goals, as: wildfly:deploy, wildfly:run, and the like.
Let’s suppose that we want to deploy our Java EE 7 application on a WildFly application server. We can accomplish this very easily because our pom.xml comes with this plugin configured. This is normal, because we have generated the project from a WildFly dedicated Maven archetype. Here is the plugin:
<!-- The WildFly plugin deploys your war to a local WildFly container --> <!-- To use, run: mvn package wildfly:deploy --> <plugin> <groupId>org.wildfly.plugins</groupId> <artifactId>wildfly-maven-plugin</artifactId> <version>${version.wildfly.maven.plugin}</version> </plugin>
This means that we can use the goals of WildFly Maven Plugin out-of-the-box. For example, if you want Maven to download WildFly, start it and deploy our Java EE 7 application; then, you can simply execute the mvn wildfly:run command. Just ensure that the default WildFly ports are free (8080 and 9990). When Maven finishes the job, you will be able to see the application at http://localhost:8080/javaee7.quickstart:
Figure 18: Deploying our Java EE 7 application on WildFly application server via Maven
If you already have WildFly installed, you can start it easily and execute the mvn wildfly:deploy command. This will deploy the application on your WildFly. Check out the rest of goals here.
Working with Profiles
Among the advantages provided by Maven, we have project portability. Maven tries to keep the projects independent (portable) across filesystem references via local repository and pom.xml configurations.
When portability is affected (which means that the independency of the filesystem cannot be achieved), Maven provides a solution materialized in profiles. Profiles are defined in pom.xml and they are demarcated by the <profile/> tag. Profiles can be triggered: through the command line using the -P option, via Maven settings, or environment specific triggers.
If we take a closer look to the pom.xml of our Java EE 7 application, we notice several profiles, as follows (please, read the comments because they help you to understand what each profile does):
... <profiles> <profile> <!-- The default profile skips all tests, though you can tune it to run just unit tests based on a custom pattern --> <!-- Seperate profiles are provided for running all tests, including Arquillian tests that execute in the specified container --> <id>default</id> <activation> <activeByDefault>true</activeByDefault> </activation> <build> <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>${version.surefire.plugin}</version> <configuration> <skip>true</skip> </configuration> </plugin> </plugins> </build> </profile> <profile> <!-- An optional Arquillian testing profile that executes tests in your WildFly instance --> <!-- This profile will start a new WildFly instance, and execute the test, shutting it down when done --> <!-- Run with: mvn clean test -Parq-wildfly-managed --> <id>arq-wildfly-managed</id> <dependencies> <dependency> <groupId>org.wildfly</groupId> <artifactId> wildfly-arquillian-container-managed </artifactId> <scope>test</scope> </dependency> </dependencies> </profile> <profile> <!-- An optional Arquillian testing profile that executes tests in a remote WildFly instance --> <!-- Run with: mvn clean test -Parq-wildfly-remote --> <id>arq-wildfly-remote</id> <dependencies> <dependency> <groupId>org.wildfly</groupId> <artifactId> wildfly-arquillian-container-remote </artifactId> <scope>test</scope> </dependency> </dependencies> </profile> <profile> <!-- When built in OpenShift the 'openshift' profile will be used when invoking mvn. --> <!-- Use this profile for any OpenShift specific customization your app will need. --> <!-- By default that is to put the resulting archive into the 'deployments' folder. --> <!-- http://maven.apache.org/guides/mini/guide-building-for- different-environments.html --> <id>openshift</id> <build> <plugins> <plugin> <artifactId>maven-war-plugin</artifactId> <version>${version.war.plugin}</version> <configuration> <outputDirectory>deployments</outputDirectory> <warName>ROOT</warName> </configuration> </plugin> </plugins> </build> </profile> </profiles> ...
As you can see, all the above profiles are dedicated to testing the application. For example, let’s try to test our Java EE 7 application via mvn clean test -Parq-wildfly-remote. First, download, install, and start JBoss WildFly 10.0.0 with default settings (ports 8080 and 9990) on your computer. You can easily start it from the command line by executing the standalone batch file:
Figure 19: Running standalone WildFly from the command line
Furthermore, open a new command prompt and execute mvn clean test -Parq-wildfly-remote. You may notice that Maven will deploy the application on WildFly, execute the tests, and un-deploy the application:
Figure 20: Execute tests
You can read more about Maven profiles here.
Build Automation
As you saw in the previous sections, Maven builds a project by executing several phases of the build lifecycle. You know that, to test an application, you can sequentially execute the following commands:
// 1. Execute validate and wait to finish mvn validate ... // 2. Execute compile and wait to finish mvn compile ... // 3. Execute package and wait to finish mvn package ... // 4. Execute test and wait to finish mvn test
Although these are individual commands executed one by one, Maven allows us to automate the process by executing the last of the phases (we choose which one to be the last). When you specify a certain phase, Maven will execute every build phase that is before the called build phase. For example, if you execute the test phase, Maven will execute all build phases before it. This means that instead of executing four different commands, we can simply execute only the last one as mvn test. Maven will execute validate, compile, and package as a result of automation skills. Or, if we execute mvn deploy, Maven will execute all build phases.
Project Modularization
Maven project modularization, or multi-modular projects, consist of a Parent Project that contains Child Projects known as Modules. Practically, this is a technique of organizing large projects that can be divided in sub-modules. The Parent Project contains a POM file that defines the containing sub-modules. Each sub-module has its own POM, also. Check Figure 21:
Figure 21: Maven modularization
To provide an example, we start by creating a new pom.xml file in the main application folder. We name this folder MyBigApp and the pom.xml looks like the following; it’s important to notice the <packaging>pom</packaging> part:
<?xml version="1.0" encoding="UTF-8"?>
<project
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>my.big.apps</groupId>
<artifactId>BigApp</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<name>MyBigApp</name>
</project>
Further, navigate from the command line in the MyBigApp folder and generate the exact same projects as we did earlier in this article. First, the Java SE quickstart:
Figure 22: Generate the Java SE quickstart application
Next, create the Java EE 7 quickstart application:
Figure 23: Generate the Java EE 7 quickstart application
After the job is done, take a look into the main pom.xml file and notice the <modules/> section:
... <modules> <module>quickstart</module> <module>javaee7.quickstart</module> </modules> ...
Now, you can compile the entire project from the MyBigApp folder via mvn compile:
Figure 24: Compile a project based on multiple modules
Dependency Management
Probably the most known feature of Maven is represented by the dependency management (managing the dependency of the current project of other libraries, projects, and tools). Since version 2, Maven provides transient dependency, which means that Maven will automatically discover artifacts that your dependencies require. In POM, the dependencies section is demarcated by the <dependencies/> tag and each dependency is demarcated by the <dependency/> tag. Essentially, the code skeleton looks like this:
<dependencies> <dependency> <groupId>...</groupId> <artifactId>...</artifactId> <version>...</version> <scope>...</scope> </dependency> <dependency> ... </dependency> ... </dependencies>
Dependencies Scopes
There are six dependencies scopes as follows (the scope is specified in the <scope/> tag):
- compile (default): Dependencies are available in the classpaths.
- provided: JDK or the environment provides dependencies at runtime.
- runtime: Dependencies are required at runtime and are indicated in the runtime classpaths.
- test: Dependencies required for test compilation and execution.
- system: Dependency is always available, but the JAR is provided anyway.
- import: Imports dependencies specified in POM included via the <dependencyManagement/> element.
Dependencies Plug-in
The dependency plug-in is a very useful tool. As you can see here, this plug-in comes with a significant number of goals. Among these, the most used are listed below:
dependency:analyze: Performs dependencies analysis and reports the dependencies that are: used and declared; used and undeclared; unused and declared. For our Java EE 7 application, you can see the output in Figure 25:
Figure 25: Executing dependency:analyze
dependency:analyze-duplicate: Performs an analysis of the <dependencies/> and <dependencyManagement/> tags and determines the duplicate declared dependencies:
Figure 26: Executing dependency:analyze-duplicate
dependency:resolve: Instructs Maven to resolve all dependencies and displays the version.
dependency:resolve-plugin: Resolves all plug-ins.
dependency:tree: Displays dependency trees.
Generating Javadoc
To generate Javadoc (API documentation in HTML), we can use the Maven Javadoc plug-in. Among its 10+ goals, we have the one that generates the documentation for the project. This is javadoc:javadoc and you can see it at work in Figure 27:
Figure 27: Executing javadoc:javadoc
For the Java EE 7 project, the documentation will be located in the javaee7.quickstart/target/site/apidocs folder. Simply open the index.xhtml file in your browser.
Generating Unit Tests Reports
The Maven Surefire plug-in is fully capable to run the unit tests, generate plain text, and XML reports of the tests. The only goal of this plug-in is surefire:test. Normally, you will have this plug-in already available in your pom.xml or you should add it manually, as shown below:
<version.surefire.plugin>2.16</version.surefire.plugin> ... <build> <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>${version.surefire.plugin}</version> <configuration> <skip>false</skip> </configuration> </plugin> </plugins> </build>
To execute the Sunfire plug-in, simply execute the command mvn test. To see the output to the console, use mvn test -Dsurefire.useFile=false.
Summary
In this article, we have covered some of the main aspects of Maven. You have to know that Maven can do more than this, and it is a very important tool in any developer arsenal. In addition to what was discussed here, you may also be interested in generating code coverage reports, creating centralized remote repositories, Java/Google/Scala/Groovy/Flex … and Maven, IDE integration, and so on.