CLOVER🍀

That was when it all began.

WildFly Maven Pluginのdevゴールで、Jakarta EEアプリケーションの開発を始める

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

Jakarta EEアプリケーションを開発する時に、WildFly Maven Pluginのdevゴールを使ったりすると楽そうだなと思ったので、ちょっと
試してみることにしました。

同じような発想でWildFly Bootable JARを試したことはありますが、WildFly Maven Pluginはデプロイ/アンデプロイくらいでしか
使ったことがないので。

しかも、過去のエントリーを見るとWildFlyベースだったInfinispan Serverへのデプロイに使ったりしていました…。

WildFly Maven Pluginで、WildFly/Infinispan Serverにデプロイする - CLOVER🍀

WildFly Maven Plugin

WildFly Maven Pluginは、Gallenを使ってWildFlyサーバーをプロビジョニングし、WildFlyサーバーへのデプロイ、再デプロイ、
アンデプロイやアプリケーションの実行ができるMavenプラグインです。

The wildfly-maven-plugin is used to provision a server using Galleon, package your application in Galleon provisioned server, deploy, redeploy, undeploy or run your application.

WildFly Maven Plugin – WildFly Maven Plugin (wildfly-maven-plugin)

現バージョンは、4.2.0.Finalです。

使い方は、こちらを見つつ

それから興味のあるゴールの個々のページを見るとよいでしょう。

WildFly Maven Plugin (wildfly-maven-plugin) / Goals Overview

サンプルもあります。

WildFly Maven Plugin (wildfly-maven-plugin) / Examples

用意されているゴールとしては、ざっくり以下の感じみたいですね。

  • WildFlyの起動、停止
  • WildFlyへのアプリケーションのデプロイ、再デプロイ、アンデプロイ
  • WildFlyサーバーのプロビジョニング、アプリケーションを含んだWildFlyサーバーのプロビジョニング
    • コンテナイメージの作成
  • WildFlyサーバーへのリソースの追加、コマンドの実行
  • WildFlyサーバーを起動し、アプリケーションのソースコードの変更を監視してビルド、再デプロイ

今回は主に最後に書いたdevゴールについて、試していきたいと思います。

WildFly Maven Plugin – wildfly:dev

wildfly:dev

devゴールは、WildFlyサーバーを起動してアプリケーションをパッケージング、デプロイします。その後、アプリケーションのソースコード
変更を監視して、変更があった場合は再度パッケージング、再デプロイを行います。

WildFly Maven Plugin – wildfly:dev

WildFly Maven Plugin 4.1.0.Finalから使えるゴールみたいなので、割と最近(2023年4月)の機能なんですね。

Release 4.1.0.Final · wildfly/wildfly-maven-plugin · GitHub

今回は、こちらを使って簡単なJakarta EEアプリケーションを作って試してみたいと思います。

環境

今回の環境は、こちら。

$ java --version
openjdk 17.0.8.1 2023-08-24
OpenJDK Runtime Environment (build 17.0.8.1+1-Ubuntu-0ubuntu122.04)
OpenJDK 64-Bit Server VM (build 17.0.8.1+1-Ubuntu-0ubuntu122.04, mixed mode, sharing)


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

準備

Mavenプロジェクトの構成は、こんな感じにしました。

    <groupId>...</groupId>
    <artifactId>...</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>10.0.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <finalName>ROOT</finalName>
        <plugins>
            <plugin>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.4.0</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.wildfly.plugins</groupId>
                <artifactId>wildfly-maven-plugin</artifactId>
                <version>4.2.0.Final</version>
                <configuration>
                    <version>29.0.1.Final</version>
                    <server-config>standalone.xml</server-config>
                </configuration>
            </plugin>
        </plugins>
    </build>

Jakarta EEはWeb Profileで10を使うことにします。

WildFly Maven Pluginはこちらですね。

            <plugin>
                <groupId>org.wildfly.plugins</groupId>
                <artifactId>wildfly-maven-plugin</artifactId>
                <version>4.2.0.Final</version>
                <configuration>
                    <version>29.0.1.Final</version>
                    <server-config>standalone.xml</server-config>
                </configuration>
            </plugin>

設定は、最低限versionは指定した方が良いかなと。未指定だと、最新安定版のWildFlyをダウンロードしてきます。
また、server-configでは使用する設定ファイルを指定しています。

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

ひとまず、簡単なJakarata EEアプリケーションを作ります。まずはJakarta RESTful Web Services(JAX-RS)から。

JAX-RSの有効化。

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

package org.littlewings.wildfly.maven;

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

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

JAX-RSリソースクラス。

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

package org.littlewings.wildfly.maven;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("hello")
public class HelloResource {
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String message() {
        return "Hello World";
    }
}

これでwildfly:devゴールを実行します。

$ mvn wildfly:dev

初回はWildFlyに関するアーティファクトをダウンロードしてくるので、それなりに時間がかかります。

それからWildFlyをプロビジョニングして起動、

[INFO] --- wildfly:4.2.0.Final:dev (default-cli) @ ... ---
[INFO] Provisioning default server in /path/to/target/server
[INFO] JBoss Threads version 2.4.0.Final
[INFO] JBoss Remoting version 5.0.27.Final
[INFO] XNIO version 3.8.9.Final
[INFO] XNIO NIO Implementation Version 3.8.9.Final
[INFO] ELY00001: WildFly Elytron version 2.2.1.Final
[INFO] JAVA_HOME : /usr/lib/jvm/java-17-openjdk-amd64
[INFO] JBOSS_HOME: /path/to/target/server
[INFO] JAVA_OPTS : -Xms64m -Xmx512m -Djava.net.preferIPv4Stack=true -Djava.awt.headless=true -Djboss.modules.system.pkgs=org.jboss.byteman --add-exports=java.base/sun.nio.ch=ALL-UNNAMED --add-exports=jdk.unsupported/sun.reflect=ALL-UNNAMED --add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED --add-modules=java.se
[INFO] STANDALONE server is starting up.

〜省略〜

21:33:26,953 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://127.0.0.1:9990/management
21:33:26,953 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0051: Admin console listening on http://127.0.0.1:9990
21:33:26,954 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 29.0.1.Final (WildFly Core 21.1.1.Final) started in 2855ms - Started 280 of 522 services (317 services are lazy, passive or on-demand) - Server configuration file in use: standalone.xml

アプリケーションをパッケージングしてデプロイします。

[INFO] Copying 0 resource from src/main/resources to target/classes
[INFO] Changes detected - recompiling the module! :source
[INFO] Compiling 2 source files with javac [debug target 17] to target/classes
[INFO] Exploding webapp
[INFO] Assembling webapp [...] in [/path/to/target/ROOT]
[INFO] Processing war project
21:33:28,359 INFO  [org.jboss.as.server.deployment] (MSC service thread 1-1) WFLYSRV0027: Starting deployment of "ROOT.war" (runtime-name: "ROOT.war")
21:33:29,187 INFO  [org.jboss.resteasy.resteasy_jaxrs.i18n] (ServerService Thread Pool -- 22) RESTEASY002225: Deploying jakarta.ws.rs.core.Application: class org.littlewings.wildfly.maven.JaxrsActivator
21:33:29,220 INFO  [org.hibernate.validator.internal.util.Version] (ServerService Thread Pool -- 22) HV000001: Hibernate Validator 8.0.0.Final
21:33:29,239 INFO  [org.wildfly.extension.undertow] (ServerService Thread Pool -- 22) WFLYUT0021: Registered web context: '/' for server 'default-server'
21:33:29,299 INFO  [org.jboss.as.server] (management-handler-thread - 1) WFLYSRV0010: Deployed "ROOT.war" (runtime-name : "ROOT.war")

この状態でアプリケーションはデプロイされているので、アクセスして確認することができます。

$ curl localhost:8080/hello
Hello World

ここでmvn wildfly:devは実行したまま、ソースコードを少し変更してみます。

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String message() {
        return "Hello World!!";
    }

すると、変更が検知されて再度パッケージングと再デプロイが行われます。

[INFO] Copying 0 resource from src/main/resources to target/classes
[INFO] Changes detected - recompiling the module! :source
[INFO] Compiling 2 source files with javac [debug target 17] to target/classes
[INFO] Exploding webapp
[INFO] Assembling webapp [...] in [/path/to/target/ROOT]
[INFO] Processing war project
21:33:28,359 INFO  [org.jboss.as.server.deployment] (MSC service thread 1-1) WFLYSRV0027: Starting deployment of "ROOT.war" (runtime-name: "ROOT.war")
21:33:29,187 INFO  [org.jboss.resteasy.resteasy_jaxrs.i18n] (ServerService Thread Pool -- 22) RESTEASY002225: Deploying jakarta.ws.rs.core.Application: class org.littlewings.wildfly.maven.JaxrsActivator
21:33:29,220 INFO  [org.hibernate.validator.internal.util.Version] (ServerService Thread Pool -- 22) HV000001: Hibernate Validator 8.0.0.Final
21:33:29,239 INFO  [org.wildfly.extension.undertow] (ServerService Thread Pool -- 22) WFLYUT0021: Registered web context: '/' for server 'default-server'
21:33:29,299 INFO  [org.jboss.as.server] (management-handler-thread - 1) WFLYSRV0010: Deployed "ROOT.war" (runtime-name : "ROOT.war")
[INFO] Changes detected - recompiling the module! :source
[INFO] Compiling 2 source files with javac [debug target 17] to target/classes
[INFO] Exploding webapp
[INFO] Assembling webapp [...] in [/path/to/target/ROOT]
[INFO] Processing war project
21:36:25,861 INFO  [org.wildfly.extension.undertow] (ServerService Thread Pool -- 22) WFLYUT0022: Unregistered web context: '/' from server 'default-server'
21:36:25,880 INFO  [org.jboss.as.server.deployment] (MSC service thread 1-5) WFLYSRV0028: Stopped deployment ROOT.war (runtime-name: ROOT.war) in 22ms
21:36:25,885 INFO  [org.jboss.as.server.deployment] (MSC service thread 1-2) WFLYSRV0027: Starting deployment of "ROOT.war" (runtime-name: "ROOT.war")
21:36:26,004 INFO  [org.jboss.resteasy.resteasy_jaxrs.i18n] (ServerService Thread Pool -- 81) RESTEASY002225: Deploying jakarta.ws.rs.core.Application: class org.littlewings.wildfly.maven.JaxrsActivator
21:36:26,006 INFO  [org.wildfly.extension.undertow] (ServerService Thread Pool -- 81) WFLYUT0021: Registered web context: '/' for server 'default-server'
21:36:26,026 INFO  [org.jboss.as.server] (management-handler-thread - 1) WFLYSRV0013: Redeployed "ROOT.war"
[INFO] Nothing to compile - all classes are up to date
[INFO] Exploding webapp
[INFO] Assembling webapp [...] in [/path/to/target/ROOT]
[INFO] Processing war project
21:36:26,042 INFO  [org.wildfly.extension.undertow] (ServerService Thread Pool -- 22) WFLYUT0022: Unregistered web context: '/' from server 'default-server'
21:36:26,047 INFO  [org.jboss.as.server.deployment] (MSC service thread 1-3) WFLYSRV0028: Stopped deployment ROOT.war (runtime-name: ROOT.war) in 5ms
21:36:26,048 INFO  [org.jboss.as.server.deployment] (MSC service thread 1-5) WFLYSRV0027: Starting deployment of "ROOT.war" (runtime-name: "ROOT.war")
21:36:26,248 INFO  [org.jboss.resteasy.resteasy_jaxrs.i18n] (ServerService Thread Pool -- 22) RESTEASY002225: Deploying jakarta.ws.rs.core.Application: class org.littlewings.wildfly.maven.JaxrsActivator
21:36:26,251 INFO  [org.wildfly.extension.undertow] (ServerService Thread Pool -- 22) WFLYUT0021: Registered web context: '/' for server 'default-server'
21:36:26,268 INFO  [org.jboss.as.server] (management-handler-thread - 1) WFLYSRV0013: Redeployed "ROOT.war"

確認。

$ curl localhost:8080/hello
Hello World!!

変更が反映されています。

便利ですね。

ところで、「WildFlyサーバーをプロビジョニングする」と言っていましたが、これはどういうことかというとtarget/serverディレクト
provisioningDirで変更可能)にWildFlyが構築されることを指しています。

