Building a Maven Complex Release Artifact

From NovaOrdis Knowledge Base
Jump to navigation Jump to search

Internal

Overview

This article describes the procedure of configuring Maven to build one complex release artifact, which includes multiple individual artifacts, possibly produced by multiple modules, as well as dependencies and arbitrary files from the project tree. This is achieved by using the Maven assembly plugin, whose final product is a assembly.

Don't Follow your Intuition

An intuitive approach would be to assemble the release artifact of a multi-module project by specifying the assembly plug-in in the project root pom.xml. However, this approach does not work well in practice. These are the reasons:

Root Module Builds First

... so if the final assembly depends on the artifacts of the children modules, we'll get into a deadlock.

Repeated Execution

Because the assembly plug-in is specified in the parent POM, the plug-in will be executed for each module (including root) at the specified phase, usually "package". When executing, the module will try to resolve the specified descriptor relative to the module root, so if we have the following declaration and layout:

<project>
   </build>
        <plugins>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <descriptors>
                        <descriptor>assembly.xml</descriptor>
                    </descriptors>
                </configuration>
                ...
            </plugin>
        </plugins>
    </build>
</project>
<pre>

<pre>
  .
  +-- pom.xml
  |
  +-- assembly.xml   (1)
  |
  +-- module-1
       |
       +-- pom.xml
       |
       +-- assembly.xml (2)

then the root module will create its assembly based on assembly.xml (1) and module-1 will create its assembly based on assembly.xml (2).

Recommended Approach

Dedicated Release Module

Create a separated module called "release". The only purpose of this module is to build the final release assembly. Thus, it can be declared as dependent of of all other modules that contribute artifacts to the final release bundle. The modules will be built in order, the final release module will be last, when all internal dependencies are available, and there won't be any complications related to dependencies.

The Public Release Version

There are two main options:

  1. the top level pom.xml maintains the public release information, as usually, as a value of its <version> element. This implies that all modules increment their versions at the same time, in what is describe here as "lockstep versions". This model is appropriate for simple cases, where modules do not need to evolve their versions independently, which is usually the case.
  2. the release module pom.xml maintains the version of the public release. This model allows the component modules' versions to evolve independently and it is described here: "Independent Versions".

Dedicated Release Module POM

Assuming that the parent project artifact ID is "my-project" (for more on how to name the parent project artifact ID, see the "Multi-Module Maven Projects" section), and other modules are "module-1" and "module-2", then the "release" pom.xml should look similar to:

<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>

    <parent>
        <groupId>my.example.group</groupId>
        <artifactId>my-project</artifactId>
        <version>0</version>
    </parent>

    <artifactId>release</artifactId>
    <packaging>pom</packaging>
    <version>...</version>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>2.6</version>
                <configuration>
                    <finalName>my-public-application-name-${project.version}</finalName>
                    <appendAssemblyId>false</appendAssemblyId>
                    <descriptors>
                        <descriptor>src/assembly/release.xml</descriptor>
                    </descriptors>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>my.example.group</groupId>
            <artifactId>module-1</artifactId>
            <version>...</version>
        </dependency>
        <dependency>
            <groupId>my.example.group</groupId>
            <artifactId>module-2</artifactId>
            <version>...</version>
        </dependency>
    </dependencies>
</project>

The "release" module must be declared in the project parent pom.xml, amongst the project's modules.

Conventionally, we name the artifactId of a release module "release", we use a <finalName> as shown above, to specify the name of the release artifact, for reasons described in the "Name of a Release Artifact - <finalName>" section.

Custom Assembly File

A working example of a custom example file used to build the release assembly of a multi-module project is available below:

https://github.com/NovaOrdis/events/blob/master/release/src/assembly/release.xml