Maven: The Complete Reference - 8.6. Best Practices |
|
The Assembly plugin provides enough flexibility to solve many problems in a number of different ways. If you have a unique requirement for your project, there’s a good chance that you can use the methods documented in this chapter to achieve almost any assembly structure. This section of the chapter details some common best practices which, if adhered to, will make your experiences with the assembly plugin more productive and less painful. Up to now, we’ve been talking mainly about one-off solutions for building a particular type of assembly. But what do you do if you have dozens of projects that all need a particular type of assembly? In short, how can we reuse the effort we’ve invested to get our assemblies just the way we like them across more than one project without copying and pasting our assembly descriptor? The simplest answer is to create a standardized, versioned artifact
out of the assembly descriptor, and deploy it. Once that’s done, you
can specify that the Assembly plugin section of your project’s POM
include the assembly-descriptor artifact as a plugin-level dependency,
which will prompt Maven to resolve and include that artifact in the
plugin’s classpath. At that point, you can use the assembly descriptor
via the <assembly> <id>war-fragment</id> <formats> <format>zip</format> </formats> <includeBaseDirectory>false</includeBaseDirectory> <dependencySets> <dependencySet> <outputDirectory>WEB-INF/lib</outputDirectory> </dependencySet> </dependencySets> <fileSets> <fileSet> <directory>src/main/webapp</directory> <outputDirectory>/</outputDirectory> <excludes> <exclude>**/web.xml</exclude> </excludes> </fileSet> </fileSets> </assembly> Included in your project, this descriptor would be a useful way to bundle the project contents so that it could be unpacked directly into an existing web application in order to add to it (for adding an extending feature, say). However, if your team builds more than one of these web-fragment projects, it will likely want to reuse this descriptor rather than duplicating it. To deploy this descriptor as its own artifact, we’re going to put it in its own project, under the src/main/resources/assemblies directory. The project structure for this assembly-descriptor artifact will look similar to the following: |-- pom.xml `-- src `-- main `-- resources `-- assemblies `-- web-fragment.xml Notice the path of our Remember, this project is separate from your actual $ mvn install (...) [INFO] [install:install] [INFO] Installing (...)/web-fragment-descriptor/target/\ web-fragment-descriptor-1.0-SNAPSHOT.jar to /Users/~/.m2/repository/org/sonatype/mavenbook/assemblies/\ web-fragment-descriptor/1.0-SNAPSHOT/\ web-fragment-descriptor-1.0-SNAPSHOT.jar [INFO] --------------------------------------------------------------- [INFO] BUILD SUCCESSFUL [INFO] --------------------------------------------------------------- [INFO] Total time: 5 seconds (...) Since there are no sources for the <project> (...) <artifactId>my-web-fragment</artifactId> (...) <build> <plugins> <plugin> <artifactId>maven-assembly-plugin</artifactId> <version>2.2-beta-2</version> <dependencies> <dependency> <groupId>org.sonatype.mavenbook.assemblies</groupId> <artifactId>web-fragment-descriptor</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> <executions> <execution> <id>assemble</id> <phase>package</phase> <goals> <goal>single</goal> </goals> <configuration> <descriptorRefs> <descriptorRef>web-fragment</descriptorRef> </descriptorRefs> </configuration> </execution> </executions> </plugin> (...) </plugins> </build> (...) </project> Two things are special about this Assembly plugin configuration:
Now, you’re free to reuse the POM configuration above in as many projects as you like, with the assurance that all of their web-fragment assemblies will turn out the same. As you need to make adjustments to the assembly format - maybe to include other resources, or to fine-tune the dependency and file sets - you can simply increment the version of the assembly descriptor’s project, and release it again. POMs referencing the assembly-descriptor artifact can then adopt this new version of the descriptor as they are able. One final point about assembly-descriptor reuse: you may want to
consider sharing the plugin configuration itself as well as publishing
the descriptor as an artifact. This is a fairly simple step; you
simply add the configuration listed above to the (...) <build> <plugins> <plugin> <artifactId>maven-assembly-plugin</artifactId> </plugin> (...) If you’ve added the rest of the plugin’s configuration - listed in the
previous example - to the As mentioned above, the Assembly plugin provides multiple ways of
creating many archive formats. Distribution archives are typically
very good examples of this, since they often combine modules from a
multi-module build, along with their dependencies and possibly, other
files and artifacts besides these. The distribution aims to include
all these different sources into a single archive that the user can
download, unpack, and run with convenience. However, we also examined
some of the potential drawbacks of using the Specifically, if module POMs reference as their parent the POM that contains the Assembly-plugin configuration, that parent project will be built ahead of the module projects when the multi-module build executes. The parent’s assembly expects to find artifacts in place for its modules, but these module projects are waiting on the parent itself to finish building, a gridlock situation is reached and the parent build cannot succeed (since it’s unable to find artifacts for its module projects). In other words, the child project depends on the parent project which in turn depends on the child project. As an example, consider the assembly descriptor below, designed to be used from the top-level project of a multi-module hierarchy: <assembly> <id>distribution</id> <formats> <format>zip</format> <format>tar.gz</format> <format>tar.bz2</format> </formats> <moduleSets> <moduleSet> <includes> <include>*-web</include> </includes> <binaries> <outputDirectory>/</outputDirectory> <unpack>true</unpack> <includeDependencies>true</includeDependencies> <dependencySets> <dependencySet> <outputDirectory>/WEB-INF/lib</outputDirectory> </dependencySet> </dependencySets> </binaries> </moduleSet> <moduleSet> <includes> <include>*-addons</include> </includes> <binaries> <outputDirectory>/WEB-INF/lib</outputDirectory> <includeDependencies>true</includeDependencies> <dependencySets> <dependencySet/> </dependencySets> </binaries> </moduleSet> </moduleSets> </assembly> Given a parent project - called app-parent - with three modules called
$ mvn package [INFO] Reactor build order: [INFO] app-parent <----- PARENT BUILDS FIRST [INFO] app-core [INFO] app-web [INFO] app-addons [INFO] --------------------------------------------------------------- [INFO] Building app-parent [INFO]task-segment: [package] [INFO] --------------------------------------------------------------- [INFO] [site:attach-descriptor] [INFO] [assembly:single {execution: distro}] [INFO] Reading assembly descriptor: src/main/assembly/distro.xml [INFO] --------------------------------------------------------------- [ERROR] BUILD ERROR [INFO] --------------------------------------------------------------- [INFO] Failed to create assembly: Artifact: org.sonatype.mavenbook.assemblies:app-web:jar:1.0-SNAPSHOT (included by module) does not have an artifact with a file. Please ensure the package phase is run before the assembly is generated. ... The parent project - One workaround for this is to remove the executions section of the
Assembly-plugin declaration, that binds the plugin to the $ mvn package assembly:assembly However, this approach has several drawbacks. First, it makes the distribution-assembly process more of a manual task that can increase the complexity and potential for error in the overall build process significantly. Additionally, it could mean that attached artifacts - which are associated in memory as the project build executes - are not reachable on the second pass without resorting to file-system references. Instead of using a To do this, we can create a new project structure that’s very similar
to the one used for the module-set approach above, with the addition
of a new distribution project, we might end up with five POMs in
total: <project> <parent> <artifactId>app-parent</artifactId> <groupId>org.sonatype.mavenbook.assemblies</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>app-distribution</artifactId> <name>app-distribution</name> <dependencies> <dependency> <artifactId>app-web</artifactId> <groupId>org.sonatype.mavenbook.assemblies</groupId> <version>1.0-SNAPSHOT</version> <type>war</type> </dependency> <dependency> <artifactId>app-addons</artifactId> <groupId>org.sonatype.mavenbook.assemblies</groupId> <version>1.0-SNAPSHOT</version> </dependency> <!-- Not necessary since it's brought in via app-web. <dependency> [2] <artifactId>app-core</artifactId> <groupId>org.sonatype.mavenbook.assemblies</groupId> <version>1.0-SNAPSHOT</version> </dependency> --> </dependencies> </project> Notice that we have to include dependencies for the other modules in
the project structure, since we don’t have a modules section to rely
on in this POM. Also, notice that we’re not using an explicit
dependency on Next, when we move the distro.xml assembly descriptor into the
<assembly> ... <dependencySets> <dependencySet> <includes> <include>*-web</include> </includes> <useTransitiveDependencies>false</useTransitiveDependencies> <outputDirectory>/</outputDirectory> <unpack>true</unpack> </dependencySet> <dependencySet> <excludes> <exclude>*-web</exclude> </excludes> <useProjectArtifact>false</useProjectArtifact> <outputDirectory>/WEB-INF/lib</outputDirectory> </dependencySet> </dependencySets> ... </assembly> This time, if we run the build from the top-level project directory, we get better news: $ mvn package (...) [INFO] --------------------------------------------------------------- [INFO] Reactor Summary: [INFO] --------------------------------------------------------------- [INFO] module-set-distro-parent ...............SUCCESS [3.070s] [INFO] app-core .............................. SUCCESS [2.970s] [INFO] app-web ............................... SUCCESS [1.424s] [INFO] app-addons ............................ SUCCESS [0.543s] [INFO] app-distribution ...................... SUCCESS [2.603s] [INFO] --------------------------------------------------------------- [INFO] --------------------------------------------------------------- [INFO] BUILD SUCCESSFUL [INFO] --------------------------------------------------------------- [INFO] Total time: 10 seconds [INFO] Finished at: Thu May 01 18:00:09 EDT 2008 [INFO] Final Memory: 16M/29M [INFO] --------------------------------------------------------------- As you can see, the dependency-set approach is much more stable and - at least until Maven’s internal project-sorting logic catches up with the Assembly plugin’s capabilities, - involves less opportunity to get things wrong when running a build. |