Building a fat jar using maven
Asked Answered
F

8

158

I have a code base which I want to distribute as jar. It also have dependency on external jars, which I want to bundle in the final jar.

I heard that this can be done using maven-assembly-plug-in, but I don't understand how. Could someone point me to some examples.

Right now, I'm using fat jar to bundle the final jar. I want to achieve the same thing using maven.

Fiberglass answered 25/4, 2013 at 19:4 Comment(0)
G
196

Note: If you are a spring-boot application, read the end of answer

Add following plugin to your pom.xml The latest version can be found at

...
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>CHOOSE LATEST VERSION HERE</version>
            <configuration>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
            </configuration>
            <executions>
                <execution>
                    <id>assemble-all</id>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>
...

After configuring this plug-in, running mvn package will produce two jars: one containing just the project classes, and a second fat jar with all dependencies with the suffix "-jar-with-dependencies".

if you want correct classpath setup at runtime then also add following plugin

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <configuration>
        <archive>
            <manifest>
                <addClasspath>true</addClasspath>
                <mainClass>fully.qualified.MainClass</mainClass>
            </manifest>
        </archive>
    </configuration>
</plugin>

For spring boot application use just following plugin (choose appropriate version of it)

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <fork>true</fork>
        <mainClass>${start-class}</mainClass>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>repackage</goal>
            </goals>
        </execution>
    </executions>
</plugin>
Guadiana answered 25/4, 2013 at 19:17 Comment(10)
This is now deprecated and the maven-shade-plugin should be used instead. See @phlogratos answer: https://mcmap.net/q/150685/-building-a-fat-jar-using-maven.Crackdown
Can maven-assembly-plugin minimize size of fat jar?Chesser
what do you mean by minimizing size of fat jar ? @ChesserGuadiana
Automatcally excluding unusable dependencies from jar. I read that this function is supported in maven-shadow plugin. But i use assembly plugin and i decided to ask.Chesser
@Richard Where do you see it is deprecated ? I couldn't find anything around here maven.apache.org/plugins/maven-assembly-pluginAiring
@May assembly allows you to hand pick what goes in jar. not sure about automatic detection.Airing
@Airing "deprecated" may be the wrong word, but the documentation at maven.apache.org/plugins/maven-assembly-plugin says "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."Crackdown
The repackage goal of the spring-boot-maven-plugin may also be used simply to package a JAR with nested dependencies with layout=NONE. This doesn't require the project to use spring or spring boot. docs.spring.io/spring-boot/docs/current/maven-plugin/reference/…Buzz
similar issue: #70833695Kopp
When using the maven-assembly-plugin, you need to add the archive/manifest part also to the configuration section of the plugin in order to get the correct classpath setup. You then don't need the maven-jar-plugin at all. For full answer see: https://mcmap.net/q/150685/-building-a-fat-jar-using-mavenHalfcaste
S
72

You can use the maven-shade-plugin.

After configuring the shade plugin in your build the command mvn package will create one single jar with all dependencies merged into it.

Singband answered 25/4, 2013 at 19:16 Comment(5)
This is now the correct way to do it. The maven-assembly-plugin documentation 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."Crackdown
But how do I take that single .jar and use it for publishing?Fur
I disagree that this is now the "correct" way to do it. if you just want basic behaviour then you can absolutely continue to use the assembly plugin. the shade plugin does advanced stuff like rewriting the bytecode to modify package names of dependencies, once you start getting into that with production code you are introducing additional risk which is not worth it if you don't have a good reason.Blanchette
@AdamBurley Can you expand on the risk that the shade plugin can potentially introduce with an example? Thank youNe
@Ne No I can't give a specific example. But rewriting bytecode is an incredibly complex thing to do and subject to more bugs. It also defeats part of the purpose of developing in Java, as the bytecode you test in development will not be the same as what is running in production. While most bugs in the build will lead to easily-detectable compilation or "file not found" errors, here the build is actually modifying the stuff you wrote, and any bugs could lead to weird behaviour that's very hard to detect. Most enterprise-level companies wouldn't take this risk without a VERY good reason.Blanchette
P
55

Maybe you want maven-shade-plugin, bundle dependencies, minimize unused code and hide external dependencies to avoid conflicts.

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.1.1</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <minimizeJar>true</minimizeJar>
                        <createDependencyReducedPom>true</createDependencyReducedPom>
                        <dependencyReducedPomLocation>
                            ${java.io.tmpdir}/dependency-reduced-pom.xml
                        </dependencyReducedPomLocation>
                        <relocations>
                            <relocation>
                                <pattern>com.acme.coyote</pattern>
                                <shadedPattern>hidden.coyote</shadedPattern>
                            </relocation>
                        </relocations>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

