WildFly Swarm - Basic Setup

Original post was in 2016, migrating to new blog

So I came across WildFly probably a month or two ago, bookmarked as something to come back to. While reading up more on it recently I stumbled upon WildFly Swarm, which seems to be like Spring Boot, Dropwizard and other uberjar/fatjar micro-service deployment solutions.

Definitely cool stuff! I love the idea of containers and just dropping down a self-encapsulated build of an API and being able to quickly rollback if needed. Front with nginx and what could be simpler?

I thought I would share some learnings and maven config stuff as a "get up and running in 10 minutes" sort of blog post.

Maven POM

First up, create your pom and add some properties.

<properties>  
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <maven.min.version>3.2.1</maven.min.version>
    <maven.compiler.target>1.8</maven.compiler.target>
    <maven.compiler.source>1.8</maven.compiler.source>
    <version.wildfly-swarm>1.0.0.Alpha3</version.wildfly-swarm>
    <version.lombok>1.16.4</version.lombok>
</properties>  

Second, we need to add our dependencies block which includes the WildFly Swarm deps for JAX-RS and CDI (via Weld). I've also included Project Lombok because it's amazing, and joda-time was on some other article I came across.

<dependencies>  
    <!-- JAX-RS -->
    <dependency>
        <groupId>org.wildfly.swarm</groupId>
        <artifactId>wildfly-swarm-jaxrs</artifactId>
        <version>${version.wildfly-swarm}</version>
    </dependency>

    <!-- JAX-RS/CDI -->
    <dependency>
        <groupId>org.wildfly.swarm</groupId>
        <artifactId>wildfly-swarm-weld-jaxrs</artifactId>
        <version>${version.wildfly-swarm}</version>
    </dependency>

    <!-- Project Lombok, it's awesome -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>${version.lombok}</version>
    </dependency>

    <!-- EJB API -->
    <dependency>
        <groupId>org.jboss.spec.javax.ejb</groupId>
        <artifactId>jboss-ejb-api_3.2_spec</artifactId>
        <version>1.0.0.Final</version>
        <scope>provided</scope>
    </dependency>

    <!-- CDI API -->
    <dependency>
        <groupId>javax.enterprise</groupId>
        <artifactId>cdi-api</artifactId>
        <!--<version>1.2</version>-->
        <version>2.0-EDR1</version>
        <scope>provided</scope>
    </dependency>

    <!-- Common Annotations API (JSR-250) -->
    <dependency>
        <groupId>org.jboss.spec.javax.annotation</groupId>
        <artifactId>jboss-annotations-api_1.2_spec</artifactId>
        <version>1.0.0.Final</version>
        <scope>provided</scope>
    </dependency>

    <dependency>
        <groupId>joda-time</groupId>
        <artifactId>joda-time</artifactId>
        <version>2.8.2</version>
    </dependency>
</dependencies>  

Third, add our build section. A change from Alpha1 is that there is no longer an execution of "create", in case you read older articles/tutorials.

<build>  
    <finalName>${project.artifactId}</finalName>

    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <version>2.6</version>
            <configuration>
                <failOnMissingWebXml>false</failOnMissingWebXml>
            </configuration>
        </plugin>

        <plugin>
            <groupId>org.wildfly.swarm</groupId>
            <artifactId>wildfly-swarm-plugin</artifactId>
            <version>${version.wildfly-swarm}</version>
            <executions>
                <execution>
                    <goals>
                        <goal>package</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.3</version>
            <configuration>
                <source>${maven.compiler.source}</source>
                <target>${maven.compiler.target}</target>
            </configuration>
        </plugin>
    </plugins>
</build>  

That concludes the pom configuration and at this point you should be able to do a mvn clean package and get a success.

Beans, beans, beans

You'll need to create a src/main/webapp/WEB-INF directory and then create a beans.xml file.

<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://java.sun.com/xml/ns/javaee"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
    http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/beans_1_1.xsd">
</beans>  

JAX-RS Application

To active JAX-RS, you will need to create a class which extends Application and is annotated with @ApplicationPath.

