Smooth and powerful Swagger integration via JaxRS Analyzer

TL;DR – watch this video: https://youtu.be/aKShM1AUbIU

This post is an update from “Some words on JavaEE, REST and Swagger” and will show the following:

  • Generating Swagger spec file via JaxRS Analyzer
  • Executable API documentation with Swagger UI
  • Automate everything with Swagger Addon

Generating Swagger 2.0 spec based on your REST endpoints

We will start with the most important (and easy) step shown in this post.

Let’s suppose that you have the following (JaxRS) REST endpoint which intends to update a given car:

@Path("/cars")
@Produces("application/json;charset=utf-8")
public class CarEndpoint {

    /**
     * @param id The identifier of the car to be updated
     * @param entity the changes to be applied
     */
    @PUT
    @Path("/{id:[0-9][0-9]*}")
    @Consumes("application/json")
    public Response update(@PathParam("id") Integer id, Car entity) {
        if (entity == null) {
            return Response.status(Status.BAD_REQUEST).build();
        }
        if (!id.equals(entity.getId())) {
            return Response.status(Status.CONFLICT).entity(entity).build();
        }
        if (carService.crud().eq("id", id).count() == 0) {
            return Response.status(Status.NOT_FOUND).build();
        }
        try {
            carService.update(entity);
        } catch (OptimisticLockException e) {
            return Response.status(Status.CONFLICT).entity(e.getEntity()).build();
        }

        return Response.noContent().build();
    }
}

 

Now to generate the swagger specification from this endpoint you can use the JaxRS Analyzer. JaxRS Analyzer will use byte code analysis to infer the endpoint payload, response status, response types and so on. It also reads javadoc tags to enrich endpoint and parameters descriptions.

We will use the analyzer maven plugin to generate swagger spec file at build time:

 

<plugin>
    <groupId>com.sebastian-daschner</groupId>
    <artifactId>jaxrs-analyzer-maven-plugin</artifactId>
    <version>0.14</version>
    <executions>
        <execution>
            <goals>
                <goal>analyze-jaxrs</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <backend>swagger</backend>
        <resourcesDir>cdi-crud/apidocs</resourcesDir>
    </configuration>
</plugin>

The minimal configuration for the plugin will require a backend which can be swagger, plain text (default) and asciidoc.

The resourcesDir, which defaults to jaxrs-analyzer, is a directory (relative to build directory) where swagger specification file will be generated. Other configuration attributes can be found here.

For our example, after maven build the swagger.json file will be generated at target/cdi-crud/apidocs, here is its content:

 

{
    "swagger":"2.0",
    "info":{
        "version":"4.0.0",
        "title":"cdi-crud"
    },
    "host":"",
    "basePath":"/cdi-crud/rest",
    "schemes":[
        "http"
    ],
    "tags":[
        {
            "name":"cars"
        }
    ],
    "paths":{
        "/cars/{id}":{
            "put":{
                "consumes":[
                    "application/json"
                ],
                "produces":[
                    "application/json;charset=utf-8"
                ],
                "parameters":[
                    {
                        "type":"integer",
                        "name":"id",
                        "in":"path",
                        "required":true,
                        "description":"The identifier of the car to be updated"
                    },
                    {
                        "name":"body",
                        "in":"body",
                        "required":true,
                        "schema":{
                            "$ref":"#/definitions/Car"
                        },
                        "description":"the changes to be applied"
                    }
                ],
                "responses":{
                    "204":{
                        "description":"No Content",
                        "headers":{
                        }
                    },
                    "400":{
                        "description":"Bad Request",
                        "headers":{
                        }
                    },
                    "404":{
                        "description":"Not Found",
                        "headers":{
                        }
                    },
                    "409":{
                        "description":"Conflict",
                        "headers":{
                        },
                        "schema":{
                            "$ref":"#/definitions/Car"
                        }
                    }
                },
                "tags":[
                    "cars"
                ]
            }
        }
    },
    "definitions":{
        "Car":{
            "properties":{
                "id":{
                    "type":"integer"
                },
                "model":{
                    "type":"string"
                },
                "name":{
                    "type":"string"
                },
                "price":{
                    "type":"number"
                },
                "version":{
                    "type":"integer"
                }
            }
        }
    }
}

