Ship Your WildFly Additions via Galleon Feature Packs

Galleon is a tool for provisioning Java runtimes. It comes with plugins for provisioning WildFly server instances. We have been using it internally in WildFly to build and configure the server the past few releases, and we recently introduced it into our OpenShift cloud image to be able to create a server with a smaller footprint than the default.

This post will give an overview of how to use Galleon to provide your additions to WildFly so that users can easily install them. The intended audience is someone who is familiar with writing WildFly subsystems, and how our JBoss Modules classloading system works. We will see how this is a great way for:

  • Third-party vendors to ship their products which are intended to run on top of WildFly.

  • Shipping experimental features that we don’t want in the main WildFly code base yet.

Up until recently there was not really any standard way to do this. You would need one of the following options:

  • Have your code included in the main WildFly code base. This is not really a realistic option for a lot of people since what we put into there is mainly driven by where we, the WildFly community, want WildFly to go next.

  • Provide your subsystem via other means, for example a script to copy your additions into an existing WildFly installation and add them to the configuration.

Template Galleon feature pack repository

This blog post references a repository which extends WildFly with a generic subsystem whose only job is to make a CDI @Produces method available to deployments. The repository also contains the needed bits to have an Arquillian test suite running outside of the WildFly code base, and of course to create the Galleon feature pack. The subsystem code is basic, as the intent is for you to be able to use this repository as a template for creating your own Galleon feature packs. This is not meant to be an in-depth guide, more a high level overview to get you started.

The template repository README goes into more depth in some areas. Also, as people provide feedback, its explanation of how things work can evolve independently of the static snapshot of information contained in this blog post. The blog-post-snapshot tag is from when this blog post was written.

Initial run

If you are impatient, you can simply run:

$mvn install
$./build/target/wildfly-<WildFly Version>-template-<Template Version>/bin/standalone.sh

The server in the build/target directory is a trimmed down version of ‘official’ WildFly with our subsystem added.

Then in another terminal window:

$mvn package wildfly:deploy -pl example

Then go to http://localhost:8080/example/greeting and you will see a greeting message. This message comes from the CDI @Produces method made available by the template’s subsystem, and the language used for the message changes as you refresh the page.

We will look at various ways of customising your WildFly server with our feature pack later in this post.

Core Galleon concepts

The Galleon documentation discusses the concepts in depth but we will summarise some of the core concepts which we have used for this template, mainly to make sense of what we have done.

Package - A package is file system content that should be included in the built server. In our example this means JBoss Modules for our subsystem and its dependency, and also licenses. A package may have a task to run when it is installed.

Features and Feature groups - we use a feature to add configuration for our subsystem to the WildFly configuration. Features can be added to groups. Features and feature groups may (among other things) have dependencies on other features and feature groups, and may include dependencies on packages.

Layers - Layers split up the server into fine-grained bits, so that we can pick and choose exactly the parts we want. For example you could ask for a WildFly server just implementing JAX-RS (and its dependencies) and not implementing the management interfaces. The resulting server will be a lot smaller than the one you download from https://www.wildfly.org/downloads/. Layers can have dependencies on other layers, e.g. when asking for JAX-RS it will also install the web server.

There is a list of all our layers defined by WildFly and WildFly Core in our documentation. If you want to understand better what their dependencies are, you can look at the layer-spec.xml for the various layers that make up WildFly in the following locations

The above links take you to the versions used for WildFly 18.0.1.Final; adjust them to see what is available in other server versions. Once you have read this article and browsed the template repository you should be able to work out what all those files contain.

The template subsystem Galleon feature pack

This section will explain our feature pack. Let’s take a quick look at its packages, feature groups, layers, feature pack configuration, and the build.

Packages

We have some packages which contain the JBoss Modules for our subsystem and its dependency. These can be found under the galleon-pack/src/main/resources/modules/system/layers/base/ directory. The subsystem code is in the subsystem/ folder of the project, while the example subsystem dependency (which contains the CDI @Produces method mentioned) code is in the dependency/ folder of the project.

In addition we have some packages to deal with installing licenses for your added libraries. We won’t go into details of that here. See the the template README for more information.

Feature Groups

We define a single feature group in galleon-pack/src/main/resources/feature_groups/template-subsystem.xml. The group contains the <feature spec="subsystem.template-subsystem"/> for our subsystem. The value of 'spec' used for these feature specifications is of the form

