Fraction Authoring

Introduction

The composable pieces of WildFly Swarm are called fractions. Each fraction starts with a single Maven-addressable artifact which ultimately brings in others.

We refer to the various pieces as

  • parent: The top-most pom.xml to aggregate the build of the lower portions.

  • root: The user-referenced piece that provides BOM-like capabilities in order to make all the functionality available.

  • api: Transitively brought in from the root, and provides whatever user-facing API is required for configuring the fraction.

  • runtime: The back-end portion of the fraction (if required) that provides runtime-support.

  • modules: An artifact containing a tree of module.xml files used by JBoss Modules in order to support our modular classloading.

Each of the above is typically laid out as a multi-project Maven tree.

For a hypothetical Jolokia fraction:

jolokia/
  pom.xml                    (1)
  exposed-components.json    (2)
  jolokia/
    pom.xml                  (3)
  api/
    pom.xml                  (4)
    src/...
  runtime/
    pom.xml                  (5)
    src/...
  modules/
    pom.xml                  (6)
    src/...
  1. The -parent aggregator POM.

  2. Defines the components to be included in WildFly Swarm BOM.

  3. The simply-named root artifact which can also act as a BOM.

  4. The -api artifact which exposes any user-facing APIs. along with transitive dependencies of relevant specifications. It includes the *Fraction.java class.

  5. The -runtime artifact which provides from back-end configuration of the WildFly Swarm server based upon the fraction.

  6. The -modules artifact to bring in a JBoss-Modules module tree of modules.

The Parent

At the top of the tree, the pom.xml is the -parent artifact simply to arrange Maven <dependencyManagement>, configure plugins and aggregate the builds of the lower projects.

It is useful to set at least a pair of properties, specifying the version of the WildFly Swarm SPI and fraction-plugin being used:

<properties>
  <version.swarm.spi>1.0.0.Beta3</version.swarm.spi>
  <version.swarm.fraction-plugin>1.0.0.Beta2</version.swarm.fraction-plugin>
</properties>

The SPI should be mixed into the <dependencyManagement> section, imported as a BOM. Additionally, the components of the fraction itself can be managed in order to make cross-dependency specification in the lower-level pom.xml files easier:

<dependencyManagement>
  <dependencies>
    <dependency>                                (1)
      <groupId>org.wildfly.swarm</groupId>
      <artifactId>spi</artifactId>
      <version>${version.swarm.spi}</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>

    <dependency>                                (2)
      <groupId>org.wildfly.swarm</groupId>
      <artifactId>jolokia</artifactId>
      <version>1.0.0.Beta3-SNAPSHOT</version>
    </dependency>
    <dependency>
      <groupId>org.wildfly.swarm</groupId>
      <artifactId>jolokia-api</artifactId>
      <version>1.0.0.Beta3-SNAPSHOT</version>
    </dependency>
    <dependency>
      <groupId>org.wildfly.swarm</groupId>
      <artifactId>jolokia-modules</artifactId>
      <version>1.0.0.Beta3-SNAPSHOT</version>
    </dependency>
    <dependency>
      <groupId>org.wildfly.swarm</groupId>
      <artifactId>jolokia-runtime</artifactId>
      <version>1.0.0.Beta3-SNAPSHOT</version>
    </dependency>
  </dependencies>
</dependencyManagement>
  1. Import the SPI BOM

  2. Manage all other components of this fraction

Additionally, the wildfly-swarm-fraction-plugin should be configured within the parent pom.xml so that it fires for every sub-module:

<build>
  <plugins>
    <plugin>
      <groupId>org.wildfly.swarm</groupId>
      <artifactId>wildfly-swarm-fraction-plugin</artifactId>
      <version>${version.swarm.fraction-plugin}</version>
      <executions>
        <execution>
          <phase>process-classes</phase>
          <goals>
            <goal>process</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

Making a fraction available in the WildFly Swarm BOM requires a exposed-components.json to be defined:

{"Jolokia": [                                                     (1)
  {"name": "jolokia",              "doc": "jolokia-api"},         (2)
  {"name": "jolokia-modules"},                                    (3)
]}
  1. Define the name to be used within generated javadocs

  2. Define name as the Root module and doc as the API module

  3. Define the name for the Modules module

To ensure this JSON is available to the BOM generation process, we need to add the following into pom.xml as well:

<profile>
  <id>attach-exposed-components</id>
  <activation>
    <file>
      <exists>exposed-components.json</exists>
    </file>
  </activation>
  <build>
    <plugins>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>build-helper-maven-plugin</artifactId>
        <executions>
          <execution>
            <id>add-exposed-components</id>
            <phase>package</phase>
            <goals>
              <goal>attach-artifact</goal>
            </goals>
            <configuration>
              <artifacts>
                <artifact>
                  <file>exposed-components.json</file>
                  <type>json</type>
                  <classifier>exposed-components</classifier>
                </artifact>
              </artifacts>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</profile>

The Root

In our above example, jolokia/jolokia/pom.xml lives by itself, with no related source, but ties together the remaining bits, and is the artifact referenced by a user’s application. This is the root artifact.

The project, while it has <packaging>jar</packaging>, does not include any source. Instead it provides transitive dependencies to the various other bits of the fraction, along with transitive dependencies to any other entire fractions required by this one.

Jolokia, for instance, is deployed as a WAR file, and thus this fraction requires the undertow fraction also.

For Jolokia to be available from within the Project Generator we need to make sure that some properties within pom.xml are set appropriately. The <name> and <description> will be displayed to a user, and <swarm.fraction.tags> defines which categories within the generator will show Jolokia as an option. If the fraction is not intended to be added as a direct dependency, such as a low level fraction which is included in others, then <swarm.fraction.internal> can be set to true to ensure it is not visible.

<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>org.wildfly.swarm</groupId>
    <artifactId>jolokia-parent</artifactId>
    <version>1.0.0.Beta3-SNAPSHOT</version>
    <relativePath>../</relativePath>
  </parent>

  <groupId>org.wildfly.swarm</groupId>
  <artifactId>jolokia</artifactId>                                       (1)

  <name>Jolokia</name>                                                   (2)
  <description>Deploys the jolokia.war to activate JMX-HTTP bridge
  as an alternative to JSR-160 connectors</description>                  (3)

  <packaging>jar</packaging>

  <properties>
    <swarm.fraction.tags>Management</swarm.fraction.tags>                (4)
  </properties>

  <dependencies>
    <dependency>                                                         (5)
      <groupId>org.wildfly.swarm</groupId>
      <artifactId>jolokia-api</artifactId>
    </dependency>
    <dependency>                                                         (6)
      <groupId>org.wildfly.swarm</groupId>
      <artifactId>jolokia-modules</artifactId>
    </dependency>
    <dependency>                                                         (7)
      <groupId>org.wildfly.swarm</groupId>
      <artifactId>jolokia-runtime</artifactId>
      <scope>provided</scope>
    </dependency>

    <dependency>                                                         (8)
      <groupId>org.wildfly.swarm</groupId>
      <artifactId>undertow</artifactId>
    </dependency>

  </dependencies>

</project>
  1. The artifactId is the simple, user-referencable name of the fraction.

  2. Name of the fraction to be displayed in the Project Generator.

  3. Description of the fraction to be displayed in the Project Generator.

  4. The Categories that the fraction will be displayed under in the Project Generator.

  5. The -api artifact is implicitly <scope>compile</scope>

  6. The -modules artifact is implicitly <scope>compile</scope>

  7. The -runtime artifact is explicitly <scope>provided</scope>

  8. Any other required fractions are referenced by their simple root artifactId with an implicit <scope>compile</scope>.

Please note, the versions of each dependency are managed by the -parent POM described in the previous section.

API

The -api artifact represents any user-facing configuration API, including the any relevant *Fraction.java class.

The *Fraction.java

If the fraction includes configuration capabilities, or otherwise alters the runtime system through deployments or adjustments to the server, it should include an implement of org.wildfly.swarm.spi.api.Fraction.

Any opaque POJO configuration details that are required may be added the implementation, and will be passed to the back-end runtime portion during server boot-up to control configuration.

In the event that no particular configuration values are required, a basic no-op *Fraction.java may be created.

package com.mycorp.cheese;

import java.util.Set;
import java.util.HashSet;
import org.wildfly.swarm.spi.api.Fraction;

public class CheeseFraction implements Fraction {

  // arbitrary configuration parameters are allowed

  public void cheese(String type) {
    this.cheeses.add( type );
  }

  public void cheeses(Set<String> types) {
    this.cheeses.addAll( types );
  }

  public Set<String> cheeses() {
    return this.cheeses;
  }

  private Set<String> cheeses = new HashSet<>();
}

Exposing Transitive Dependencies to Users' Applications

If your fraction aims to enable some API or specification beyond the fraction itself (such as enabling usage of javax.jms for instance), the -api pom.xml should include such dependency as <scope>compile</scope>. In the root of the pom.xml, on the <project> element, you need to add an xmlns for the WildFly Swarm XML namespace, and also mark the relevant <dependency> as below:

<project xmlns:swarm="http://wildfly-swarm.io/">
  ...
  <dependencies>

    ...

    <dependency swarm:scope="provided">
      <groupId>org.jboss.spec.javax.jms</groupId>
      <artifactId>jboss-jms-api_2.0_spec</artifactId>
    </dependency>
  </dependencies>
  ...
</project>

This allows for a user to reference only your fraction, and implicitly receive the JMS specification classes on their classpath.

By marking it with swarm:scope, it enables the WildFly Swarm plugin to know that the specification artifact is actually provided at runtime by the server, and shouldn’t be considered a application dependency.

Runtime

The -runtime artifact is responsible for taking the above Fraction implementation and effecting changes to the runtime server based upon its existance and configuration values.

For every *Fraction.java, there should exist a matching *Configuration.java. For the simplest cases, the *Configuration.java can actually be synthesized based upon Java annotations on the *Fraction.java itself. If that suffices, the entire -runtime artifact may be avoided.

Using only annotations within -api

Usually only useful for fractions that are based upon the WildFly config-api and represent aspects of a usual standalone.xml

The org.wildfly.swarm.spi.api.annotations.Configuration annotation may be applied directly to a Fraction implementation, with the following properties optionally set:

Name Description

extension

The WildFly module holding the extension. Maps directly to the <extension> element in standalone.xml

marshal

Boolean indicating if the Fraction should be marshalled using the config-api marshaller

ignorable

Boolean indicating if the fraction may be entirely ignored. I don’t remember why we would want to. But it’s there.

parserFactoryClassName

Name of the class (as a string) of the ParserFactory from the -runtime portion of the fraction to allow for parsing of standalone.xml fragments for this fraction.

Modules

The -modules artifact is required to handle the variety of classloading aspects of each fraction. There are typically 3 modules in the tree related to the -api and -runtime portions of the fraction. General convention requires that the base module name is exactly the same as the package portion of the *Fraction implementation.

For instance, if you have com.mycorp.cheese.CheeseFraction, then the base module name should be com.mycorp.cheese.

The :main slot

Will follow the format of:

<module xmlns="urn:jboss:module:1.3" name="com.mycorp.cheese">
  <dependencies>
    <!-- For when run with bonafide IDE classpath -->
    <system export="true">
      <paths>
        <path name="com/mycorp/cheese"/>                                             (1)
        <path name="com/mycorp/cheese/sub"/>
      </paths>
    </system>

    <!-- For when bootstrapped through a fat-jar -->
    <module name="com.mycorp.cheese" slot="api" export="true" services="export"/>    (2)
  </dependencies>
</module>
  1. List all package paths within the API project, including sub paths.

  2. Add the :api slot as a module dependency, ensuring to export classes and services.

The :api slot

Defines the artifact from the API project as a resource and declares any module dependencies that are required:

<module xmlns="urn:jboss:module:1.3" name="com.mycorp.cheese" slot="api">
  <resources>
    <artifact name="com.mycorp.cheese:cheese-api:${project.version}"/>     (1)
  </resources>

  <dependencies>
    <module name="org.wildfly.swarm.container"/>                           (2)
    <module name="org.jboss.shrinkwrap"/>                                  (3)
  </dependencies>
</module>
  1. The Maven artifact created from the API project.

  2. Provides classes for Fraction and the Container.

  3. Any additional module dependencies from WildFly or WildFly Swarm

The :runtime slot

Defines the artifact from the Runtime project as a resource and declares any module dependencies that are required:

<module xmlns="urn:jboss:module:1.3" name="com.mycorp.cheese" slot="runtime">
  <resources>
    <artifact name="com.mycorp.cheese:cheese-runtime:${project.version}"/>      (1)
  </resources>

  <dependencies>
    <module name="com.mycorp.cheese"/>                                          (2)
    <module name="org.wildfly.swarm.container"/>                                (3)
    <module name="org.wildfly.swarm.container" slot="runtime"/>
    <module name="org.jboss.shrinkwrap"/>                                       (4)
  </dependencies>
</module>
  1. The Maven artifact created from the Runtime project.

  2. :main slot module of the fraction

  3. container modules for :main and :runtime are needed.

  4. Any additional module dependencies from WildFly or WildFly Swarm

Availability in WildFly Swarm BOM

For the fraction to be available through the BOM, pom.xml in https://github.com/wildfly-swarm/wildfly-swarm needs to be updated to include it. This is done by adding another <module> entry to the wildfly-swarm-fraction-plugin such as:

<plugin>
  <groupId>org.wildfly.swarm</groupId>
  <artifactId>wildfly-swarm-fraction-plugin</artifactId>
  <version>${version.swarm.fraction-plugin}</version>
  <configuration>
    <modules>
...
      <module>jolokia-parent:${version.swarm.jolokia}</module>
...
    </modules>
  </configuration>
</plugin>
</plugins>