As you can see the analyzer was able to infer all response codes from our endpoint as well as the payload. It also could read the javadoc and added parameters description.

You can paste the above json content into swagger editor to see how it looks like.

Executable API documentation with Swagger UI

A nice way to expose your API documentation is through Swagger UI. It is mainly a javascript library which will read your swagger.json spec file and turn it into a nice executable api documentation. As you are generating the swagger spec at build time it turns out that this documentation is always updated.

The steps to add swagger ui (as documented in the official site) is mainly copy the library to your project. I’ve done that and made a small change to make swagger ui read the swagger.json from within our application instead of a remote URL.

Here is how our sample endpoint is rendered in swagger ui:

cdi-crud-rest-api

 

You can also browse the API docs live on Openshift here (it may be unavailable on your first access because of my openshift free account [it shuts the app down when there is no access in a day], just wait a few minutes and try again).

Automating everything with JBoss Forge Swagger Addon

All the above steps described can be automated with Swagger Addon as showed in the video on the beginning of this post.

Conclusion

We saw that creating and maintaining an executable API documentation based on JaxRS endpoints is quite easy (thanks to JaxRS analyzer). You can even automate that with the Swagger Addon.

All code is available on github here.

 

Test your REST endpoints inside the container (ArqTip #1)

 

read the asciidoc based version of this post here.

 

Since Arquillian 1.1.9.Final it is possible to get deployment URL even in in-container tests. This enables REST endpoints testing inside the container.

The main advantage of running this kind of test inside the container (same JVM) is that you can call any service/method of your application before making the (test)rest call.

Even better, you can prepare your database or whatever configuration you need before running the test. Here is an example using arquillian persistence (which doesn’t work outside the container – see Arq1077):

 

 

@RunWith(Arquillian.class)
public class CrudRestIt {

    @Deployment(name = "cdi-rest.war")
    public static Archive<?> createDeployment() {
        WebArchive war = Deployments.getBaseDeployment();
        MavenResolverSystem resolver = Maven.resolver();
        war.addAsLibraries(resolver.loadPomFromFile("pom.xml").resolve("com.jayway.restassured:rest-assured").withTransitivity().asFile());
        war.addAsLibraries(resolver.loadPomFromFile("pom.xml").resolve("com.google.code.gson:gson:2.4").withoutTransitivity().asSingleFile());
        System.out.println(war.toString(true));
        return war;
    }

    @ArquillianResource
    URL basePath;


    @Test
    @UsingDataSet("car.yml")
    public void shouldListCars() {
        given().
                queryParam("start", 0).queryParam("max", 10).
        when().
                get(basePath + "rest/cars").
        then().
                statusCode(Status.OK.getStatusCode()).
                body("", hasSize(4)).//dataset has 4 cars
                body("model", hasItem("Ferrari")).
                body("price", hasItem(2450.8f)).
                body(containsString("Porche274"));
    }

}

Note that I have included two libs into the deployment, RestAssured and Gson because both are used inside the test.

As a bonus you can get code coverage of your REST endpoints, something you don’t have when running as client (testable=false a.k.a blackbox):

 

cov

cov2

 

Test source code can be found here: https://github.com/rmpestano/cdi-crud/blob/cdi-crud-universe/src/test/java/com/cdi/crud/it/CrudRestIt.java

RESTFul Java with JAX-RS 2.0 Book Review

During my vacation i’ve read RESTFul Java with JAX-RS 2.0 second edition by Bill Burk.

This review is just some notes i made while i was visiting each chapter. It is mainly listing the topics i found important and interesting in the book. I’ve also made some comments.

Positive aspects:

  • Very practical with plenty of nice examples using latest JAX-RS version;
  • really easy to ready and understand book;
  • interesting topics were covered.

Negative Aspects:

  • Missing a REST API documentation chapter;
  • not enough attention on testing, maybe a dedicate chapter with best practices;
  • use more json in favor of xml;
  • Jersey was not mentioned.

Overall it is a great book and fully recommended, even for those already working with REST.

Here are my notes on each chapter. A narrative on most important topics covered so you can have an idea on the content of the book:

Chapter 1 – Introduction to REST

Although it is very objective and succinct, this chapter goes directly to the heart of REST. Compares with CORBA, SOAP and WS-* standards. How REST and HTTP are related. A bit of SOA. Refers to Roy Fielding’s PhD thesis[LINK], a must read article. Finally describes the five RESTful architectural principles: Addressability, Constrained Interface, Representation-Oriented, Communicate Statelessly and HATEOAS.
An excellent overview.

Chapter 2 – Designing RESTful Services

This chapter presents a RESTFul Order entry system interface(a.k.a endpoint) of an hypothetical ecommerce. It shows the concepts described on the first chapter explaining it in an HTTP oriented way, no Java code yet. There is an interesting discussion about “State vs Operation” and best practice to model REST resources. The data format chosen(XML) for model was not the best option i think, in my opinion json would be a best approach both for exemplifying as for best practices (i don’t buy that xml is for Java and json is for web related technologies such as Ajax). This chapter accomplishes well it’s objectives which is to illustrate RESTful concepts in practice.

Chapter 3 – Your First JAX-RS Service

It starts talking a bit of servlets then jumps to JAX-RS, it summarizes well the framework for writing RESTFul services in Java.
Next the Order system designed in chapter 2 is implemented in Java using JAX-RS. For the ones already working with REST in Java it does not adds much to the table but it is a necessary step so the application can evolve during the book. Maybe its a personal taste but i have to say again that “application/xml” was not a good choice. In my opinion the examples would be simpler with json, for example


return new StreamingOutput() {
public void write(OutputStream outputStream)
throws IOException, WebApplicationException {
outputCustomer(outputStream, customer);
}
};

Maybe introducing JAX-B in this chapter could be an option to avoid “streams”, inner classes and could simplify the client example.

Chapter 4 – HTTP Method and URI Matching

Details @Path annotation and its matching rules, a bit of sub resources and dispatching, matrix x query params and finally some gotchas in request matching.

Chapter 5 – JAX-RS Injection

Interesting hint on field injection versus singleton resources. @PathParam injection is revisited and more examples are presented. PathSegment and UroInfo are introduced. MatrixParam, QueryParam, FormParam, HeaderParam, CookieParam and BeanParam are detailed with nice examples. BeanParam is new and added to JAX-RS 2.0, a very useful feature. Next it talks about automatic type conversion. How JAX-RS can map request Strings to primitives, enums, lists and objects. Later it goes into details about ParamConverter so JAX-RS can convert http request String into Java objects. Finally the chapter ends explaining @Encoded annotation.

Chapter 6 – JAX-RS Content Handlers

The chapter starts with content marshalling and build in providers (maybe here is the motivation of streams in previous chapters). Some byte and File related examples are presented. Next there is an example of posting a form with Multivalued Map<String, String>. Next the chapter focus JAXB. There is a small intro and some examples. There is an interesting section about JAXB and JSON and how they integrate. Later the chapter details JSON objects. Finally it talks about custom marshalling and exemplifies message body reader and writer.

Chapter 7 – Server Responses and Exception Handling

It starts talking about successful and error responses. Next topic is how to create response with ResponseBuilder. A bit on cookies and later status codes. Next GenericEntity is presented to deal with generics. Finally exception handling is detailed by showing WebApplicationException, exception mappers. It ends explaining error codes and build in JAX-RS exceptions.

Chapter 8 – JAX-RS Client API

A very good introduction to the Client API that comes in JAX-RS 2.0. It really do its job in a very practical way with nice examples.

Chapter 9 – HTTP Content Negotiation

A nice overview of how JAX-RS supports the Conneg protocol to easy the integration with heterogeneous clients and evolution of the system. It starts explaining the negotiation protocol with media type examples, language negotiation and encoding. Next, examples with JAX-RS are presented.The chapter ends with Variants (multiple types of response for the same uri), URI negotiation, new media types (for versioning) and flexible schemas using content negotiation.

Chapter 10 – HATEOAS

A little introduction to the concept. How it can be applied to Web Services. Atom links is presented. Next, the advantages of HATEOAS are explained. Later JAX-RS and HATEOAS plus URI builder and URIInfo are presented with examples. Finally building links and link header is presented.

Chapter 11 – Scaling JAX-RS Applications

The chapter begins talking about the web and mechanisms that help it scale. It talks about caching (browser, proxy and CDN). After introducing caching it explores the HTTP caching with JAX-RS examples. Cache revalidation is visited, again with nice examples. Next topic is concurrency with conditional PUT and POST followed by JAX-RS examples.

Chapter 12 – Filters and Interceptors

Server side filter are presented first with Request and Response filter examples like cache control and authorization. Next, reader and writer interceptors with GZIP example. Client Filters are presented using JAX-RS client API. A cache control filter example is explained, it basically caches some requests and manipulates “If-None-Match” and “If-Modified-Since” headers. Deploying (@Provider) and ordering (@Priority) of filters and interceptors are visited. Method filters and interceptors are exemplified with DynamicFeature and NameBinding. Finally there is a note on exception on filters or interceptors.

Chapter 13 – Asynchronous JAX-RS

It first starts with Client API and AsyncInvoker using futures. Next, Callbacks are presented with nice examples. Server side asynchronous response is introduced. The Internet HTTP request/response thread model and its challenges is explained. Next, the AsyncResponse API is presented with JAX-RS examples.
The chapter made clear that asynchronous responses is for specific applications and most of the times “normal” request/response paradigm is sufficient. Later exception handling and response with resume and cancel is explained. Timeouts and response callbacks are explained. Use cases like server push and publish subscribe (chat) are presented and exemplified. A note on WebSockets and Server Sent Events versus pure HTTP server push apps. Finally scheduling using executors is presented.

Chapter 14 – Deployment and Integration

It starts by registering REST resources by extending application class, initializing Singletons and Classes. Difference between Servlet container and JavaEE JAX-RS deployment is explained. Web.xml configuration is presented. Next topics are EJB 3.1 ad Spring integration. Pretty simple but useful chapter.

Chapter 15 – Securing JAX-RS

A small introduction to security in the web and JavaEE like authentication, authorization, and encryption. It dive in servlet authentication and authorization mechanism followed by encryption. Next authorization annotations like @RolesAllowed and @PermitAll is presented. Next topic is programmatic security with SecurityContext. A JAX-RS RequestFilter for authorization is exemplified. Next is Client side security using JAX-RS client API. OAuth is the next topic. The CNN case is presented as an example of OAuth. Signing and encrypting message bodies is next security topic. It is basically concerned with security in intermediary REST services (a.k.a integrations), twitter is used as example. Later Digital signatures is introduced. DKIM and JOSE JSON Signature (JWS) are exemplified. The last topic is the encryption of representations (the message body). JOSE JSON Encryption (JWE) is used as example. The chapter is more conceptual, the majority of security examples are in Chapter 29.

In chapter 29 there is an interesting OTPAuthenticated Request Filter(one time password). @AllowedPerDay is introduced in the security chain, a nice example of limiting number of access to a resource by user. It is also a ContainerRequestFilter by with lower priority meaning that it runs after OTPAuthenticated filter.

Chapter 16 – Alternative Java Clients

Besides JAX-RS client API other clients are presented. It starts with “pure Java” and HttpURLConnection examples. There is a small note on caching and authentication using java.net classes. Standard Java certificate auth using “keytool” command is introduced. Next topic is HttpClient examples. Client authentication with HttpClient is presented. RESTEasy client proxy is introduced.

CHAPTER 17 – Workbook Introduction

This chapter is a step by step tutorial on how to setup your environment with RESTEasy 3.x(an implementation of JAX-RS 2.0 spec). It uses JDK 6, Maven 3, Jetty 8.1 for servlet container and Wildfly 8.0 for examples that require JavaEE 7. It creates the project and illustrates its directory structure.

From chapter 18 to Chapter 29 is reserved to more elaborate and complete examples for each chapter. This is the great thing on this book, it is example oriented and has dedicate chapters for examples. I will not comment the examples but recommend you to try them when you read, it is great for learning.

Some Words on JavaEE, REST and Swagger

Introduction

In this post i will cover the following topics:

  1. Create a simple JavaEE REST application using JBoss Forge
  2. Add some Rest authentication and authorization
  3. Create some Arquillian tests for the created Rest endpoint
  4. Use Swagger to generate Rest API documentation

I will enhance the CDI Crud application presented on previous posts: CDI Generic Dao, CDI Crud Multi “Tenancy” and Arquillian + DBUnit + Cucumber. All source code is avaiable here: https://github.com/rmpestano/cdi-crud

 Creating the REST Endpoint

I used JBoss Forge to execute this task. As Forge can be used to evolve an application i have just executed Rest setup command:

img01

I have chosen to use JaxRS 1.1 because i want to run the app in JBoss AS and Wildfly:

img02

As we already have our JPA entities, creating the endpoint is done with generate endpoints from entities:

img03

And the CarEndpoint is created and ready to CRUD cars via REST:

@Stateless
@Path("/cars")
public class CarEndpoint {
    @Inject
    CarService carService;

    @POST
    @Consumes("application/json")
    public Response create(Car entity) {
        carService.insert(entity);
        return Response.created(UriBuilder.fromResource(CarEndpoint.class).path(String.valueOf(entity.getId())).build()).build();
    }

    @DELETE
    @Path("/{id:[0-9][0-9]*}")
    public Response deleteById(@PathParam("id") Integer id) {
        Car entity = carService.findById(id);
        if (entity == null) {
            return Response.status(Status.NOT_FOUND).build();
        }
        carService.remove(entity);
        return Response.noContent().build();
    }

    @GET
    @Path("/{id:[0-9][0-9]*}")
    @Produces("application/json")
    public Response findById(@PathParam("id") Integer id) {
        Car entity;
        try {
            entity = carService.findById(id);
        } catch (NoResultException nre) {
            entity = null;
        }

        if (entity == null) {
            return Response.status(Status.NOT_FOUND).build();
        }
        return Response.ok(entity).build();
    }

    @GET
    @Produces("application/json")
    @Path("list")
    public List<Car> listAll(@QueryParam("start") @DefaultValue("0") Integer startPosition, @QueryParam("max") @DefaultValue("10") Integer maxResult) {
        Filter<Car> filter = new Filter<>();
        filter.setFirst(startPosition).setPageSize(maxResult);
        final List<Car> results = carService.paginate(filter);
        return results;
    }

    @PUT
    @Path("/{id:[0-9][0-9]*}")
    @Consumes("application/json")
    public Response update(@PathParam("id") Integer id,  Car entity) {
        if (entity == null) {
            return Response.status(Status.BAD_REQUEST).build();
        }
        if (!id.equals(entity.getId())) {
            return Response.status(Status.CONFLICT).entity(entity).build();
        }
        if (carService.crud().eq("id",id).count() == 0) {
            return Response.status(Status.NOT_FOUND).build();
        }
        try {
            carService.update(entity);
        } catch (OptimisticLockException e) {
            return Response.status(Response.Status.CONFLICT).entity(e.getEntity()).build();
        }

        return Response.noContent().build();
    }
}

I have only replaced EntityManager used by Forge with CarService which was created on previous posts, the rest of the code was generated by Forge. This step was really straightforward, thanks to Forge.

REST Authentication

To authenticate client before calling the REST endpoint i’ve created a CDI Interceptor:

@RestSecured
@Interceptor
public class RestSecuredImpl implements Serializable{

    @Inject
    CustomAuthorizer authorizer;

    @Inject
    Instance<HttpServletRequest> request;

    @AroundInvoke
    public Object invoke(InvocationContext context) throws Exception {
        String currentUser = request.get().getHeader("user");
         if( currentUser != null){
             authorizer.login(currentUser);
         } else{
             throw new CustomException("Access forbidden");
         }
        return context.proceed();
    }

}

 

So for this app we are getting current user from HttpHeader of name user. If the interceptor doesn’t find the header it will throw a CustomException, it will be explained in next section. Note that only endpoints annotated with @RestSecured will be intercepted. Authorization is done by CustomAuthorizer

Verifying Authorization

Authorization is performed by CustomAuthorizer which is based on DeltaSpike security module. A very simple authorizer was created, it is based on username and stores logged user in a hashmap:

@ApplicationScoped
public class CustomAuthorizer implements Serializable {

    Map<String, String> currentUser = new HashMap<>();

    @Secures
    @Admin
    public boolean doAdminCheck(InvocationContext invocationContext, BeanManager manager) throws Exception {
        boolean allowed = currentUser.containsKey("user") && currentUser.get("user").equals("admin");
        if(!allowed){
            throw new CustomException("Access denied");
        }
        return allowed;
    }

   
    public void login(String username) {
        currentUser.put("user", username);
    }
}

When authorization fails (check method returns false) we are throwing another CustomException.

I have created a Rest Provider to map CustomException into Response types:

@Provider
public class CustomExceptionMapper implements ExceptionMapper<CustomException> {

    @Override
    public Response toResponse(CustomException e) {
        Map map = new HashMap();
        map.put("message", e.getMessage());

        if (e.getMessage().equals("Access forbidden")) {//TODO create specific exception and its mapper
            return Response.status(Response.Status.FORBIDDEN).type(MediaType.APPLICATION_JSON).entity(map).build();
        }
        if (e.getMessage().equals("Access denied")) {//TODO create specific exception and its mapper
            return Response.status(Response.Status.UNAUTHORIZED).type(MediaType.APPLICATION_JSON).entity(map).build();
        }
        return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON).entity(map).build();
    }
}