package com.example.application;

import javax.ws.rs.ApplicationPath;

@ApplicationPath("/")
public class Application extends javax.ws.rs.core.Application {  
  // let the container scan for @Path resources
}

API Model

At this point we will create our WebAPI model. This blog post is designed using a 2-layer approach for simplicity, showing a controller, model and a service (controller and service share the same model).

I was introduced to the Project Lombok library from the guys at Tomitribe. It is a fantastic little library which generates no-args constructor, all-args consturctor, getters (accessors), setters (mutators), hashcode and equals, internal builders and a few other treats. It helped reduce so much boilerplate code, I was blown away so I use it everywhere I can.

package com.example.webapi.models;

import java.io.Serializable;

import javax.xml.bind.annotation.XmlAccessOrder;  
import javax.xml.bind.annotation.XmlAccessType;  
import javax.xml.bind.annotation.XmlAccessorOrder;  
import javax.xml.bind.annotation.XmlAccessorType;  
import javax.xml.bind.annotation.XmlRootElement;

import lombok.AllArgsConstructor;  
import lombok.Data;  
import lombok.NoArgsConstructor;

@XmlRootElement(name = "test")
@XmlAccessorType(XmlAccessType.PROPERTY)
@XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Test implements Serializable {  
  private int id;
  private String name;
}

In case you are wondering, lombok will create methods for getId(), setId(), getName() and setName().

JAX-RS Resource

There are multiple semantics/names for this class, you might see it as Resource, Service or Controller (I use Controller).

This is the class that is annotated with the JAX-RS annotations and handles the API requests. Just some fair warning, there is no error checking/handling here. So if you pass a 2 or greater as the ID this will croak.

package com.example.webapi.controllers;

import com.example.webapi.models.Test;  
import com.example.webapi.providers.TestProvider;

import java.util.List;

import javax.ejb.Lock;  
import javax.ejb.Singleton;  
import javax.inject.Inject;  
import javax.ws.rs.Consumes;  
import javax.ws.rs.DefaultValue;  
import javax.ws.rs.GET;  
import javax.ws.rs.Path;  
import javax.ws.rs.PathParam;  
import javax.ws.rs.Produces;  
import javax.ws.rs.QueryParam;  
import javax.ws.rs.core.Response;

import lombok.extern.java.Log;

import static javax.ejb.LockType.READ;  
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;  
import static javax.ws.rs.core.MediaType.APPLICATION_XML;

@Singleton
@Lock(READ)
@Path("tests")
@Consumes({APPLICATION_JSON, APPLICATION_XML})
@Produces({APPLICATION_JSON, APPLICATION_XML})
@Log
public class TestController {  
    @Inject
    private TestProvider testProvider;

    @GET
    @Path("{id: \\d+}")
    public Response getCollection(
        @PathParam("id")
        final int id
    ) {
        log.info("id:" + id);
        final List<Test> models = testProvider.findAll();
        return Response.ok(models.get(id)).build();
    }
}

Test Provider (Service Pattern)

The last class needed for this very basic example is the TestProvider, which is following the Service Pattern, which would provide all of your business logic.

package com.example.webapi.providers;

import com.example.webapi.models.Test;

import java.util.ArrayList;  
import java.util.List;

import javax.ejb.Singleton;  
import javax.ejb.Lock;  
import static javax.ejb.LockType.READ;

@Singleton
@Lock(READ)
public class TestProvider {  
    public List<Test> findAll() {
        return new ArrayList<Test>() {{
            add(new Test(1, "emp01"));
            add(new Test(2, "emp02"));
        }};
    }
}

Making It Go

You should now be able to fire off maven and run this thing. Execution can happen two ways, via the maven wildfly-swarm:run target, or packaging then running via java -jar.

mvn clean package wildfly-swarm:run  

Or build then run

mvn clean package  
java -jar target/yourpackage-swarm.jar  

Once this loads you can try to hit your endpoint by going to http://localhost:8080/tests/0 and http://localhost:8080/tests/1.

This concludes a very basic WildFly Swarm tutorial.