Including dependencies in a jar with Maven
Asked Answered
A

16

447

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.

Adjustment answered 13/11, 2009 at 12:51 Comment(3)
possible duplicate of How can I create an executable jar with dependencies using Maven?Toner
#35217628Footwear
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.Immerge
A
583

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>
Anelace answered 13/11, 2009 at 13:1 Comment(11)
In case any new mvn folks got stuck like me, add the plugin to <plugins> inside <build> which is inside <project>.Sepulchre
How can I avoid including libraries I don't use in my code? My jar just using SSJ sums up to 10 MB :(Sergeant
@D.A. Why people leave out relevant (read: pertinent) details like that are beyond me.Barye
Still can't get this to work, I've followed like 15 different answers on different questions too.Clearcole
@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.jarMarmite
How can I get the install goal to put the jar-with-dependencies into the repo?Cheer
@Christian.tucker mvn assembly:assembly -DdescriptorId=jar-with-dependenciesAramanta
Just add one more thing in configuration. Your main class information. <archive> <manifest> <mainClass>fully qualified name</mainClass> </manifest> </archive>Margaretemargaretha
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?Beatrisbeatrisa
How to include a jar file into jar file in maven ?Subchloride
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... ` <dependency> <groupId>com.kachop</groupId> <artifactId>mylocal</artifactId> <version>1.0.0</version> <scope>system</scope> <systemPath>${basedir}/lib/lk/mylocal.jar</systemPath> </dependency>`Asthenopia
D
180

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).

Danyelldanyelle answered 13/11, 2009 at 13:29 Comment(3)
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?Pettifogging
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 wayKalakalaazar
@SouvikBhattacharya add <appendAssemblyId>false</appendAssemblyId> to maven-assembly-plugin configutation.Triangulate
E
58

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>
Everywhere answered 29/12, 2016 at 16:55 Comment(2)
Why is the name of jar appended by "jar-with-dependencies"?! Any workarounds?Babe
@Tina J you can add <appendAssemblyId>false</appendAssemblyId> inside the <configuration> tag to exclude the "-jar-with-dependencies" suffix in the final name.Devlin
K
24

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>
Khoury answered 21/3, 2018 at 5:39 Comment(5)
The first option is the best for faster rebuilds. assembly-plugin just takes too much time packaging dependencies, which is expected.Eberhardt
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?Bilyeu
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.Bilyeu
Option 2 works for me, thanks!Bloodcurdling
For option 1 (If you don't want to copy JUnit dependencies to target folder) you must add <includeScope>compile</includeScope> to the maven-dependency-plugin <configuration>Gan
F
21

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

Fourinhand answered 13/11, 2009 at 13:2 Comment(2)
if you don't like jar-with-dependencies as part of the file name.Cylindroid
minimize feature maven.apache.org/plugins/maven-shade-plugin/examples/…Decane
P
19

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>
Passacaglia answered 18/1, 2012 at 19:10 Comment(0)
C
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).

Creolized answered 16/5, 2013 at 15:21 Comment(2)
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.Eddo
@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.Creolized
O
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.
Oink answered 31/8, 2015 at 16:46 Comment(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: maven.apache.org/plugins/maven-shade-plugin/examples/…Ratal
It basically encapsulates the artifact in its own namespace. I cannot think of any scenario where this still allows for clashes.Engelhardt
C
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>
Canotas answered 10/9, 2015 at 22:1 Comment(4)
the maven-dependency-plugin <outputDirectory> dont work, always write on "dependency" folderCanotas
create a jar with a internal folder "dependency" containing project dependencies and put it on MANIFEST.MFCanotas
<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>Canotas
Based on this answer I created this project. You shouldn't need to change anything except the pom file: github.com/raisercostin/jarinjarloaderSubtend
F
2

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/

Feud answered 4/2, 2023 at 0:1 Comment(0)
L
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

Lord answered 13/11, 2009 at 13:56 Comment(1)
There is also a Maven plugin for One-JAR: onejar-maven-plugin.googlecode.com/svn/mavensite/usage.htmlBohemian
R
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>
Ragg answered 10/3, 2021 at 15:42 Comment(0)
G
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

Gloam answered 8/11, 2014 at 18:18 Comment(0)
I
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>

Inner answered 17/6, 2016 at 20:50 Comment(0)
D
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>
Dumfound answered 17/10, 2016 at 9:17 Comment(0)
O
-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>
Ohm answered 9/2, 2021 at 7:34 Comment(3)
Please describe, how it exactly solves the problemGravesend
I have added only this pluginOhm
This is for something else: A Spring Boot specific solution, and needs Spring Boot to understand the resulting archive. See docs.spring.io/spring-boot/docs/current/maven-plugin/reference/…Immerge

© 2022 - 2024 — McMap. All rights reserved.