Swagger documentation with JAX-RS Jersey 2 and Grizzly

I have implementated a Rest web service (the function is not relevant) using JAX-RS. Now I want to generate its documentation using Swagger. I have followed these steps:

1) In build.gradle I get all the dependencies I need:

compile 'org.glassfish.jersey.media:jersey-media-moxy:2.13'

2) I documentate my code with Swagger annotations

3) I hook up Swagger in my Application subclass:

public class ApplicationConfig extends ResourceConfig  {

    /**
     * Main constructor
     * @param addressBook a provided address book
     */
    public ApplicationConfig(final AddressBook addressBook) {
        register(AddressBookService.class);
        register(MOXyJsonProvider.class);
        register(new AbstractBinder() {
            @Override
            protected void configure() {
                bind(addressBook).to(AddressBook.class);
            }
        });
        register(io.swagger.jaxrs.listing.ApiListingResource.class);
        register(io.swagger.jaxrs.listing.SwaggerSerializers.class);

        BeanConfig beanConfig = new BeanConfig();
        beanConfig.setVersion("1.0.2");
        beanConfig.setSchemes(new String[]{"http"});
        beanConfig.setHost("localhost:8282");
        beanConfig.setBasePath("/");
        beanConfig.setResourcePackage("rest.addressbook");
        beanConfig.setScan(true);
    }
}

However, when going to my service in http://localhost:8282/swagger.json, I get this output.

enter image description here

You can check my public repo here.


Solution 1:

It's times like this (when there is no real explanation for the problem) that I throw in an ExceptionMapper<Throwable>. Often with server related exceptions, there are no mappers to handle the exception, so it bubbles up to the container and we get a useless 500 status code and maybe some useless message from the server (as you are seeing from Grizzly).

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;

public class DebugMapper implements ExceptionMapper<Throwable>  {

    @Override
    public Response toResponse(Throwable exception) {
        exception.printStackTrace();
        if (exception instanceof WebApplicationException) {
            return ((WebApplicationException)exception).getResponse();
        }
        return Response.serverError().entity(exception.getMessage()).build();
    }  
}

Then just register with the application

public ApplicationConfig(final AddressBook addressBook) {
    ...
    register(DebugMapper.class);
}

When you run the application again and try to hit the endpoint, you will now see a stacktrace with the cause of the exception

java.lang.NullPointerException
  at io.swagger.jaxrs.listing.ApiListingResource.getListingJson(ApiListingResource.java:90)

If you look at the source code for ApiListingResource.java:90, you will see

Swagger swagger = (Swagger) context.getAttribute("swagger");

The only thing here that could cause the NPE is the context, which scrolling up will show you it's the ServletContext. Now here's the reason it's null. In order for there to even be a ServletContext, the app needs to be run in a Servlet environment. But look at your set up:

HttpServer server = GrizzlyHttpServerFactory
        .createHttpServer(uri, new ApplicationConfig(ab));

This does not create a Servlet container. It only creates an HTTP server. You have the dependency required to create the Servlet container (jersey-container-grizzly2-servlet), but you just need to make use of it. So instead of the previous configuration, you should do

ServletContainer sc = new ServletContainer(new ApplicationConfig(ab));
HttpServer server = GrizzlyWebContainerFactory.create(uri, sc, null, null); 
// you will need to catch IOException or add a throws clause

See the API for GrizzlyWebContainerFactory for other configuration options.

Now if you run it and hit the endpoint again, you will see the Swagger JSON. Do note that the response from the endpoint is only the JSON, it is not the documentation interface. For that you need to use the Swagger UI that can interpret the JSON.

Thanks for the MCVE project BTW.

Solution 2:

Swagger fixed this issue in 1.5.7. It was Issue 1103, but the fix was rolled in last February. peeskillet's answer will still work, but so will OP's now.