$ ll target/server
合計 524
drwxrwxr-x 12 xxxxx xxxxx   4096  9月 18 21:33 ./
drwxrwxr-x  7 xxxxx xxxxx   4096  9月 18 21:33 ../
drwxrwxr-x  3 xxxxx xxxxx   4096  9月 18 21:33 .galleon/
drwxrwxr-x  2 xxxxx xxxxx   4096  9月 18 21:33 .installation/
drwxrwxr-x  3 xxxxx xxxxx   4096  9月 18 21:33 .well-known/
-rw-rw-r--  1 xxxxx xxxxx  26530  9月 18 21:32 LICENSE.txt
-rw-rw-r--  1 xxxxx xxxxx   2161  9月 18 21:32 README.txt
drwxrwxr-x  3 xxxxx xxxxx   4096  9月 18 21:32 appclient/
drwxrwxr-x  3 xxxxx xxxxx   4096  9月 18 21:32 bin/
-rw-rw-r--  1 xxxxx xxxxx  19358  9月 18 21:32 copyright.txt
drwxrwxr-x  6 xxxxx xxxxx   4096  9月 18 21:32 docs/
drwxrwxr-x  4 xxxxx xxxxx   4096  9月 18 21:33 domain/
-rw-rw-r--  1 xxxxx xxxxx 431393  9月 18 21:33 jboss-modules.jar
drwxrwxr-x  3 xxxxx xxxxx   4096  9月 18 21:32 modules/
drwxrwxr-x  8 xxxxx xxxxx   4096  9月 18 21:33 standalone/
drwxrwxr-x  2 xxxxx xxxxx   4096  9月 18 21:32 welcome-content/

設定ファイルなどもしっかりあります。

$ ll target/server/standalone/configuration
合計 244
drwxrwxr-x 3 xxxxx xxxxx  4096  9月 18 21:36 ./
drwxrwxr-x 8 xxxxx xxxxx  4096  9月 18 21:33 ../
-rw------- 1 xxxxx xxxxx   711  9月 18 21:32 application-roles.properties
-rw------- 1 xxxxx xxxxx   935  9月 18 21:32 application-users.properties
-rw-rw-r-- 1 xxxxx xxxxx  1754  9月 18 21:33 logging.properties
-rw------- 1 xxxxx xxxxx   669  9月 18 21:32 mgmt-groups.properties
-rw------- 1 xxxxx xxxxx  1112  9月 18 21:32 mgmt-users.properties
-rw-rw-r-- 1 xxxxx xxxxx 38419  9月 18 21:33 standalone-full-ha.xml
-rw-rw-r-- 1 xxxxx xxxxx 32949  9月 18 21:33 standalone-full.xml
-rw-rw-r-- 1 xxxxx xxxxx 34548  9月 18 21:33 standalone-ha.xml
-rw-rw-r-- 1 xxxxx xxxxx 12643  9月 18 21:33 standalone-load-balancer.xml
-rw-rw-r-- 1 xxxxx xxxxx 26082  9月 18 21:33 standalone-microprofile-ha.xml
-rw-rw-r-- 1 xxxxx xxxxx 22920  9月 18 21:33 standalone-microprofile.xml
-rw-rw-r-- 1 xxxxx xxxxx 29855  9月 18 21:36 standalone.xml
drwxrwxr-x 4 xxxxx xxxxx  4096  9月 18 21:36 standalone_xml_history/

Jakarta Persistence(JPA)を使う

JAX-RSだけで終わってもなんなので、JPAも使ってみましょう。

wildfly:devゴールを実行しているターミナルは、1度停止します。

JPAを使うということは、JDBCドライバーやデータソースを登録することになります。

そういったサンプルはこちらにありますが、devゴールとはちょっと相性が悪いです。PostgreSQLJDBCドライバーとデータソースを
デプロイ、登録するサンプルになっているのですが、これはすでに起動済みのWildFlyを利用するものだからですね。
※devゴールでもリモートのWildFlyを使うように設定することはできます

WildFly Maven Plugin – Adding Resources Examples

起動済みのWildFlyを使う場合は、以降の記載は読み飛ばしてよいでしょう。

今回の構成で進める場合はどうするかというと、JDBCドライバーやデータソースを組み込んだWildFlyをプロビジョニングします。

これにはGalleonを使うことになります。

Galleon Provisioning Guide

以前、WildFly Bootable JARを試している時に少し調べてみました。

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

Galleonを使うことで、WildFlyを好きな構成に組み上げることができます。

WildFly Maven Pluginで、こちらに特化したエントリーはこちらを参照してください。

