CLOVER🍀

That was when it all began.

WildFly Bootable JARで作成したアプリケーションの起動時に、システムプロパティや起動引数を指定する

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

これまで何回かWildFly Bootable JARを使ってきましたが、そういえばWildFlyで使っていたようなシステムプロパティや起動引数の指定は
有効なのかな?とふと疑問に思ったので試してみました。

結論を言うと、同じように使えます。

WildFly Bootable JARの起動引数について

WildFly Bootable JARの起動引数については、こちらに書かれています。

Bootable JAR Guide / WildFly bootable JAR application development / Running your application / Bootable JAR arguments

以下のように、まあ見慣れたものがありますね。

  • -b[interface]=<value> … Set system property jboss.bind.address. to the given value
  • -b=<value> … Set system property jboss.bind.address to the given value
  • -D<name>[=<value>] … Set a system property. The system properties are set by the server. They are not set by the bootable JAR JVM.
  • --help … Display help then exit …
  • -u=<value> … Set system property jboss.default.multicast.address to the given value.
  • --version … Print version and exit.

--helpも使えるんですね。

ここで、-Dを使ったシステムプロパティで指定できるのは、どのあたりなんだろうと思ったのが今回扱ったきっかけです。

お題

今回は、WildFly Bootable JARを使ってひとつのホスト内に同じアプリケーションを2つ起動させてみます。この時、なにも考えずに起動すると
ポートが重複するはずですが、-Djboss.socket.binding.port-offsetでポートをずらせるかどうか確認したいと思います。

せっかくなので、クラスタリングもしておきましょう。

環境

今回の環境は、こちら。

$ java --version
openjdk 17.0.4 2022-07-19
OpenJDK Runtime Environment (build 17.0.4+8-Ubuntu-120.04)
OpenJDK 64-Bit Server VM (build 17.0.4+8-Ubuntu-120.04, mixed mode, sharing)


$ mvn --version
Apache Maven 3.8.6 (84538c9988a25aec085021c365c560670ad80f63)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 17.0.4, 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-128-generic", arch: "amd64", family: "unix"

WildFly Bootable JARを使ったサンプルアプリケーションを作成する

それでは、アプリケーションを作成します。

まずはpom.xmlから。

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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>

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

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

    <dependencies>
        <dependency>
            <groupId>jakarta.platform</groupId>
            <artifactId>jakarta.jakartaee-web-api</artifactId>
            <version>8.0.0</version>
            <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>8.0.1.Final</version>
                <configuration>
                    <feature-pack-location>wildfly@maven(org.jboss.universe:community-universe)#26.1.2.Final</feature-pack-location>
                    <layers>
                        <layer>jaxrs</layer>
                        <layer>web-clustering</layer>
                    </layers>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>package</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

レイヤーには、jaxrsweb-clusteringを入れました。

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

使用するWildFlyは26.1.2.Finalとします。

HttpSessionを使ったJAX-RSリソースクラス。

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

package org.littlewings.wildfly.bootable;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;

@Path("hello")
public class HelloResource {
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String messageAndSessionStartedTime(@Context HttpServletRequest request) {
        HttpSession session = request.getSession();

        LocalDateTime startedTime;

        if (session.isNew()) {
            startedTime = LocalDateTime.now();
            session.setAttribute("startedTime", startedTime);
        }

        return String.format(
                "Hello[%s]",
                ((LocalDateTime) session.getAttribute("startedTime")).format(DateTimeFormatter.ISO_DATE_TIME)
        );
    }
}

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 {
}

<distributable/>が必要なので、web.xmlも作成。

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
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <distributable/>
</web-app>

パッケージング。

$ mvn package

起動。

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

動作確認。

$ curl -b cookie.txt -c cookie.txt localhost:8080/hello
Hello[2022-10-19T00:04:39.439503952]


$ curl -b cookie.txt -c cookie.txt localhost:8080/hello
Hello[2022-10-19T00:04:39.439503952]


$ curl -b cookie.txt -c cookie.txt localhost:8080/hello
Hello[2022-10-19T00:04:39.439503952]

ひとまず、シングルノードのアプリケーションとしてはOKです。

2つ目のアプリケーションを起動する

では、この状態で同じアプリケーションをもうひとつ起動します。

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

当然のことながら、起動に失敗します。

00:05:55,841 ERROR [org.jboss.msc.service.fail] (MSC service thread 1-7) MSC000001: Failed to start service org.wildfly.undertow.listener.default: org.jboss.msc.service.StartException in service org.wildfly.undertow.listener.default: アドレスは既に使用中です /127.0.0.1:8080
        at org.wildfly.extension.undertow@26.1.2.Final//org.wildfly.extension.undertow.ListenerService.start(ListenerService.java:212)
        at org.jboss.msc@1.4.13.Final//org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:1739)
        at org.jboss.msc@1.4.13.Final//org.jboss.msc.service.ServiceControllerImpl$StartTask.execute(ServiceControllerImpl.java:1701)
        at org.jboss.msc@1.4.13.Final//org.jboss.msc.service.ServiceControllerImpl$ControllerTask.run(ServiceControllerImpl.java:1559)
        at org.jboss.threads@2.4.0.Final//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
        at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1990)
        at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
        at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377)
        at java.base/java.lang.Thread.run(Thread.java:833)

00:05:56,786 ERROR [org.jboss.msc.service.fail] (ServerService Thread Pool -- 30) MSC000001: Failed to start service org.wildfly.clustering.jgroups.channel.ee: org.jboss.msc.service.StartException in service org.wildfly.clustering.jgroups.channel.ee: java.lang.IllegalStateException: java.lang.Exception: failed to open a port in range 55200-55200 (last exception: java.net.BindException: アドレスは既に使用中です)
        at org.wildfly.clustering.service@26.1.2.Final//org.wildfly.clustering.service.FunctionalService.start(FunctionalService.java:66)
        at org.wildfly.clustering.service@26.1.2.Final//org.wildfly.clustering.service.AsyncServiceConfigurator$AsyncService.lambda$start$0(AsyncServiceConfigurator.java:117)
        at org.jboss.threads@2.4.0.Final//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
        at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1990)
        at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
        at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377)
        at java.base/java.lang.Thread.run(Thread.java:833)
        at org.jboss.threads@2.4.0.Final//org.jboss.threads.JBossThread.run(JBossThread.java:513)
Caused by: java.lang.IllegalStateException: java.lang.Exception: failed to open a port in range 55200-55200 (last exception: java.net.BindException: アドレスは既に使用中です)
        at org.jboss.as.clustering.jgroups@26.1.2.Final//org.jboss.as.clustering.jgroups.subsystem.ChannelServiceConfigurator.get(ChannelServiceConfigurator.java:116)
        at org.jboss.as.clustering.jgroups@26.1.2.Final//org.jboss.as.clustering.jgroups.subsystem.ChannelServiceConfigurator.get(ChannelServiceConfigurator.java:58)
        at org.wildfly.clustering.service@26.1.2.Final//org.wildfly.clustering.service.FunctionalService.start(FunctionalService.java:63)
        ... 7 more

ここで、-Djboss.socket.binding.port-offsetを指定して起動しなおします。

$ java -Djboss.socket.binding.port-offset=1000 -jar target/wildfly-bootable-jar-clustering-0.0.1-SNAPSHOT-bootable.jar

今度は起動に成功するので、動作確認してみましょう。

$ curl -b cookie.txt -c cookie.txt localhost:8080/hello
Hello[2022-10-19T00:04:39.439503952]


$ curl -b cookie.txt -c cookie.txt localhost:9080/hello
Hello[2022-10-19T00:04:39.439503952]


$ curl -b cookie.txt -c cookie.txt localhost:8080/hello
Hello[2022-10-19T00:04:39.439503952]


$ curl -b cookie.txt -c cookie.txt localhost:9080/hello
Hello[2022-10-19T00:04:39.439503952]

OKですね。クラスタリングもうまくいってみます。

これで、今回のお題の確認は終わりました。

ヘルプを見てみる

ヘルプを見てみます。

$ java -jar target/wildfly-bootable-jar-clustering-0.0.1-SNAPSHOT-bootable.jar --help

ふつうに表示されて、ちょっと面白いです。

Usage: java -jar <bootable jar> [args...]
where args include:
    --deployment=<value>                Path to deployment artifact
                                        (war,jar,ear or exploded deployment
                                        dir) to deploy in hollow jar


    -b=<value>                          Set system property jboss.bind.address
                                        to the given value


    -b<interface>=<value>               Set system property
                                        jboss.bind.address.<interface> to the
                                        given value


    -D<name>[=<value>]                  Set a system property


    --cli-script=<value>                Path to a CLI script to execute when
                                        starting the Bootable JAR


    --display-galleon-config            Display the content of the Galleon
                                        configuration used to build this
                                        bootable JAR


    --help                              Display this message and exit


    --install-dir=<value>               Path to directory in which the server
                                        is installed. By default the server is
                                        installed in TEMP directory.


    --properties=<url>                  Load system properties from the given
                                        url


    -secmgr                             Activate the SecurityManager


    -S<name>[=<value>]                  Set a security property


    -u=<value>                          Set system property
                                        jboss.default.multicast.address to the
                                        given value


    --version                           Print version and exit

java -jarで実行するので-Dで指定するシステムプロパティは-jarよりも前の指定になりますが

$ java -Djboss.http.port=9080 -jar target/wildfly-bootable-jar-clustering-0.0.1-SNAPSHOT-bootable.jar

その他の引数を指定する場合は、コマンドライン引数として指定することになります。

$ java -jar target/wildfly-bootable-jar-clustering-0.0.1-SNAPSHOT-bootable.jar -b=192.168.0.6

それはそうと、--versionでバージョンが見られるのが個人的にはちょっと意表を突かれた感じがしておもしろかった(?)です。

$ java -jar target/wildfly-bootable-jar-clustering-0.0.1-SNAPSHOT-bootable.jar --version
WildFly Full 26.1.2.Final (WildFly Core 18.1.2.Final)

どうなっている?

ところで、-Djboss.socket.binding.port-offsetなどがふつうに使えましたが、どうなっているんでしょう?

standalone.xmlでも入っているのかなと思い、Bootable JARからXMLファイルを探してみます。

$ unzip -l target/wildfly-bootable-jar-clustering-0.0.1-SNAPSHOT-bootable.jar | grep xml

standalone.xmlファイルは入っていないのですが、provisioning.xmlというファイルがあったのでこちらを見てみます。

$ unzip -p target/wildfly-bootable-jar-clustering-0.0.1-SNAPSHOT-bootable.jar provisioning.xml
<?xml version="1.0" ?>

<installation xmlns="urn:jboss:galleon:provisioning:3.0">
    <feature-pack location="wildfly@maven(org.jboss.universe:community-universe)#26.1.2.Final">
        <default-configs inherit="false"/>
        <packages inherit="false"/>
    </feature-pack>
    <config model="standalone" name="standalone.xml">
        <layers>
            <include name="jaxrs"/>
            <include name="web-clustering"/>
        </layers>
    </config>
    <options>
        <option name="optional-packages" value="passive+"/>
    </options>
</installation>

WildFly Bootable JARを作成するためにWildFly JAR Maven Pluginに指定した内容が、ほぼ入っている感じですね。

で、stanalone.xmlから指定したレイヤーだけ使っている感じです。

    <config model="standalone" name="standalone.xml">
        <layers>
            <include name="jaxrs"/>
            <include name="web-clustering"/>
        </layers>
    </config>

そして、作成されたJARファイルの中を見ていると、wildfly.zipというひときわ大きなファイルがあります。

$ unzip -l target/wildfly-bootable-jar-clustering-0.0.1-SNAPSHOT-bootable.jar | grep wildfly.zip
 90700689  2022-10-19 00:04   wildfly.zip

ちなみに、今回作成したJARファイルは87Mでした。

$ ll -h target/wildfly-bootable-jar-clustering-0.0.1-SNAPSHOT-bootable.jar
-rw-rw-r-- 1 xxxxx xxxxx 87M 10月 19 00:04 target/wildfly-bootable-jar-clustering-0.0.1-SNAPSHOT-bootable.jar

wildfly.zipを取り出してみます。

$ unzip target/wildfly-bootable-jar-clustering-0.0.1-SNAPSHOT-bootable.jar wildfly.zip
Archive:  target/wildfly-bootable-jar-clustering-0.0.1-SNAPSHOT-bootable.jar
  inflating: wildfly.zip             

今回のBootable JARは、ほぼwildfly.zipのサイズですね。

$ ll -h wildfly.zip
-rw-rw-r-- 1 xxxxx xxxxx 87M 10月 19 00:04 wildfly.zip

中身は、サイズが小さくなったWildFlyみたいですね。WildFly JAR Maven Pluginで指定したレイヤーが必要とする程度のサイズになっている
ような気がします。

standalone.xmlを取り出してみます。

$ unzip wildfly.zip standalone/configuration/standalone.xml
Archive:  wildfly.zip
  inflating: standalone/configuration/standalone.xml

standalone.xmlの最後の方を見ると、今回作成したWARファイルがデプロイされる設定になっています。

    <deployments>
        <deployment name="wildfly-bootable-jar-clustering-0.0.1-SNAPSHOT.war" runtime-name="ROOT.war">
            <content sha1="6c47a2feb9d4b149c7b26dfbfb7e172646d6e6ed"/>
        </deployment>
    </deployments>

ということは、こちらがWildFly Bootable JARで起動されるWildFlyの実体なんでしょうね。

standalone.xml自体は通常のWildFlyのものとほとんど変わらないようですね。
以下はwildfly.zipに含まれていたstandalone.xmlsocket-binding-groupの抜粋です。

    <socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
        <socket-binding name="ajp" port="${jboss.ajp.port:8009}"/>
        <socket-binding name="http" port="${jboss.http.port:8080}"/>
        <socket-binding name="https" port="${jboss.https.port:8443}"/>
        <socket-binding name="jgroups-mping" interface="private" multicast-address="${jboss.default.multicast.address:230.0.0.4}" multicast-port="45700"/>
        <socket-binding name="jgroups-tcp" interface="private" port="7600"/>
        <socket-binding name="jgroups-tcp-fd" interface="private" port="57600"/>
        <socket-binding name="jgroups-udp" interface="private" port="55200" multicast-address="${jboss.default.multicast.address:230.0.0.4}" multicast-port="45688"/>
        <socket-binding name="jgroups-udp-fd" interface="private" port="54200"/>
        <socket-binding name="txn-recovery-environment" port="4712"/>
        <socket-binding name="txn-status-manager" port="4713"/>
    </socket-binding-group>

standalone.xml内で、レイヤーで使えるようにしている範囲は起動時にシステムプロパティで変更できそうです。今回もそれを利用しましたし。

なんとなく雰囲気がわかったので、今回はこのあたりでOKかなと思います。

構成をもっと大きく変えたい場合は?

システムプロパティや起動引数の範囲ではなく、そもそもサブシステムの設定等を変えたい場合はこちらを参照、ですね。

管理CLIスクリプトを使って変更できたりします。

まとめ

今回は、WildFly Bootable JARで作成したアプリケーションの起動時に、システムプロパティや起動引数が指定できたりするのか?WildFly
どう違うのか?という点でちょっと見てみました。

割とそのままでしたね。

ちょっと気にしすぎた感じもしますが、これで確認できたので今後は安心して使えそうです。