これは、なにをしたくて書いたもの?
WildFly Bootable JAR Maven Pluginを使って、Bootable JARを作成してみました。
WildFly Bootable JARを試す - CLOVER🍀
今度は、Bootable JARにDataSourceを組み込んでみたいと思います。
WildFly DataSources Galleon Feature Pack
WildFly Bootable JARにDataSourceを組み込むためには、Feature Packを使用します。
GitHub - wildfly-extras/wildfly-datasources-galleon-pack: WildFly Feature Pack for DataSources
こちらをWildFly Bootable JAR Maven PluginにFeature Packおよびlayerに組み込むことで、JDBCドライバのデプロイと
DataSourceの作成を行うことができます。
対応しているデータベースは、
となります。
今回は、MySQLのJDBCドライバおよびDataSourceを使ってみましょう。
環境
今回の環境は、こちらです。
$ java --version openjdk 11.0.11 2021-04-20 OpenJDK Runtime Environment (build 11.0.11+9-Ubuntu-0ubuntu2.20.04) OpenJDK 64-Bit Server VM (build 11.0.11+9-Ubuntu-0ubuntu2.20.04, mixed mode, sharing) $ mvn --version Apache Maven 3.8.1 (05c21c65bdfed0f71a2f2ada8b84da59348c4c5d) Maven home: $HOME/.sdkman/candidates/maven/current Java version: 11.0.11, vendor: Ubuntu, runtime: /usr/lib/jvm/java-11-openjdk-amd64 Default locale: ja_JP, platform encoding: UTF-8 OS name: "linux", version: "5.4.0-77-generic", arch: "amd64", family: "unix"
MySQLは8.0.25を使い、172.17.0.2で動作しているものとします。
サンプルアプリケーション
まずは、WildFlyにデプロイするためのアプリケーションを作成します。
DataSourceを使いたいので、JPAを使うことにしましょう。あとは、JAX-RSとCDI、JTAも使う感じで。
まずは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-with-datasource</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>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <failOnMissingWebXml>false</failOnMissingWebXml> </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.wildfly.plugins</groupId> <artifactId>wildfly-jar-maven-plugin</artifactId> <version>5.0.1.Final</version> <configuration> <!-- 後で --> </configuration> </plugin> </plugins> </project>
JAX-RSの有効化。
src/main/java/org/littlewings/jakartaee/wildfly/bootable/JaxrsActivator.java
package org.littlewings.jakartaee.wildfly.bootable; import javax.ws.rs.ApplicationPath; import javax.ws.rs.core.Application; @ApplicationPath("") public class JaxrsActivator extends Application { }
JPAのエンティティクラス。
src/main/java/org/littlewings/jakartaee/wildfly/bootable/Book.java
package org.littlewings.jakartaee.wildfly.bootable; import javax.persistence.Entity; import javax.persistence.Id; import javax.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/jakartaee/wildfly/bootable/BookResource.java
package org.littlewings.jakartaee.wildfly.bootable; import java.util.List; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.transaction.Transactional; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; @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; } }
Persistence Unitの設定は、こちら。DataSourceを使うようにしています。
src/main/resources/META-INF/persistence.xml
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_2.xsd" version="2.2"> <persistence-unit name="wildfly.bootable.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.MySQL8Dialect"/> <property name="hibernate.show_sql" value="true"/> <property name="hibernate.format_sql" value="true"/> <property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/> </properties> </persistence-unit> </persistence>
簡単に済ませたいので、Entityに対応するテーブルはHibernateで作成するようにしました。
WildFly DataSources Galleon Feature Packを設定する
では、WildFly DataSources Galleon Feature Packを使って、WildFly Bootable JAR Maven Pluginを設定していきましょう。
参考にするとよいサンプルは、こちらですね。
https://github.com/wildfly-extras/wildfly-jar-maven-plugin/tree/5.0.1.Final/examples/postgresql
WildFly DataSources Galleon Feature Packには、データベースの種類ごとにREADME.md
が用意されています。
MySQLの場合はこちら。
まずは、WildFly DataSources Galleon Feature PackをWildFly Bootable JAR Maven Pluginの設定に追加します。
<feature-packs> <feature-pack> <location>wildfly@maven(org.jboss.universe:community-universe)#24.0.0.Final</location> </feature-pack> <feature-pack> <groupId>org.wildfly</groupId> <artifactId>wildfly-datasources-galleon-pack</artifactId> <version>2.0.2.Final</version> </feature-pack> </feature-packs>
feature-packs
を使用すると、feature-pack-location
要素は使えなくなります。WildFlyのどのバージョンを使うかは、
feature-pack
、location
として指定することになります。
Providing a list of Galleon feature-packs
wildfly-datasources-galleon-pack
を追加すると、Galleon layerが追加されます。
MySQLの場合はmysql-datasource
とmysql-driver
が使えるようになります。
<layers> <layer>jaxrs-server</layer> <layer>mysql-datasource</layer> <layer>mysql-driver</layer> <layer>management</layer> </layers>
mysql-datasource
は非XA DataSourceをプロビジョニングするlayerで、mysql-driver
に依存しています。
なので、mysql-driver
layerは、実は明示しなくてもかまいません。
<layers> <layer>jaxrs-server</layer> <layer>mysql-datasource</layer> <!-- <layer>mysql-driver</layer> --> <layer>management</layer> </layers>
mysql-driver
は、MySQLのJDBCドライバをプロビジョニングするlayerです。
MySQLに限らず、JDBCドライバをプロビジョニングするlayerを使う場合、どのバージョンのJDBCドライバが使われるかは
wildfly-datasources-galleon-pack-parent
のpom.xml
で確認できます。
wildfly-datasources-galleon-pack
の2.0.2.Finalの場合、MySQLのJDBCドライバは8.0.25がプロビジョニングされます。
ここまでの、全体の設定はこんな感じです。
<plugin> <groupId>org.wildfly.plugins</groupId> <artifactId>wildfly-jar-maven-plugin</artifactId> <version>5.0.1.Final</version> <configuration> <feature-packs> <feature-pack> <location>wildfly@maven(org.jboss.universe:community-universe)#24.0.0.Final</location> </feature-pack> <feature-pack> <groupId>org.wildfly</groupId> <artifactId>wildfly-datasources-galleon-pack</artifactId> <version>2.0.2.Final</version> </feature-pack> </feature-packs> <layers> <layer>jaxrs-server</layer> <layer>mysql-datasource</layer> <!-- <layer>mysql-driver</layer> --> <layer>management</layer> </layers> <excluded-layers> <layer>deployment-scanner</layer> </excluded-layers> <plugin-options> <jboss-fork-embedded>true</jboss-fork-embedded> </plugin-options> </configuration> </plugin>
deployment-scanner
は不要なので外しています。
jboss-fork-embedded
というのは、Mavenセッションで多くのBootable JARを構築する際にtrue
とすることが推奨されて
いるのですが、ほとんどのサンプルがtrue
になっているので合わせることにしました。
これで、パッケージングします。
※execution
は設定していません
$ mvn package wildfly-jar:package
起動してみます。
$ java -jar target/wildfly-bootable-jar-with-datasource-0.0.1-SNAPSHOT-bootable.jar
18:59:34,714 INFO [org.jboss.as.connector.subsystems.datasources] (ServerService Thread Pool -- 24) WFLYJCA0005: Deploying non-JDBC-compliant driver class com.mysql.cj.jdbc.Driver (version 8.0) 18:59:34,725 INFO [org.jboss.as.connector.deployers.jdbc] (MSC service thread 1-7) WFLYJCA0018: Started Driver service with driver-name = mysql
起動時にいくつかERROR
も表示されます。
18:59:34,807 ERROR [org.jboss.as.controller.management-operation] (ServerService Thread Pool -- 24) WFLYCTL0013: Operation ("add") failed - address: ([ ("subsystem" => "datasources"), ("data-source" => "MySQLDS") ]) - failure description: "WFLYCTL0211: Cannot resolve expression '${org.wildfly.datasources.mysql.user-name,env.MYSQL_USER,env.OPENSHIFT_MYSQL_DB_USERNAME}'" 18:59:36,786 ERROR [org.jboss.as.controller.management-operation] (Controller Boot Thread) WFLYCTL0013: Operation ("add") failed - address: ([ ("subsystem" => "datasources"), ("data-source" => "MySQLDS") ]) - failure description: "WFLYCTL0211: Cannot resolve expression '${org.wildfly.datasources.mysql.user-name,env.MYSQL_USER,env.OPENSHIFT_MYSQL_DB_USERNAME}'" 18:59:36,788 ERROR [org.jboss.as.controller.management-operation] (Controller Boot Thread) WFLYCTL0013: Operation ("add") failed - address: ([("deployment" => "wildfly-bootable-jar-with-datasource-0.0.1-SNAPSHOT.war")]) - failure description: { "WFLYCTL0412: Required services that are not installed:" => ["jboss.naming.context.java.jboss.datasources.MySqlDs"], "WFLYCTL0180: Services with missing/unavailable dependencies" => [ "jboss.persistenceunit.\"ROOT.war#wildfly.bootable.pu\" is missing [jboss.naming.context.java.jboss.datasources.MySqlDs]", "jboss.persistenceunit.\"ROOT.war#wildfly.bootable.pu\".__FIRST_PHASE__ is missing [jboss.naming.context.java.jboss.datasources.MySqlDs]" ] }
これは、DataSourceをプロビジョニングしたものの、設定をしていないからですね。
DataSourceの設定は、こちらを参照して行います。
設定方法は環境変数、もしくはシステムプロパティで行います。必須の設定とオプションの設定があります。
今回は、システムプロパティで行うことにしましょう。
$ java \ -Dorg.wildfly.datasources.mysql.connection-url='jdbc:mysql://172.17.0.2:3306/practice?characterEncoding=utf-8&characterSetResults=utf-8' \ -Dorg.wildfly.datasources.mysql.user-name=kazuhira \ -Dorg.wildfly.datasources.mysql.password=password \ -Dorg.wildfly.datasources.mysql.jndi-name=java:jboss/datasources/MySqlDs \ -jar target/wildfly-bootable-jar-with-datasource-0.0.1-SNAPSHOT-bootable.jar
JNDI名は必須ではないのですが、今回は明示的に指定することにしました。
今度は、ERROR
は表示されません。
動作確認してみます。
$ 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です。
動作確認できました。
つまり、WildFly DataSources Galleon Feature Packを使ったDataSourceの設定は、アプリケーションの実行時に指定することが
わかりました。
開発モードで使う
ところで、個人的にはDataSourceも開発モードで使いたいものです。つまり、以下のようなコマンドですね。
$ mvn wildfly-jar:dev-watch $ mvn wildfly-jar:dev
今回はwildfly-jar:dev-watch
を使いたいと思います。
DataSourceの設定は実行時に行うことがわかりましたが、開発モードで使う時にはどうも相性が悪いようです(mvn
コマンド
実行時に指定しても無視される)。
どうしようかなと思いましたが、WildFly CLIでDataSourceを設定することにしました。
Configuring the server during packaging
DataSourceを作成するスクリプトを作成して
jboss-cli-scripts/script1.cli
data-source add \ --name=MySqlDs \ --jndi-name=java:jboss/datasources/MySqlDs \ --user-name=kazuhira \ --password=password \ --driver-name=mysql \ --connection-url=jdbc:mysql://172.17.0.2:3306/practice?characterEncoding=utf-8&characterSetResults=utf-8
WildFly Bootable JAR Maven Pluginは、こんな感じに設定。
<plugin> <groupId>org.wildfly.plugins</groupId> <artifactId>wildfly-jar-maven-plugin</artifactId> <version>5.0.1.Final</version> <configuration> <feature-packs> <feature-pack> <location>wildfly@maven(org.jboss.universe:community-universe)#24.0.0.Final</location> </feature-pack> <feature-pack> <groupId>org.wildfly</groupId> <artifactId>wildfly-datasources-galleon-pack</artifactId> <version>2.0.2.Final</version> </feature-pack> </feature-packs> <layers> <layer>jaxrs-server</layer> <layer>mysql-driver</layer> <layer>management</layer> </layers> <excluded-layers> <layer>deployment-scanner</layer> </excluded-layers> <cli-sessions> <cli-session> <script-files> <script>jboss-cli-scripts/script1.cli</script> </script-files> </cli-session> </cli-sessions> <plugin-options> <jboss-maven-dist/> <jboss-fork-embedded>true</jboss-fork-embedded> </plugin-options> </configuration> </plugin>
先ほどとの差は、layerからmysql-datasource
を外してmysql-driver
のみとし
<layers> <layer>jaxrs-server</layer> <layer>mysql-driver</layer> <layer>management</layer> </layers>
WildFly CLIスクリプトを実行するため、cli-sessions
を追加。
<cli-sessions> <cli-session> <script-files> <script>jboss-cli-scripts/script1.cli</script> </script-files> </cli-session> </cli-sessions>
あとはJARファイルを小さくするためにjboss-maven-dist
も入れておきます。
<plugin-options> <jboss-maven-dist/> <jboss-fork-embedded>true</jboss-fork-embedded> </plugin-options>
では、wildfly-jar:dev-watch
で起動。
$ mvn wildfly-jar:dev-watch
実行中に、WildFly CLIが実行されている様子が確認できます。
[INFO] Executing CLI, CLI Session, scripts=[jboss-cli-scripts/script1.cli], resolve-expressions=true, properties-file=null [INFO] CLI scripts execution done.
これでDataSourceが作成されるので、あとはふつうに使えます。
WildFly CLI向けのスクリプトですが、パッケージングだけではなく起動時にも適用できるようなので、これはこれで
覚えておいた方が良さそうですね(--cli-script=<path to CLI script file>
)。
オマケ
データソースを作成するために、デプロイされるJDBCドライバがどの名前で登録されるのかをログから確認して
いたのですが、よく見るとこちらで確認できますね。
また、JBoss Moduleとしての定義もこちらにあります。
このあたりを見ていると、なんとなくFeature Packを作れるようになるんじゃないか、という気になってきますね。
そういう気になるだけですが。
まとめ
WildFly Bootable JARに、DataSourceを組み込んでみました。
開発モードでも使えた方が良いな、と思っていたので、そのあたりもクリアできる方法がわかってよかったかなと思います。