I have only added authentication to delete endpoint:

    @DELETE
    @Path("/{id:[0-9][0-9]*}")
    @RestSecured
    public Response deleteById(@PathParam("id") Integer id) {
        Car entity = carService.findById(id);
        if (entity == null) {
            return Response.status(Status.NOT_FOUND).build();
        }
        carService.remove(entity);
        return Response.noContent().build();
    }

Basically added @RestSecured annotation. It means that if a client fires a request to this endpoint without providing a user on http header, the method will not be called and response will be 403. If client provides a user but it is not allowed then http response must be 401.

For authorization we use @Admin in the service method:

@Stateless
public class CarService extends CrudService<Car> {

    @Override
    @Admin
    public void remove(Car car) {
        super.remove(car);
    }
}

@Admin activates our CustomAuthorizer which verifies if current user has authorization to execute annotated method.

Testing the REST Endpoint

To test CarEndpoint i have used Arquillian, RestAssured and DBUnit. Before tests our database is populated with 4 cars described in car.yml dataset:

car:
  - id: 1
    model: "Ferrari"
    price: 2450.8
  - id: 2
    model: "Mustang"
    price: 12999.0
  - id: 3
    model: "Porche"
    price: 1390.3
  - id: 4
    model: "Porche274"
    price: 18990.23

