PDStat
PDStat

Reputation: 5825

Maven shade executable uber jar NoClassDefFoundError

I am creating a executable uber jar with the maven shade plugin, it has a dependency on some classes in a local system jar (mobile-dock-support.jar) that I want it to include during the package phase, so I have the shade plugin configured like so

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>2.4.2</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <transformers>
                    <transformer
                        implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <mainClass>uk.xxx.mobiledock.PCSyncApplication</mainClass>
                    </transformer>
                </transformers>
            </configuration>
        </execution>
    </executions>
    <dependencies>
        <dependency>
            <groupId>uk.xxx</groupId>
            <artifactId>mobile-dock-support</artifactId>
            <version>0.0.1-SNAPSHOT</version>
            <scope>system</scope>
            <systemPath>${project.build.directory}/../src/main/libs/mobile-dock-support.jar</systemPath>
        </dependency>
    </dependencies>
</plugin>

It seems to package the jar fine but when I run the jar using java -jar xxx.jar it gives me the following

java.lang.NoClassDefFoundError: uk/xxx/mobiledock/MobileDockException
        at java.lang.Class.getDeclaredMethods0(Native Method)
        at java.lang.Class.privateGetDeclaredMethods(Unknown Source)
        at java.lang.Class.privateGetMethodRecursive(Unknown Source)
        at java.lang.Class.getMethod0(Unknown Source)
        at java.lang.Class.getMethod(Unknown Source)
        at sun.launcher.LauncherHelper.validateMainClass(Unknown Source)
        at sun.launcher.LauncherHelper.checkAndLoadMain(Unknown Source)
Caused by: java.lang.ClassNotFoundException: uk.xxx.mobiledock.MobileDockException
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        ... 7 more
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main"

I've also setup the exec maven plugin, and running mvn exec:java -Dexec.mainClass=uk.xxx.mobiledock.PCSyncApplication runs the program as expected.

Here's how exec maven plugin is setup

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>exec-maven-plugin</artifactId>
    <version>1.4.0</version>
    <executions>
        <execution>
            <goals>
                <goal>java</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <includePluginDependencies>true</includePluginDependencies>
        <mainClass>uk.xxx.mobiledock.PCSyncApplication</mainClass>
    </configuration>
    <dependencies>
        <dependency>
            <groupId>uk.xxx</groupId>
            <artifactId>mobile-dock-support</artifactId>
            <version>0.0.1-SNAPSHOT</version>
            <scope>system</scope>
            <systemPath>${project.build.directory}/../src/main/libs/mobile-dock-support.jar</systemPath>
        </dependency>
    </dependencies>
</plugin>

Upvotes: 4

Views: 5096

Answers (2)

Panagiotis Drakatos
Panagiotis Drakatos

Reputation: 3204

I believe your approach may work but it is incorrect. The correct way is to use the install maven plugin to build in all the 3party dependencies you have. Here is an example with a proper way on how you can achieve it:

<dependencies>
        <dependency>
            <groupId>uk.xxx</groupId>
            <artifactId>mobile-dock-support</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
</dependencies>
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.5.0</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <id>shade-my-jar</id>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <transformers>
                            <transformer
                                    implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                <resource>META-INF/spring.handlers</resource>
                            </transformer>
                            <transformer
                                    implementation="org.springframework.boot.maven.PropertiesMergingResourceTransformer">
                                <resource>META-INF/spring.factories</resource>
                            </transformer>
                            <transformer
                                    implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                <resource>META-INF/spring.schemas</resource>
                            </transformer>
                            <transformer
                                    implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
                            <transformer
                                    implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                <manifestEntries>
                                    <Main-Class>io.Adrestus.Backend.App</Main-Class>
                                </manifestEntries>
                            </transformer>
                        </transformers>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-install-plugin</artifactId>
            <version>3.1.1</version>
            <executions>
                <execution>
                    <id>id0</id>
                    <phase>initialize</phase>
                    <goals>
                        <goal>install-file</goal>
                    </goals>
                    <configuration>
                        <file>
                            ${project.basedir}/../src/main/libs/mobile-dock-support.jar
                        </file>
                        <groupId>uk.xxx</groupId>
                        <artifactId>mobile-dock-support</artifactId>
                        <version>0.0.1-SNAPSHOT</version>
                        <packaging>jar</packaging>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

Upvotes: 1

PDStat
PDStat

Reputation: 5825

OK then it appears the issue is the shade plugin doesn't copy system dependencies to the uber jar. There are a couple of 'correct' ways of fixing this

  1. Install the dependency to your local maven repository using mvn install

  2. Use a repository manager such as Nexus

If however you're like me and didn't have the option of the above two as it's a collaborative project and there was no option for something like Nexus, then here is what I did to get it working

Use the maven dependency plugin to copy any system dependencies to somewhere relative to the packaged jar, eg.

<plugin>
    <artifactId>maven-dependency-plugin</artifactId>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>copy-dependencies</goal>
            </goals>
            <configuration>
                <outputDirectory>${project.build.directory}/lib</outputDirectory>
                <includeArtifactIds>mobile-dock-support</includeArtifactIds>
            </configuration>
        </execution>
    </executions>
</plugin>

Add the copied jars as part of the class path in the uber jar's manifest file eg.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>2.4.2</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <transformers>
                    <transformer
                        implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <manifestEntries>
                            <Main-Class>uk.xxx.mobiledock.PCSyncApplication</Main-Class>
                            <Class-Path>lib/mobile-dock-support-${mobile-dock-support.version}.jar</Class-Path>
                        </manifestEntries>
                    </transformer>
                </transformers>
            </configuration>
        </execution>
    </executions>
</plugin>

Upvotes: 2

Related Questions