Packaging a multimodule maven Spring app in a standalone jar

First of all, as a Java developer you should be using a build tool, whether it be Ant, Maven, Ivy, Gradle or some combination of those (you like complicating things, don’t you), or some OSGI container. If you are not, you should be. Period.

It is an undeniable fact that Maven and Spring are highly used nowadays, so what I am going to describe, I believe, is a very common case.

Packaging a simple one-maven-module Spring app

This is the simplest possible thing: you have a single module app (like a single pom.xml) or at least one that doesn’t contain multiple spring.xml configuration files in different projects. You can simply use the maven assembly plugin to package your app: (taken from this Stackoverflow Question)

<build>
  <plugins>
    <plugin>
      <artifactId>maven-assembly-plugin</artifactId>
      <configuration>
        <archive>
          <manifest>
            <mainClass>fully.qualified.MainClass</mainClass>
          </manifest>
        </archive>
        <descriptorRefs>
          <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
      </configuration>
    </plugin>
  </plugins>
</build>

The jar-with-dependencies is one of the predefined assembly descriptors that tell the plugin how to find the dependencies and the manifest entry for the main class makes the jar executable by a main class by setting the manifest of the jar to a specific class. You can omit the main class config  in the manifest file and choose your main class while runnig the jar like that:

java -jar <your-jar.jar> <fully.qualified.MainClass>

Packaging a multi-maven-module Spring project

However, as the documentation for maven assembly plugin states:

If your project wants to package your artifact in an uber-jar, the assembly plugin provides only basic support. For more control, use the Maven Shade Plugin

Trying with the assembly-plugin anyway

If you are an optimist, you might try using the specified above simple configuration of the maven-assembly plugin to package your multi-module Spring app. Hell, it might even work for you. Chances are, though, that in some of your projects you would have resource files with the same names (e.g spring.xml). And therein comes the hard part: the assembly plugin regards those files as the same file and would just insert the first one it finds on the classpath and when it is time to insert the next one, it is going to override the first one if it exists. As a result, you’d have a jar with incomplete Spring context configuration, which most likely wouldn’t work.

Trying the maven shade plugin

Assuming you are one of the 99% for which the simple configuration wouldn’t work, you are probably off to use the suggested maven-shade-plugin. This is where I faced something like a brick wall of sorts. The maven-shade plugin has so many configuration options and is possible to be so finely tuned, does so much classloader change magic, that if you, like me, have a project with about 4 or five subprojects each having about 3 subprojects, chances are you’d have missed something. And then stuff wouldn’t work spectacularly. You’d have to examine the resulting jar, see if each file is there, see if classpaths are correct and many other things. Thinking about fine details, as we established in a previous post. is not something that comes naturally to an application architect, so chances are you’d want to avoid that. Seeing that I am not alone in my struggle with shade plugin (and even got the same error), I decided to follow the advice of the guy on Stackoverflow, and instead of using a single blackbox plugin (I regard anything with above average complexity to be a blackbox), I decided to use a combination of three plugins that I already was familiar with and where each plugin was simple enough and doing its own task. To me, the idea appealed with the fact that I’d be having three separate components, each executing a simple task, instead of having one component which has to execute a series of tasks, which, as we know, is one of the most important things you should think of when you are designing software.

Enough, just show me how it’s done

OK, so we would have to use three plugins:

  • maven-dependency-plugin You’d use this plugin to just copy all the dependency jars of your main project into a single folder (default is ${project.location}/target/dependency).
  • maven-jar-plugin You’d use this plugin to add stuff to the manifest of your standalone jar. The important thing is to add the classpath of each of your dependency jars into the manifest. For this you’d have to use a custom classpath layout
  • maven-assembly-plugin And finally, as in the simple example, you’d have to use the assembly plugin to generate your jar, but this time you are not going to be using a predefined assembly descriptor but you’d have to create one on your own.

And the configuration (with some explanation):

Step1

