Documenting REST API using MicroProfile OpenAPI, Swagger UI and Payara Micro

by Hayri Cicek


Posted on 10-Aug-2018

In this tutorial I will show you how to generate api documentation using MicroProfile OpenAPI and Payara Micro runtime.
Learn more about MicroProfile OpenAPI Specification

Tools You Will Need
Maven 3.3+
Your favorite IDE. I'm using NetBeans
JDK 1.8

Generate Project
We will use Payara Micro Maven archetype to generate our project, run the following command.

$ mvn archetype:generate -DarchetypeGroupId=fish.payara.maven.archetypes -DarchetypeArtifactId=payara-micro-maven-archetype -DarchetypeVersion=1.0.1 -DgroupId=fish.payara.micro -DartifactId=mp-openapi-example -Dversion=1.0-SNAPSHOT -Dpackage=com.kodnito.openapi.rest -Darchetype.interactive=false
Adding Dependencies
Open the project in your favorite IDE and add the following to the pom.xml dependencies section :


<dependency>
    <groupId>org.eclipse.microprofile.openapi</groupId>
    <artifactId>microprofile-openapi-api</artifactId>
    <version>1.0.1</version>
</dependency>

and in plugins section add the following :


<plugin>
    <groupId>com.googlecode.maven-download-plugin</groupId>
    <artifactId>download-maven-plugin</artifactId>
    <version>1.2.1</version>
    <executions>
        <execution>
            <id>swagger-ui</id>
            <goals>
                <goal>wget</goal>
            </goals>
            <configuration>
                <skipCache>true</skipCache>
                <url>https://github.com/swagger-api/swagger-ui/archive/master.tar.gz</url>
                <unpack>true</unpack>
                <outputDirectory>${project.build.directory}</outputDirectory>
            </configuration>
        </execution>
    </executions>
</plugin>
<plugin>
    <artifactId>maven-resources-plugin</artifactId>
    <version>2.6</version>
    <executions>
        <execution>
            <id>copy-resources</id>
            <phase>validate</phase>
            <goals>
                <goal>copy-resources</goal>
            </goals>
            <configuration>
                <outputDirectory>target/${project.artifactId}-${project.version}</outputDirectory>
                <resources>
                    <resource>
                        <directory>${project.build.directory}/swagger-ui-master/dist</directory>
                        <filtering>true</filtering>
                        <excludes>
                            <exclude>index.html</exclude>
                        </excludes>
                    </resource>
                </resources>
            </configuration>
        </execution>
    </executions>
</plugin>

Your pom.xml should look like this :


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>fish.payara.micro</groupId>
    <artifactId>mp-openapi-example</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>mp-openapi-example</name>

    <properties>
        <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <version.javaee>8.0</version.javaee>
        <version.payara.micro>5.182</version.payara.micro>
    </properties>
   
    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>${version.javaee}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.eclipse.microprofile.openapi</groupId>
            <artifactId>microprofile-openapi-api</artifactId>
            <version>1.0.1</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>com.googlecode.maven-download-plugin</groupId>
                <artifactId>download-maven-plugin</artifactId>
                <version>1.2.1</version>
                <executions>
                    <execution>
                        <id>swagger-ui</id>
                        <goals>
                            <goal>wget</goal>
                        </goals>
                        <configuration>
                            <skipCache>true</skipCache>
                            <url>https://github.com/swagger-api/swagger-ui/archive/master.tar.gz</url>
                            <unpack>true</unpack>
                            <outputDirectory>${project.build.directory}</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-resources-plugin</artifactId>
                <version>2.6</version>
                <executions>
                    <execution>
                        <id>copy-resources</id>
                        <phase>validate</phase>
                        <goals>
                            <goal>copy-resources</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>target/${project.artifactId}-${project.version}</outputDirectory>
                            <resources>
                                <resource>
                                    <directory>${project.build.directory}/swagger-ui-master/dist</directory>
                                    <filtering>true</filtering>
                                    <excludes>
                                        <exclude>index.html</exclude>
                                    </excludes>
                                </resource>
                            </resources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <compilerArguments>
                        <endorseddirs>${endorsed.dir}</endorseddirs>
                    </compilerArguments>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
            <plugin>
                <groupId>fish.payara.maven.plugins</groupId>
                <artifactId>payara-micro-maven-plugin</artifactId>
                <version>1.0.1</version>
                <configuration>
                    <payaraVersion>${version.payara.micro}</payaraVersion>
                    <deployWar>true</deployWar>
                    <commandLineOptions>
                        <option>
                            <key>--autoBindHttp</key>
                        </option>
                    </commandLineOptions>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

