1

I have developed a simple application with an embedded Jetty Server that uses Jersey.

All works fine when I run the application on Eclipse, however when I generate the .jar file using mvn clean compile package and then try to run the executable on the command line by doing java -jar PATH_TO_THE_JAR_FILE I get the following stack trace:

Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.NoClassDefFoundError: org/eclipse/jetty/server/Handler
    at java.lang.Class.getDeclaredMethods0(Native Method)
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
    at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
    at java.lang.Class.getMethod0(Class.java:3018)
    at java.lang.Class.getMethod(Class.java:1784)
    at sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:544)
    at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:526)
Caused by: java.lang.ClassNotFoundException: org.eclipse.jetty.server.Handler
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 7 more

My pom:

<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.myserver</groupId>
<artifactId>lineserver</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>lineserver</name>
<url>http://maven.apache.org</url>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.0</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>

        <plugin>
            <!-- Build an executable JAR -->
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>3.1.1</version>
            <configuration>
                <archive>
                    <manifest>
                        <addClasspath>true</addClasspath>
                        <classpathPrefix>lib/</classpathPrefix>
                        <mainClass>com.salsify.lineserver.server.Main</mainClass>
                    </manifest>
                </archive>
            </configuration>
        </plugin>
    </plugins>
</build>

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>3.8.1</version>
        <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.glassfish.jersey.core/jersey-server -->
    <dependency>
        <groupId>org.glassfish.jersey.core</groupId>
        <artifactId>jersey-server</artifactId>
        <version>2.27</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.glassfish.jersey.containers/jersey-container-servlet-core -->
    <dependency>
        <groupId>org.glassfish.jersey.containers</groupId>
        <artifactId>jersey-container-servlet-core</artifactId>
        <version>2.27</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.glassfish.jersey.containers/jersey-container-jetty-http -->
    <!-- <dependency>
        <groupId>org.glassfish.jersey.containers</groupId>
        <artifactId>jersey-container-jetty-http</artifactId>
        <version>2.27</version>
    </dependency>-->
    <dependency>
        <groupId>org.glassfish.jersey.inject</groupId>
        <artifactId>jersey-hk2</artifactId>
        <version>2.27</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.eclipse.jetty/jetty-server -->
    <dependency>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-server</artifactId>
        <version>9.4.12.v20180830</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.eclipse.jetty/jetty-servlet -->
    <dependency>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-servlet</artifactId>
        <version>9.4.12.v20180830</version>
    </dependency>
    <!-- <dependency>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-util</artifactId>
        <version>9.4.14.v20181114</version>
    </dependency>-->
    <!-- https://mvnrepository.com/artifact/com.google.inject/guice -->
    <dependency>
        <groupId>com.google.inject</groupId>
        <artifactId>guice</artifactId>
        <version>4.2.2</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.google.inject.extensions/guice-servlet -->
    <dependency>
        <groupId>com.google.inject.extensions</groupId>
        <artifactId>guice-servlet</artifactId>
        <version>4.2.2</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.sun.jersey.contribs/jersey-guice -->
    <dependency>
        <groupId>com.sun.jersey.contribs</groupId>
        <artifactId>jersey-guice</artifactId>
        <version>1.19.4</version>
    </dependency>

</dependencies>

There are no conflicts between dependencies, running mvn dependency:tree I get this:

