0
votes

I am using JBoss AS 7.1.1, RestEasy 2.3.5.Final, Swagger 1.2.0, Spring 3.1.1

Here is my web.xml,

<?xml version="1.0" encoding="UTF-8"?>
<web-app>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:/META-INF/spring/application-context.xml</param-value>
    </context-param>

    <servlet>
        <servlet-name>Bootstrap</servlet-name>
        <servlet-class>com.js.api.Bootstrap</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet>
        <servlet-name>springServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring/servlet-context.xml</param-value>
        </init-param>
        <init-param>
            <param-name>swagger.config.reader</param-name>
            <param-value>com.js.api.RestEasyConfigReader</param-value>
        </init-param>
        <init-param>
            <param-name>swagger.api.basepath</param-name>
            <param-value>http://localhost:8080/js</param-value>
        </init-param>
        <init-param>
            <param-name>api.version</param-name>
            <param-value>1.0</param-value>
        </init-param>
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

spring application-context & servlet-context.xml are fine and my REST services are wired up and working. But only swagger is not working. Here is ApiListingResource,

package com.js.resource;

import javax.ws.rs.Path;
import javax.ws.rs.Produces;

import org.springframework.stereotype.Controller;

import com.wordnik.swagger.annotations.Api;
import com.wordnik.swagger.jaxrs.listing.ApiListing;

@Path("/api-docs")
@Api("/api-docs")
@Produces({ "application/json" })
@Controller
public class ApiListingResource extends ApiListing {
}

I checked and @Context injection is not working in RestEasy 2.3.5. Anyway created a custom config reader,

import javax.servlet.ServletConfig;
import com.wordnik.swagger.jaxrs.ConfigReader;

public class RestEasyConfigReader extends ConfigReader {

  private ServletConfig config;

  public RestEasyConfigReader(ServletConfig config){
    this.config =  config;
  }

  @Override
  public String basePath() {
    return getParameterOrDefault("swagger.api.basepath", "http://localhost:8080/js");
  }

  @Override
  public String swaggerVersion() {
    return "1.2";
  }

  @Override
  public String apiVersion() {
    return "1.0";
  }

  @Override
  public String modelPackages() {
    return "com.js.model";
  }

  @Override
  public String apiFilterClassName() {
    return null;
  }

  private String getParameterOrDefault(String key, String defaultValue){
    if ((config != null) && (config.getInitParameter(key) !=null ))
      return config.getInitParameter(key);

    return defaultValue;
  }
}

I am always getting NPE error when I try to access http://localhost:8080/js/api-docs

java.lang.NullPointerException
    com.wordnik.swagger.jaxrs.ConfigReaderFactory$.getConfigReader(Help.scala:88)
    com.wordnik.swagger.jaxrs.listing.ApiListing.resourceListing(ApiListing.scala:64)
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    java.lang.reflect.Method.invoke(Method.java:601)
    org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:155)
    org.jboss.resteasy.core.ResourceMethod.invokeOnTarget(ResourceMethod.java:257)
    org.jboss.resteasy.core.ResourceMethod.invoke(ResourceMethod.java:222)
    org.jboss.resteasy.core.ResourceMethod.invoke(ResourceMethod.java:211)
    org.jboss.resteasy.springmvc.ResteasyHandlerAdapter.createModelAndView(ResteasyHandlerAdapter.java:87)
    org.jboss.resteasy.springmvc.ResteasyHandlerAdapter.handle(ResteasyHandlerAdapter.java:74)
    org.jboss.resteasy.springmvc.ResteasyHandlerAdapter.handle(ResteasyHandlerAdapter.java:24)
    org.jboss.resteasy.springmvc.ResteasyWebHandlerTemplate.handle(ResteasyWebHandlerTemplate.java:39)
    org.jboss.resteasy.springmvc.ResteasyHandlerAdapter.handle(ResteasyHandlerAdapter.java:45)
    org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923)
    org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
    org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:734)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:847)
3
I checked the code. @ Context ServletConfig injection in Swagger code is not working when bootstrapped with Spring. It is strange. Without Spring, RESTEasy is injecting @ Context - jaks
For reference, you can get the same stacktrace without Spring if you use the RESTEasy FilterDispatcher rather than the HttpServletDispatcher. - James Baxter
Its all due to @Context injection not working when it is specified as function parameter. - jaks

