Chapter 4. WildFly Swarm for Microservices
The last Java microservice framework we’ll look at is a relative newcomer to the scene yet leverages tried-and-trusted Java EE functionality found in the JBoss WildFly application server. WildFly Swarm is a complete teardown of the WildFly application server into bite-sized, reusable components called fractions that can be assembled and formed into a microservice application that leverages Java EE APIs. Assembling these fractions is as simple as including a dependency in your Java Maven (or Gradle) build file, and WildFly Swarm takes care of the rest.
Application servers and Java EE have been the workhorse of enterprise Java applications for more than 15 years. WildFly (formerly JBoss Application Server) emerged as an enterprise-capable, open source application server. Many enterprises heavily invested in the Java EE technology (whether open source or proprietary vendors) from how they hire software talent as well as overall training, tooling, and management. Java EE has always been very capable at helping developers build tiered applications by offering functionality like servlets/JSPs, transactions, component models, messaging, and persistence. Deployments of Java EE applications were packaged as EARs, which typically contained many WARs, JARs, and associated configuration. Once you had your Java archive file (EAR/WAR), you would need to find a server, verify it was configured the way you expect, and then install your archive. You could even take advantage of dynamic deployment and redeployment (although doing this in production is not recommended, it can be useful in development). This meant your archives could be fairly lean and only include the business code you needed. Unfortunately, this lead to bloated implementations of Java EE servers that had to account for any functionality that an application might need. It also led to over-optimization in terms of which dependencies to share (just put everything in the app server!) and which dependencies needed isolation because they would change at a different rate from other applications.
The application server provided a single point of surface area for managing, deploying, and configuring multiple applications within a single instance of the app server. Typically you’d cluster these for high availability by creating exact instances of the app server on different nodes. The problems start to arise when too many applications share a single deployment model, a single process, and a single JVM. The impedance arises when multiple teams who develop the applications running inside the app server have different types of applications, velocities of change, performance or SLA needs, and so on. Insofar as microservices architecture enables rapid change, innovation, and autonomy, Java EE application servers and managing a collection of applications as a single, all-in-one server don’t enable rapid change. Additionally, from the operations side of the house, it becomes very complex to accurately manage and monitor the services and applications running within a single application server. In theory a single JVM is easier to manage, since it’s just one thing, but the applications within the JVM are all independent deployments and should be treated as such. We can feel this pain when we try to treat the individual applications and services within a single process as “one thing,” which is why we have very expensive and complicated tooling to try and accomplish that introspection. One way teams get around some of these issues is by deploying a single application to an application server.
Even though the deployment and management of applications within a Java EE environment may not suit a microservices environment, the component models, APIs, and libraries that Java EE provides to application developers still provide a lot of value. We still want to be able to use persistence, transactions, security, dependency injection, etc., but we want an à la carte usage of those libraries where needed. So how do we leverage our knowledge of Java EE, the power it brings within the context of microservices? That’s where WildFly Swarm fits in.
WildFly Swarm evaluates your pom.xml (or Gradle file) and determines what Java EE dependencies your microservice actually uses (e.g., CDI, messaging, and servlet) and then builds an uber JAR (just like Spring Boot and Dropwizard) that includes the minimal Java EE APIs and implementations necessary to run your service. This process is known as “just enough application server,” which allows you to continue to use the Java EE APIs you know and love and to deploy them both in a microservices and traditional-application style. You can even just start using your existing WAR projects and WildFly Swarm can introspect them automatically and properly include the requisite Java EE APIs/fractions without having to explicitly specify them. This is a very powerful way to move your existing applications to a microservice-style deployment.
Getting Started
There are three ways to get started with WildFly Swarm. You can start out with a blank Java Maven or Gradle project and manually add dependencies and Maven plug-ins. Another option is to use the WildFly Swarm Generator web console to bootstrap your project (similar to Spring Initializr for Spring Boot). Lastly, you can use the JBoss Forge tool, which is a generic Java project creation and altering tool which makes it easy to add Java classes, dependencies, and entire classes of functionality (e.g., JPA and transactions) to a Java Maven project. We highly recommend JBoss Forge in general, and we will use it in the guide here. For completeness, we’ll also include the minimal plug-ins and dependencies you might need for a vanilla Java project. JBoss Forge also has plug-ins for the three most popular Java IDEs (Eclipse, Netbeans, or IntelliJ).
Vanilla Java Project
If you have an existing Java project or you create one from scratch using a Maven archetype or your favorite IDE, then you can add the following pieces to your pom.xml to get up and running with WildFly Swarm. First we want to be able to create uber JARs that know what pieces of the Java EE API functionality should be included. To do this, we’ll use the WildFly Swarm Maven plug-in. Let’s add the WildFly Swarm Maven plug-in to our pom.xml:
<plugins>
<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>
</plugins>
We also want to include the WildFly Swarm BOM (bill of materials) as a
dependency in our <dependencyManagement>
section to help sort out the
proper versions of all of the APIs and WildFly Swarm fractions that we
may depend on:
<dependencyManagement>
<dependencies>
<!-- JBoss distributes a complete set of Java EE 7 APIs
including a Bill of Materials (BOM). A BOM specifies
the versions of a "stack" (or a collection) of
artifacts. We use this here so that we always get
the correct versions of artifacts. -->
<dependency>
<groupId>
org.wildfly.swarm</groupId>
<artifactId>
bom</artifactId>
<version>
${version.wildfly.swarm}</version>
<type>
pom</type>
<scope>
import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Now you can add the fractions of the Java EE API you need (or leave the fractions out and let WildFly Swarm autodetect them; useful if migrating an existing WAR application)! Let’s take a look at some of the convenience that JBoss Forge brings.
Using JBoss Forge
JBoss Forge is a set of IDE plug-ins and CLI for quickly creating and working on Java projects. It has plug-ins for Netbeans, Eclipse, and IntelliJ to help you create Java projects, add CDI beans, add JPA entities, add and configure servlets, etc. Let’s look at a quick example. First verify you have JDK/Java 1.8 installed then install JBoss Forge.
Once you have Forge installed, you should be able to start up the CLI (all of these commands available in the IDE plug-in as well):
$
forge
Feel free to explore what commands are available by pressing Tab, which also gives auto-completion for any command. JBoss Forge is built on a modular, plug-in–based architecture which allows others to write plug-ins to take advantage of the built-in tooling for the CLI and your favorite IDE. Take a look at some of the addons contributed by the community, including AsciiDoctor, Twitter, Arquillian, and AssertJ. Let’s also install the WildFly Swarm addon for JBoss Forge:
[
temp]
$
addon-install\
--coordinate org.jboss.forge.addon:wildfly-swarm,1.0.0.Beta2 ***SUCCESS*** Addon org.jboss.forge.addon:wildfly-swarm, 1.0.0.Beta2 was installed successfully.
Let’s try a project-new
command to build a new Java EE project that
will be built and packaged with WildFly Swarm. Follow the interactive
command prompt with the following inputs:
[
swarm]
$
project-new ***INFO*** Required inputs not satisfied, interactive mode * Project name: hola-wildflyswarm ? Package[
org.hola.wildflyswarm]
: com.redhat.examples.wfswarm ? Version[
1.0.0-SNAPSHOT]
: 1.0 ? Final name: hola-wildflyswarm ? Project location[
/Users/ceposta/temp/swarm]
:[
0]
(
x)
war[
1]
(
)
jar[
2]
(
)
parent[
3]
(
)
forge-addon[
4]
(
)
resource-jar[
5]
(
)
ear[
6]
(
)
from-archetype[
7]
(
)
generic Press <ENTER> to confirm, or <CTRL>+C to cancel. * Projecttype
:[
0-7]
[
0]
(
x)
Maven Press <ENTER> to confirm, or <CTRL>+C to cancel. * Build system:[
0]
[
0]
(
)
JAVA_EE_7[
1]
(
)
JAVA_EE_6[
2]
(
)
NONE Press <ENTER> to confirm, or <CTRL>+C to cancel. ? Stack(
The technology stack to be used in project)
:[
0-2]
2 ***SUCCESS*** Project named'hola-wildflyswarm'
has been created.
So what we have right now is an empty Java project that doesn’t do too much. That’s OK, though; we’re just getting started. Let’s set it up for a JAX-RS application:
[
hola-wildflyswarm]
$
rest-setup --application-path=
/ ***SUCCESS*** JAX-RS has been installed.
Now, let’s add in the WildFly Swarm configurations like the Maven plug-in and the BOM dependency management section:
[
hola-wildflyswarm]
$
wildfly-swarm-setup --context-path=
/ ***SUCCESS*** Wildfly Swarm is nowset
up! Enjoy!
That’s it! Now let’s build and try to run our new WildFly Swarm microservice:
[
HelloResource.java]
$
cd
~~[
hola-wildflyswarm]
$
wildfly-swarm-run
You should see it successfully start, but it doesn’t do anything or expose any REST services. But what did JBoss Forge create for us here? If you look at the directory structure, you should see something similar:
./pom.xml ./src ./src/main ./src/main/java ./src/main/java/com/redhat/examples/wfswarm/rest /RestApplication.java ./src/main/resources ./src/main/webapp ./src/test ./src/test/java ./src/test/resources
Pretty bare bones! If we look at the pom.xml, we see some relevant Java EE APIs and the WildFly Swarm plug-in/BOM:
<dependencyManagement> <dependencies> <dependency> <groupId>org.wildfly.swarm</groupId> <artifactId>bom</artifactId> <version>${
version
.wildfly-swarm
}
</version> <type
>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.redhat.spec</groupId> <artifactId>jboss-javaee-6.0</artifactId> <version>3.0.3.Final</version> <type
>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>com.redhat.spec.javax.servlet</groupId> <artifactId>jboss-servlet-api_3.0_spec</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>com.redhat.spec.javax.ws.rs</groupId> <artifactId>jboss-jaxrs-api_1.1_spec</artifactId> <scope>provided</scope> </dependency> </dependencies> <build> <finalName>hola-wildflyswarm</finalName> <plugins> <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> <configuration> <properties> <swarm.context.path>/</swarm.context.path> </properties> </configuration> </plugin> </plugins> </build>
Remember, however, WildFly Swarm will only package the pieces of the Java EE framework that you need to run your application. In this case, we’ve already set up JAX-RS APIs, so WildFly Swarm will automatically include the JAX-RS and servlet fractions of an application server and embed them in your application.
Let’s see how we can add some more functionality.
Hello World
Just like with the other frameworks in the preceding chapters, we want
to add some basic hello-world functionality and then incrementally add
more functionality on top of it. Let’s start by creating a HolaResource
in our project. You can do this with your IDE, or however
you’d like; but again we can leverage JBoss Forge to do any of the heavy
lifting for us here.
Navigate to the directory where you have your project, and fire up
forge
if it’s not already running:
$
forge
Add the HTTP Endpoints
Now let’s create a new JAX-RS endpoint with the rest-new-endpoint
command and the interactive wizard, filling in the prompts using the following example as guidance:
[
hola-wildflyswarm]
$
rest-new-endpoint ***INFO*** Required inputs not satisfied, interactive mode ? Package Name(
The package name wheretype
will be created)
\
[
com.redhat.examples.wfswarm.rest]
: * Type Name(
Thetype
name)
: HolaResource[
0]
(
x)
GET[
1]
(
)
POST[
2]
(
)
PUT[
3]
(
)
DELETE Press <ENTER> to confirm, or <CTRL>+C to cancel. ? Methods(
REST methods to be defined)
:[
0-3]
? Path(
The root path of the endpoint)
: api/hola ***SUCCESS*** REST com.redhat.examples.wfswarm.rest.HolaResource created
That’s it! Forge has created the ./src/main/java/com/redhat/examples/wfswarm/rest/HolaResource.java JAX-RS resource for us, and it looks similar to this:
package
com
.
redhat
.
examples
.
wfswarm
.
rest
;
import
javax.ws.rs.Path
;
import
javax.ws.rs.core.Response
;
import
javax.ws.rs.GET
;
import
javax.ws.rs.Produces
;
@Path
(
"/api/hola"
)
public
class
HolaResource
{
@GET
@Produces
(
"text/plain"
)
public
Response
doGet
()
{
return
Response
.
ok
(
"method doGet invoked"
)
.
build
();
}
}
Let’s go to the root of the project, build it, and try to fire it up again:
[
HelloResource.java]
$
cd
~~[
hola-wildflyswarm]
$
wildfly-swarm-run
And navigate in a web browser to http://localhost:8080/api/hola (if an endpoint is not correctly displayed at this endpoint, please go back and check the preceding steps):
What did we just do? We built a JAX-RS web application using native Java EE with the JBoss Forge tooling and then ran it as a microservice inside WildFly Swarm!
Externalize Configuration
At the time of this writing, WildFly Swarm does not have an opinionated way of doing configuration and folks can choose to use well-established configuration, frameworks like Apache Commons Configuration or Apache DeltaSpike Configuration. Feel free to pay attention to this JIRA thread for more. In this section, we’ll take a look at quickly adding Apache DeltaSpike Configuration for our configuration needs.
Apache DeltaSpike is a collection of CDI extensions that can be used to
simplify things like configuration, data access, and security.
Take a look at the DeltaSpike documentation. We’re going to use a CDI extension that
lets us easily inject configuration properties that can be sourced from
properties files, the command line, JNDI, and environment variables. To
take advantage of CDI, JAX-RS, and DeltaSpike, let’s add a dependency on
the jaxrs-cdi
WildFly Swarm Fraction for integrating CDI and JAX-RS:
<dependency>
<groupId>
org.wildfly.swarm</groupId>
<artifactId>
jaxrs-cdi</artifactId>
</dependency>
We’ll also want to add a dependency on the DeltaSpike libraries:
<dependency>
<groupId>
org.apache.deltaspike.core</groupId>
<artifactId>
deltaspike-core-api</artifactId>
<version>
1.5.3</version>
</dependency>
<dependency>
<groupId>
org.apache.deltaspike.core</groupId>
<artifactId>
deltaspike-core-impl</artifactId>
<version>
1.5.3</version>
</dependency>
We can create a new file called META-INF/apache-deltaspike.properties
as well to store our application-specific properties. In this example,
we’ll try to grab our environment-specific properties from OS
environment variables like we’ve been doing with the other frameworks
and then default to values that may not exist. Edit your HolaResource
class to add the @ConfigProperty
annotation:
@Path
(
"/api/hola"
)
public
class
HolaResource
{
@Inject
@ConfigProperty
(
name
=
"WF_SWARM_SAYING"
,
defaultValue
=
"Hola"
)
private
String
saying
;
@GET
@Produces
(
"text/plain"
)
public
Response
doGet
()
{
return
Response
.
ok
(
saying
+
" from WF Swarm"
).
build
();
}
}
With this simple annotation, we’re able to quickly inject our properties from either the META-INF/apache-deltaspike.properties file, from the command line, from environment variables, or from JNDI. We will default to “Hola” if there is no environmental variable set. Take a look at the Apache DeltaSpike documentation for more ways to group or customize the functionality.
Now we can run our service with either java -jar target/hola-wildflyswarm-swarm.jar
or with
mvn clean install wildfly-swarm:run
. We should see the default response
“Hola from WF Swarm” and if we set the environment variable
WF_SWARM_SAYING
, then we should be able to alter the saying:
$
mvn clean install wildfly-swarm:run
$
export
WF_SWARM_SAYING
=
Yo$
mvn clean install wildfly-swarm:run
Expose Application Metrics and Information
To expose useful information about our microserivce, all we need to do
is add the monitor
fraction to our pom.xml:
<dependency>
<groupId>
org.wildfly.swarm</groupId>
<artifactId>
monitor</artifactId>
</dependency>
This will enable the WildFly management and monitoring functionality. From the monitoring perspective, WildFly Swarm exposes some basic metrics:
-
Information about the node on which WildFly Swarm is running at /node
-
JVM heap usage at /heap
-
JVM/process thread information at /threads
We can also add our own health endpoints that can invoke some actions or query parts of our microservice to easily expose how our service is doing. You can leverage the built-in health checking probes of most modern clusters to call your microservice health endpoints to determine whether or not the microservice is healthy, and in some cases, just kill it and restart. See the WildFly Swarm documentation for more on adding health probes.
How to Run This Outside of Maven
We’ve seen a couple ways to run our WildFly Swarm microservice. For development, you’ll probably run with the Maven plug-in like this:
$
mvn clean install wildfly-swarm:run
When you build the binary, you can run it like this:
$
mvn clean package
This will take our project, whether it’s packaged as a JAR or a WAR (as
specified by the <packaging>
definition in your pom.xml) and turn it
into an executable uber JAR. Then you can run it like this:
$
java -jar target/hola-wildfly-swarm.jar
Note, whatever your final build-artifact is named, the WildFly Swarm Maven plug-in will add the -swarm.jar extension to the name.
Calling Another Service
In a microservice environment, each service is responsible for providing the functionality or service to other collaborators. If we wish to extend the “hello world” microservice, we will need to create a service to which we can call using JAX-RS client functionality. Just like we did for the Spring Boot microservice, we’ll leverage the backend service from the source code that accompanies the book. The interaction will look similar to this:
If you look in this book’s source code, we’ll see a Maven module called backend
which contains a very simple HTTP servlet that can be invoked with a GET request and query parameters. The code for this backend is very simple, and does not use any of the microservice frameworks (Spring Boot, Dropwizard, or WildFly Swarm).
To start up the backend service on port 8080
, navigate to the
backend directory and run the following:
$
mvn clean install jetty:run
This service is exposed at /api/backend and takes a query parameter
greeting.
For example, when we call this service with this path
/api/backend?greeting=Hello, then the backend service will respond with
a JSON object like this:
$
curl -X GET http://localhost:8080/api/backend?greeting=
Hello
We get something like this:
{
"greeting"
:
"Hello from cluster Backend"
,
"time"
:
1459189860895
,
"ip"
:
"172.20.10.3"
}
We will create a new HTTP endpoint, /api/greeting in our WildFly Swarm
hola-wildflyswarm
example and use JAX-RS client to call this backend!
Create a new class in src/main/java/com/redhat/examples/wfswarm/rest
called GreeterResource
, and fill it in similar to what we did for the
HolaResource
like in Example 4-1.
Example 4-1. src/main/java/com/redhat/examples/wfswarm/rest/GreeterResource.java
@Path
(
"/api"
)
public
class
GreeterResource
{
@Inject
@ConfigProperty
(
name
=
"WF_SWARM_SAYING"
,
defaultValue
=
"Hola"
)
private
String
saying
;
@Inject
@ConfigProperty
(
name
=
"GREETING_BACKEND_SERVICE_HOST"
,
defaultValue
=
"localhost"
)
private
String
backendServiceHost
;
@Inject
@ConfigProperty
(
name
=
"GREETING_BACKEND_SERVICE_PORT"
,
defaultValue
=
"8080"
)
private
int
backendServicePort
;
@Path
(
"/greeting"
)
@GET
public
String
greeting
()
{
String
backendServiceUrl
=
String
.
format
(
"http://%s:%d"
,
backendServiceHost
,
backendServicePort
);
System
.
out
.
println
(
"Sending to: "
+
backendServiceUrl
);
return
backendServiceUrl
;
}
}
We’ve created a simple JAX-RS resource here that exposes an
/api/greeting endpoint that just returns the value of the
backendServiceUrl
field. Also note, we’re injecting the backend host
and port as environment variables that have default values if no
environment variables are set. Again, we’re just using DeltaSpike
@ConfigProperty
to accomplish this.
Let’s also add the BackendDTO
class as shown in Example 4-2, which is used to encapsulate
responses from the backend.
Example 4-2. src/main/java/com/redhat/examples/wfswarm/rest/BackendDTO.java
public
class
BackendDTO
{
private
String
greeting
;
private
long
time
;
private
String
ip
;
public
String
getGreeting
()
{
return
greeting
;
}
public
void
setGreeting
(
String
greeting
)
{
this
.
greeting
=
greeting
;
}
public
long
getTime
()
{
return
time
;
}
public
void
setTime
(
long
time
)
{
this
.
time
=
time
;
}
public
String
getIp
()
{
return
ip
;
}
public
void
setIp
(
String
ip
)
{
this
.
ip
=
ip
;
}
}
Next, let’s add our JAX-RS client implementation to communicate with the backend service. It should look like Example 4-3.
Example 4-3. src/main/java/com/redhat/examples/wfswarm/rest/GreeterResource.java
@Path
(
"/api"
)
public
class
GreeterResource
{
@Inject
@ConfigProperty
(
name
=
"WF_SWARM_SAYING"
,
defaultValue
=
"Hola"
)
private
String
saying
;
@Inject
@ConfigProperty
(
name
=
"GREETING_BACKEND_SERVICE_HOST"
,
defaultValue
=
"localhost"
)
private
String
backendServiceHost
;
@Inject
@ConfigProperty
(
name
=
"GREETING_BACKEND_SERVICE_PORT"
,
defaultValue
=
"8080"
)
private
int
backendServicePort
;
@Path
(
"/greeting"
)
@GET
public
String
greeting
()
{
String
backendServiceUrl
=
String
.
format
(
"http://%s:%d"
,
backendServiceHost
,
backendServicePort
);
System
.
out
.
println
(
"Sending to: "
+
backendServiceUrl
);
Client
client
=
ClientBuilder
.
newClient
();
BackendDTO
backendDTO
=
client
.
target
(
backendServiceUrl
)
.
path
(
"api"
)
.
path
(
"backend"
)
.
queryParam
(
"greeting"
,
saying
)
.
request
(
MediaType
.
APPLICATION_JSON_TYPE
)
.
get
(
BackendDTO
.
class
);
return
backendDTO
.
getGreeting
()
+
" at host: "
+
backendDTO
.
getIp
();
}
}
Now we can build our microservice either using Maven at the command line; or if you’re still in JBoss Forge, you can run the build
command:
$
mvn clean install
or:
[
hola-wildflyswarm]
$
build
When we start up our WildFly Swarm microservice, we will need to specify
a new HTTP port (since the backend service is already running on port
8080
), or we can just specify a port offset. If we specify a port
offset, WildFly Swarm will try to deploy under its default port of
8080
; but if that port is already in use, then it will increment the
port by the swarm.port.offset
amount and try again. If we use an
offset of 1, and there is a collision on port 8080
, then port 8081
will be what WildFly Swarm tries next. Let’s run our microservice with a
port offset:
$
mvn clean install wildfly-swarm:run -Dswarm.port.offset=
1
Now, let’s navigate our browser to http://localhost:8081/api/greeting to see if our microservice properly calls the backend and displays what we’re expecting:
Where to Look Next
In this chapter, we learned about WildFly Swarm and saw some differences and similarities with Dropwizard and Spring Boot. We also learned how to expose REST endpoints, configuration, and metrics and make calls to external services. This was meant as a quick introduction to WildFly Swarm and is by no means a comprehensive guide. Check out the following links for more information:
Get Microservices for Java Developers now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.