WildFly Maven Pluginで、WildFlyをプロビジョニングしたり、アプリケーションを含めたりする - CLOVER🍀

データソースとJDBCドライバーのデプロイには、WildFly DataSources Galleon Feature Packを使用します。

GitHub - wildfly-extras/wildfly-datasources-galleon-pack: WildFly Feature Pack for DataSources

これはWildFly Bootable JARでも使ったことがあります。

WildFly Bootable JARに、DataSourceを組み込む - CLOVER🍀

使用するデータベースは、MySQLとします。MySQLは8.0.34で、IPアドレスは172.17.0.2で動作しているものとします。

 MySQL  localhost:3306 ssl  practice  SQL > select version();
+-----------+
| version() |
+-----------+
| 8.0.34    |
+-----------+
1 row in set (0.0006 sec)

WildFly Maven Pluginの設定は、最終的にこうなりました。

            <plugin>
                <groupId>org.wildfly.plugins</groupId>
                <artifactId>wildfly-maven-plugin</artifactId>
                <version>4.2.0.Final</version>
                <configuration>
                    <feature-packs>
                        <feature-pack>
                            <location>wildfly@maven(org.jboss.universe:community-universe)#29.0.1.Final</location>
                        </feature-pack>
                        <feature-pack>
                            <location>org.wildfly:wildfly-datasources-galleon-pack:5.0.0.Final</location>
                        </feature-pack>
                    </feature-packs>
                    <layers>
                        <layer>jaxrs-server</layer>
                        <layer>mysql-datasource</layer>
                    </layers>
                    <env>
                        <MYSQL_DATASOURCE>MySqlDs</MYSQL_DATASOURCE>
                        <MYSQL_JNDI>java:jboss/datasources/MySqlDs</MYSQL_JNDI>
                        <MYSQL_URL>jdbc:mysql://172.17.0.2:3306/practice?characterEncoding=utf-8&amp;connectionCollation=utf8mb4_0900_bin</MYSQL_URL>
                        <MYSQL_USER>kazuhira</MYSQL_USER>
                        <MYSQL_PASSWORD>password</MYSQL_PASSWORD>
                        <MYSQL_ENABLED>true</MYSQL_ENABLED>
                    </env>
                </configuration>
            </plugin>

WildFly DataSources Galleon Feature Packの方にWildFly Maven PluginとWildFly JAR Maven Pluginのサンプルがあるのですが、
こちらを参考に。

Provisioning using the WildFly Maven Plugin or the WildFly JAR Maven plugin

GalleonのFeature Packとして、WildFly 29.0.1.FinalとWildFly DataSources Galleon Feature Packを指定。

                    <feature-packs>
                        <feature-pack>
                            <location>wildfly@maven(org.jboss.universe:community-universe)#29.0.1.Final</location>
                        </feature-pack>
                        <feature-pack>
                            <location>org.wildfly:wildfly-datasources-galleon-pack:5.0.0.Final</location>
                        </feature-pack>
                    </feature-packs>

レイヤーにはjaxrs-servermysql-datasourceを選択。

                    <layers>
                        <layer>jaxrs-server</layer>
                        <layer>mysql-datasource</layer>
                    </layers>

jaxrs-serverレイヤーについては、こちらに記載があります。ざっくり、JAX-RSCDIJPAが使えるWildFlyを構築できる、くらいに
捉えておけばよいと思います。

Galleon Provisioning Guide

mysql-datasourceWildFly DataSources Galleon Feature Packが提供するレイヤーです。

MySQLJDBCドライバーは、mysql-datasourceレイヤーを加えるとデプロイされます。
あとは、データソースの設定を行います。設定は接続先のデータベースごとにあるのですが、MySQLの場合はこちら。

https://github.com/wildfly-extras/wildfly-datasources-galleon-pack/blob/5.0.0.Final/doc/mysql/README.md

設定は、今回は環境変数で行うことにしました。

                    <env>
                        <MYSQL_DATASOURCE>MySqlDs</MYSQL_DATASOURCE>
                        <MYSQL_JNDI>java:jboss/datasources/MySqlDs</MYSQL_JNDI>
                        <MYSQL_URL>jdbc:mysql://172.17.0.2:3306/practice?characterEncoding=utf-8&amp;connectionCollation=utf8mb4_0900_bin</MYSQL_URL>
                        <MYSQL_USER>kazuhira</MYSQL_USER>
                        <MYSQL_PASSWORD>password</MYSQL_PASSWORD>
                        <MYSQL_ENABLED>true</MYSQL_ENABLED>
                    </env>