References:

Pliam answered 29/5, 2014 at 15:43 Comment(3)
If one encounters Provider org.apache.xerces.jaxp.SAXParserFactoryImpl not found, just remove <minimizeJar>true</minimizeJar>.Michalmichalak
If you get an "java.lang.SecurityException: Invalid signature file digest for Manifest main attributes" exception, then this addition to the above configuration section helps: https://mcmap.net/q/45819/-quot-invalid-signature-file-quot-when-attempting-to-run-a-jarEnolaenormity
I was looking for a method to combine the dependencies of jackson-dataformat-xml and the newly generated jar file in one big jar file. I hope that maven-shade-plugin will be of help. My question: Is this a reasonable approach? Is combining such dependencies in one jar file a good approach to simplify the deployment?Hanleigh
C
19

actually, adding the

<archive>
   <manifest>
    <addClasspath>true</addClasspath>
    <packageName>com.some.pkg</packageName>                     
    <mainClass>com.MainClass</mainClass>
  </manifest>
</archive>

declaration to maven-jar-plugin does not add the main class entry to the manifest file for me. I had to add it to the maven-assembly-plugin in order to get that in the manifest

Cacodyl answered 27/6, 2014 at 18:6 Comment(2)
I found that it added the MANIFEST but to the original jar not the jar-with-dependencies.jar (sigh)Batt
Yes this is correct, the maven-jar-plugin is not required at all. More information here: https://mcmap.net/q/150685/-building-a-fat-jar-using-mavenHalfcaste
F
6

You can use the onejar-maven-plugin for packaging. Basically, it assembles your project and its dependencies in as one jar, including not just your project jar file, but also all external dependencies as a "jar of jars", e.g.

<build>
    <plugins>
        <plugin>
            <groupId>com.jolira</groupId>
            <artifactId>onejar-maven-plugin</artifactId>
                <version>1.4.4</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>one-jar</goal>
                        </goals>
                    </execution>
                </executions>
        </plugin>
    </plugins>
</build>

Note 1: Configuration options is available at the project home page.

Note 2: For one reason or the other, the onejar-maven-plugin project is not published at Maven Central. However jolira.com tracks the original project and publishes it to with the groupId com.jolira.

Fricative answered 25/4, 2013 at 19:32 Comment(3)
Is it possible to choose dependencies that you want to package with? For example, if a.jar and b.jar are external jars and I want to package only a.jar into final jar, is it something possible/configurable?Fiberglass
I also see one-jar stuff got bundled in final jar too. This increased size of the final jar a lot. Is it possible to choose external jars that you intend to include in final jar? Here is the one-jar logs: pastebin.com/UwniQJ2XFiberglass
I don't know if you can configure what is included or not. Basically, one-jar includes all dependencies that are specified by your project, including transitive dependencies, so yes, the final jar is likely to become big if you have a lot of dependencies. Some overhead will be added by one-jar, in order to get Java working with the "jar of jars" structure.Fricative
H
6

Read if you want to use the maven-assembly-plugin.

As other answers have already outlined, it seems that the maven-shade-plugin offers more features and is the recommended plugin to build a fat jar, but in case you would like to use the maven-assembly-plugin the following plugin configuration will work.

The answer of @jmj explains that the correct classpath can be setup with an additional maven-jar-plugin, but this will only add the classpath to the original jar and not the fat jar. The information must instead be directly included into the configuration section of the maven-assembly-plugin.

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>3.4.2</version>
            <configuration>
                <archive>
                    <manifest>
                        <addClasspath>true</addClasspath>
                        <mainClass>com.package.YourMainClass</mainClass>
                    </manifest>
                </archive>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
            </configuration>
            <executions>
                <execution>
                    <id>assemble-all</id>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

When you now run maven package, your normal and fat jar will be created and you can run your fat jar with java -jar yourJar.jar.

Halfcaste answered 3/1, 2023 at 13:20 Comment(0)
D
3

An alternative is to use the maven shade plugin to build an uber-jar.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version> Your Version Here </version>
    <configuration>
            <!-- put your configurations here -->
    </configuration>
    <executions>
            <execution>
                    <phase>package</phase>
                    <goals>
                            <goal>shade</goal>
                    </goals>
            </execution>
    </executions>
</plugin>
Dogbane answered 30/11, 2016 at 20:15 Comment(0)
L
1

To avoid the jar-with-dependencies suffix with the jar file name, set the appendAssemblyId element to false.

   <configuration>
        <finalName>final-jar-name</finalName>
        <appendAssemblyId>false</appendAssemblyId>
    </configuration>
Labourite answered 3/1 at 17:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.