I’ve implemented tests for all CRUD and HTTP operations. I will show only List and DELETE tests, other tests can be found in CrudRest.java. Here is how List all cars test look like:


    @Test
    public void shouldListCars() {
        given().
                queryParam("start",0).queryParam("max", 10).
        when().
                get(basePath + "rest/cars/list").
        then().
                statusCode(Response.Status.OK.getStatusCode()).
                body("", hasSize(4)).//dataset has 4 cars
                body("model", hasItem("Ferrari")).
                body("price", hasItem(2450.8f)).
                body(containsString("Porche274"));
    }

For DELETE methods i have one that fails with authentication, another fails with authorization and one which can delete a car:


    @Test
    public void shouldFailToDeleteCarWithoutAuthentication() {
        given().
                contentType(ContentType.JSON).
                when().
                delete(basePath + "rest/cars/1").  //dataset has car with id =1
                then().
                statusCode(Response.Status.FORBIDDEN.getStatusCode());
    }

    @Test
    public void shouldFailToDeleteCarWithoutAuthorization() {
        given().
                contentType(ContentType.JSON).
                header("user", "guest"). //only admin can delete
                when().
                delete(basePath + "rest/cars/1").  //dataset has car with id =1
                then().
                statusCode(Response.Status.UNAUTHORIZED.getStatusCode());
    }

    @Test
    public void shouldDeleteCar() {
        given().
                contentType(ContentType.JSON).
                header("user","admin").
        when().
                delete(basePath + "rest/cars/1").  //dataset has car with id =1
        then().
                statusCode(Response.Status.NO_CONTENT.getStatusCode());

        //ferrari should not be in db anymore
        given().
        when().
                get(basePath + "rest/cars/list").
         then().
                statusCode(Response.Status.OK.getStatusCode()).
                body("", hasSize(3)).
                body("model", not(hasItem("Ferrari")));
    }

Generating the REST API Documentation

To generate the API documentation i will use Swagger which is a specification for REST apis. Swagger is composed by various components, the main ones are:

  • swagger-spec: describes the format of REST APIs
  • swagger-codgen: generates REST clients based on swagger spec
  • swagger-ui: generates web pages describing the API based on swagger spec
  • swagger-editor: designing swagger specifications from scratch, using a simple YAML structure

Instead of using “pure” swagger, which requires its own annotations, i will use swagger-jaxrs-doclet that is based on javadoc and leverages JAXRS annotations.

The first thing to do is to copy the swagger-ui distribution (the swagger-ui i’ve used can be found here, i’ve made minor changes to index.html)to your application in webapp/apidocs as in image below:

img04

Now we just have to generate the swagger spec files based on our REST endpoints. This is done by the doclet maven plugin:


     <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-javadoc-plugin</artifactId>
                <version>2.9.1</version>
                <executions>
                    <execution>
                        <id>generate-service-docs</id>
                        <phase>generate-resources</phase>
                        <configuration>
                            <doclet>com.carma.swagger.doclet.ServiceDoclet</doclet>
                            <docletArtifact>
                                <groupId>com.carma</groupId>
                                <artifactId>swagger-doclet</artifactId>
                                <version>1.0.2</version>
                            </docletArtifact>
                            <reportOutputDirectory>src/main/webapp</reportOutputDirectory>
                            <useStandardDocletOptions>false</useStandardDocletOptions>
                            <additionalparam>-apiVersion 1 -docBasePath /cdi-crud/apidocs
                                -apiBasePath /cdi-crud/rest
                                -swaggerUiPath ${project.build.directory}/
                            </additionalparam>
                        </configuration>
                        <goals>
                            <goal>javadoc</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

The plugin has 3 main configurations:

  • docBasePath: where swagger spec files(.json) will be generated
  • apiBasePath: path used in the calls made from the API documentation(swagger-ui generates executable documentation)
  • swaggerUiPath: the plugin can generate the swagger-ui ditribution. As i am copying the dist manually i do not use this option and point it to target folder (in fact i could not get it working well so i need to play more with this option).

With this configuration the swagger spec files will be generated on each build which makes the documentation and API synchronized, see .json spec files (in red):

img05

Now you can access your REST API documentation in /apisdocs url:

img06

you can also fire REST requests through the API Docs:

img12

We can also enhance the documentation via Javadoc so for example we can add response types to describe the response codes, see modified delete method:


   /**
     * @description deletes a car based on its ID
     * @param user name of the user to log in
     * @param id car ID
     * @status 401 only authorized users can access this resource
     * @status 403 only authenticated users can access this resource
     * @status 404 car not found
     * @status 204 Car deleted successfully
     */
    @DELETE
    @Path("/{id:[0-9][0-9]*}")
    @RestSecured
    public Response deleteById(@HeaderParam("user") String user, @PathParam("id") Integer id) {
        Car entity = carService.findById(id);
        if (entity == null) {
            return Response.status(Status.NOT_FOUND).build();
        }
        carService.remove(entity);
        return Response.noContent().build();
    }

and here is the generated doc(note that i’ve added @HeaderParam so we can authenticate through documentation page):

img11

All supported doclet annotations can be found here.

This app and REST documentation is available at openshift here: http://cdicrud-rpestano.rhcloud.com/cdi-crud/apidocs. There is also a simple car crud app.   To see a “bit more elaborated” documentation generated by swagger and doclet see Carma Rest API.