We added MicroProfile OpenAPI dependency and plugin for downloading Swagger UI files for us.
Run the following command to download the dependencies :

$ mvn clean install
inside src/main/webapp/META-INF create the openapi.yaml file and add the following :


openapi: 3.0.0
info:
  title: This is my TODO application API Title
  description: This is my TODO application description
  license:
    name: Eclipse Public License - v 1.0
    url: https://www.eclipse.org/legal/epl-v10.html
  version: 1.0.0
servers:
- url: http://localhost:8080

This is our configuration for our OpenAPI documentation.
Inside src/main/webapp/WEB-INF create glassfish-web.xml and add the following :


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
<glassfish-web-app error-url="">
  <context-root>/</context-root>
</glassfish-web-app>

Here we are changing how we want our application to be deployed.

Resources
Inside src/main/java/com/kodnito/openapi/rest package create the standard ApplicationConfig.java needed by JAX-RS to identify our base endpoint and add the following :


package com.kodnito.openapi.rest;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("/api")
public class ApplicationConfig extends Application {
   
}

Now create TodoResource.java file inside src/main/java/com/kodnito/openapi/rest package and add the following :


package com.kodnito.openapi.rest;

import java.util.Properties;
import javax.enterprise.context.RequestScoped;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.media.Content;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;

@RequestScoped
@Path("/todos")
public class TodoResource {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @APIResponses(
            value = {
                @APIResponse(
                        responseCode = "404",
                        description = "We could not find the Hello message ",
                        content = @Content(mediaType = "text/plain"))
                ,
        @APIResponse(
                        responseCode = "200",
                        description = "We found the Hello message",
                        content = @Content(mediaType = "application/json",
                                schema = @Schema(implementation = Properties.class)))})
    @Operation(summary = "Outputs Hello Message",
            description = "This method outputs Hello message")
    public Response sayHello() {
        return Response.ok("Hello").build();
    }

    @GET
    @Path("/{id}")
    @Operation(summary = "Get Todo by id",
        description = "This method gets a single todo by id")
    @APIResponse(responseCode = "200", description = "Everything went ok, we found TODO by id")    
    @APIResponse(responseCode = "404", description = "Todo not found")
    public Response getTodoById(@Parameter(description = "Get a specific Todo by id", required = true) @PathParam("id") Long id) {
        return Response.ok("").build();
    }
}


@APIResponses annotation describes multiple responses
@APIResponse annotation describes a single response
@Operation annotation describes a single operation on a path
@Parameter annotation describes a single operation parameter
Learn more about MicroProfile OpenAPI Specification

Open the index.html file insde src/main/webapp/ and change it to the following :


<!-- HTML for static distribution bundle build -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Swagger UI</title>
    <link rel="stylesheet" type="text/css" href="./swagger-ui.css" >
    <link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
    <link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
    <style>
      html
      {
        box-sizing: border-box;
        overflow: -moz-scrollbars-vertical;
        overflow-y: scroll;
      }

      *,
      *:before,
      *:after
      {
        box-sizing: inherit;
      }

      body
      {
        margin:0;
        background: #fafafa;
      }
    </style>
  </head>

  <body>
    <div id="swagger-ui"></div>

    <script src="./swagger-ui-bundle.js"> </script>
    <script src="./swagger-ui-standalone-preset.js"> </script>
    <script>
    window.onload = function() {

      // Build a system
      const ui = SwaggerUIBundle({
        url: "http://localhost:8080/openapi",
        dom_id: '#swagger-ui',
        deepLinking: true,
        presets: [
          SwaggerUIBundle.presets.apis,
          SwaggerUIStandalonePreset
        ],
        plugins: [
          SwaggerUIBundle.plugins.DownloadUrl
        ],
        layout: "StandaloneLayout"
      })

      window.ui = ui
    }
  </script>
  </body>
</html>

Your project directory structure should look like this :

tree .
.
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── kodnito
    │   │           └── openapi
    │   │               └── rest
    │   │                   ├── ApplicationConfig.java
    │   │                   └── TodoResource.java
    │   └── webapp
    │       ├── META-INF
    │       │   └── openapi.yaml
    │       ├── WEB-INF
    │       │   └── glassfish-web.xml
    │       └── index.html
    └── test
        └── java

Now inside your project directory run the following command to start our application :

$ mvn package payara-micro:start
Navigate to http://localhost:8080 and watch your new shiny api documentation :)



Share this: