Search code examples
javamaven-2build-processmaven-pluginuberjar

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


I'm trying to create a executable jar(using maven) that contains the project classes and it's dependencies with a manifest file that has the entry for the main class and the class path entry that points to the dependencies packed in the root of the jar;something like this :

Manifest File:

.....
Main-Class : com.acme.MainClass
Class-Path : dependecy1.jar dependecy2.jar
.....

Jar:

jar-root
|-- ....
|-- com/acme/../*.class
|-- dependecy1.jar
`-- dependecy2.jar

I'm using the maven-jar-plugin to create the manifest file and the maven-shade-plugin to create the "uber" jar but the dependencies are unpacked and added as classes to my jar.


Solution

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