3 Answers

2
votes

Not only the @Context injection is working. Swagger relies on Application & ServletConfig, which is not injected by Spring in method parameters.

I decided its better to write code to fix this.

  1. Configuration is read by a Spring bean. It can be configured in spring xml.
  2. Written a REST resource to expose Documentation.
  3. The REST classes are found by scanning all classes. Used the solution from How do I read all classes from a Java package in the classpath?.
  4. API document for a class is formed by calling Swagger-Jaxrs code

    JaxrsApiReader.read(clazz, apiVersion, swaggerVersion, basePath, apiPath)

1
votes

I am able to wire up swagger 1.3.0, resteasy, spring 3.x and jboss-as-7.

You can configure swagger related beans in spring.xml as follows:

 <!-- Swagger providers -->
   <bean id="apiDeclarationProvider" class="com.wordnik.swagger.jaxrs.listing.ApiDeclarationProvider" />
   <bean id="resourceListingProvider" class="com.wordnik.swagger.jaxrs.listing.ResourceListingProvider" />

<!-- Swagger API listing resource -->
   <bean id="swaggerResourceJSON" class="com.wordnik.swagger.jaxrs.listing.ApiListingResourceJSON" />

<!-- this scans the classes for resources -->
   <bean id="swaggerConfig" class="com.wordnik.swagger.jaxrs.config.BeanConfig">
     <property name="resourcePackage" value="package.name"/>
     <property name="version" value="1.0.0"/>
     <property name="basePath" value="http://localhost:8081/resteasy-spring"/>
     <property name="title" value="Petstore sample app"/>
     <property name="description" value="This is a app."/>
     <property name="contact" value="[email protected]"/>
     <property name="license" value="Apache 2.0 License"/>
     <property name="licenseUrl" value="http://www.apache.org/licenses/LICENSE-2.0.html"/>
     <property name="scan" value="true"/>
   </bean>

Then You can configure swagger in web.xml as:

<servlet>
     <servlet-name>DefaultJaxrsConfig</servlet-name>
     <servlet-class>com.wordnik.swagger.jaxrs.config.DefaultJaxrsConfig</servlet-class>
     <init-param>
         <param-name>api.version</param-name>
         <param-value>1.0.0</param-value>
     </init-param>
     <load-on-startup>2</load-on-startup>
</servlet>

For more detail: visit here.

0
votes

Recently, we have shifted our Spring application from tomcat to wildfly(JBoos 8). After that, swagger did not work. Previuosly, we have mentioned everytinh in web.xml and it was working fine on tomcat 7. After changing to wildfly, we have done following changes:

We have removed following from web.xml:

<servlet>
        <servlet-name>DefaultJaxrsConfig</servlet-name>
        <servlet-class>com.wordnik.swagger.jaxrs.config.DefaultJaxrsConfig</servlet-class>
        <init-param>
            <param-name>api.version</param-name>
            <param-value>1.0.0</param-value>
        </init-param>
        <init-param>
            <param-name>swagger.api.basepath</param-name>
            <param-value>http://localhost:8080/rest/api</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
</servlet>

Add following in spring-context.xml:

<!-- Swagger providers -->
    <bean id="apiDeclarationProvider"
        class="com.wordnik.swagger.jaxrs.listing.ApiDeclarationProvider" scope="singleton" />
    <bean id="resourceListingProvider"
        class="com.wordnik.swagger.jaxrs.listing.ResourceListingProvider"  scope="singleton"/>

    <!-- Swagger API listing resource -->
    <bean id="swaggerResourceJSON"
        class="com.wordnik.swagger.jaxrs.listing.ApiListingResourceJSON" />

<!-- this scans the classes for resources -->
    <bean id="swaggerConfig" class="com.wordnik.swagger.jaxrs.config.BeanConfig">
        <property name="resourcePackage" value="com.rest" />
        <property name="version" value="1.0.0" />
        <property name="basePath" value="https://localhost:9880/rest/api" />
        <property name="title" value="REST API" />
        <property name="scan" value="true" />
    </bean>

NOTE: You need to define scope=singleton otherwise wildfly will throw an exception.

Now, Restart your server, and load your URL. It should work.