154

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.

OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
bianca
  • 7,004
  • 12
  • 43
  • 58

7 Answers7

190

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>
forkdbloke
  • 1,505
  • 2
  • 12
  • 29
jmj
  • 237,923
  • 42
  • 401
  • 438
  • 24
    This is now deprecated and the maven-shade-plugin should be used instead. See @phlogratos answer: http://stackoverflow.com/a/16222965/274350. – Richard Neish Sep 05 '14 at 09:22
  • 2
    Can maven-assembly-plugin minimize size of fat jar? – May12 Dec 04 '15 at 05:25
  • what do you mean by minimizing size of fat jar ? @May12 – jmj Dec 04 '15 at 05:26
  • 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. – May12 Dec 04 '15 at 05:29
  • 1
    @Richard Where do you see it is deprecated ? I couldn't find anything around here http://maven.apache.org/plugins/maven-assembly-plugin/ – user3833308 Mar 12 '18 at 23:52
  • @May assembly allows you to hand pick what goes in jar. not sure about automatic detection. – user3833308 Mar 12 '18 at 23:54
  • 3
    @user3833308 "deprecated" may be the wrong word, but the documentation at https://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." – Richard Neish Mar 13 '18 at 10:25
  • 1
    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. https://docs.spring.io/spring-boot/docs/current/maven-plugin/reference/html/#goals – Derek Cochran Jun 13 '20 at 00:13
  • similar issue: https://stackoverflow.com/questions/70833195/spring-boot-multi-module-spring-boot-maven-plugin-compilation-failure/70833786#70833786 – emoleumassi Jan 24 '22 at 13:21
  • 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://stackoverflow.com/a/74993923/4311428 – maxeh Jan 03 '23 at 13:24
71

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.

Community
  • 1
  • 1
phlogratos
  • 13,234
  • 1
  • 32
  • 37
  • 13
    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." – Richard Neish Sep 05 '14 at 09:20
  • But how do I take that single .jar and use it for publishing? – DanGordon Jun 05 '18 at 20:00
  • 2
    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. – Adam Burley Jul 29 '21 at 13:36
  • @AdamBurley Can you expand on the risk that the shade plugin can potentially introduce with an example? Thank you – dutoitns Nov 09 '22 at 07:26
  • 3
    @dutoitns 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. – Adam Burley Nov 10 '22 at 08:27
54

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:

Patrick
  • 33,984
  • 10
  • 106
  • 126
ggrandes
  • 2,067
  • 22
  • 16
  • If one encounters `Provider org.apache.xerces.jaxp.SAXParserFactoryImpl not found`, just remove `true`. – koppor Sep 06 '17 at 10:21
  • 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://stackoverflow.com/a/6743609/38368 – Danny Varod May 16 '18 at 14:29
  • 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? – tarekahf Mar 07 '23 at 17:15
17

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

Faisal Feroz
  • 12,458
  • 4
  • 40
  • 51
ameet chaubal
  • 1,440
  • 16
  • 37
  • I found that it added the MANIFEST but to the original jar not the jar-with-dependencies.jar (sigh) – PatS Sep 24 '22 at 21:12
  • Yes this is correct, the maven-jar-plugin is not required at all. More information here: https://stackoverflow.com/a/74993923/4311428 – maxeh Jan 03 '23 at 13:27
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.

matsev
  • 32,104
  • 16
  • 121
  • 156
  • 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? – bianca Apr 25 '13 at 21:36
  • 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: http://pastebin.com/UwniQJ2X – bianca Apr 25 '13 at 21:52
  • 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. – matsev Apr 26 '13 at 13:09
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>
Stanislav
  • 2,629
  • 1
  • 29
  • 38
3

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.

maxeh
  • 1,373
  • 1
  • 15
  • 24