If you are working with Java projects, you will most likely have to deal with Maven. Maven is a project management tool, used as an industry standard for medium to large scale Java applications. In this post, I will teach you to create production artifacts of multi-module Maven projects using the assembly plugin.
Initial project structure
For the purposes of this article, I will assume a very bare-bones project in Maven. We will have 4 modules: root
, which is the main (container) module, common
, which is used by other modules, app
, the app itself, and tools
. We are not writing any actual code here, so do not worry about the logic. Here is the dependency tree for these modules:
POM files
Now, we need to create POM files for these modules. Firstly, the root
module:
This is the umbrella module that holds the common
, app
, and tools
together. You can see them enumerated on lines 11-15. Another important aspect is the pom
packaging mode on line 9. It is used for modules that contain of other modules. Now, the POM for the common
module:
Notice right away the jar
packaging mode on line 10. Since this is a standalone module, it does not need the pom
mode, we will be building a JAR file. On lines 5-9 we define the module as a child of root
, and add some dependencies on lines 15-26.
Lastly, here are POM files for the app
and tools
:
These 2 look almost the same. They both compile as JAR files and depend on common
(to show you inter-module dependency resolution).
At this moment, you can run mvn package
in root
and get a JAR file for each of your projects (in <module name>/target
). This is very good, but most large project need more fine-tuning in regards of production artifacts. This is where the assembly
plugin is going to help out.
maven-assembly-plugin
maven-assembly-plugin
is used to assemble all modules into 1 entity, while providing overrides for all aspects of the build (as always, convention over configuration). To make it work, we need to add another module to our project, we will call it packaging
:
This is, again, a standard POM file, with the pom
packaging mode (since packaging
module does not need a JAR file). Now, to connect the maven-assembly-plugin
, add the following code on line 13:
This is a lot, so let’s go step-by-step. Firstly, we define the desired plugin coordinates on lines 4-6. Then, we set it up to run during the package
build phase on lines 10-13 (it happens when you run mvn package
). Lastly, we specify the assembly.xml
(coming in the next step) file as the assembly descriptor. This is effectively a configuration file for the plugin which describes how do we want our artifact to be built.
Lastly, let’s add the app
and tools
modules as dependencies for packaging
, to make sure Maven builds them before the packaging
module:
Assembly descriptor
Assembly descriptor is an XML file that specifies how the production artifact should be structured. Create a file called assembly.xml
in the packaging
module folder:
This is quite a big file, but I assure you there is nothing confusing going on there. Firstly, one line 3, we set the name for this particular assembly (production-artifact
, in our case). Then, on line 5 we set the format
to dir
. This means the resulting artifact will be a folder with JAR files in it. As an alternative, you can change that to jar
and get one BIG jar file with all resources and dependencies. On lines 7-17 we define the dependencies. We say that (line 9) do not use the artifact from the packaging
module itself, (line 10) put the dependencies in the shared
folder, (11) keep them as JAR files instead of unpacking, and (lines 12-15) exclude the app
and tools
from the dependency list.
On lines 18-30 we define the modules included in this artifact. On line 20 we let the plugin know to discover all modules managed by maven and (line 22-23) include only the app
and tools
ones. We do not specify common
here, because it will be included as a dependency under 7-17
. I did this to show you how you can use Maven modules as both means to an end (executable) or a library (dependency). Lastly, on lines 26-27 we say to keep the artifacts as JAR and do not include the dependencies in the JAR files directly. They are stored in a separate folder for a number of reasons: saving space, cutting the startup time, dynamic updates, and so on.
Finally, you can run mvn package
in project root and gaze upon your magnificent production build (under packaging/target
):
Closing notes
Thank you for reading, this is my first time writing about Java, I hope you liked it.
Fantastic article and very well explained. Thank you for sharing your knowledge.
Thanks a lot it helped. I have trying to resolve my maven project for 2 days now. Finally it worked.
Very well written! A lot of help to get a basic understanding.
Now working on packing a single jar of maven projects, spring boot, to make them runnable.
Mna, thanks a lot for sharing this with us. I was having problems to understand this assembly and you helped me