How to Create an "Uber" Jar Containing the Project Classes and the Project Dependencies as Jars with a Custom Manifest File

Is it possible to create an uber jar containing the project classes and the project dependencies as jars with a custom manifest file?

Actually, I didn't check what the maven-shade-plugin is doing exactly (or any other plugin) as maven 2 has everything built-in to create a megajar or uberjar. You just have to use the maven-assembly-plugin with the predefined jar-with-dependencies descriptor.

Just add this snippet to your pom.xml to customize the manifest:

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

And the following command will generate your uberjar:

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

But, again, the default behavior of this descriptor is to unpack dependencies (like the maven-shade-plugin). To be honest, I don't get why this is a problem but, if this is really not what you want, you can use your own custom assembly descriptor.

To do so, first, create your assembly descriptor, let's say src/assembly/uberjar.xml, with the following content:

<assembly>
<id>uberjar</id>
<formats>
<format>jar</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<dependencySets>
<dependencySet>
<unpack>false</unpack>
<scope>runtime</scope>
<useProjectArtifact>false</useProjectArtifact>
</dependencySet>
</dependencySets>
<fileSets>
<fileSet>
<directory>${project.build.outputDirectory}</directory>
<outputDirectory>/</outputDirectory>
</fileSet>
</fileSets>
</assembly>

Then, configure the maven-assembly-plugin to use this descriptor and to add the dependencies to the Class-Path entry of the manifest:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>
<descriptor>src/assembly/uberjar.xml</descriptor>
</descriptors>
<archive>
<manifest>
<mainClass>my.package.to.my.MainClass</mainClass>
<addClasspath>true</addClasspath>
</manifest>
</archive>
</configuration>
<!--
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
-->
</plugin>

Finally run mvn assembly:assembly to produce your uberjar.

Optionally, uncomment the executions element to bind the assembly plugin on the package phase (and have the assembly produced as part of the normal build).

How can I create an executable/runnable JAR with dependencies using Maven?

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

and you run it with

mvn clean compile assembly:single

Compile goal should be added before assembly:single or otherwise the code on your own project is not included.

See more details in comments.


Commonly this goal is tied to a build phase to execute automatically. This ensures the JAR is built when executing mvn install or performing a deployment/release.

<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>
<executions>
<execution>
<id>make-assembly</id> <!-- this is used for inheritance merges -->
<phase>package</phase> <!-- bind to the packaging phase -->
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

Including dependencies in a jar with Maven

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>

Generate jar file in Maven with dependencies and tests

My solution is to build an archive (zip or other formats) that contains your classes in a jar, the dependencies jars, a folder with runtime configuration files and scripts to launch the application. The scope is to have a run ready application just by unzipping the archive.

The built archive content is:

artifactId-version.zip:
<artifactId folder>
├─ config
| ├─ log4j2.xml
├─ lib
| ├─ <all dependencies jars>
├─ leanft.cmd
├─ leanft.sh
└─ artifactId-version.jar

You should tailor the solution based on what/if you need configuration files. You don't need the META-INF folders with MANIFEST.MF files because will be generated automatically by maven plugin.

Project structure

<maven_module_root>
├─ src
| ├─ main
| | ├─ assembly
| | | ├─ leanft-assembly.xml
| | ├─ java
| | | ├─ <your classes content>
| | ├─ resources
| | | ├─ log4j2.xml
| | | ├─ <your runtime configuration files>
| | ├─ scripts
| | | ├─ leanft.cmd
| | | ├─ leanft.sh
│ └───test
├─ pom.xml

The project structure is similar with your current structure with two additional folders:

- assembly: in this folder is the leanft-assembly.xml to customize the maven-assembly-plugin.

- scripts: in this folder are the launcher scripts for your application. This is necessary it if you need to have runtime configuration files available for edit. In my example, the resources/log4j2.xml will be in a config file so the user can edit this file without touching any jar/archive.

Assembly descriptor

The solution is based on maven assembly plugin with custom configuration. I recommend to get familiar with it, starting with descriptor assembly descriptor

Your leanft-assembly.xml could look like:

<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
<id>dist</id>
<formats>
<!-- <format>tar.gz</format> -->
<format>zip</format>
</formats>
<includeBaseDirectory>true</includeBaseDirectory>
<baseDirectory>${project.artifactId}</baseDirectory>
<fileSets>
<fileSet>
<directory>${project.build.directory}</directory>
<outputDirectory>/</outputDirectory>
<includes>
<include>${project.artifactId}-${project.version}.jar</include>
</includes>
</fileSet>
<fileSet>
<directory>${project.build.directory}/lib</directory>
<outputDirectory>/lib</outputDirectory>
<includes>
<include>*.*</include>
</includes>
</fileSet>
<fileSet>
<directory>${project.build.outputDirectory}</directory>
<outputDirectory>/config</outputDirectory>
<includes>
<include>log4j2.xml</include>
</includes>
</fileSet>
<fileSet>
<directory>${project.basedir}/src/main/scripts</directory>
<outputDirectory>/</outputDirectory>
<includes>
<include>leanft.*</include>
</includes>
</fileSet>
</fileSets>
</assembly>

Maven pom

Finally, the pom.xml make use of 3 plugins:

    <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
<overWriteIfNewer>true</overWriteIfNewer>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>test.LeanFTest</mainClass>
</manifest>
</archive>
<excludes>
<exclude>log4j2.xml</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
<configuration>
<finalName>${project.artifactId}-${project.version}</finalName>
<appendAssemblyId>false</appendAssemblyId>
<descriptors>
<descriptor>src/main/assembly/leanft-assembly.xml</descriptor>
</descriptors>
</configuration>
</plugin>

I will explain the usage of maven plugins:

- maven-dependency-plugin: copy all dependencies at package phase in a lib folder under the target folder.

- maven-jar-plugin:

    - archive: generate manifest file, define in manifest all dependencies from the lib folder and what is the main class so you can run the application also with "java -jar"

    - excludes: don't include the log4j2.xml file in the module jar because will be in config folder available at runtime from outside jar.

- maven-assembly-plugin: create at package phase a zip file that contains your distribution. The archive is placed in target folder. The descriptor tag reference your assembly configuration file leanft-assembly.xml.

Script

The script to launch the application call java with predefined parameters, the main line from script being:

%JAVA_HOME%\bin\java %JVM_ARGS% -cp %SCRIPT_DIR%\*;%SCRIPT_DIR%\config\ test.LeanFTest

Maven 3 uber jar how to embed a jar within a jar?

I think the uber jar is the wrong route. Instead to accomplish your goal of hiding methods from the DatabaseLibrary but make them accessible to your Logic I would declare your methods as "protected" which limits its visibility to the package level. Given that you would make sure that any classes that needed to talk between jars were within the same package (though in seperate jars). This will force a 3rd party vendor only to use the public methods that are available to them.

- DatabaseLibrary
|- com.mycompany
|- DatabaseClass

-LogicLibrary
|- com.mycompany
|- LogicClass

public class DatabaseClass{
protected void doSomething(){
}
}

public class LogicClass{
public void doSomething(){
DatabaseClass dbClass = new DatabaseClass();
dbClass.doSomething();
}
}

Now you're 3rd party vendor will only see LogicClass.doSomething()

See also: http://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html

Classpath including JAR within a JAR

If you're trying to create a single jar that contains your application and its required libraries, there are two ways (that I know of) to do that. The first is One-Jar, which uses a special classloader to allow the nesting of jars. The second is UberJar, (or Shade), which explodes the included libraries and puts all the classes in the top-level jar.

I should also mention that UberJar and Shade are plugins for Maven1 and Maven2 respectively. As mentioned below, you can also use the assembly plugin (which in reality is much more powerful, but much harder to properly configure).

Using Gradle to build a JAR with dependencies

I posted a solution in JIRA against Gradle:

// Include dependent libraries in archive.
mainClassName = "com.company.application.Main"

jar {
manifest {
attributes "Main-Class": "$mainClassName"
}

from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}

Note that mainClassName must appear BEFORE jar {.

Multiple entry points (main classes) inside jar's top level

I found a solution here Spring Boot - How to specify an alternate start-class? (Multiple Entry Points). Ended up using -Dloader.main property when launching jar.
Command line looks like these: java -jar -Dloader.main=<main_class> ./final.jar



Related Topics



Leave a reply



Submit