これで、WildFly側の準備はできました。

あとはJPAを使うようにアプリケーションにソースコードを追加します。

Persistence Unitの設定。WildFly Maven Pluginによって構成されるデータソースを使うようにしています。

src/main/resources/META-INF/persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="https://jakarta.ee/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd"
             version="3.0">
    <persistence-unit name="sample.pu" transaction-type="JTA">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <jta-data-source>java:jboss/datasources/MySqlDs</jta-data-source>
        <properties>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>
            <property name="jakarta.persistence.schema-generation.database.action" value="drop-and-create"/>
        </properties>
    </persistence-unit>
</persistence>

スキーマ生成は、今回は簡単にdrop-and-createにしておきました…。

JPAのエンティティ。

src/main/java/org/littlewings/wildfly/maven/Book.java

package org.littlewings.wildfly.maven;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;

@Entity
@Table(name = "book")
public class Book {
    @Id
    private String isbn;

    private String title;

    private Integer price;

    // getter/setterは省略
}

EntityManagerを使う、JAX-RSリソースクラス。

src/main/java/org/littlewings/wildfly/maven/BookResource.java

package org.littlewings.wildfly.maven;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.transaction.Transactional;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;

import java.util.List;

@ApplicationScoped
@Path("book")
public class BookResource {
    @PersistenceContext
    EntityManager entityManager;

    @GET
    @Path("{isbn}")
    @Produces(MediaType.APPLICATION_JSON)
    @Transactional
    public Book find(@PathParam("isbn") String isbn) {
        return entityManager.find(Book.class, isbn);
    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Transactional
    public List<Book> findAll() {
        return entityManager
                .createQuery("select b from Book b order by b.price desc", Book.class)
                .getResultList();
    }

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    @Transactional
    public Book register(Book book) {
        if (entityManager.find(Book.class, book.getIsbn()) != null) {
            entityManager.merge(book);
        } else {
            entityManager.persist(book);
        }

        return book;
    }
}

ここまで設定したら、1度mvn cleanしておきます。プロビジョニング済みのWildFlyがあると、設定を変えても再プロビジョニングされない
みたいなので。

$ mvn clean

では、起動。

$ mvn wildfly:dev

すると、WildFlyサーバーがプロビジョニングされるのですが、少しログの雰囲気が変わります。

[INFO] --- wildfly:4.2.0.Final:dev (default-cli) @ ... ---
[INFO] Provisioning server in /path/to/target/server
[INFO] Resolving feature-packs
[INFO] Installing packages
[INFO] Resolving artifacts
[INFO] Generating configurations
[INFO] JBoss Modules version 2.1.0.Final

〜省略〜

GalleonのFeature Packを使っていることがわかります。

確認。

$ curl -XPOST -H 'Content-Type: application/json' http://localhost:8080/book -d '{"isbn": "978-4774183169", "title": "パーフェクト Java EE", "price": 3520}'
{"isbn":"978-4774183169","price":3520,"title":"パーフェクト Java EE"}


$ curl http://localhost:8080/book
[{"isbn":"978-4774183169","price":3520,"title":"パーフェクト Java EE"}]


$ curl http://localhost:8080/book/978-4774183169
{"isbn":"978-4774183169","price":3520,"title":"パーフェクト Java EE"}

OKですね。

なお、今回はpersistence.xmlに以下のように指定したので、ソースコードを変更する度にテーブルが再作成されるので注意を…。

            <property name="jakarta.persistence.schema-generation.database.action" value="drop-and-create"/>

おわりに

WildFly Maven Pluginのdevゴールを試してみました。

なんとなく、簡単にJAX-RSを試してそれからJPAを試しておわり…くらいに思っていて、Galleonは今回は出さないつもりだったのですが、
この使い方だとGalleonを使って構成しないとどうにもならなくなったので、こうなりました…。
思った以上に長くなりましたね…。

WildFly Maven PluginとGalleonを使ったプロビジョニングは、別の機会にまた深堀したいかなと思います。