subsystem.<subsystem name in the model>

and it essentially configures the subsystem. In this case <subsystem name in the model> is 'template-subsystem', and this configuration results in a

/subsystem=template-subsystem:add()

when provisioning the server. Our subsystem is empty, if it had some attributes that need setting they would also be defined in the feature spec.

Layers

galleon-pack/src/main/resources/layers/standalone/template-layer/layer-spec.xml contains our single layer called ‘template-layer’. It adds dependencies on the following other constructs:

  • A dependency on the cdi layer. This is needed so that when we install our layer, which uses CDI, Galleon will automatically pull in the CDI dependencies as well if they are not already there.

  • The feature group we already discussed. When this layer is installed, our subsystem gets configured.

  • Any additional packages (i.e. content) needed. In this case there are none, since we add the dependency on the subsystem’s extension module in wildfly-feature-pack-build.xml as we see in the next section. The layer-spec.xml and template README contain some more information about this.

Feature pack configuration

galleon-pack/wildfly-feature-pack-build.xml is used to provision new features (the template README explains how to install additions which have no configuration). It adds our extension module org.wildfly.extension.template-subsystem to the configuration. Galleon is smart enough to inspect this module’s dependencies and automatically add our org.wildfly.template-dependency module.

The file also sets up the feature packs we depend upon in order to provide the underlying server.

Build

Finally we have the feature pack build in galleon-pack/pom.xml. It contains the wildfly-galleon-maven-plugin which creates the Galleon feature pack. Note that it uses the build-feature-pack goal which is needed to add a new subsystem along with the mentioned entry in wildfly-feature-pack-build.xml.

When building the galleon-pack/ module you can see what the feature pack contains in the galleon-pack/target/layout/org.wildfly.extras.galleon-feature-pack-template/template-galleon-pack/<version>/ directory to help you figure out what is missing if your feature pack does not work the way you expected.

Provisioning a server

There are a few ways we can provision a server to include our feature pack. They are via a Maven plugin, or via Galleon CLI. Galleon CLI has a few different ways to provision a server.

Maven plugin

Using the wildfly-galleon-maven-plugin Maven plugin isn’t really for end users, but it is very handy to be able to use it from within our project. For example, build/pom.xml provisions a server that we can use to verify that our feature pack works. Earlier in this post, we already played with the server in the build/target directory and deployed our example into that. Also, in our testsuite, testsuite/integration/subsystem/pom.xml provisions a server to run the Arquillian tests against.

In both cases we use the provision goal of the galleon-maven-plugin to provision a server. It lists the feature packs that our feature pack depends on (note that they are 'transitive') as well as the layers to install into our server. However, the layers used are slightly different in the two cases since we are doing two different things.

They both need our template-layer of course.

The build/ version is used to run our example from the example/ directory. As this uses a REST endpoint, we need the jaxrs layer to provide this functionality. Also, the wildfly:deploy goal we used to deploy the application uses the management interfaces, so it also needs the management layer.

The testsuite version, on the other hand, does not use REST for the test, so we just use the plain web-server layer (the jaxrs layer used in build/ depends on this web-server layer). Also, since Arquillian uses JMX to do the interactions with the server, we also need the jmx-remoting layer.

Galleon CLI

Galleon CLI is explained in more detail in the Galleon documentation. We will look at a few ideas for how to provision a WildFly server containing our plugin. There are probably other ways too, Galleon is very powerful! The two main ways to do this are to install the various parts manually, or to provision it from an xml file. Doing it manually is powerful, but there can be a lot of commands to remember. If you use an xml file to provision it, everything is contained in that file and you can run it again and again to provision servers using a simpler to remember command.

To use the Galleon CLI you need to download it. The following examples assume that you have added the bin/ folder of the distribution to your PATH (This is where the galleon.sh command comes from in the following examples).

We will look at how to provision the server via the two main mechanisms, then the Trying the provisioned server section below tells you how to run your application to check that our example subsystem got installed into the server and works as expected.

Manual Galleon CLI installation

To install additional feature packs via Galleon you generally follow the following two steps:

  • Install the main server - here we can for example choose the version of the main server, and also trim it down if we don’t want the full server.

  • Install the feature pack. Note that this part is only possible if the main server was provisioned via Galleon. It is currently not possible to install feature packs into e.g. the unzipped zip from the wildfly.org/downloads page.

