Search code examples
javaspring-bootswaggerjersey-2.0swagger-ui

Can't serve static content with Spring Boot and Jersey 2


Is there any way of making Spring Boot with Jersey serve static content? I've been through a series of tutorials and code samples on integrating Swagger into a Spring Boot's application. I can make it deliver the basic swagger.json, but I can't make Swagger UI work.

I can't even make it deliver a simple hello.txt static file.

The relevant parts of my pom.xml are:

<!--Spring Boot-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<!-- Jersey -->
<dependency>
    <groupId>org.glassfish.jersey.core</groupId>
    <artifactId>jersey-client</artifactId>
    <version>${jersey.version}</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.ext</groupId>
    <artifactId>jersey-spring3</artifactId>
    <version>${jersey.version}</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.ext</groupId>
    <artifactId>jersey-bean-validation</artifactId>
    <version>${jersey.version}</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-servlet</artifactId>
    <version>${jersey.version}</version>
</dependency>

<!-- Swagger -->
<dependency>
    <groupId>io.swagger</groupId>
    <artifactId>swagger-jersey2-jaxrs</artifactId>
    <version>1.5.7</version>
</dependency>

And my code:

@Configuration
@EnableAutoConfiguration
@ComponentScan({"com.xxxx"})
public class AdminApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext applicationContext = new SpringApplicationBuilder(AdminApplication.class)
                .run(args);
    }

    @Bean
    public ServletRegistrationBean jerseyServlet() {
        ServletRegistrationBean registration = new ServletRegistrationBean(new ServletContainer(), "/*");
        registration.addInitParameter(ServletProperties.JAXRS_APPLICATION_CLASS, JerseyConfig.class.getName());
        return registration;
    }
}




package com.xxxxxx.admin.config;
import com.xxxxxx.admin.resource.Status;
import org.glassfish.jersey.filter.LoggingFilter;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.ServerProperties;
import org.glassfish.jersey.server.spring.scope.RequestContextFilter;    
import io.swagger.jaxrs.config.BeanConfig;

public class JerseyConfig extends ResourceConfig {

    public JerseyConfig() {
        register(RequestContextFilter.class);
        packages("com"); // TODO needs more detailed level
        register(LoggingFilter.class);
        // Validation
        this.property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
        this.property(ServerProperties.RESPONSE_SET_STATUS_OVER_SEND_ERROR, true);
        configureSwagger();
    }

    private void configureSwagger() {
        register(io.swagger.jaxrs.listing.ApiListingResource.class);
        register(io.swagger.jaxrs.listing.SwaggerSerializers.class);
        BeanConfig beanConfig = new BeanConfig();
        beanConfig.setVersion("1.0.0");
        beanConfig.setSchemes(new String[]{"http"});
        beanConfig.setHost("localhost:8080");
        beanConfig.setBasePath("/"); // tried other things like "/api", but doesn't change anything
        beanConfig.setResourcePackage("com.xxxxxx.admin");
        beanConfig.setPrettyPrint(true);
        beanConfig.setScan(true);
    }

}



//other imports
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

@Service
@Path("/status")
@Api(value = "status", description = "Check status")
public class Status {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @ApiOperation("Return status")
    public Response status() {
        return Response.ok("Up").build();
    }
}

I also tried making Jersey run as a filter (with spring.jersey.type=filter) and changing Jersey's servlet pattern as stated in this answer, but that doesn't seem to affect anything.

@ApplicationPath("/rootPath")
public class JerseyConfig extends ResourceConfig {

I have a hello.txt file under /src/main/resources/public and Swagger UI's static files under /src/main/resources/public/swagger.

As I said, my application works fine, and GET http://localhost:8080/swagger.json shows me the plain json documentation, but both http://localhost:8080/hello.txt and http://localhost:8080/swagger/index.html return 404.

I'm using Jersey 2.8 and Spring Boot 1.3.0


Solution

  • I also tried changing Jersey's servlet pattern

    @ApplicationPath("/rootPath")
    public class JerseyConfig extends ResourceConfig {
    

    The way you are configuring your app, the @ApplicationPath doesn't matter. The reason it worked for this answer you linked to, is because the Spring Boot auto configuration sets the servlet mapping when it extracts the @ApplicationPath value from your resource config.

    You are currently not using the ServletRegistrationBean provided by Spring Boot, that accomplishes this. If you goal, by using your own ServletRegistrationBean, was so that you can register your ResourceConfig, you could've have done the same simply by either

    1. Annotating your ResourceConfig with @Component to make it a Spring bean, or
    2. Make it a Spring bean in your configuration class

      @Bean
      public ResourceConfig config() {
          return new JerseyConfig();
      }
      

    Spring Boot will then inject your ResourceConfig into the JerseyAutoConfiguration, where it will get the @ApplicationPath value (if present) on the ResourceConfig, and use it to register its own ServletRegistrationBean.

    You can see the JerseyAutoConfiguration to get an idea of everything you get for free, when you let Spring Boot handle the configuration.

    Of if you want to keep your current SpringRegistrationBean, just change the path you are using. You are using /*, which is mentioned to be the problem in the linked answer. So just change to /rooPath/* if that's what you want.