Search code examples
javaeclipsegradleswaggerjersey-2.0

Swagger with Jersey 2 throws java.lang.NoClassDefFoundError: javax/servlet/ServletConfig


Trying to setup my first REST API (using Jersey 2 and Gradle) and add some documentation to it by using swagger. But when adding swagger dependencies and following this swagger documentation, "Using a custom Application subclass" approach, it throws me this exception, when executing the main method from Eclipse:

Exception in thread "main" java.lang.NoClassDefFoundError: javax/servlet/ServletConfig
    at java.lang.Class.getDeclaredMethods0(Native Method)
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
    at java.lang.Class.getDeclaredMethods(Class.java:1975)
    at org.glassfish.jersey.server.model.IntrospectionModeller$2.run(IntrospectionModeller.java:253)
    at java.security.AccessController.doPrivileged(Native Method)
    at org.glassfish.jersey.server.model.IntrospectionModeller.getAllDeclaredMethods(IntrospectionModeller.java:247)
    at org.glassfish.jersey.server.model.IntrospectionModeller.checkForNonPublicMethodIssues(IntrospectionModeller.java:172)
    at org.glassfish.jersey.server.model.IntrospectionModeller.doCreateResourceBuilder(IntrospectionModeller.java:119)
    at org.glassfish.jersey.server.model.IntrospectionModeller.access$000(IntrospectionModeller.java:80)
    at org.glassfish.jersey.server.model.IntrospectionModeller$1.call(IntrospectionModeller.java:112)
    at org.glassfish.jersey.server.model.IntrospectionModeller$1.call(IntrospectionModeller.java:109)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
    at org.glassfish.jersey.internal.Errors.processWithException(Errors.java:255)
    at org.glassfish.jersey.server.model.IntrospectionModeller.createResourceBuilder(IntrospectionModeller.java:109)
    at org.glassfish.jersey.server.model.Resource.from(Resource.java:797)
    at org.glassfish.jersey.server.ApplicationHandler.initialize(ApplicationHandler.java:465)
    at org.glassfish.jersey.server.ApplicationHandler.access$500(ApplicationHandler.java:184)
    at org.glassfish.jersey.server.ApplicationHandler$3.call(ApplicationHandler.java:350)
    at org.glassfish.jersey.server.ApplicationHandler$3.call(ApplicationHandler.java:347)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
    at org.glassfish.jersey.internal.Errors.processWithException(Errors.java:255)
    at org.glassfish.jersey.server.ApplicationHandler.<init>(ApplicationHandler.java:347)
    at org.glassfish.jersey.server.ApplicationHandler.<init>(ApplicationHandler.java:299)
    at org.glassfish.jersey.jdkhttp.JdkHttpHandlerContainer.<init>(JdkHttpHandlerContainer.java:98)
    at org.glassfish.jersey.jdkhttp.JdkHttpServerFactory.createHttpServer(JdkHttpServerFactory.java:111)
    at org.glassfish.jersey.jdkhttp.JdkHttpServerFactory.createHttpServer(JdkHttpServerFactory.java:93)
    at example.MyApp.main(MyApp.java:21)
Caused by: java.lang.ClassNotFoundException: javax.servlet.ServletConfig
    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)
    ... 29 more

My code looks like this:

package example;
import static org.glassfish.jersey.jdkhttp.JdkHttpServerFactory.createHttpServer;
import java.net.URI;
import javax.ws.rs.core.UriBuilder;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;
import com.sun.net.httpserver.HttpServer;
import io.swagger.jaxrs.listing.ApiListingResource;
import io.swagger.jaxrs.listing.SwaggerSerializers;

public class MyApp extends ResourceConfig {

    public static void main(String[] args) throws Throwable {
        URI baseUri = UriBuilder.fromUri("http://localhost/").port(9999).build();
        HttpServer server = createHttpServer(baseUri, new MyApp());
        System.out.println("SERVICE started at: " + baseUri);
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            server.stop(0);
        }));
    }

    public MyApp() {
        packages("example");
        register(SwaggerSerializers.class); // <-- swagger specific
        register(ApiListingResource.class); // <-- swagger specific
        register(JacksonFeature.class);
    }
}

My gradle dependencies

dependencies {
    compile 'com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:+'
    compile 'org.glassfish.jersey.containers:jersey-container-jdk-http:+'
    compile 'org.glassfish.jersey.media:jersey-media-moxy:+'
    compile 'org.glassfish.jersey.media:jersey-media-json-jackson:+'
    compile 'io.swagger:swagger-jersey2-jaxrs:1.5.9'
}

Using jdk1.8.0_77 on Windows 7

However, if I comment out the swagger dependency and the swagger specifics in the code, then the actual REST service works as expected. How can I make swagger work without using a servlet container? The REST service can work without it

dependencies {
    compile 'com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:+'
    compile 'org.glassfish.jersey.containers:jersey-container-jdk-http:+'
    compile 'org.glassfish.jersey.media:jersey-media-moxy:+'
    compile 'org.glassfish.jersey.media:jersey-media-json-jackson:+'
//    compile 'io.swagger:swagger-jersey2-jaxrs:1.5.9'
}

code:

package example;
import static org.glassfish.jersey.jdkhttp.JdkHttpServerFactory.createHttpServer;
import java.net.URI;
import javax.ws.rs.core.UriBuilder;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;
import com.sun.net.httpserver.HttpServer;
import io.swagger.jaxrs.listing.ApiListingResource;
import io.swagger.jaxrs.listing.SwaggerSerializers;

public class MyApp extends ResourceConfig {

    public static void main(String[] args) throws Throwable {
        URI baseUri = UriBuilder.fromUri("http://localhost/").port(9999).build();
        HttpServer server = createHttpServer(baseUri, new MyApp());
        System.out.println("SERVICE started at: " + baseUri);
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            server.stop(0);
        }));
    }

    public MyApp() {
        packages("example");
        // register(SwaggerSerializers.class); // <-- swagger specific
        // register(ApiListingResource.class); // <-- swagger specific
        register(JacksonFeature.class);
    }
}

Solution

  • So it looks like the issue is derived from you running in a non-servlet environment. While Jersey supports it, swagger-core... not so much. This poses some issues with specific deployment, although they are less common.

    The easiest solution would, obviously, be to use a servlet-container engine. Something lightweight like Jetty would work.