CXF worked a lot of Swagger integration and it is now way cleaner (if you read my old post on how to get it running in TomEE you probably remember it was not that easy before).

Let see how to make it running with Apache Meecrowave as a concrete example but any CXF based server (or custom application) would behave almost the same.

Setup the swagger dependencies

Part of this nice CXF work is to have extracted swagger model, so to activate it now you need to add this dependency:

<dependency>
  <groupId>org.apache.cxf</groupId>
  <artifactId>cxf-rt-rs-service-description-swagger</artifactId>
  <version>${cxf.version}</version> <!-- 3.1.9, 3.1.10, ... -->
</dependency>

Make your Application swagger friendly

This part is optional but not doing it can lead to activate auto scanning of swagger which can either mean preventing your container to start for some or to slow down a lot the startup for a poor gain in general so I would recommand you to not bypass it.

Idea there is to simply implement the getClasses() method of your JAX-RS Application and not let the container scan for you. Why that? Simply because then Swagger integration will just reuse that method to find your documented API.

Here an example:

@Dependent
@ApplicationPath("api")
public class SwaggerApp extends Application {
    private final Set<Class<?>> classes = Stream.of(
         MyFeature.class,
         MyFilter.class,
         MyEndpoint1.class, MyEndpoint2.class)
       .collect(toSet());

    @Override
    public Set<Class<?>> getClasses() {
        return classes;
    }
}

Nothing very surprising there but I guarantee you paying this small configuration cost will help a lot to use Swagger on an everyday basis.

Activate swagger integration

This part depends your container, with TomEE you will use openejb-jar.xml or the same configuration than Meecrowave we'll see in a second, with Spring you will use spring-configuration or XML configuration, etc...

The requirement is to add the swagger feature of CXF as a CXF feature of the application.

As previous snippet can let you guess you just need to put the feature in the getClasses() or getSingleton(). Last one enabling you to customize the configuration:

@Dependent
@ApplicationPath("api")
public class SwaggerApp extends Application {
    private final Set<Class<?>> classes = singleton(MyEndpoint.class);
    private final Set<Object> singletons = singleton(new Swagger2Feature() {{
        setScan(false);
        setRunAsFilter(true);

        setPrettyPrint(true);
        setSupportSwaggerUi(true);
    }});

    @Override
    public Set<Class<?>> getClasses() {
        return classes;
    }

    @Override
    public Set<Object> getSingletons() {
        return singletons;
    }
}

Nothing very complicated right?

Run it!

If you run that application with Meecrowave you will get this kind of output:

REST Application: /api -> com.github.rmannibucau.meecrowave.SwaggerApp
      Service URI: /api/hi                        -> com.github.rmannibucau.meecrowave.HelloWorld
               GET /api/hi/                       ->      String hi()
      Service URI: /api/swagger.{type:json|yaml}  -> io.swagger.jaxrs.listing.ApiListingResource
               GET /api/swagger.{type:json|yaml}/ ->      Response getListing(Application, ServletConfig, HttpHeaders, UriInfo, String)

If you check the previous code, ApiListingResource is automatically added by the swagger feature and that's the one doing all the work.

Tip: since swagger will bring slf4j requirement I recommand you to add that dependency:

<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-slf4j-impl</artifactId>
  <version>${log4j2.version}</version>
</dependency>

Once this is setup you can it /swagger.json and you will get:

{
   swagger:"2.0",
   info:{
      description:"The Application",
      version:"1.0.0",
      title:"Sample REST Application",
      contact:{
         name:"users@cxf.apache.org"
      },
      license:{
         name:"Apache 2.0 License",
         url:"http://www.apache.org/licenses/LICENSE-2.0.html"
      }
   },
   basePath:"/api",
   paths:{
      /hi:{
         get:{
            operationId:"test",
            parameters:[

            ],
            responses:{
               200:{
                  description:"successful operation",
                  schema:{
                     type:"string"
                  },
                  headers:{

                  }
               }
            }
         }
      }
   }
}

(yes I didn't use much swagger API, goal was just to show how to activate it)

Wait I did all that but my endpoint is not documented!

Swagger will integrate only endpoints part of the api. This is a manual step requiring to add @Api annotation on your class. Here is my hello world endpoint for instance:

import io.swagger.annotations.Api;

import javax.enterprise.context.ApplicationScoped;
import javax.ws.rs.GET;
import javax.ws.rs.Path;

@Path("hi")
@ApplicationScoped
@Api
public class HelloWorld {
    @GET
    public String test() {
        return "hi";
    }
}

Of course Swagger API is way richer and you can document all part of the API through annotations but this is the one making your API part of swagger.json or not.

Conclusion

Documenting your API is still some work but activating the documentation is now way easier and pretty straight forward thanks to all the work CXF did to enable it so don't let your consumers guess your API anymore ;).

What can be the next steps? Activating swagger ui to let your consumers visualize this documentation in a human friendly manner or even test it!

From the same author:

In the same category: