Spring and Open API (Swagger)

The Web Application Description Language (WADL) is a machine readable XML description of REST services. In the Java world WADL implementation libraries tend to be JAX-RS specific or constructed using maven plugins.

My current project is using Spring 4.3 and I needed a way to present my REST API in a format that could be consumed by Ready! API and SOAP UI.

Enter Swagger, which has become the reference implementation for OpenAPI. OpenAPI has the backing of companies like IBM (check out Watson’s API), Google and Microsoft.

To get Swagger to work with Spring I used SpringFox‘s library, which had it’s origins in swagger-springmvc.

The first step is to include Springfox’s library in your maven or gradle build. Here is a maven pom.xml example:

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.5.0</version>
</dependency>

The second step is to configure Swagger.

@EnableSwagger2
public class SwaggerAPIConfig {

    private final TypeResolver typeResolver;

    @Inject
    public SwaggerAPIConfig(TypeResolver typeResolver) {
        this.typeResolver = typeResolver;
    }

    @Bean
    public Docket gpcApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                    .apis(RequestHandlerSelectors.any())
                    .paths(PathSelectors.any())
                    .build()
                .pathMapping("/")
                .directModelSubstitute(LocalDate.class, String.class)
                .genericModelSubstitutes(RequestEntity.class)
                .alternateTypeRules(
                        newRule(typeResolver.resolve(DeferredResult.class,
                                typeResolver.resolve(ResponseEntity.class, WildcardType.class)),
                                typeResolver.resolve(WildcardType.class)))
                .useDefaultResponseMessages(false)
                .globalResponseMessage(RequestMethod.GET,
                        newArrayList(new ResponseMessageBuilder()
                        .code(500)
                        .message("500 message")
                        .responseModel(new ModelRef("Error"))
                        .build()))
                .enableUrlTemplating(true)
                .apiInfo(apiInfo())
                .tags(new Tag("My Services", "All APIs related to my service")
        );
    }

    private ApiInfo apiInfo() {
        return new ApiInfo(
                "API Documentation",
                "My REST APIs",
                "0.1",
                "",
                new Contact(
                        "Kyle Ryan",
                        "www.kyleryan.net",
                        "my_email@address"),
                "",
                ""
        );
    }

Finally, update your Spring WebConfig to import your SwaggerAPIConfig. This will tell swagger where your controllers are so that it can generate the API feed.

@Configuration
@EnableWebMvc
@Import(SwaggerAPIConfig.class)
@ComponentScan("net.kyleryan.api.controller")
public class WebConfig extends WebMvcConfigurerAdapter {


    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars");
    }
}

Once you have your application running with these changes, the API will be available to load into Postman, Ready! API, SOAP UI etc at a URL that looks similar to this: http://localhost:8080/v2/api-docs.

Hope this helps!

Kyle

Add Maven to Docker Oracle Linux Image

I spent way too much time figuring out how to install Maven on Oracle Linux from behind a proxy. This is the solution I came up with.

Sample Dockerfile

FROM oraclelinux:6
MAINTAINER Kyle Ryan <kyleus@gmail.com>

# Uncomment and update if you are behind a proxy
#RUN export http_proxy=http://user:pass@proxyurl
#RUN export https_proxy=http://user:pass@proxyurl

# Install Maven
RUN yum -y install wget
RUN wget http://repos.fedorapeople.org/repos/dchen/apache-maven/epel-apache-maven.repo -O /etc/yum.repos.d/epel-apache-maven.repo
RUN yum -y install apache-maven

# Uncomment this in order to connect to maven repos
#ADD settings.xml /root/.m2/settings.xml

Sample Maven settings.xml

<?xml version="1.0" encoding="UTF-8"?>
<settings xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0 http://maven.apache.org/xsd/settings-1.1.0.xsd" xmlns="http://maven.apache.org/SETTINGS/1.1.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 
    <proxies>
        <proxy>
            <active>true</active>
            <protocol>http</protocol>
            <username>user</username>
            <password>pass</password>
            <host>proxyURL</host>
        </proxy>
    </proxies>
</settings>

Maven Publish 2nd Client Jar From Same Repo

I recently need to be able to publish two artifacts from one maven module within a multi module build. I fumbled around for a while looking for the best way to handle this. The key was defining the classifier under configuration.

Maven pom.xml

<?xml version="1.0"?>
<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>
 
    <parent>
        <groupId>net.kyleryan</groupId>
        <artifactId>myproject</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
 
    <artifactId>implementation</artifactId>
    <version>1.0-SNAPSHOT</version>
 
    <name>GSSMFrameworkV2</name>
 
    <dependencies>        
        <!-- dependencies -->
    </dependencies>
 
    <build>
        <!-- Define the classes that will make up the client jar -->
        <plugins>
            <plugin>
                <artifactId>maven-jar-plugin</artifactId>
                <version>2.3.2</version>
                <executions>
                    <execution>
                        <id>client</id>
                        <phase>package</phase>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                        <configuration>
                            <classifier>client</classifier>
                            <includes>
                                <include>net/kyleryan/exception/FrameworkExceptionHandler.class</include>
                                <include>net/kyleryan/exception/UserException.class</include>
                                <include>net/gm/gssm/framework/ui/controller/MyController.class</include>
                                <include>net/gm/gssm/framework/ui/handler/MyHandler.class</include>
                            </includes>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
 
</project>

Referencing the client jar in another module looks like this.

Dependency Example

<dependency>
    <groupId>net.kyleryan</groupId>
    <artifactId>implementation</artifactId>
    <version>1.0-SNAPSHOT</version>
    <classifier>client</classifier>
</dependency>

JRebel and Maven Resource Filtering

Today, I was having an issue running an ear on a WLS application server. The build for the ear is a multi-module maven build. One of the modules contains a lot of properties files. Several of the property files have configuration information that changes based on environment. For example, a web service endpoint can change between environments.

Maven has is able to do property replacement as part of the packaging process.

# Web Service endpoint
hello_world_url=http://${ws.host}/HelloWorld?wsdl</pre> 

Maven pom.xml

<properties>
  <ws.host>helloworld.com</ws.host>
</properties>
 
<build>
  <resources>
    <resource>
      <directory>src/main/resources</directory>
      <filtering>true</filtering>
    </resource>
  </resources>
 
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-resources-plugin</artifactId>
      <version>2.7</version>
      <configuration>
        <encoding>UTF-8</encoding>
      </configuration>
    </plugin>
  </plugins>
</build>

The issue is that JRebel will default to using the properties in src/main/resources that contain the replacement string ${ws.host} which will not resolve to a valid url. This will result calls to the web service to fail.

One way to fix this is by excluding the properties files containing replacement strings from being looked at by JRebel. This can be done by editing the jrebel.xml. The syntax for defining the property files to be excluded follows ant style globbing.

Rebel.xml

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.zeroturnaround.com"
             xsi:schemaLocation="http://www.zeroturnaround.com http://www.zeroturnaround.com/alderaan/rebel-2_0.xsd">
 
    <classpath>
        <dir name="/configuration/src/main/resources">
              <exclude name="config.properties"/>
              <exclude name="**/report_labels*.properties"/>
        </dir>
    </classpath>
 
</application>

Change JBoss Debug Levels

Another option for changing JBoss logging levels without editing jboss-log4j.xml is to update the logging level using the JMX console. Changes made using the JMX console will not be persisted after restarting JBoss.

  • Log into JMX Console. Typically http://your.ip.address:8080
  • Navigate: jboss.system->service=Logging,type=Log4jService
  • getLoggerLevel net.kyleryan.ClassName will tell you the current logging level
  • setLoggerLevel net.kyleryan.ClassName DEBUG will change the logging level to DEBUG
  • Documentation on the Road

    My 3 year old daughter started a new school this year that has fairly rigid drop off times. This has forced me to set an alarm at night and become more regimented in our morning routines, to make sure that we get to school on time. A nice side effect of this is that I’m getting out of the house early enough to take the train or express bus to work. Both the bus and the train have wireless available. Unfortunately, because of spotty cell coverage on the routes and 50 people trying to use a pipe feels the size of a 56k modem, network access is not something that can be counted on. Continue reading

    Rules Engines

    At work we struggle with our feelings concerning our rules engine (Jess). The promise of rules engines is that the business can make rules and the Rete algorithm will determine the correct answer every time. However, the business has never shown an interest in learning how to program new rules using Jess’ lisp like language. Additionally, QA doesn’t approve of Jess’ deterministic Rete algorithm. The fact that one acceptable and correctly written new rule can change expected behaviors for all related rules and break regression tests is a nightmare for QA. ¬†As a result, I found Martin¬†Fowler’s observations about rules engines very interesting. Continue reading