Maven: The Complete Reference
4.3. Common Lifecycle Goals

Many of the packaging lifecycles have similar goals. If you look at
the goals bound to the WAR and JAR lifecycles, you’ll see that they
differ only in the package phase. The package phase of the WAR
lifecycle calls war:war and the package phase of the JAR lifecycle
calls jar:jar. Most of the lifecycles you will come into contact
with share some common lifecycle goals for managing resources, running
tests, and compiling source code. In this section, we’ll explore some
of these common lifecycle goals in detail.
The process-resources phase "processes" resources and copies them to
the output directory. If you haven’t customized the default directory
locations defined in the Super POM, this means that Maven will copy
the files from ${basedir}/src/main/resources to
${basedir}/target/classes or the directory defined in
${project.build.outputDirectory}. In addition to copying the
resources to the output directory, Maven can also apply a filter to
the resources that allows you to replace tokens within resource
file. Just like variables are referenced in a POM using ${...}
notation, you can reference variables in your project’s resources
using the same syntax. Coupled with build profiles, such a facility
can be used to produce build artifacts which target different
deployment platforms. This is something that is common in environments
which need to produce output for development, testing, staging, and
production platforms from the same project. For more information about
build profiles, see Chapter 5, Build Profiles.
To illustrate resource filtering, assume that you have a project with an XML file in src/main/resources/META-INF/service.xml. You want to externalize some configuration variables to a properties file. In other words, you might want to reference a JDBC URL, username, and password for your database, and you don’t want to put these values directly into the service.xml file. Instead, you would like to use a properties file to capture all of the configuration points for your program. Doing this will allow you to consolidate all configuration into a single properties file and make it easier to change configuration values when you need to target a new deployment environment. First, take a look at the contents of service.xml in src/main/resources/META-INF.
Using Properties in Project Resources.
<service>
<!-- This URL was set by project version ${project.version} -->
<url>${jdbc.url}</url>
<user>${jdbc.username}</user>
<password>${jdbc.password}</password>
</service>
This XML file uses the same property reference syntax you can use in
the POM. In fact, the first variable referenced is the project
variable which is also an implicit variable made available in the
POM. The project variable provides access to POM information. The
next three variable references are jdbc.url, jdbc.username, and
jdbc.password. These custom variables are defined in a properties
file src/main/filters/default.properties.
default.properties in src/main/filters.
jdbc.url=jdbc:hsqldb:mem:mydb jdbc.username=sa jdbc.password=
To configure resource filtering with this default.properties file,
we need to specify two things in a project’s POM: a list of properties
files in the filters element of the build configuration, and a flag
to Maven that the resources directory is to be filtered. The default
Maven behavior is to skip filtering and just copy the resources to the
output directory; you’ll need to explicitly configure resource filter,
or Maven will skip the step altogether. This default ensures that
Maven’s resource filtering feature doesn’t surprise you out of nowhere
and clobbering any ${...} references you didn’t want it to
replace.
Filter Resources (Replacing Properties).
<build>
<filters>
<filter>src/main/filters/default.properties</filter>
</filters>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
As with all directories in Maven, the resources directory does not
need to be in src/main/resources. This is just the default value
defined in the Super POM. You should also note that you don’t need to
consolidate all of your resources into a single directory. You can
always separate resources into separate directories under
src/main. Assume that you have a project which contains hundreds of
XML documents and hundreds of images. Instead of mixing the resources
in the src/main/resources directory, you might want to create two
directories src/main/xml and src/main/images to hold this
content. To add directories to the list of resource directories, you
would add the following resource elements to your build
configuration.
Configuring Additional Resource Directories.
<build>
...
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/main/xml</directory>
</resource>
<resource>
<directory>src/main/images</directory>
</resource>
</resources>
...
</build>
When you are building a project that produces a console application or a command-line tool, you’ll often find yourself writing simple shell scripts that need to reference the JAR produced by a build. When you are using the assembly plugin to produce a distribution for an application as a ZIP or TAR, you might place all of your scripts in a directory like src/main/command. In the following POM resource configuration, you’ll see how we can use resource filtering and a reference to the project variable to capture the final output name of the JAR. For more information about the Maven Assembly plugin, see Chapter 8, Maven Assemblies.
<build>
<groupId>org.sonatype.mavenbook</groupId>
<artifactId>simple-cmd</artifactId>
<version>2.3.1</version>
...
<resources>
<resource>
<filtering>true</filtering>
<directory>${basedir}/src/main/command</directory>
<includes>
<include>run.bat</include>
<include>run.sh</include>
</includes>
<targetPath>${basedir}</targetPath>
</resource>
<resource>
<directory>${basedir}/src/main/resources</directory>
</resource>
</resources>
...
</build>
If you run mvn process-resources in this project, you will end up
with two files, run.sh and run.bat, in ${basedir}. We’ve
singled out these two files in a resource element, configuring
filtering, and set the targetPath to be ${basedir}. In a
second resource element, we’ve configured the default resources path
to be copied to the default output directory without any
filtering. Filtering Script Resources shows you how to declare two resource
directories and supply them with different filtering and target
directory preferences. The project from Filtering Script Resources would
contain a run.bat file in src/main/command with the following
content:
@echo off
java -jar ${project.build.finalName}.jar %*
After running mvn process-resources, a file named run.bat would
appear in ${basedir} with the following content:
@echo off java -jar simple-cmd-2.3.1.jar %*
The ability to customize filtering for specific subsets of resources is another reason why complex projects with many different kinds of resources often find it advantageous to separate resources into multiple directories. The alternative to storing different kinds of resources with different filtering requirements in different directories is to use a more complex set of include and exclude patterns to match all resource files which match a certain pattern.
Most lifecycles bind the Compiler plugin’s compile goal to the
compile phase. This phase calls out to compile:compile which is
configured to compile all of the source code and copy the bytecode to
the build output directory. If you haven’t customized the values
defined in the Super POM, compile:compile is going to compile
everything from src/main/java to target/classes. The Compiler
plugin calls out to javac and uses default source and target
settings of 1.3 and 1.1. In other words, the compiler plugin assumes
that your Java source conforms to Java 1.3 and that you are targeting
a Java 1.1 JVM. If you would like to change these settings, you’ll
need to supply the target and source configuration to the Compiler
plugin in your project’s POM as shown in Setting the Source and Target Versions for the Compiler Plugin.
Setting the Source and Target Versions for the Compiler Plugin.
<project>
...
<build>
...
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
</plugins>
...
</build>
...
</project>
Notice we are configuring the Compiler plugin, and not the specific
compile:compile goal. If we were going to configure the source and
target for just the compile:compile goal, we would place the
configuration element below an execution element for the
compile:compile goal. We’ve configured the target and source for the
plugin because compile:compile isn’t the only goal we’re interested
in configuring. The Compiler plugin is reused when Maven compiles
tests using the compile:testCompile goal, and configuring target and
source at the plugin level allows us to define it once for all goals
in a plugin.
If you need to customize the location of the source code, you can do
so by changing the build configuration. If you wanted to store your
project’s source code in src/java instead of src/main/java and if
you wanted build output to go to classes instead of
target/classes, you could always override the default
sourceDirectory defined by the Super POM.
Overriding the Default Source Directory.
<build>
...
<sourceDirectory>src/java</sourceDirectory>
<outputDirectory>classes</outputDirectory>
...
</build>
Warning
While it might seem necessary to bend Maven to your own idea of project directory structure, we can’t emphasize enough that you should sacrifice your own ideas of directory structure in favor of the Maven defaults. This isn’t because we’re trying to brainwash you into accepting the Maven Way, but it will be easier for people to understand your project if it adheres to the most basic conventions. Just forget about this. Don’t do it.
The process-test-resources phase is almost indistinguishable from
the process-resources phase. There are some trivial differences in
the POM, but most everything the same. You can filter test resources
just as you filter regular resources. The default location for test
resources is defined in the Super POM as src/test/resources, and the
default output directory for test resources is target/test-classes
as defined in ${project.build.testOutputDirectory}.
The test-compile phase is almost identical to the compile
phase. The only difference is that test-compile is going to invoke
compile:testCompile to compile source from the test source directory
to the test build output directory. If you haven’t customized the
default directories from the Super POM, compile:testCompile is going
to compile the source in src/test/java to the target/test-classes
directory.
As with the source code directory, if you want to customize the
location of the test source code and the output of test compilation,
you can do so by overriding the testSourceDirectory and the
testOutputDirectory. If you wanted to store test source in src-test/
instead of src/test/java and you wanted to save test bytecode to
classes-test/ instead of target/test-classes, you would use the
following configuration.
Overriding the Location of Test Source and Output.
<build>
...
<testSourceDirectory>src-test</testSourceDirectory>
<testOutputDirectory>classes-test</testOutputDirectory>
...
</build>
Most lifecycles bind the test goal of the Surefire plugin to the test phase. The Surefire plugin is Maven’s unit testing plugin, the default behavior of Surefire is to look for all classes ending in *Test in the test source directory and to run them as JUnit tests. The Surefire plugin can also be configured to run TestNG unit tests.
After running mvn test, you should also notice that the Surefire
produces a number of reports in target/surefire-reports. This
reports directory will have two files for each test executed by the
Surefire plugin: an XML document containing execution information for
the test, and a text file containing the output of the unit test. If
there is a problem during the test phase and a unit test has failed,
you can use the output of Maven and the contents of this directory to
track down the cause of a test failure. This surefire-reports/
directory is also used during site generation to create an easy to
read summary of all the unit tests in a project.
If you are working on a project that has some failing unit tests, but
you want the project to produce output, you’ll need to configure the
Surefire plugin to continue a build even if it encounters a
failure. The default behavior is to stop a build whenever a unit test
failure is encountered. To override this behavior, you’ll need to set
the testFailureIgnore configuration property on the Surefire plugin
to true.
Configuring Surefire to Ignore Test Failures.
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<testFailureIgnore>true</testFailureIgnore>
</configuration>
</plugin>
...
</plugins>
</build>
If you would like to skip tests altogether, you can do so by executing the following command:
$ mvn install -Dmaven.test.skip=true
The maven.test.skip variable controls both the Compiler and the
Surefire plugin, if you pass in maven.test.skip you’ve told Maven to
ignore tests altogether.
The install goal of the Install plugin is almost always bound to the
install lifecycle phase. This install:install goal simply installs
a project’s main artifact to the local repository. If you have a
project with a groupId of org.sonatype.mavenbook, an artifactId
of simple-test, and a version of 1.0.2, the install:install goal
is going to copy the JAR file from target/simple-test-1.0.2.jar to
~/.m2/repository/org/sonatype/mavenbook/simple-test/1.0.2/simple-test-1.0.2.jar.
If the project has POM packaging, this goal will copy the POM to the
local repository.
The deploy goal of the Deploy plugin is usually bound to the
deploy lifecycle phase. This phase is used to deploy an artifact to
a remote Maven repository, this is usually required to update a remote
repository when you are performing a release. The deployment procedure
can be as simple as copying a file to another directory or as complex
as transferring a file over SCP using a public key. Deployment
settings usually involve credentials to a remote repository, and, as
such, deployment settings are usually not stored in a
pom.xml. Instead, deployment settings are more frequently found in
an individual user’s ~/.m2/settings.xml. For now, all you need to
know is that the deploy:deploy goal is bound to the deploy phase
and it takes care of transporting an artifact to a published
repository and updating any repository information which might be
affected by such a deployment.