[INFO] +- junit:junit:jar:3.8.1:test
[INFO] +- org.glassfish.jersey.core:jersey-server:jar:2.27:compile
[INFO] |  +- org.glassfish.jersey.core:jersey-common:jar:2.27:compile
[INFO] |  |  \- org.glassfish.hk2:osgi-resource-locator:jar:1.0.1:compile
[INFO] |  +- org.glassfish.jersey.core:jersey-client:jar:2.27:compile
[INFO] |  +- javax.ws.rs:javax.ws.rs-api:jar:2.1:compile
[INFO] |  +- org.glassfish.jersey.media:jersey-media-jaxb:jar:2.27:compile
[INFO] |  +- javax.annotation:javax.annotation-api:jar:1.2:compile
[INFO] |  +- org.glassfish.hk2.external:javax.inject:jar:2.5.0-b42:compile
[INFO] |  \- javax.validation:validation-api:jar:1.1.0.Final:compile
[INFO] +- org.glassfish.jersey.containers:jersey-container-servlet-core:jar:2.27:compile
[INFO] +- org.glassfish.jersey.inject:jersey-hk2:jar:2.27:compile
[INFO] |  \- org.glassfish.hk2:hk2-locator:jar:2.5.0-b42:compile
[INFO] |     +- org.glassfish.hk2.external:aopalliance-repackaged:jar:2.5.0-b42:compile
[INFO] |     +- org.glassfish.hk2:hk2-api:jar:2.5.0-b42:compile
[INFO] |     +- org.glassfish.hk2:hk2-utils:jar:2.5.0-b42:compile
[INFO] |     \- org.javassist:javassist:jar:3.22.0-CR2:compile
[INFO] +- org.eclipse.jetty:jetty-server:jar:9.4.14.v20181114:compile
[INFO] |  +- javax.servlet:javax.servlet-api:jar:3.1.0:compile
[INFO] |  +- org.eclipse.jetty:jetty-http:jar:9.4.14.v20181114:compile
[INFO] |  |  \- org.eclipse.jetty:jetty-util:jar:9.4.14.v20181114:compile
[INFO] |  \- org.eclipse.jetty:jetty-io:jar:9.4.14.v20181114:compile
[INFO] +- org.eclipse.jetty:jetty-servlet:jar:9.4.14.v20181114:compile
[INFO] |  \- org.eclipse.jetty:jetty-security:jar:9.4.14.v20181114:compile
[INFO] +- com.google.inject:guice:jar:4.2.2:compile
[INFO] |  +- javax.inject:javax.inject:jar:1:compile
[INFO] |  +- aopalliance:aopalliance:jar:1.0:compile
[INFO] |  \- com.google.guava:guava:jar:25.1-android:compile
[INFO] |     +- com.google.code.findbugs:jsr305:jar:3.0.2:compile
[INFO] |     +- org.checkerframework:checker-compat-qual:jar:2.0.0:compile
[INFO] |     +- com.google.errorprone:error_prone_annotations:jar:2.1.3:compile
[INFO] |     +- com.google.j2objc:j2objc-annotations:jar:1.1:compile
[INFO] |     \- org.codehaus.mojo:animal-sniffer-annotations:jar:1.14:compile
[INFO] +- com.google.inject.extensions:guice-servlet:jar:4.2.2:compile
[INFO] \- com.sun.jersey.contribs:jersey-guice:jar:1.19.4:compile
[INFO]    \- com.sun.jersey:jersey-servlet:jar:1.19.4:compile
[INFO]       \- com.sun.jersey:jersey-server:jar:1.19.4:compile
[INFO]          \- com.sun.jersey:jersey-core:jar:1.19.4:compile
[INFO]             \- javax.ws.rs:jsr311-api:jar:1.1.1:compile
LuisF
  • 564
  • 1
  • 7
  • 18
  • Did you check if the libraries are being packaged inside the final jar? – nortontgueno Jan 20 '19 at 02:07
  • @ngueno indeed they aren't being packaged. however, when I build a jar with eclipse they are packaged. I'm running jar -tf jarfile to check the contents... is it something to do with the maven package process – LuisF Jan 20 '19 at 10:11
  • Possible duplicate of https://stackoverflow.com/questions/574594/how-can-i-create-an-executable-jar-with-dependencies-using-maven – Joakim Erdfelt Jan 20 '19 at 16:34

1 Answers1

0

To package your classes with dependencies in an executable jar you should use the maven-shade-plugin:

  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.2.1</version>
    <executions>
      <execution>
        <phase>package</phase>
        <goals>
          <goal>shade</goal>
        </goals>
        <configuration>
          <transformers>
            <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
              <mainClass>com.salsify.lineserver.server.Main</mainClass>
            </transformer>
          </transformers>
        </configuration>
      </execution>
    </executions>
  </plugin>

You can remove the maven-jar-plugin configuration from your pom.

gjoranv
  • 4,376
  • 3
  • 21
  • 37
  • `maven-assembly-plugin` with `jar-with-dependencies` configuration is [another choice](https://stackoverflow.com/questions/574594/how-can-i-create-an-executable-jar-with-dependencies-using-maven). – Joakim Erdfelt Jan 20 '19 at 16:35
  • I recommend `maven-shade-plugin` over `maven-assembly-plugin` as it seems to be better maintained and more frequently released. Version `3.1.0` of the assembly plugin has a bug causing it to spend 2-3 minutes creating some of our jars. I don't know if that's fixed in the latest `3.1.1`, but I would still go for the shade-plugin. – gjoranv Jan 20 '19 at 16:50
  • The only benefit that `maven-shade-plugin` brings to the table over `maven-assembly-plugin` are the resource transformers (for things like merging `META-INF/services/` files). The Eclipse Jetty project itself uses both assembly and shade plugins depending on role/purpose of the resulting artifact. (public == assembly, private == shade) – Joakim Erdfelt Jan 20 '19 at 17:23
  • @JoakimErdfelt I already mentioned build time as an advantage. Most likely due to a bug in the assembly plugin, it adds 2-3 minutes of build time for some or our modules. Shade-plugin also makes it easy to filter out signature files when you need to embed e.g. bouncycastle artifacts, which will make the signature check fail on startup if included. Why does it matter to the choice of plugin whether the artifact is private or public? Can you point at a concrete advantage of choosing assembly over shade? – gjoranv Jan 20 '19 at 19:07