© 2022 The original authors.
1. Overview
This is the Vertx extension for WildFly Application Server.
Eclipse Vert.x is a toolkit to build reactive applications on the JVM. Integrating it adds more reactive power to WildFly.
It allows you to define a Vert.x instance using WildFly’s management model. The Vert.x instance is managed by the WildFly server. It can be accessed using JNDI lookup or CDI injection in your enterprise or web applications.
You can access the Vert.x core APIs and some component APIs in your applications.
You can package the Vert.x verticle implementation in the application, and deploy it to a WildFly server with a deployment descriptor. The server will do the deployment accordingly.
In case of clustering Vert.x instance, this extension uses vertx-infinispan as the cluster manager to be able to talk with remote Vert.x instances. You can specify the necessary JGroups settings for Vert.x, either using the standard WildFly management model, or specifying a JGroups stack file.
The generated distribution of this extension is a full WildFly server with above capabilities. The build produces only standalone configurations. Domain mode is not supported.
It also produces a Galleon feature pack, which can be installed to an existing WildFly server. You can install either the full feature pack, or layers of the feature pack.
The main goal of this extension is to explore a way for JakartaEE developers to benefit from some reactive utilities.
2. Build the extension
This extension uses Maven
as the build tool.
Requirements to build the extension are:
-
JDK 11+
-
Maven 3.2.5+
To build the extension, simply run the following command:
mvn clean install
When the command finishes, it will have produced two server distributions and a Galleon feature pack:
-
Thin server
-
Located at
build/target/wildfly-vertx-build-{version}/
. -
The thin server contains all configurations and JBoss module files (
module.xml
) which define the Maven artifact, without the jar files.
-
-
Full server
-
Located at
dist/target/wildfly-vertx-dist-{version}/
. -
The full server contains everything including jar files.
-
-
Galleon feature pack
-
Located at
galleon-feature-pack/wildfly-feature-pack/target/wildfly-vertx-feature-pack-{version}.zip
. -
This feature pack can be installed in an existing WildFly server.
-
Both produced servers have 2
standalone configurations:
-
standalone.xml
-
This is the setup with the Vert.x subsystem added to the default
standalone.xml
in WildFly server.
-
-
standalone-ha.xml
-
This is the setup with the Vert.x subsystem added to the default
standalone-ha.xml
in WildFly server. -
The default Vert.x in this setup is a clustered Vert.x with default configuration.
-
2.1. Start the server
You can start the server like you do for normal WildFly server by specifying the desired configuration file:
$SERVER_HOME/bin/standalone.sh -c standalone-ha.xml
3. How to install to an existing WildFly server
If you have an existing WildFly server already, you have 2 options to have this extension installed, one is to install the whole feature pack, the other is to install part of the feature pack.
3.1. Install the whole feature pack
# This line installs a WildFly server to local_server directory:
galleon.sh install --dir=local_server "wildfly-ee@maven(org.jboss.universe:community-universe):current#${version.wildfly}"
# This line installs the wildfly-vertx-feature-pack on top of local WildFly server:
galleon.sh install --dir=local_server org.wildfly.extension.vertx:wildfly-vertx-feature-pack:1.0.0-SNAPSHOT
after it completes, the vertx
subsystem and the required bits are downloaded and installed to the existing server.
3.2. Install the layers of the feature pack
The feature pack has 2 Galleon layers produced:
-
vertx
-
vertx-ha
You can specify one of them to install to an existing server, similar as above, run:
# This line installs a WildFly server to local_server directory:
galleon.sh install --dir=local_server "wildfly-ee@maven(org.jboss.universe:community-universe):current#${version.wildfly}"
# This line installs the vertx layer only from wildfly-vertx-feature-pack.
galleon.sh install --dir=local_server org.wildfly.extension.vertx:wildfly-vertx-feature-pack:1.0.0-SNAPSHOT --layers=vertx
this will update the default configuration standalone.xml
and it will download necessary bits.
in case of clustered Vertx, you need layer of vertx-ha
, and you need to specify the ha configuration to update:
# This line installs a WildFly server to local_server directory:
galleon.sh install --dir=local_server "wildfly-ee@maven(org.jboss.universe:community-universe):current#${version.wildfly}"
# This line installs the vertx-ha layer only from wildfly-vertx-feature-pack with config to standalone-ha.xml.
galleon.sh install --dir=local_server org.wildfly.extension.vertx:wildfly-vertx-feature-pack:1.0.0-SNAPSHOT --layers=vertx-ha --config=standalone-ha.xml
To have galleon.sh
script work, please check the Galleon Provisioning Guide on how to download and install the Gallon CLI tool.
4. The vertx subsystem configuration
There is a Vert.x instance created in each standalone configuration by default.
After server starts, in a JBoss CLI console, you can see the Vert.x resource like:
[standalone@localhost:9990 /] /subsystem=vertx/service=vertx:read-resource()
{
"outcome" => "success",
"result" => {
"clustered" => false,
"forked-channel" => false,
"jgroups-channel" => undefined,
"jgroups-stack-file" => undefined,
"option-name" => undefined
}
}
The Vert.x instance has the following attributes:
-
clustered
- Flag that if it is a clustered Vert.x instance.-
Defaults to
false
-
When it is set to
true
, eitherjgroups-channel
orjgroups-stack-file
should be specified, otherwise, the default InfinispanClusterManager will be used.
-
-
forked-channel
- Flag if the forked channel needs to be used-
It is used only when
clustered
istrue
andjgroups-channel
is specified. -
Technically, when it is set to
true
, theJGroupsRequirement.CHANNEL_FACTORY
from WildFly clustering JGroups SPI is used for the JChannel creation, while it is set tofalse
(default), theJGroupsRequirement.CHANNEL_SOURCE
is used for the JChannel creation.
-
-
jgroups-channel
- The JGroups Channel which is used for JChannel creation-
It is used only when
clustered
istrue
. -
It comes from resources under
/subsystem=jgroups/channel=XX
.
-
-
jgroups-stack-file
- The JGroups stack file used to compose the JGroups cluster-
It is used only when
clustered
istrue
. -
It is either an absolute file starts with
/
or a file relatives to${jboss.server.config.dir}
-
-
option-name
- The option name used to create the Vert.x instance-
The default
VertxOptions
instance is used to create the Vert.x instance if this is not set. -
It comes from resources either in
/subsystem=vertx/vertx-option=XX
or in/subsystem=vertx/vertx-option-file=XX
.
-
when both jgroups-channel
andjgroups-stack-file
are specified, there will be an exception thrown out.
4.1. Create an option for Vert.x
You have 2 options to create a VertxOptions
instance, one is to create the option by specifying a JSON file path, the other is to create the option by specifying each attribute using WildFly management model.
Create VertxOptions using a JSON file
Using the following command in JBoss CLI to create a VertxOptions by specifying a JSON file path:
[standalone@localhost:9990 /] /subsystem=vertx/vertx-option-file=vof:add(path=default-vertx-options.json)
{"outcome" => "success"}
Here the path of default-vertx-options.json
is expected at ${jboss.server.config.dir}
. After it completes, you can refer to vof
in the command:
[standalone@localhost:9990 /] /subsystem=vertx/service=vertx:write-attribute(name=option-name,value=vof)
{
"outcome" => "success",
"response-headers" => {
"operation-requires-reload" => true,
"process-state" => "reload-required"
}
}
You need to run :reload
after updating any attribute of the Vert.x instance.
The format of the JSON file follows what VertxOptions(JsonObject json) requires.
Create an option with attributes
You can create a VertxOptions
by specifying each attribute as well.
Let’s take a look at what an option resource normally has:
[standalone@localhost:9990 /] /subsystem=vertx/vertx-option=vo:read-resource()
{
"outcome" => "success",
"result" => {
"address-resolver-option" => "aro",
"blocked-thread-check-interval" => undefined,
"blocked-thread-check-interval-unit" => undefined,
"classpath-resolving-enabled" => undefined,
"disable-tccl" => undefined,
"event-loop-pool-size" => undefined,
"eventbus-option" => "ebo",
"file-cache-enabled" => undefined,
"ha-enabled" => undefined,
"ha-group" => undefined,
"internal-blocking-pool-size" => undefined,
"max-eventloop-execute-time" => undefined,
"max-eventloop-execute-time-unit" => undefined,
"max-worker-execute-time" => undefined,
"max-worker-execute-time-unit" => undefined,
"prefer-native-transport" => undefined,
"quorum-size" => undefined,
"warning-exception-time" => undefined,
"warning-exception-time-unit" => undefined,
"worker-pool-size" => undefined
}
}
All attributes except for address-resolver-option
and eventbus-option
are primitive types.
-
The
address-resolver-option
refers to what/subsystem=vertx/address-resolver-option=aro
defines:
[standalone@localhost:9990 /] /subsystem=vertx/address-resolver-option=aro:read-resource()
{
"outcome" => "success",
"result" => {
"cache-max-time-to-live" => undefined,
"cache-min-time-to-live" => undefined,
"cache-negative-time-to-live" => undefined,
"hosts-path" => undefined,
"hosts-value" => undefined,
"max-queries" => 50,
"n-dots" => undefined,
"opt-resource-enabled" => undefined,
"query-time-out" => undefined,
"rd-flag" => undefined,
"rotate-servers" => undefined,
"round-robin-inet-address" => undefined,
"search-domains" => undefined,
"servers" => undefined
}
}
-
The
eventbus-option
refers to what/subsystem=vertx/eventbus-option=ebo
defines:
[standalone@localhost:9990 /] /subsystem=vertx/eventbus-option=ebo:read-resource()
{
"outcome" => "success",
"result" => {
"accept-backlog" => undefined,
"client-auth" => undefined,
"cluster-node-metadata" => undefined,
"cluster-ping-interval" => undefined,
"cluster-ping-reply-interval" => undefined,
"cluster-public-host" => undefined,
"cluster-public-port" => undefined,
"connect-timeout" => undefined,
"crl-paths" => undefined,
"crl-values" => undefined,
"enabled-cipher-suites" => undefined,
"enabled-secure-transport-protocols" => undefined,
"host" => undefined,
"idle-timeout" => undefined,
"idle-timeout-unit" => undefined,
"key-cert-option" => undefined,
"log-activity" => undefined,
"openssl-session-cache-enabled" => undefined,
"port" => undefined,
"read-idle-timeout" => undefined,
"receive-buffer-size" => undefined,
"reconnect-attempts" => undefined,
"reconnect-interval" => undefined,
"reuse-address" => undefined,
"reuse-port" => undefined,
"send-buffer-size" => undefined,
"so-linger" => 200,
"ssl" => undefined,
"ssl-engine-type" => undefined,
"ssl-hand-shake-timeout" => undefined,
"ssl-hand-shake-timeout-unit" => undefined,
"tcp-cork" => undefined,
"tcp-fast-open" => undefined,
"tcp-keep-alive" => undefined,
"tcp-no-delay" => undefined,
"tcp-quick-ack" => undefined,
"traffic-class" => undefined,
"trust-all" => undefined,
"trust-option" => undefined,
"use-alpn" => undefined,
"write-idle-timeout" => undefined
}
}
There are sub options used to create the EventBusOptions
, includingkey-store-option
,pem-key-cert-option
,pem-trust-option
,cluster-node-metadata
, please use the corresponding:read-resource-description()
operation for each attribute description.
Any update to the options won’t require reload unless the option is referenced by the Vertx instance.
4.2. Clustered Vert.x instance
You need to specify clustered=true
to for a clustered Vert.x instance, and you need to start the server with vertx-ha
available, basically with standalone-vertx*-ha.xml
configurations.
You have 2 options to set up the clustering configuration, one is to specify the JGroups stack file, the other is to specify the jgroups-channel
from jgroups
subsystem to compose the JGroups cluster.
Using a JGroups stack file
You can update the clustered Vert.x instance by specifying the JGroups stack file with the following command:
[standalone@localhost:9990 /] /subsystem=vertx/service=vertx:write-attribute(name=jgroups-stack-file,value=jgroups-stack.xml)
{
"outcome" => "success",
"response-headers" => {
"operation-requires-reload" => true,
"process-state" => "reload-required"
}
}
The above command suppose there is a jgroups-stack.xml
file existing at ${jboss.server.config.dir}
.
The content inside the JGroups stack file should be consistent with the remote Vert.x instance to be able to compose a cluster. Please refer to http://www.jgroups.org/manual4/ on the detail configuration of the stack.
Using a channel from jgroups subsystem
Please use the following commands to create a JGroups channel for the clustered Vert.x instance, which matches what default Vert.x Infinispan cluster ships:
batch
/socket-binding-group=standard-sockets/socket-binding=jgroups-vertx:add(port=7800)
/socket-binding-group=standard-sockets/socket-binding=jgroups-vertx-mping:add(interface=private, multicast-port=46655, multicast-address=228.6.7.8
/socket-binding-group=standard-sockets/socket-binding=jgroups-vertx-tcp-fd:add(interface=private, port=57800)
/subsystem=jgroups/stack=tcp-vertx:add()
/subsystem=jgroups/stack=tcp-vertx/transport=TCP:add(socket-binding=jgroups-vertx)
/subsystem=jgroups/stack=tcp-vertx/protocol=MPING:add(socket-binding=jgroups-vertx-mping)
/subsystem=jgroups/stack=tcp-vertx/protocol=MERGE3:add()
/subsystem=jgroups/stack=tcp-vertx/protocol=FD_SOCK:add(socket-binding=jgroups-vertx-tcp-fd)
/subsystem=jgroups/stack=tcp-vertx/protocol=FD_ALL:add()
/subsystem=jgroups/stack=tcp-vertx/protocol=VERIFY_SUSPECT:add()
/subsystem=jgroups/stack=tcp-vertx/protocol=pbcast.NAKACK2:add()
/subsystem=jgroups/stack=tcp-vertx/protocol=UNICAST3:add()
/subsystem=jgroups/stack=tcp-vertx/protocol=pbcast.STABLE:add()
/subsystem=jgroups/stack=tcp-vertx/protocol=pbcast.GMS:add()
/subsystem=jgroups/stack=tcp-vertx/protocol=MFC:add()
/subsystem=jgroups/stack=tcp-vertx/protocol=FRAG3:add()
/subsystem=jgroups/channel=vertx:add(stack=tcp-vertx, cluster=ISPN)
run-batch
:reload
Now update the Vert.x instance to use the JGroups channel created above:
[standalone@localhost:9990 /] /subsystem=vertx/service=vertx:write-attribute(name=jgroups-channel,value=vertx)
{
"outcome" => "success",
"response-headers" => {
"operation-requires-reload" => true,
"process-state" => "reload-required"
}
}
[standalone@localhost:9990 /] :reload
{
"outcome" => "success",
"result" => undefined
}
Now you have updated the clustered Vert.x instance with JGroups channel set up from jgroups
subsystem.
Remember to specify -Djgroups.bind.address=127.0.0.1
in your another Vert.x instance to be able to compose the cluster for local test.
5. Vertx used in application
You can use the Vert.x instance in your applications.
Vert.x instance is the key to all Vert.x applications, each function and capability comes from the Vert.x instance, so having the Vert.x instance injected in the application codes is important.
This extension provides 2 ways to access the Vert.x instance.
5.1. Access the Vert.x instance using JNDI lookup
you can access the Vert.x using the @Resource(lookup = "java:/jboss/vertx/default")
annotation like:
@WebServlet(value = "/async", asyncSupported = true)
public class AsyncServlet extends HttpServlet {
@Resource(name = "java:/jboss/vertx/default")
private Vertx vertx;
@Override
public void init() throws ServletException {
vertx.eventBus()
.<String>consumer("echo")
.handler(msg -> msg.reply(msg.body()));
}
}
5.2. Access the Vert.x instance using CDI
The other way to use the Vert.x instance in the application is to use @Inject Vertx vertx
annotation. When the CDI is activated(via a beans.xml
file bundled in the deployment archive), the Vert.x instance will be injected.
@Stateless
public class EchoService {
@Inject
private Vertx vertx;
@Asynchronous
public Future<String> echo(String message) {
return (CompletableFuture<String>)vertx.eventBus()
.request("echo", message)
.map(msg -> msg.body().toString()).toCompletionStage();
}
}
6. Deploy verticles to WildFly server
With this extension integrated, you can deploy the Vert.x verticle implementation to the WildFly server by specifying a deployment descriptor.
The availability of the deployment descriptor makes it a vertx deployment
, which means that users can access Vert.x instance in their applications.
A typical war deployment structure which contains a verticle implementation is:
test-verticle.war:
/WEB-INF/
/WEB-INF/vertx.json
/WEB-INF/classes/
/WEB-INF/classes/org/
/WEB-INF/classes/org/wildfly/
/WEB-INF/classes/org/wildfly/extension/
/WEB-INF/classes/org/wildfly/extension/vertx/
/WEB-INF/classes/org/wildfly/extension/vertx/test/
/WEB-INF/classes/org/wildfly/extension/vertx/test/mini/
/WEB-INF/classes/org/wildfly/extension/vertx/test/mini/deployment/
/WEB-INF/classes/org/wildfly/extension/vertx/test/mini/deployment/TestVerticle.class
The content of the deployment descriptor(WEB-INF/vertx.json
) is:
{
"deployments": [
{
"verticle-class": "org.wildfly.extension.vertx.test.mini.deployment.TestVerticle"
}
]
}
when the war is deployed, the TestVerticle
will be deployed to the Vert.x instance using the default DeploymentOptions
.
These deployed verticles inside the Vert.x instance will be un-deployed when the deployment gets un-deployed from WildFly server.
6.1. Deployment Descriptor
The deployment descriptor to be able to deploy the verticle is either at META-INF/vertx.json
or WEB-INF/vertx.json
.
A full example of vertx.json
is:
{
"ver": 1,
"deployments": [
{
"verticle-class": "org.wildfly.extension.vertx.test.mini.deployment.TestVerticle",
"deploy-options": {}
}
]
}
-
ver
- The version of the deployment descriptor, it is1
, and it can be skipped now. -
deployments
- An array of the deployments, each of which is a json structure.-
verticle-class
- The FQCN of the verticle class which will be deployed. -
deploy-options
- TheDeploymentOptions
which is used to do the deployment.-
The format of the content matches what new DeploymentOptions(JsonObject) constructor requires.
-
Default
DeploymentOptions
is used if not specified.
-
-