CLOVER🍀

That was when it all began.

WildFly Bootable JARを作る時に指定するGalleon layer、Galleon feature-packというものを少し見てみたい

これは、なにをしたくて書いたもの?

WildFly Bootable JARを見ていると、Galleonというものが出てきます。

ここで選択できるGalleon layer、Galleon feature-packというものを、もう少し見ておきたいなと思いまして。

指定できるGalleon layerにどんなものが含まれているのか、Galleon feature-packで指定できるものは?というのと、WildFly Bootable JARで
どのようなものが指定できるのか?というあたりが知りたいと思って見てみた動機です。

結論

まず、WildFly Bootable JARで指定できるWildFly Galleon layerは、以下に書かれている「Foundational Galleon layers」および
「Basic Galleon Layers」の中から選択します。

WildFly bootable JAR application development / Galleon configuration / WildFly Layers

WildFly Galleon feature-packは、WildFly extrasにある以下の3つですね。

WildFlyのorganizationには、WildFly Bootable JARで指定するようなfeature-packはなさそうです。

Galleon

Galleonというのは、ひとつ以上の製品(またはコンポーネント)で構成されたソフトウェアディストリビューションを作成および
メンテナンスするために設計されたプロビジョニングツールです。

Galleon is a provisioning tool designed to create and maintain software distributions that consist of one or more products (or components).

GitHub - wildfly/galleon

Galleon Documentation

Galleonツールを使用して、インストールまたはアンインストールできるソフトウェア(プロジェクト、製品、コンポーネントなど)の
単位を「feature-pack」と呼びます。feature-packはzipアーカイブで、feature-packの作成者によってMavenリポジトリなどにデプロイされ、
Galleonツールを介してユーザーが利用します。

Feature-pack represents a released unit of software (project, product, component, etc) that can be installed or uninstalled using Galleon tools. Feature-packs are ZIP archives that are normally deployed by the feature-pack producers to artifact repositories (such as Maven) where they can be discovered and consumed by users using Galleon tools.

Galleon Documentation / Feature-packs

Galleon feature-packの仕組みを使うと、WildFlyを自分で拡張することもできるようです。

自分でfeature-packを作成する場合は、こちらのドキュメントを参照。

WildFly Galleon Plugins Documentation

Galleonについては、以下のブログエントリーを見るのも良さそうです。

Ship Your WildFly Additions via Galleon Feature Packs

Galleonには、以下のコアとなる概念があります。

  • package
    • ビルドされたサーバーに含める必要があるファイルシステムコンテンツ
    • サブシステムや依存するJBossモジュール、ライセンスなどが含まれる
  • feature、feature-group
    • WildFlyの構成にサブシステム構成を追加する
    • feature-groupには、featureやfeature-groupを追加できる
    • 他のfeatureやpackageへの依存関係を持っていることもある
  • layer
    • サーバーを細かい部品に分解したもので、利用者が必要なものを選択できる

そして、feature-packはpackage、feature、feature-group、layerをまとめたものです。

WildFlyのfeature-packは、以下の3つがあります。

Jakarta EE 9用、

WildFly extraもあります。

ここまで見ているとわかるかと思いますが、今回はひたすらWildFly Bootable JARと絡めて書き進めるものの、GalleonはWildFly Bootable JARの
ための機能、というわけではありません。
ただ、WildFly Bootable JARを作成する時の依存関係を取捨選択するところで登場することが多いのかな、という気もしますが、
どうなのでしょうね。

まあ、今回はWildFly Bootable JARで使う、という目線で書いていきます。

WildFly Galleon layers

WildFly Bootable JARを作成する時は、主にWildFly Galleon layersを指定します。

WildFly Bootable JAR Documentation

Bootable JAR Guide

Galleon layersについては、「Galleon Provisioning Guide」にも「Bootable JAR Guide」と同じことが書かれています。

Galleon Provisioning Guide

WildFly Bootable JARを作成する際には、特定のGalleon layerを選択することになります。選択しない場合は、standalone-microprofile.xml
同等の構成になるようです。

If you decide not to specify Galleon layers, a server containing all MicroProfile subsystems is provisioned. (The server configuration is identical to the standalone-microprofile.xml configuration in the traditional WildFly server zip.)

WildFly bootable JAR application development / Galleon configuration

ドキュメントを見ると、「使いたいWildFly Galleon feature-packを決めたら、サーバーの構成に使用するWildFly Galleon layerを選択する」
ということになっています。

Once you have identified a WildFly Galleon feature-pack, you need to select a set of WildFly Layers that are used to compose the server.

WildFly bootable JAR application development / Galleon configuration

layerの説明をもう少し見てみましょう。
layerとはサーバーの機能や機能の集合を指す名前のようです。拡張機能、サブシステム、インターフェース、モジュールなどが含まると
されています。

A Galleon layer is a name that identifies a server capability (e.g.: jaxrs, ejb, microprofile-config, jpa) or an aggregation of such capabilities. A layer captures a server capability in the form of:

  • A piece of server XML configuration (e.g.: extension, configured subsystem, interfaces) that describes the capability.
  • A set of modules and other filesystem content that implements the capability.

WildFly bootable JAR application development / Galleon configuration / WildFly Layers

WildFly Galleon layerには、「Foundational Galleon layers」と「Basic Galleon Layers」と呼ばれる2つのlayer定義があります。
どのようなlayerがあるのかも、リストアップされています。

layerには依存関係があり、あるlayerを指定すると依存しているlayerも引き込まれます。layerの除外もできます。

基本的なlayerが、「Basic Galleon Layers」としてドキュメントにまとめられています。一方で、よく使うものをまとめたlayerとして、
「Foundational Galleon layers」があります。

たとえば、Basic Galleon Layersに「jaxrs」というlayerがありますが、この依存layerとして「web-server」と
「microprofile-rest-client(オプション)」が含まれます。

一方でFoundational Galleon layersの「jaxrs-server」を選択すると、依存するlayerとして「bean-validation」、「cdi」、「datasources-web-server」、
「jaxrs」、「jpa」とそこそこ多めのlayerが含まれることになります。

まずはFoundational Galleon layersからlayerを選び、より細かく選びたい場合はBasic Galleon Layersから選択するという感じに
なるのでしょう。

WildFly Bootable JARを作成する際にWildFly Galleon layer、WildFly Galleon feature-packを指定するには

WildFly Bootable JARのドキュメントを見ると、

WildFly Bootable JAR Documentation

layerとfeature-packそれぞれ指定できることがわかります。

layerを指定するかfeature-packを指定するかで書き方が変わります。

layerのみを使う場合。

    <plugin>
      <groupId>org.wildfly.plugins</groupId>
      <artifactId>wildfly-jar-maven-plugin</artifactId>
      <version>...</version>
      <configuration>
        <feature-pack-location>wildfly@maven(org.jboss.universe:community-universe)</feature-pack-location>
        <layers>
          <layer>jaxrs-server</layer>
        </layers>
      </configuration>

      〜省略〜

    </plugin>

feature-packを使う場合。

    <plugin>
      <groupId>org.wildfly.plugins</groupId>
      <artifactId>wildfly-jar-maven-plugin</artifactId>
      <version>...</version>
      <configuration>
        <feature-packs>
          <feature-pack>
            <location>wildfly@maven(org.jboss.universe:community-universe)#26.1.0.0.Final</location>
          </feature-pack>
          <feature-pack>
            <groupId>org.wildfly</groupId>
            <artifactId>wildfly-datasources-galleon-pack</artifactId>
            <version>...</version>
          </feature-pack>
        </feature-packs>
      </configuration>

      〜省略〜

    </plugin>

feature-packを使う場合は、feature-pack-locationの表現方法が変わります。

両方使う場合。

    <plugin>
      <groupId>org.wildfly.plugins</groupId>
      <artifactId>wildfly-jar-maven-plugin</artifactId>
      <version>...</version>
      <configuration>
        <feature-packs>
          <feature-pack>
            <location>wildfly@maven(org.jboss.universe:community-universe)#26.1.0.0.Final</location>
          </feature-pack>
          <feature-pack>
            <groupId>org.wildfly</groupId>
            <artifactId>wildfly-datasources-galleon-pack</artifactId>
            <version>...</version>
          </feature-pack>
        </feature-packs>
        <layers>
          <layer>jaxrs-server</layer>
          <layer>mysql-datasource</layer>
          <layer>management</layer>
       </layers>
      </configuration>

      〜省略〜

    </plugin>

WildFly Galleon layer

ここまで見ていると、layerやfeature-packの種類はわかってきたものの、その定義内容についても見てみたくなりますね。
まず、layerからいきましょう。

layerは、こちらに定義があります。

たとえば、Foundational Galleon layersに書かれていた「jaxrs-server」layer。

<?xml version="1.0" ?>
<layer-spec xmlns="urn:jboss:galleon:layer-spec:1.0" name="jaxrs-server">
    <dependencies>
        <layer name="datasources-web-server"/>
        <!-- cloud-server depends on jaxrs-server, jaxrs could be excluded from cloud-server,
             this is why jaxrs is an optional dependency -->
        <layer name="jaxrs" optional="true"/>
        <layer name="bean-validation" optional="true"/>
        <layer name="cdi" optional="true"/>
        <layer name="jpa" optional="true"/>
    </dependencies>
</layer-spec>

https://github.com/wildfly/wildfly/blob/26.1.1.Final/ee-feature-pack/galleon-common/src/main/resources/layers/standalone/jaxrs-server/layer-spec.xml

他のlayerに依存していることがわかります。

続いて、Basic Galleon Layersに書かれていた「jaxrs」 layer。

<?xml version="1.0" ?>
<layer-spec xmlns="urn:jboss:galleon:layer-spec:1.0" name="jaxrs">
    <dependencies>
        <layer name="web-server"/>
    </dependencies>
    <feature-group name="jaxrs"/>
</layer-spec>

https://github.com/wildfly/wildfly/blob/26.1.1.Final/ee-feature-pack/galleon-common/src/main/resources/layers/standalone/jaxrs/layer-spec.xml

他のlayerに依存しているのと、「feature-group」も参照しています。

他も見てみましょう。「cdi」layerだと、featureも参照しています。

<?xml version="1.0" ?>
<layer-spec xmlns="urn:jboss:galleon:layer-spec:1.0" name="cdi">
    <dependencies>
        <layer name="base-server"/>
        <layer name="bean-validation" optional="true"/>
    </dependencies>
    <feature spec="subsystem.weld"/>
</layer-spec>

https://github.com/wildfly/wildfly/blob/26.1.1.Final/ee-feature-pack/galleon-common/src/main/resources/layers/standalone/cdi/layer-spec.xml

ざっくり、layerの定義を見ると依存しているものが具体的にわかりそうですね。

このあたりの定義内容は、Galleonのドキュメントを見るのが良さそうです。

Galleon Documentation

ちなみに、WildFly Bootable JARにlayerを指定すればよいという話までであれば、このブログエントリーはここまで読めばもういいのではと
思ったりします。

以下、延々と調べたことや補足が続きます。

Galleon feature-packの説明を読む

Galleon layerの構成内容は、feature-packの構成の説明を見るのがよさそうです。

Galleon Documentation / Generic configuration model

こちらを見ると、feature、feature-group、layerともにfeature-packの構成要素ということになっています。

最初の方にも書きましたが、feature-groupはfeatureを含めたり、別のfeature-groupを含めたりできるものです。

一方で、layerについては「layer自体、または他のlayerと組み合わせて最終的な構成を作成するために使用できる特定の構成フレーバーを
表すことを目的としている」そうです。

A layer is meant to represent a certain configuration flavor that can be used on its own or in combination with other layers to produce the final configuration.

両者、似たような印象を受けるのですが、feature-groupとlayerの違いはこちらに書かれています。

Galleon Documentation / Generic configuration model / Layers / Layers vs feature groups

feature-groupの定義

feature-groupは、このあたりに定義がありますね。

たとえば、「jaxrs」feature group。

<?xml version="1.0" encoding="UTF-8"?>
<feature-group-spec name="jaxrs" xmlns="urn:jboss:galleon:feature-group:1.0">
    <feature spec="subsystem.jaxrs"/>
</feature-group-spec>

https://github.com/wildfly/wildfly/blob/26.1.1.Final/ee-feature-pack/galleon-common/src/main/resources/feature_groups/jaxrs.xml

「undertow-servlet」feature-group。

<?xml version="1.0" encoding="UTF-8"?>
<feature-group-spec name="undertow-servlet" xmlns="urn:jboss:galleon:feature-group:1.0">
    <!-- ejb over http and servlet-container -->
    <feature spec="subsystem.undertow">
        <feature spec="subsystem.undertow.server">
            <param name="server" value="default-server" />
            <feature spec="subsystem.undertow.server.host">
                <param name="host" value="default-host" />
                <param name="alias" value="[localhost]"/>
                <feature spec="subsystem.undertow.server.host.setting.http-invoker"/> <!-- TODO Should this be here? -->
            </feature>
        </feature>
        <feature spec="subsystem.undertow.servlet-container">
            <param name="servlet-container" value="default" />
            <feature spec="subsystem.undertow.servlet-container.setting.jsp"/>
            <feature spec="subsystem.undertow.servlet-container.setting.websockets">
                <unset param="buffer-pool" />
                <unset param="worker" />
            </feature>
        </feature>
    </feature>
</feature-group-spec>

https://github.com/wildfly/wildfly/blob/26.1.1.Final/servlet-feature-pack/galleon-common/src/main/resources/feature_groups/undertow-servlet.xml

サブシステムへの参照を含んでいそうなことはわかりますね。

WildFlyのstandalone.xmlなどの構成を見る

こんな感じで、layerやfeature-groupを見ていくと、WildFlyの機能の構成がなんとなくわかりそうですね。

実際、standalone.xml等は以下をベースに作られているようです。

たとえば、standalone-ha.xml

<?xml version="1.0" ?>

<config xmlns="urn:jboss:galleon:config:1.0" name="standalone-ha.xml" model="standalone">
    <feature-group name="standalone-ha"/>
</config>

https://github.com/wildfly/wildfly/blob/26.1.1.Final/ee-feature-pack/galleon-content/src/main/resources/configs/standalone/standalone-ha.xml/config.xml

<?xml version="1.0" encoding="UTF-8"?>
<feature-group-spec name="standalone-ha" xmlns="urn:jboss:galleon:feature-group:1.0">
    <feature spec="server-root">
        <param name="server-root" value="/" />
    </feature>
    <feature-group name="interfaces"/>
    <feature spec="interface">
        <param name="interface" value="private"/>
        <param name="inet-address" value="${jboss.bind.address.private:127.0.0.1}"/>
    </feature>

    <feature spec="socket-binding-group">
        <param name="socket-binding-group" value="standard-sockets" />
        <param name="port-offset" value="${jboss.socket.binding.port-offset:0}"/>
        <param name="default-interface" value="public"/>
        <feature-group name="standalone-sockets"/>
        <feature-group name="ha-sockets"/>
    </feature>

    <feature-group name="management-audit"/>
    <feature-group name="management-interfaces"/>
    <feature-group name="access-control"/>
    <feature-group name="health"/>
    <feature-group name="metrics" />

    <feature-group name="servlet-standalone-profile"/>
    <feature-group name="basic-ha-profile"/>

</feature-group-spec>

https://github.com/wildfly/wildfly/blob/26.1.1.Final/ee-feature-pack/galleon-content/src/main/resources/feature_groups/standalone-ha.xml

<?xml version="1.0" encoding="UTF-8"?>
<feature-group-spec name="standalone-ha" xmlns="urn:jboss:galleon:feature-group:1.0">

    <origin name="org.wildfly:wildfly-ee-galleon-pack">
        <feature-group name="standalone-ha"/>
    </origin>

    <feature spec="subsystem.microprofile-config-smallrye"/>
    <feature spec="subsystem.microprofile-jwt-smallrye"/>
    <feature spec="subsystem.elytron-oidc-client"/>
    <feature-group name="microprofile-opentracing-jaeger"/>

</feature-group-spec>

https://github.com/wildfly/wildfly/blob/26.1.1.Final/galleon-pack/galleon-content/src/main/resources/feature_groups/standalone-ha.xml

なんとなく、どんなfeature-group、featureで構成するか読めそうな気がしますね。

WildFly Galleon feature-packとWildFly extras feature-pack

ところで、feature-packという言葉が全然出てきませんね。

WildFly Galleon feature-packは、以下があります。

これらは、Mavenリポジトリーにデプロイはされているものの、WildFlyサーバーのディストリビューションに含まれているわけでは
ありません。

というか、これらの範囲はWildFlyサーバーのディストリビューションの範囲そのものなので、これをWildFly Bootable JARのfeature-packに
指定しようものならWildFlyサーバーをそのまま使えばいいのでは?という感じになるでしょうね。

また、feature-packはWildFly extrasというorganizationでも提供されています。

WildFly extras · GitHub

以下の4つです(実質3つ)。

WildFly提供外のfeature-packを使う場合を除き、WildFly Bootable JARのfeature-packに指定するとしたら、これらですね。

featureは?

ここまで、まったく出てこなかったのがfeatureです。WildFlyリポジトリーのどこを見てもfeatureの定義は見当たりません。

どうやら、自動生成しているみたいです。

    <generate-feature-specs>
        <extensions>
            <standalone>
                <extension>org.jboss.as.deployment-scanner</extension>
                <extension>org.jboss.as.jmx</extension>
                <extension>org.jboss.as.logging</extension>
                <extension>org.jboss.as.remoting</extension>
                <extension>org.wildfly.extension.core-management</extension>
                <extension>org.wildfly.extension.discovery</extension>
                <extension>org.wildfly.extension.elytron</extension>
                <extension>org.wildfly.extension.io</extension>
                <extension>org.wildfly.extension.request-controller</extension>
                <extension>org.wildfly.extension.security.manager</extension>
            </standalone>
            <domain>
                <extension>org.jboss.as.jmx</extension>
                <extension>org.jboss.as.logging</extension>
                <extension>org.jboss.as.remoting</extension>
                <extension>org.wildfly.extension.core-management</extension>
                <extension>org.wildfly.extension.discovery</extension>
                <extension>org.wildfly.extension.elytron</extension>
                <extension>org.wildfly.extension.io</extension>
                <extension>org.wildfly.extension.request-controller</extension>
                <extension>org.wildfly.extension.security.manager</extension>
            </domain>
            <host>
                <extension>org.jboss.as.jmx</extension>
                <extension>org.wildfly.extension.core-management</extension>
                <extension>org.wildfly.extension.discovery</extension>
                <extension>org.wildfly.extension.elytron</extension>
            </host>
        </extensions>
    </generate-feature-specs>

https://github.com/wildfly/wildfly-core/blob/18.1.1.Final/core-feature-pack/galleon-feature-pack/wildfly-feature-pack-build.xml#L68-L100

    <generate-feature-specs>
        <extensions>
            <standalone>
                <extension>org.jboss.as.deployment-scanner</extension>
                <extension>org.jboss.as.ee</extension>
                <extension>org.jboss.as.jmx</extension>
                <extension>org.jboss.as.logging</extension>
                <extension>org.jboss.as.naming</extension>
                <extension>org.jboss.as.remoting</extension>
                <extension>org.jboss.as.security</extension>
                <extension>org.wildfly.extension.core-management</extension>
                <extension>org.wildfly.extension.discovery</extension>
                <extension>org.wildfly.extension.elytron</extension>
                <extension>org.wildfly.extension.io</extension>
                <extension>org.wildfly.extension.request-controller</extension>
                <extension>org.wildfly.extension.security.manager</extension>
                <extension>org.wildfly.extension.undertow</extension>
            </standalone>
            <domain>
                <extension>org.jboss.as.ee</extension>
                <extension>org.jboss.as.jmx</extension>
                <extension>org.jboss.as.logging</extension>
                <extension>org.jboss.as.naming</extension>
                <extension>org.jboss.as.remoting</extension>
                <extension>org.jboss.as.security</extension>
                <extension>org.wildfly.extension.core-management</extension>
                <extension>org.wildfly.extension.discovery</extension>
                <extension>org.wildfly.extension.elytron</extension>
                <extension>org.wildfly.extension.io</extension>
                <extension>org.wildfly.extension.request-controller</extension>
                <extension>org.wildfly.extension.security.manager</extension>
                <extension>org.wildfly.extension.undertow</extension>
            </domain>
            <host>
                <extension>org.jboss.as.jmx</extension>
                <extension>org.wildfly.extension.core-management</extension>
                <extension>org.wildfly.extension.discovery</extension>
                <extension>org.wildfly.extension.elytron</extension>
            </host>
        </extensions>
    </generate-feature-specs>

https://github.com/wildfly/wildfly/blob/26.1.1.Final/servlet-feature-pack/galleon-feature-pack/wildfly-feature-pack-build.xml#L51-L91

    <generate-feature-specs>
        <extensions>
            <standalone>
                <extension>org.jboss.as.clustering.infinispan</extension>
                <extension>org.jboss.as.clustering.jgroups</extension>
                <extension>org.jboss.as.connector</extension>
                <extension>org.jboss.as.deployment-scanner</extension>
                <extension>org.jboss.as.ee</extension>
                <extension>org.jboss.as.ejb3</extension>
                <extension>org.jboss.as.jaxrs</extension>
                <extension>org.jboss.as.jdr</extension>
                <extension>org.jboss.as.jmx</extension>
                <extension>org.jboss.as.jpa</extension>
                <extension>org.jboss.as.jsf</extension>
                <extension>org.jboss.as.jsr77</extension>
                <extension>org.jboss.as.logging</extension>
                <extension>org.jboss.as.mail</extension>
                <extension>org.jboss.as.modcluster</extension>
                <extension>org.jboss.as.naming</extension>
                <extension>org.jboss.as.pojo</extension>
                <extension>org.jboss.as.remoting</extension>
                <extension>org.jboss.as.sar</extension>
                <extension>org.jboss.as.security</extension>
                <extension>org.jboss.as.transactions</extension>
                <extension>org.jboss.as.webservices</extension>
                <extension>org.jboss.as.weld</extension>
                <extension>org.jboss.as.xts</extension>
                <extension>org.wildfly.extension.batch.jberet</extension>
                <extension>org.wildfly.extension.bean-validation</extension>
                <extension>org.wildfly.extension.clustering.singleton</extension>
                <extension>org.wildfly.extension.clustering.web</extension>
                <extension>org.wildfly.extension.core-management</extension>
                <extension>org.wildfly.extension.discovery</extension>
                <extension>org.wildfly.extension.ee-security</extension>
                <extension>org.wildfly.extension.elytron</extension>
                <extension>org.wildfly.extension.health</extension>
                <extension>org.wildfly.extension.io</extension>
                <extension>org.wildfly.extension.messaging-activemq</extension>
                <extension>org.wildfly.extension.metrics</extension>
                <extension>org.wildfly.extension.opentelemetry</extension>
                <extension>org.wildfly.extension.request-controller</extension>
                <extension>org.wildfly.extension.rts</extension>
                <extension>org.wildfly.extension.security.manager</extension>
                <extension>org.wildfly.extension.undertow</extension>
                <extension>org.wildfly.iiop-openjdk</extension>
            </standalone>
            <domain>
                <extension>org.jboss.as.clustering.infinispan</extension>
                <extension>org.jboss.as.clustering.jgroups</extension>
                <extension>org.jboss.as.connector</extension>
                <extension>org.jboss.as.ee</extension>
                <extension>org.jboss.as.ejb3</extension>
                <extension>org.jboss.as.jaxrs</extension>
                <extension>org.jboss.as.jdr</extension>
                <extension>org.jboss.as.jmx</extension>
                <extension>org.jboss.as.jpa</extension>
                <extension>org.jboss.as.jsf</extension>
                <extension>org.jboss.as.jsr77</extension>
                <extension>org.jboss.as.logging</extension>
                <extension>org.jboss.as.mail</extension>
                <extension>org.jboss.as.modcluster</extension>
                <extension>org.jboss.as.naming</extension>
                <extension>org.jboss.as.pojo</extension>
                <extension>org.jboss.as.remoting</extension>
                <extension>org.jboss.as.sar</extension>
                <extension>org.jboss.as.security</extension>
                <extension>org.jboss.as.transactions</extension>
                <extension>org.jboss.as.webservices</extension>
                <extension>org.jboss.as.weld</extension>
                <extension>org.jboss.as.xts</extension>
                <extension>org.wildfly.extension.batch.jberet</extension>
                <extension>org.wildfly.extension.bean-validation</extension>
                <extension>org.wildfly.extension.clustering.singleton</extension>
                <extension>org.wildfly.extension.clustering.web</extension>
                <extension>org.wildfly.extension.core-management</extension>
                <extension>org.wildfly.extension.discovery</extension>
                <extension>org.wildfly.extension.ee-security</extension>
                <extension>org.wildfly.extension.elytron</extension>
                <extension>org.wildfly.extension.io</extension>
                <extension>org.wildfly.extension.messaging-activemq</extension>
                <extension>org.wildfly.extension.opentelemetry</extension>
                <extension>org.wildfly.extension.picketlink</extension>
                <extension>org.wildfly.extension.request-controller</extension>
                <extension>org.wildfly.extension.rts</extension>
                <extension>org.wildfly.extension.security.manager</extension>
                <extension>org.wildfly.extension.undertow</extension>
                <extension>org.wildfly.iiop-openjdk</extension>
            </domain>
            <host>
                <extension>org.jboss.as.jmx</extension>
                <extension>org.wildfly.extension.core-management</extension>
                <extension>org.wildfly.extension.discovery</extension>
                <extension>org.wildfly.extension.elytron</extension>
            </host>
        </extensions>
    </generate-feature-specs>

https://github.com/wildfly/wildfly/blob/26.1.1.Final/ee-feature-pack/galleon-feature-pack/wildfly-feature-pack-build.xml#L57-L152

    <generate-feature-specs>
        <extensions>
            <standalone>
                <extension>org.wildfly.extension.elytron-oidc-client</extension>
                <extension>org.wildfly.extension.microprofile.config-smallrye</extension>
                <extension>org.wildfly.extension.microprofile.fault-tolerance-smallrye</extension>
                <extension>org.wildfly.extension.microprofile.health-smallrye</extension>
                <extension>org.wildfly.extension.microprofile.jwt-smallrye</extension>
                <extension>org.wildfly.extension.microprofile.metrics-smallrye</extension>
                <extension>org.wildfly.extension.microprofile.openapi-smallrye</extension>
                <extension>org.wildfly.extension.microprofile.opentracing-smallrye</extension>
                <extension>org.wildfly.extension.microprofile.reactive-messaging-smallrye</extension>
                <extension>org.wildfly.extension.microprofile.reactive-streams-operators-smallrye</extension>
            </standalone>
            <domain>
                <extension>org.wildfly.extension.microprofile.config-smallrye</extension>
                <extension>org.wildfly.extension.microprofile.fault-tolerance-smallrye</extension>
                <extension>org.wildfly.extension.microprofile.jwt-smallrye</extension>
                <extension>org.wildfly.extension.microprofile.openapi-smallrye</extension>
                <extension>org.wildfly.extension.microprofile.opentracing-smallrye</extension>
                <extension>org.wildfly.extension.microprofile.reactive-messaging-smallrye</extension>
                <extension>org.wildfly.extension.microprofile.reactive-streams-operators-smallrye</extension>
            </domain>
        </extensions>
    </generate-feature-specs>

https://github.com/wildfly/wildfly/blob/26.1.1.Final/galleon-pack/galleon-feature-pack/wildfly-feature-pack-build.xml#L70-L94

これらのMavenアーティファクトである、wildfly-core-galleon-packwildfly-servlet-galleon-packwildfly-ee-galleon-pack
wildfly-galleon-packの中身を見ると、大量のpackage、feature、feature-group、layer、そしてfeature-packとしての定義が
含まれていることを確認できます。

というわけで

WildFly Galleon layerに含まれる内容をざっくり知りたかったらlayer-spec.xmlを読めばよいですし、feature-groupであれば[feagure-group名].xml
読むとなにが含まれているのか、ある程度雰囲気がわかるのではないかと思います。

あとは、最後に軽くWildFly Bootable JARを作って遊んでおきましょう。

お題

今回のお題は、こちらをWildFly Bootable JARで実現することにします。WildFlyのHTTPセッションの保存先をInfinispan Serverに変更したもの
ですね。

WildFlyのHTTPセッションの保存先をInfinispan Serverに変更する - CLOVER🍀

こちらのエントリーで作ったソースコードServletを使ったものだったので、今回はJAX-RSCDIを使うことにしましょう。

WildFly Bootable JARでパッケージングする際に、必要なGalleon layerを組み込み、WildFly CLIでDistributable Webサブシステムの設定を行う
感じでいきたいと思います。

環境

今回の環境は、こちらです。

$ java --version
openjdk 17.0.3 2022-04-19
OpenJDK Runtime Environment (build 17.0.3+7-Ubuntu-0ubuntu0.20.04.1)
OpenJDK 64-Bit Server VM (build 17.0.3+7-Ubuntu-0ubuntu0.20.04.1, mixed mode, sharing)


$ mvn --version
Apache Maven 3.8.6 (84538c9988a25aec085021c365c560670ad80f63)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 17.0.3, vendor: Private Build, runtime: /usr/lib/jvm/java-17-openjdk-amd64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "5.4.0-121-generic", arch: "amd64", family: "unix"

Infinispan Serverは13.0.10.Finalを使用し、172.18.0.2で動作しているものとします。

$ bin/server.sh \
    -b 0.0.0.0 \
    -Djgroups.tcp.address=`hostname -i`

ユーザーも作成しておきます。Cacheは、WildFly側の起動時に作成することにします。

$ bin/cli.sh user create -g admin -p password web-session-user

アプリケーションを作成する

それでは、アプリケーションを作成してきましょう。

Mavenの依存関係やプラグインの設定は、こちら。

    <groupId>org.littlewings</groupId>
    <artifactId>wildfly-bootable-jar-layer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>jakarta.platform</groupId>
                <artifactId>jakarta.jakartaee-bom</artifactId>
                <version>8.0.0</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>jakarta.ws.rs</groupId>
            <artifactId>jakarta.ws.rs-api</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>jakarta.enterprise</groupId>
            <artifactId>jakarta.enterprise.cdi-api</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.3.2</version>
            </plugin>
            <plugin>
                <groupId>org.wildfly.plugins</groupId>
                <artifactId>wildfly-jar-maven-plugin</artifactId>
                <version>7.0.2.Final</version>
                <configuration>

                    <!-- 後で -->

                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>package</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

WildFly Jar Maven Pluginの部分は、また後で載せます。

続いて、ソースコード

SessionScopedなCDI管理Bean。

src/main/java/org/littlewings/wildfly/bootable/Counter.java

package org.littlewings.wildfly.bootable;

import java.io.Serializable;
import java.util.concurrent.atomic.AtomicInteger;
import javax.enterprise.context.SessionScoped;

@SessionScoped
public class Counter implements Serializable {
    private static final long serialVersionUID = 1L;

    AtomicInteger value = new AtomicInteger();

    public int incrementAndGet() {
        return value.incrementAndGet();
    }
}

JAX-RSリソースクラス。SessionScopedなCDI管理Beanは、このクラスで使います。

src/main/java/org/littlewings/wildfly/bootable/CounterResource.java

package org.littlewings.wildfly.bootable;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@ApplicationScoped
@Path("counter")
public class CounterResource {
    @Inject
    Counter counter;

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String get() {
        return String.format("count = %d", counter.incrementAndGet());
    }
}

JAX-RSの有効化。

src/main/java/org/littlewings/wildfly/bootable/JaxrsActivator.java

package org.littlewings.wildfly.bootable;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("")
public class JaxrsActivator extends Application {
}

web.xml<distributable/>を入れておきます。

src/main/webapp/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee webapp_4_0.xsd"
         version="4.0">
    <distributable/>
</web-app>

これで、アプリケーションの準備は完了です。

WildFly Jar Maven Pluginの設定をする

では、WildFly Jar Maven Pluginの設定をしていきます。

            <plugin>
                <groupId>org.wildfly.plugins</groupId>
                <artifactId>wildfly-jar-maven-plugin</artifactId>
                <version>7.0.2.Final</version>
                <configuration>

                    <!-- 後で -->

                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>package</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

とりあえず、mvn packageWildFly Bootable JARを作成するようにはしておきました。

まずはGalleon layerを選択しましょう。

                    <feature-pack-location>wildfly@maven(org.jboss.universe:community-universe)#26.1.1.Final</feature-pack-location>
                    <layers>
                        <layer>jaxrs-server</layer>
                        <layer>web-clustering</layer>
                    </layers>

feature-pack-locationWildFly 26.1.1.Finalをプロビジョニングすることを指定。

選択するWildFly Galleon layerですが、まずはFoundational Galleon layerから「jaxrs-server」を選択。含まれているものがちょっと多いですが、
これで「bean-validation」、「cdi」、「datasources-web-server」、「jaxrs」、「jpa」が利用できることになります。

WildFly bootable JAR application development / Galleon configuration / WildFly Layers / Foundational Galleon layers

「jaxrs-server」は「datasources-web-server」を、「datasources-web-server」は「core-server」を依存するlayerとしているため、
やっぱりそれなりに含まれますね。

「jaxrs」と「cdi」があれば良いのではないかと思いますが、今回はFoundational Galleon layers、Basic Galleon Layersそれぞれから
layerを選択するようにしたいと思います。

「jaxrs-server」だけではInfinispan Server(Hot Rod)によるセッション管理は実現できません。こちらを使うには、
Distributable Webサブシステムが必要になります。

WildFlyのHTTPセッションの保存先をInfinispan Serverに変更する - CLOVER🍀

Distributable Webサブシステムが含まれるlayerはというと…パッと見ではわかりませんが、「web-clustering」が該当します。
こちらは、Basic Galleon Layersから選択。

WildFly bootable JAR application development / Galleon configuration / WildFly Layers / Basic Galleon Layers

「web-clustering」の依存するlayerは「transactions」と「web-server」ということになっていて、あんまりクラスタリング感はありません。

ここで、「web-clustering」layerのlayer-spec.xmlを見てみます。

<?xml version="1.0" ?>
<layer-spec xmlns="urn:jboss:galleon:layer-spec:1.0" name="web-clustering">
    <dependencies>
        <layer name="web-server"/>
        <layer name="transactions"/>
    </dependencies>

    <feature-group name="private-interface"/>

    <feature-group name="jgroups-all"/>
    <feature-group name="distributable-web"/>
    <feature-group name="infinispan-dist-web"/>
    <packages>
        <!-- The distributable-web subsystem doesn't assume undertow,
             and undertow subysystem doesn't assume clustering, but the
             combination requires the clustering<->undertow integration package -->
        <package name="org.wildfly.clustering.web.undertow"/>
    </packages>
</layer-spec>

「distributable-web」と「infinispan-dist-web」という2つのfeature-groupを参照しています。

https://github.com/wildfly/wildfly/blob/26.1.1.Final/ee-feature-pack/galleon-common/src/main/resources/layers/standalone/web-clustering/layer-spec.xml

「infinispan-dist-web」feature-groupは、Infinispanサブシステムが定義されているようです。

https://github.com/wildfly/wildfly/blob/26.1.1.Final/ee-feature-pack/galleon-common/src/main/resources/feature_groups/infinispan-dist-web.xml

「distributable-web」feature-groupの方はどうでしょうか。

<?xml version="1.0" encoding="UTF-8"?>
<feature-group-spec name="distributable-web" xmlns="urn:jboss:galleon:feature-group:1.0">

    <feature spec="subsystem.distributable-web">
        <param name="default-session-management" value="default"/>
        <param name="default-single-sign-on-management" value="default"/>
        <feature spec="subsystem.distributable-web.infinispan-session-management">
            <param name="infinispan-session-management" value="default"/>
            <param name="cache-container" value="web"/>
            <param name="granularity" value="SESSION"/>
            <feature spec="subsystem.distributable-web.infinispan-session-management.affinity.primary-owner"/>
        </feature>
        <feature spec="subsystem.distributable-web.infinispan-single-sign-on-management">
            <param name="infinispan-single-sign-on-management" value="default"/>
            <param name="cache-container" value="web"/>
            <param name="cache" value="sso"/>
        </feature>
        <feature spec="subsystem.distributable-web.routing.infinispan">
            <param name="cache-container" value="web"/>
            <param name="cache" value="routing"/>
        </feature>
    </feature>

</feature-group-spec>

https://github.com/wildfly/wildfly/blob/26.1.1.Final/ee-feature-pack/galleon-common/src/main/resources/feature_groups/distributable-web.xml

こちらには、Distributable Webサブシステムが含まれていそうです。

というわけで、今回はこの2つのlayerを選択することにしました。

                    <layers>
                        <layer>jaxrs-server</layer>
                        <layer>web-clustering</layer>
                    </layers>

続いては、Remote Cache ContainerとDistributable Webサブシステムの設定を行う必要があります。

こちらには、WildFly CLIでセットアップすることにしましょう。

WildFly Bootable JAR Documentation / Configuring the server during packaging / WildFly CLI execution during packaging

WildFly CLIは、パッケージング時、実行時のどちらでも利用できますが、今回はパッケージング時とします。

こんなスクリプトを用意。

cli-scripts/hotrod-session.cli

/socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=infinispan-server:add(host=172.18.0.2, port=11222)

batch
/subsystem=infinispan/remote-cache-container=remote-infinispan:add(default-remote-cluster=remote-infinispan-cluster)
/subsystem=infinispan/remote-cache-container=remote-infinispan/remote-cluster=remote-infinispan-cluster:add(socket-bindings=[infinispan-server])
run-batch

/subsystem=infinispan/remote-cache-container=remote-infinispan:write-attribute(name=modules,value=[org.wildfly.clustering.web.hotrod])
/subsystem=infinispan/remote-cache-container=remote-infinispan:write-attribute(name=properties,value={infinispan.client.hotrod.auth_username=web-session-user,infinispan.client.hotrod.auth_password=password})

/subsystem=infinispan/remote-cache-container=remote-infinispan:write-attribute(name=marshaller,value=PROTOSTREAM)

/subsystem=distributable-web/hotrod-session-management=remote-session:add(remote-cache-container=remote-infinispan, cache-configuration=org.infinispan.DIST_SYNC, granularity=SESSION)
/subsystem=distributable-web/hotrod-session-management=remote-session/affinity=none:add()
/subsystem=distributable-web:write-attribute(name=default-session-management,value=remote-session)

こちらを実行するように設定。

                    <cli-sessions>
                        <cli-session>
                            <script-files>
                                <script>cli-scripts/hotrod-session.cli</script>
                            </script-files>
                        </cli-session>
                    </cli-sessions>

全体としては、このようになりました。

            <plugin>
                <groupId>org.wildfly.plugins</groupId>
                <artifactId>wildfly-jar-maven-plugin</artifactId>
                <version>7.0.2.Final</version>
                <configuration>
                    <feature-pack-location>wildfly@maven(org.jboss.universe:community-universe)#26.1.1.Final</feature-pack-location>
                    <layers>
                        <layer>jaxrs-server</layer>
                        <layer>web-clustering</layer>
                    </layers>
                    <cli-sessions>
                        <cli-session>
                            <script-files>
                                <script>cli-scripts/hotrod-session.cli</script>
                            </script-files>
                        </cli-session>
                    </cli-sessions>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>package</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

あとはパッケージングして完成です。

$ mvn package

パッケージングすると、CLIの実行が行われている様子が確認できます。

[INFO] CLI executions are done in forked process
[INFO] Executing CLI, Server configuration
[INFO] CLI scripts execution done.
[INFO] Executing CLI, CLI Session, scripts=[cli-scripts/hotrod-session.cli], resolve-expressions=true, properties-file=null
[INFO] CLI scripts execution done.

ちなみに、「web-clustering」layerを入れていない場合は、以下のようにCLIの実行に失敗します。

[ERROR] Exception in thread "main" java.lang.reflect.InvocationTargetException
[ERROR]         at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[ERROR]         at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
[ERROR]         at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
[ERROR]         at java.base/java.lang.reflect.Method.invoke(Method.java:568)
[ERROR]         at org.wildfly.plugins.bootablejar.maven.cli.CLIWrapper.handle(CLIWrapper.java:82)
[ERROR]         at org.wildfly.plugins.bootablejar.maven.cli.CLIForkedExecutor.main(CLIForkedExecutor.java:49)
[ERROR] Caused by: org.jboss.as.cli.CommandFormatException: Failed to get the list of the operation properties: "WFLYCTL0030: No resource definition is registered for address [
[ERROR]     ("subsystem" => "distributable-web"),
[ERROR]     ("hotrod-session-management" => "remote-session")
[ERROR] ]"
[ERROR]         at org.jboss.as.cli.Util.retrieveDescription(Util.java:1637)
[ERROR]         at org.jboss.as.cli.Util.validateRequest(Util.java:1655)
[ERROR]         at org.jboss.as.cli.handlers.OperationRequestHandler.handle(OperationRequestHandler.java:83)
[ERROR]         at org.jboss.as.cli.impl.CommandContextImpl.handleOperation(CommandContextImpl.java:1917)
[ERROR]         at org.jboss.as.cli.impl.CommandContextImpl.handle(CommandContextImpl.java:875)
[ERROR]         ... 6 more

動作確認する

では、動作確認してみましょう。Infinispan Serverは起動済みとします。

パッケージングして、できあがったJARファイルを実行。

$ java -jar target/wildfly-bootable-jar-layer-0.0.1-SNAPSHOT-bootable.jar

起動すると、Infinispan Server側にROOT.warというCacheがないことが表示されつつ、Infinispan Serverを認識します。

01:45:51,025 INFO  [org.infinispan.HOTROD] (ServerService Thread Pool -- 46) ISPN004021: Infinispan version: Infinispan 'Triskaidekaphobia' 13.0.10.Final
01:45:51,026 INFO  [org.jboss.as.clustering.infinispan] (ServerService Thread Pool -- 46) WFLYCLINF0029: Started remote cache container 'remote-infinispan'.
01:45:51,034 WARN  [org.infinispan.HOTROD] (HotRod-client-async-pool-0) ISPN004005: Error received from the server: org.infinispan.server.hotrod.CacheNotFoundException: Cache with name 'ROOT.war' not found amongst the configured caches
01:45:51,139 INFO  [org.infinispan.HOTROD] (HotRod-client-async-pool-0) ISPN004006: Server sent new topology view (id=1, age=0) containing 1 addresses: [172.18.0.2/<unresolved>:11222]

一方でInfinispan Server側では、Cacheが作成されたことが確認できます。

2022-06-29 16:45:51,069 WARN  (blocking-thread--p3-t1) [org.infinispan.encoding.impl.StorageConfigurationManager] ISPN000599: Configuration for cache 'ROOT.war' does not define the encoding for keys or values. If you use operations that require data conversion or queries, you should configure the cache with a specific MediaType for keys or values.

CLIでも確認。

[infinispan-server-34838@cluster//containers/default]> describe caches/ROOT.war
{
  "distributed-cache" : {
    "mode" : "SYNC",
    "remote-timeout" : "17500",
    "statistics" : true,
    "locking" : {
      "concurrency-level" : "1000",
      "acquire-timeout" : "15000",
      "striping" : false
    },
    "state-transfer" : {
      "timeout" : "60000"
    }
  }
}

動作確認。

$ curl -c cookie.txt -b cookie.txt localhost:8080/counter
count = 1


$ curl -c cookie.txt -b cookie.txt localhost:8080/counter
count = 2


$ curl -c cookie.txt -b cookie.txt localhost:8080/counter
count = 3


$ curl -c cookie.txt -b cookie.txt localhost:8080/counter
count = 4


$ curl -c cookie.txt -b cookie.txt localhost:8080/counter
count = 5

この状態で、WildFly Bootable JARで作られたアプリケーションを再起動しても、HTTPセッションが失われていないことが確認できます。

$ curl -c cookie.txt -b cookie.txt localhost:8080/counter
count = 6

OKですね。

まとめ

WildFly Bootable JARを作成する際に指定する、Galleon layer、Galleon feature-packというものを少し見てみました。

軽い気持ちで始めてみたら思ったよりも追うところが大変だったのですが、その分いろいろ情報が追えた&理解が進んだ気がするので、
まあいいかなと思います。