441

Is there a way to force maven(2.0.9) to include all the dependencies in a single jar file?

I have a project the builds into a single jar file. I want the classes from dependencies to be copied into the jar as well.

Update: I know that I cant just include a jar file in a jar file. I'm searching for a way to unpack the jars that are specified as dependencies, and package the class files into my jar.

James McMahon
  • 48,506
  • 64
  • 207
  • 283
racer
  • 4,497
  • 3
  • 18
  • 13
  • 1
    possible duplicate of [How can I create an executable jar with dependencies using Maven?](http://stackoverflow.com/questions/574594/how-can-i-create-an-executable-jar-with-dependencies-using-maven) – Moshe Katz Apr 27 '15 at 19:57
  • https://stackoverflow.com/questions/35217128/is-it-possible-to-build-a-java-project-only-once-using-eclipse-and-share/35359756#35359756 – Thanga Sep 11 '17 at 08:19
  • 1
    Flattening the dependencies into a single JAR is a bad idea in general. It causes a LOT of troubles, because of the special-purpose resources. – Ondra Žižka Oct 27 '22 at 00:16

16 Answers16

579

You can do this using the maven-assembly plugin with the "jar-with-dependencies" descriptor. Here's the relevant chunk from one of our pom.xml's that does this:

  <build>
    <plugins>
      <!-- any other plugins -->
      <plugin>
        <artifactId>maven-assembly-plugin</artifactId>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>single</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
          </descriptorRefs>
        </configuration>
      </plugin>
    </plugins>
  </build>
Alexei Khlebnikov
  • 2,126
  • 1
  • 21
  • 21
John Stauffer
  • 16,150
  • 10
  • 40
  • 35
  • 61
    In case any new mvn folks got stuck like me, add the plugin to inside which is inside . – D. A. Mar 20 '14 at 22:34
  • 2
    How can I avoid including libraries I don't use in my code? My jar just using SSJ sums up to 10 MB :( – lony Apr 09 '14 at 12:39
  • 2
    @D.A. Why people leave out relevant (read: pertinent) details like that are beyond me. – lux Jul 09 '14 at 19:13
  • 1
    Still can't get this to work, I've followed like 15 different answers on different questions too. – Hobbyist Dec 03 '14 at 16:44
  • 17
    @Christian.tucker There's a 2nd jar in the target directory created like this: **./target/example-0.0.1-SNAPSHOT.jar** and **./target/example-0.0.1-SNAPSHOT-jar-with-dependencies.jar** – technocrat Mar 09 '15 at 18:43
  • How can I get the install goal to put the jar-with-dependencies into the repo? – James Watkins Mar 15 '15 at 01:33
  • @Christian.tucker mvn assembly:assembly -DdescriptorId=jar-with-dependencies – lilalinux Aug 12 '15 at 07:29
  • 5
    Just add one more thing in configuration. Your main class information. fully qualified name – Hammad Hassan Aug 20 '15 at 10:52
  • 4
    It works but I'm seeing two JARs in the target directory: with and without dependencies. How do I generate only the JAR that has the dependencies? – geschema Dec 04 '17 at 15:01
  • 1
    How to include a jar file into jar file in maven ? – Kush Patel Mar 14 '19 at 09:36
  • Hello, if you use this plugin, will it grab jars from a local lib folder? I reference some dependencies this way in the project that I want to build into a jar... ` com.kachop mylocal 1.0.0 system ${basedir}/lib/lk/mylocal.jar ` – Kachopsticks Jul 02 '20 at 16:03
176

With Maven 2, the right way to do this is to use the Maven2 Assembly Plugin which has a pre-defined descriptor file for this purpose and that you could just use on the command line:

mvn assembly:assembly -DdescriptorId=jar-with-dependencies

If you want to make this jar executable, just add the main class to be run to the plugin configuration:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-assembly-plugin</artifactId>
  <configuration>
    <archive>
      <manifest>
        <mainClass>my.package.to.my.MainClass</mainClass>
      </manifest>
    </archive>
  </configuration>
</plugin>

If you want to create that assembly as part of the normal build process, you should bind the single or directory-single goal (the assembly goal should ONLY be run from the command line) to a lifecycle phase (package makes sense), something like this:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-assembly-plugin</artifactId>
  <executions>
    <execution>
      <id>create-my-bundle</id>
      <phase>package</phase>
      <goals>
        <goal>single</goal>
      </goals>
      <configuration>
        <descriptorRefs>
          <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
        ...
      </configuration>
    </execution>
  </executions>
</plugin>

Adapt the configuration element to suit your needs (for example with the manifest stuff as spoken).

Helen
  • 87,344
  • 17
  • 243
  • 314
Pascal Thivent
  • 562,542
  • 136
  • 1,062
  • 1,124
  • I am trying exactly this, however the plugin is not run and the jar file is not created even though the build executes smoothly. Is there a common pitfall that I might have gotten stuck with? – posdef May 28 '12 at 14:24
  • 9
    It's working for me but have a questing. after build now two jars are created one with project artifactid-version and another with artifactid-version-"jar-with-dependencies". But I want only one jar to be build. Is there any other way – Souvik Bhattacharya Sep 03 '13 at 06:47
  • 5
    @SouvikBhattacharya add `false` to `maven-assembly-plugin` configutation. – amucunguzi Sep 03 '20 at 14:37
57

If you want to do an executable jar file, them need set the main class too. So the full configuration should be.

    <plugins>
            <plugin>
                 <artifactId>maven-assembly-plugin</artifactId>
                 <executions>
                     <execution>
                          <phase>package</phase>
                          <goals>
                              <goal>single</goal>
                          </goals>
                      </execution>
                  </executions>
                  <configuration>
                       <!-- ... -->
                       <archive>
                           <manifest>
                                 <mainClass>fully.qualified.MainClass</mainClass>
                           </manifest>
                       </archive>
                       <descriptorRefs>
                           <descriptorRef>jar-with-dependencies</descriptorRef>
                      </descriptorRefs>
                 </configuration>
         </plugin>
   </plugins>
abdiel
  • 2,078
  • 16
  • 24
  • 3
    Why is the name of jar appended by "jar-with-dependencies"?! Any workarounds? – Tina J Jan 04 '19 at 17:44
  • 11
    @Tina J you can add `false` inside the `` tag to exclude the "-jar-with-dependencies" suffix in the final name. – ccu Dec 01 '19 at 16:11
22

Method 1: Copy the dependencies' JAR files into target/lib and then add them to the JAR's classpath in MANIFEST:

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <configuration>
                <outputDirectory>${project.build.directory}/lib</outputDirectory>
                <excludeTransitive>false</excludeTransitive> 
                <stripVersion>false</stripVersion>
            </configuration>
            <executions>
                <execution>
                    <id>copy-dependencies</id>
                    <phase>package</phase>
                    <goals>
                        <goal>copy-dependencies</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        <!-- Add LIB folder to classPath -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>2.4</version>
            <configuration>
                <archive>
                    <manifest>
                        <addClasspath>true</addClasspath>
                        <classpathPrefix>lib/</classpathPrefix>
                    </manifest>
                </archive>
            </configuration>
        </plugin>

Method 2: Unpack all dependencies and repack their classes and resources into one flat JAR. Note: The overlapping resources will be randomly lost!

<plugin>
    <artifactId>maven-assembly-plugin</artifactId>
    <executions>
        <execution>
            <phase>package</phase>
            <goals><goal>single</goal></goals>
        </execution>
    </executions>
    <configuration>
        <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
    </configuration>
</plugin>
Ondra Žižka
  • 43,948
  • 41
  • 217
  • 277
Leslie Li
  • 221
  • 2
  • 2
  • The first option is the best for faster rebuilds. `assembly-plugin` just takes too much time packaging dependencies, which is expected. – Kalesh Kaladharan Oct 22 '21 at 15:28
  • I tried the first approach here. In the documentation, it states ""Note: The Class-Path header points to classes or JAR files on the local network, not JAR files within the JAR file or classes accessible over Internet protocols. To load classes in JAR files within a JAR file into the class path, you must write custom code to load those classes." In your Method 1, are you referring people to writing custom jar loaders? – gtree Dec 13 '21 at 14:48
  • I noticed there was a difference in the copying and unpacking in comparison to the assembly. I kept getting "ClassNotFoundException" when trying to run my jar file for my server. How do you recommend proceeding if using your method 1? I couldn't get mine to work. – gtree Dec 13 '21 at 14:48
  • Option 2 works for me, thanks! – Tianbing Leng Apr 27 '22 at 00:39
  • For option 1 (If you don't want to copy JUnit dependencies to target folder) you must add compile to the maven-dependency-plugin – Mic Jul 04 '22 at 15:09
20

There's the shade maven plugin. It can be used to package and rename dependencies (to omit dependency problems on the classpath).

Thomas Jung
  • 32,428
  • 9
  • 84
  • 114
18

You can use the newly created jar using a <classifier> tag.

<dependencies>
    <dependency>
        <groupId>your.group.id</groupId>
        <artifactId>your.artifact.id</artifactId>
        <version>1.0</version>
        <type>jar</type>
        <classifier>jar-with-dependencies</classifier>
    </dependency>
</dependencies>
Paul Verest
  • 60,022
  • 51
  • 208
  • 332
lasantha
  • 825
  • 8
  • 7
15

If you (like me) dont particularly like the jar-with-dependencies approach described above, the maven-solution I prefer is to simply build a WAR-project, even if it is only a stand-alone java application you are building:

  1. Make a normal maven jar-project, that will build your jar-file (without the dependencies).

  2. Also, setup a maven war-project (with only an empty src/main/webapp/WEB-INF/web.xml file, which will avoid a warning/error in the maven-build), that only has your jar-project as a dependency, and make your jar-project a <module> under your war-project. (This war-project is only a simple trick to wrap all your jar-file dependencies into a zip-file.)

  3. Build the war-project to produce the war-file.

  4. In the deployment-step, simply rename your .war-file to *.zip and unzip it.

You should now have a lib-directory (which you can move where you want it) with your jar and all the dependencies you need to run your application:

java -cp 'path/lib/*' MainClass

(The wildcard in classpath works in Java-6 or higher)

I think this is both simpler to setup in maven (no need to mess around with the assembly plugin) and also gives you a clearer view of the application-structure (you will see the version-numbers of all dependent jars in plain view, and avoid clogging everything into a single jar-file).

Rop
  • 3,359
  • 3
  • 38
  • 59
  • Is it the same as Eclipse "Export as Runnable jar"? Because with this you can choose to "package all dependencies with the JAR", you then get all dependencies in project-lib folder, along your jar. – WesternGun Feb 01 '18 at 17:17
  • @FaithReaper -- It might be "the same", I never tried that. But I guess if you use Eclipse, it's not easily scriptable, which is a requirement, if you want to implement an *automated* build+deploy-pipeline. For example, let Jenkins-server do the build from your git source-repository, or similar. – Rop Feb 01 '18 at 21:49
15

http://fiji.sc/Uber-JAR provides an excellent explanation of the alternatives:

There are three common methods for constructing an uber-JAR:

  1. Unshaded. Unpack all JAR files, then repack them into a single JAR.
    • Pro: Works with Java's default class loader.
    • Con: Files present in multiple JAR files with the same path (e.g., META-INF/services/javax.script.ScriptEngineFactory) will overwrite one another, resulting in faulty behavior.
    • Tools: Maven Assembly Plugin, Classworlds Uberjar
  2. Shaded. Same as unshaded, but rename (i.e., "shade") all packages of all dependencies.
    • Pro: Works with Java's default class loader. Avoids some (not all) dependency version clashes.
    • Con: Files present in multiple JAR files with the same path (e.g., META-INF/services/javax.script.ScriptEngineFactory) will overwrite one another, resulting in faulty behavior.
    • Tools: Maven Shade Plugin
  3. JAR of JARs. The final JAR file contains the other JAR files embedded within.
    • Pro: Avoids dependency version clashes. All resource files are preserved.
    • Con: Needs to bundle a special "bootstrap" classloader to enable Java to load classes from the wrapped JAR files. Debugging class loader issues becomes more complex.
    • Tools: Eclipse JAR File Exporter, One-JAR.
Gili
  • 86,244
  • 97
  • 390
  • 689
André Willik Valenti
  • 1,702
  • 14
  • 21
  • 2
    Not quite true regarding services, shade plugin has transformers and one of them is for concatenating contents of files under the `META-INF/services` directory. More info here: https://maven.apache.org/plugins/maven-shade-plugin/examples/resource-transformers.html – vitro Jul 27 '19 at 23:46
  • It basically encapsulates the artifact in its own namespace. I cannot think of any scenario where this still allows for clashes. – tcurdt Sep 08 '20 at 18:22
5

My definitive solution on Eclipse Luna and m2eclipse: Custom Classloader (download and add to your project, 5 classes only) :http://git.eclipse.org/c/jdt/eclipse.jdt.ui.git/plain/org.eclipse.jdt.ui/jar%20in%20jar%20loader/org/eclipse/jdt/internal/jarinjarloader/; this classloader is very best of one-jar classloader and very fast;

<project.mainClass>org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader</project.mainClass> <project.realMainClass>my.Class</project.realMainClass>

Edit in JIJConstants "Rsrc-Class-Path" to "Class-Path"
mvn clean dependency:copy-dependencies package
is created a jar with dependencies in lib folder with a thin classloader

<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.java</include>
                <include>**/*.properties</include>
            </includes>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
            <includes>
                <include>**/*</include>
            </includes>
            <targetPath>META-INF/</targetPath>
        </resource>
        <resource>
            <directory>${project.build.directory}/dependency/</directory>
            <includes>
                <include>*.jar</include>
            </includes>
            <targetPath>lib/</targetPath>
        </resource>
    </resources>
<pluginManagement>
        <plugins>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <mainClass>${project.mainClass}</mainClass>
                            <classpathPrefix>lib/</classpathPrefix>
                        </manifest>

                        <manifestEntries>
                            <Rsrc-Main-Class>${project.realMainClass}  </Rsrc-Main-Class>
                            <Class-Path>./</Class-Path>
                        </manifestEntries>

                    </archive>
                </configuration>
            </plugin>
<plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </pluginManagement>
</build>
1

Putting Maven aside, you can put JAR libraries inside the Main Jar but you will need to use your own classloader.

Check this project: One-JAR link text

mohdajami
  • 9,604
  • 3
  • 32
  • 53
  • There is also a Maven plugin for One-JAR: http://onejar-maven-plugin.googlecode.com/svn/mavensite/usage.html – Thilo Jan 29 '13 at 03:47
1

I was trying to do sth similar, but I didn't want all jars to be included. I wanted to include some specific directories from the given dependency. In addition classifier tag was already occupied, so I couldn't do:

<dependencies>
    <dependency>
        <groupId>your.group.id</groupId>
        <artifactId>your.artifact.id</artifactId>
        <version>1.0</version>
        <type>jar</type>
        <classifier>jar-with-dependencies</classifier>
    </dependency>
</dependencies>
  1. I used maven-dependency-plugin and unpack goal
  2. And unpacked what I wanted to the ${project.build.directory}/classes, otherwise it will be omitted
  3. Because it was in the classes directory, maven finally placed it in the jar
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <executions>
        <execution>
            <id>unpack</id>
            <phase>prepare-package</phase>
            <goals>
                <goal>unpack</goal>
            </goals>
            <configuration>
                <artifactItems>
                    <artifactItem>
                        <groupId>my.group</groupId>
                        <artifactId>my.artifact</artifactId>
                        <classifier>occupied</classifier>
                        <version>1.0</version>
                        <type>jar</type>
                    </artifactItem>
                </artifactItems>
                <outputDirectory>${project.build.directory}/classes</outputDirectory>
                <includes>aaa/**, bbb/**, ccc/**</includes>
            </configuration>
        </execution>
    </executions>
</plugin>
jwpol
  • 1,188
  • 10
  • 22
1

I found this to be the clearest answer; other answers here were missing things that weren't obvious to me such as mvn clean package command for example, and adding the plugin separately as a dependancy also. All of which are probably obvious to more habitual maven users.

https://howtodoinjava.com/maven/executable-jar-with-dependencies/

Reece
  • 641
  • 7
  • 18
0

This post may be a bit old, but I also had the same problem recently. The first solution proposed by John Stauffer is a good one, but I had some problems as I am working this spring. The spring's dependency-jars I use have some property files and xml-schemas declaration which share the same paths and names. Although these jars come from the same versions, the jar-with-dependencies maven-goal was overwriting theses file with the last file found.

In the end, the application was not able to start as the spring jars could not find the correct properties files. In this case the solution propose by Rop have solved my problem.

Also since then, the spring-boot project now exist. It has a very cool way to manage this problem by providing a maven goal which overload the package goal and provide its own class loader. See spring-boots Reference Guide

Francois Gergaud
  • 384
  • 2
  • 11
0

Have a look at this answer:

I am creating an installer that runs as a Java JAR file and it needs to unpack WAR and JAR files into appropriate places in the installation directory. The dependency plugin can be used in the package phase with the copy goal and it will download any file in the Maven repository (including WAR files) and write them where ever you need them. I changed the output directory to ${project.build.directory}/classes and then end result is that the normal JAR task includes my files just fine. I can then extract them and write them into the installation directory.

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
    <execution>
        <id>getWar</id>
        <phase>package</phase>
        <goals>
            <goal>copy</goal>
        </goals>
        <configuration>
            <artifactItems>
                <artifactItem>
                    <groupId>the.group.I.use</groupId>
                    <artifactId>MyServerServer</artifactId>
                    <version>${env.JAVA_SERVER_REL_VER}</version>
                    <type>war</type>
                    <destFileName>myWar.war</destFileName>
                </artifactItem>
            </artifactItems>
            <outputDirectory>${project.build.directory}/classes</outputDirectory>
        </configuration>
    </execution>
</executions>

Community
  • 1
  • 1
millebi
  • 351
  • 4
  • 9
0

Thanks I have added below snippet in POM.xml file and Mp problem resolved and create fat jar file that include all dependent jars.

<plugin>
    <artifactId>maven-assembly-plugin</artifactId>
        <executions>
            <execution>
                <phase>package</phase>
                <goals>
                    <goal>single</goal>
                </goals>
            </execution>
        </executions>
        <configuration>
            <descriptorRefs>
                <descriptorRef>dependencies</descriptorRef>
            </descriptorRefs>
        </configuration>
    </plugin>
</plugins>
krlzlx
  • 5,752
  • 14
  • 47
  • 55
Rajeev Rathor
  • 1,830
  • 25
  • 20
-2

To make it more simple, You can use the below plugin.

             <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                        <configuration>
                            <classifier>spring-boot</classifier>
                            <mainClass>
                                com.nirav.certificate.CertificateUtility
                            </mainClass>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
NIrav Modi
  • 6,038
  • 8
  • 32
  • 47