For the maven dependency plugin:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.8</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<overWriteReleases>true</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
<overWriteIfNewer>true</overWriteIfNewer>
<prependGroupId>true</prependGroupId>
</configuration>
</execution>
</executions>
</plugin>
view raw gistfile1.xml hosted with ❤ by GitHub

There is nothing much to be noted about this configuration. It is attached on package phase of your build, so once you have configured it, you can run a mvn clean install or a mvn clean package and check your target/dependency folder where all your dependency jars should be placed. Simple enough?

Step2

Next step is a bit of a mental leap. Imagine in the future you’d have a single jar file of your main project that would have inside itself a folder, let it be /lib, that would have all the dependencies (from target/dependency) from Step1. For this you’d have to use the maven-jar-plugin with a custom layout. Here is a sample configuration:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>your.main.Classe</mainClass>
<classpathLayoutType>custom</classpathLayoutType>
<customClasspathLayout>lib/${artifact.groupId}.${artifact.artifactId}-${artifact.version}${dashClassifier?}.${artifact.extension}</customClasspathLayout>
</manifest>
</archive>
</configuration>
</plugin>
view raw gistfile1.xml hosted with ❤ by GitHub

The lib folder on the customClasspathLayout node doesn’t exist yet, we are going to create it in Step3. But what is important is that we have now edited the manifest of our future jar (which is also going to be created in Step3) so that it has a main class and its classpath points to a lib folder where in step 3 we are going to package the dependencies in target/dependency that we created in Step1.

Step3

Now we can use the maven-assembly-plugin again to package our main jar AND our dependencies in the way that we want them to be. For this, we would want to use a custom assembly descriptor. Assembly descriptor is just an .xml file that tells the plugin how we want to do things. If your maven project follows the standard directory structure, you should place the assembly descriptor in /src/main/resources. Here is a sample assembly descriptor:

<?xml version="1.0" encoding="UTF-8"?>
<assembly>
<id>package</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>true</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>target/dependency</directory>
<outputDirectory>/lib</outputDirectory>
<includes>
<include>**/*</include>
</includes>
</fileSet>
<fileSet>
<directory>${project.build.directory}/</directory>
<outputDirectory>/</outputDirectory>
<includes>
<include>*.jar</include>
</includes>
</fileSet>
</fileSets>
</assembly>
view raw gistfile1.xml hosted with ❤ by GitHub

It is going to create a zip file, going to place you main class in the base directory (if your project is named my-project, the base directory is going to be also named my-project),  and inside it is going to create a lib directory and take all the contents of /target/dependency (which we created in Step1) that are .jar files.

After creating the assembly file, you can tell your mavenssembly-plugin to use it:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<id>assembly:package</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<descriptors>
<descriptor>src/main/resources/assembly.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
view raw gistfile1.xml hosted with ❤ by GitHub

After you are done with Step 3, you can run mvn clean install or mvn clean package and verify that the zip containing your standalone jar and its dependencies has indeed been created.

Then you can unpack your zip file, find your jar and run it like

java -jar your.jar <args>

And see that everything else indeed runs as expected. Hopefully that helped some people.

3 thoughts on “Packaging a multimodule maven Spring app in a standalone jar

  1. Do you have any experience by using this solution on a stand-alone java application which is deployed to Cloud Foundry ?

    I think this does not work, one of the reasons is that the compiled classes are located in a jar file, so Cloud Foundry cannot determine the mainClass ?

    Do you know how to solve this?

    1. Hey, thanks for the question. I don’t have any experience with the app being deployed to CF, but have some experience with the app being deployed to EC2 where it works properly.

      The maven-jar-plugin is responsible for writing an entry to the MANIFEST.mf file saying which is the mainClass of the app. You should inspect your generated jar, open it up and check what is in the MANIFEST.mf, there should be something like:

      Main-Class: com.foo.such.Main

      Another suggestion I have for you if you haven’t yet tried Spring Boot is to take a look there as it does a lot of the things mentioned here by default and it fits a lot of use cases, so you might want to go in that direction as well.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.