Install main server

To install the main server we can do:

$galleon.sh install wildfly:current --dir=wildfly

This installs all of (i.e. the same as the downloaded zip) the latest WildFly version (at the time of writing 18.0.1.Final) into the directory specified by the --dir argument. In this case I am using a relative child directory called wildfly/.

If you want to install a particular version of WildFly you can append the version to wildfly:current. E.g.:

  • wildfly:current#18.0.0.Final - installs WildFly 18.0.0.Final.

  • wildfly:current#19.0.0.Beta1-SNAPSHOT - installs WildFly from locally built SNAPSHOT maven artifacts.

If you want to trim the base server that we install (similar to what we did in the testsuite and the example server build), you can specify which layers to install by passing in the --layers option. To install the same server as we had in the build/target/ directory that we used to run the example above, you can run:

$galleon.sh install wildfly:current --dir=wildfly --layers=jaxrs,management

Note that we did not install our template-layer because this is unknown in the main WildFly feature pack. We will add it in the next step.

Install feature pack

Now to install our layer, we run the following command:

$galleon.sh install org.wildfly.extras.galleon-feature-pack-template:template-galleon-pack:1.0.0.Alpha-SNAPSHOT --layers=template-layer --dir=wildfly

We specify the same directory that we installed the server into in the last step by specifying the same value for --dir.

org.wildfly.extras.galleon-feature-pack-template:template-galleon-pack:1.0.0.Alpha-SNAPSHOT is the Maven GAV of the Galleon feature pack (i.e. what we have in wildfly-galleon-pack-template/blob/master/galleon-pack/pom.xml.

If you went with the trimmed server in the previous step, and you look at wildfly/standalone/configuration/standalone.xml, you should see that both the template-subsystem and the weld subsystems were added in this second step. Weld is our CDI implementation. As we have seen the 'cdi' layer is a dependency of our layer, so Galleon pulls it in too!

See the Trying the provisioned server section below for how to start your server and give it a quick sanity check by deploying and running the example.

Galleon CLI provision from xml file

An alternative to having to type all the CLI commands we saw every time you want to provision a server is to use an XML file as input to the Galleon CLI. There is an example in provision.xml. This file contains all the information needed to provision our server, and as you can see, it lists the feature pack(s) we depend on, and the feature pack implemented in the template repository.

For each of those we specify the Maven GAV, as in the previous section. We can set what to include from each feature pack (Refer to the Galleon documentation for more in-depth explanation of what each setting does). Finally, we say that we want the cloud-profile and template-layer layers. cloud-profile is just to give you another example base server, we could have used the same layers as in the previous section.

To provision the server, you now simply run the following command:

$galleon.sh provision /path/to/provision.xml --dir=wildfly

Now you can start the server and run the example as outlined in the Trying the provisioned server section.

Trying the provisioned server

The example/ folder contains the sample application that you can deploy into the server. The steps to do this are the same whether you are using the example server from the build/ directory that we saw in the start of this post, or any of the Galleon CLI mechanisms.

The example itself is very simple, consisting of a single REST endpoint which is injected with a bean made available by the CDI @Produces method from the template subsystem dependency.

To run it, first start the server by running (the value of JBOSS_HOME is the root of the built server, which depends on which flavour of the provisioned server you are trying):

$"${JBOSS_HOME}"/bin/standalone.sh

Then to deploy the application, in another terminal window in the checkout folder of the template project, run:

$mvn package wildfly:deploy -pl example/

Then go to http://localhost:8080/example/greeting, which will output the message created by the CDI producer. As you refresh, the message will change.

Adapting the template for your use

To provision your own subsystem you can copy this template, but of course you should try to give your subsystem, modules, Galleon constructs, and Java package and class names sensible names for your WildFly addition. There are quite a few things that need changing, and I have tried to add some hints in the template source code in the form of TODO comments.

This template has not been officially released to Maven. Since it doesn’t do anything useful apart from provide a framework for you to copy, it is still using a SNAPSHOT version. For your purposes you should create real tags and releases, and deploy to e.g. Maven Central. Then you can advertise your feature pack to the world, and it will be easy for people to provision WildFly servers containing your super-cool feature!

Finally, if something is not clear in the template repository, please either open a pull request or an issue.