It's time to bring together all the bricks just discussed. In Chapter 1, Maven and Its Philosophy, we introduced a real project called the transportation project. Here, we apply a CI process to the proposed project.
The following list shows the organization of the proposed project:
transportation-acq-ear
transportation-acq-ejb
transportation-acq-war
transportation-common-jar
transportation-reporting-ear
transportation-reporting-war
transportation-reporting-ejb
transportation-common-jar
transportation-statistics-batch-jar
The project is based on two ear
components sharing a common jar
library and a single batch
component. All the artifacts have the same pom.xml
parent file. In this case, we will ignore the dependencies from third-party libraries (JEE, Spring, and so on), since these libraries are not active components during the release process; in other words, we do not need to release or assign a version to third-party libraries.
The proposed project elicits some issues:
transportation-acq-ear
, transportation-reporting-ear
, or transportation-statistics-batch-jar
) with no other components: how to choose the project to build using the CI tool?transportation-common-jar
): how to assign the version number and release it?batch
component, transportation-statistics-batch-jar
, can be released without any dependencies: how to assign the version number and how to release it?transportation-acq-ear
and transportation-reporting-ear
) have three subcomponents: how to assign the version number?When a large project consists of many components/modules such as a web API, a client, and some libraries, not everything has to be re-released every time a component changes. Maven allows the user to specify an alternate pom.xml
file through the –f
option:
$ mvn -f my-pom.xml
Therefore, on a multimodule project, it is common practice to define an aggregator pom.xml
file that lists the modules to be executed as a group.
The following pom.xml
file defines the POM aggregator (called transportation-acq.xml
) for the multimodule project, transportation-acq-ear
. We assume that the transportation-acq-ear
, transportation-acq-ejb
, and transportation-acq-war
projects have been included on a subdirectory called transportation-acq
(the name must be coherent with the name of the aggregator's artifact ID). Therefore, the final directory structure is as follows:
Configure the pom.xml
file of the project as follows:
<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> <parent> <groupId>com.packt.examples</groupId> <artifactId>transportation-project</artifactId> <version>0.0.1 </version> </parent> <artifactId>transportation-acq</artifactId> <version>0.0.1-SNAPHOST</version> <packaging>pom</packaging> <modules> <module>transportation-acq/transportation-acq-ejb</module> <module>transportation-acq/transportation-acq-ear</module> <module>transportation-acq/transportation-acq-war</module> </modules> </project>
We can run the following command to build only the transportation-acq-ear
and its related modules:
$ mvn -f transportation-acq.xml clean install deploy
Order is not important; Maven will sort the modules such that dependencies will always be built before the dependent modules.
The next step is to configure Hudson to ask the user which project aggregator POM should be built on.
From the Hudson home screen, click on the New Job option from the left-hand side menu and copy settings from an existing job or create a new one. Perform the following steps:
Finally, we have to configure the build section. In the build section, perform the following steps:
${projectName}/pom.xml
.Hudson is ready to ask the user which module to build.
The release process of a single component has been extensively described in the previous sections. In a multimodule component, however, dependencies to common artifacts and module versioning require a different configuration. Indeed, even if the submodules are versioned with the same version number of the EAR project, common projects should follow a different lifecycle.
In the transportation project, the proposed strategy will require the team of developers to release common projects (transportation-common-jar
) such as a unitary component, and assign the same version of the EAR project to each submodule (transportation-acq-ejb
, transportation-acq-war
, transportation-reporting-ejb
, or transportation-reporting-war
).
This mixed strategy is a good compromise between the release automation and fine-grained controls of a component's versions. Alternatively, developers can set the version to each component manually and use Maven SCM Plugin only to tag the code. This way, however, is not exactly the idea of CI, where all processes (not developing) must be as automatic and easy as possible.
We configured Maven Release Plugin as explained in the previous sections, and we can execute the following command:
$ mvn –-batch-mode -DallowTimestampedSnapshots=true -DignoreSnapshots=true -f transportation-acq.xml clean install release:clean release:prepare
The outcome is as follows:
[…] [INFO] Transforming 'transportation-acq-ejb'... [INFO] Transforming 'transportation-acq-war'... [INFO] Transforming 'transportation-acq-ear'... […] [INFO] Tagging release with the label transportation-acq-pom-0.0... […]
The -batch-mode
option will force Maven to assign the version number automatically, without any further user interaction. All the submodules will be released with the same version, upgrading the final development (SNAPSHOT
) version with a +1 on the latest number.
We can force the version number, as follows:
$ mvn –-batch-mode -f transportation-acq.xml -DallowTimestampedSnapshots=true -DignoreSnapshots=true -DreleaseVersion=0.0.2 clean install release:clean release:prepare
The project will be delivered with the provided versions.
Obviously, the plugin allows us to provide the number for each component using the following syntax:
-DdevelopmentVersion=1.3-SNAPSHOT
We can also use the following syntax:
-Dproject.dev.groupId:projectName=1.3-SNAPSHOT
Alternatively, use the following syntax:
-Dproject.rel.groupId:projectName=1.3
However, we prefer a more easy strategy.
Hudson can be easily configured by asking the user for the releaseVersion
parameter.
Perform the following steps from the left-hand side menu:
releaseVersion
.Finally, configure a new Maven build step, as follows:
clean install
with release:clean release:prepare
.clean install
with release:perform
.Hudson will show the following output:
[…] [artifact:mvn] [INFO] Building transportation-acq 0.0.1-SNAPSHOT […] [artifact:mvn] [INFO] Building transportation-acq 0.0.1 […] [artifact:mvn] [INFO] Tagging release with the label transportation-acq -0.0.1 […] [artifact:mvn] [INFO] Transforming 'transportation-acq'... [artifact:mvn] [INFO] Not removing release POMs [artifact:mvn] [INFO] Checking in modified POMs... […]
In some cases, it is not acceptable to adopt a hierarchical structure of folders, and we might therefore prefer a flat environment, as is shown in the following screenshot:
In this case, the current version of Maven Release Plugin will not tag the submodules.
In the previous sections, we introduced the Ant Maven task. We can use this task to loop over modules in order to release artifact by artifact. The Ant script works with the For task, and the Ant Maven task will cycle between modules and release each artifact.
The script reads the aggregator POM, transportation-acq.xml
, through the
Xmlproperty
task.
In the root directory of your project, create a build.xml
file with the following content:
<project name="maven-release" default="release" basedir="." xmlns:artifact="antlib:org.apache.maven.artifact.ant"> <taskdef name="for" classname="net.sf.antcontrib.logic.ForTask" classpath="${basedir}/lib/ant-contrib-1.0b3.jar" /> <typedef resource="org/apache/maven/artifact/ant/antlib.xml" uri="antlib:org.apache.maven.artifact.ant" path="${basedir}/lib/maven-ant-tasks-2.1.3.jar" /> <target name="release"> <xmlproperty file="${basedir}/${projectName}.xml" collapseAttributes="true"/> <for param="line" list="${project.modules.module}" delimiter=","> <sequential> <echo>@{line}</echo> <artifact:mvn pom="${basedir}/@{line}/pom.xml"> <arg value="-DallowTimestampedSnapshots=true" /> <arg value="-DignoreSnapshots=true" /> <arg value="release:clean"/> <arg value="release:prepare"/> </artifact:mvn> </sequential> </for> </target> </project>
In Hudson, we need to replace the Maven release step with an Ant step and configure a new Ant build step:
release
.Invoke Maven 3
with release:perform
.Hudson will show the following output:
[…] [artifact:mvn] [INFO] Building transportation-acq-ejb 0.0.1-SNAPSHOT […] [artifact:mvn] [INFO] Building transportation-acq-ejb 0.0.1 […] [artifact:mvn] [INFO] Tagging release with the label transportation-acq-ejb-0.0.1 […] [artifact:mvn] [INFO] Transforming 'transportation-acq-ejb'... [artifact:mvn] [INFO] Not removing release POMs [artifact:mvn] [INFO] Checking in modified POMs... […]
In the previous sections, we tested, versioned, and tagged the software. Then, we packaged and released the component on the repository server. Now, it's time to alert the delivery team about the new available version.
Hudson (or Jenkins) allows us to send an e-mail after the build step. Perform the following steps from the left-hand side menu:
New version: ${ENV, var="projectName"} ${ENV, var="releaseVersion"}
(see the following screenshot):In the case of build success, the delivery team will be notified.
Sometimes, we also need to communicate some related information such as the prerequisites for installation (new features or configurations of DB); this information can be easily sent in the body of an e-mail or (better) attached as a child issue of the bug issue just solved. Indeed, in the previous sections, we showed how to integrate and automate the issue-tracking system (MantisBT). The most popular bug-tracking systems (MantisBT and JIRA) support this function, and it is common to use these features.
Our environment is now ready to be built; we can now release and deploy our amazing ideas.