タグ「Java EE 7 検証環境構築」が付けられているもの

  1. Java EE 7 検証環境構築(1) WildFly + JBoss Tools で EARプロジェクトを作成し Arquillian で ユニットテストをグリーンにするところまで
  2. Java EE 7 検証環境構築(2) WildFly に DataSourceを作成
  3. Java EE 7 検証環境構築(3) JPAからMySQLに接続するユニットテストをArquillianで実行
  4. Java EE 7 検証環境構築(4) Java EE での DI(Dependency Injection) および CDI(Contexts and Dependency Injection)をながめる
  5. Java EE 7 検証環境構築(5) JBoss Toolsが生成したサンプルソースのCDIを確認する
  6. Java EE 7 検証環境構築(6) JPA エンティティの作成と挿入
  7. Java EE 7 検証環境構築(7) JPA 問い合わせ(1) 名前付きクエリを使ってみる。テストでトランザクションも意識する
  8. Java EE 7 検証環境構築(8) JPA 問い合わせ(2) 動的クエリとCriteria API を試す
  9. Java EE 7 検証環境構築(9) jBatch 概要をおさえる
  10. Java EE 7 検証環境構築(10) JBoss Tools で作成した EARプロジェクトをJava EE 6 から 7 に変更する
  11. Java EE 7 検証環境構築(11) jBatch用 プロジェクトの作成を行う
  12. Java EE 7 検証環境構築(12) jBatch 簡易サンプル作成と Arquillian でユニットテスト
  13. Java EE 7 検証環境構築(13) jBatch REST サービス経由で実行する
  14. Java EE 7 検証環境構築(14) WildFly の管理をGUIで行う
  15. Java EE 7 検証環境構築(15) WildFly を サービスとして設定する(Windows/Linux)
  16. Java EE 7 検証環境構築(16) WildFly と Apache を mod_jk で連携させる(Widows)

Hyper-V 上の Windows 7 へ Apache をインストール、WildFly との連携を設定を行ってみる。

1. WildFly での Apache との連携方法

まずは、WildFly 8 Administration から、簡単に内容を確認。

Apache Tomcat の mod_jk はこれまでロードバランスソリューションのデフォルトソフトウェアであったしWildFly 8 でもこれまで通り利用出来る。

WildFly8では、mod_clusterも利用できる。mod_cluster は、mod_jk のような、HTTP ベースのロードバランサーで、リクエストをアプリケーションサーバーインスタンスへ転送するのに利用出来る。
mod_jk との重要な違いは、コミュニケーションが、バックエンドサーバーから Webサーバー へと逆方向へも進められること(Webサーバーからアプリケーションサーバへの一方通行ではない)
以下のような利点がある。

1.1 mod_clusterの利点

(1) 設定

  • Webサーバーで、クラスタートポロジーを前もって知る必要がないため、設定を動的に出来る。
  • Webサーバー側の設定を最小限に出来る

(2) ロードバランシング

  • 主要な計算がバックグラウンドサーバーで行われるため、ロードバランシングが改善する。
  • 粒度の細かいWebアプリケーションのライフサイクルコントロールが出来る。

(3) ダウンロード

最初、mod_cluster で構成しようと思ったが、mod_cluster のサイトでは、組込み済みのApacheがダウンロード出来るような感じだったのだが、今回はすでにApacheを導入してしまっていたため、mod_jk の構成を行おうとおもう。

2. Windows へ Apacheをインストール

https://httpd.apache.org/

apache_download

ダウンロードサイトから、win32-x86 用のバイナリをダウンロード

http://www.poolsaboveground.com/apache//httpd/binaries/

apache_download2

ウィザードに従いインストールを進める

apache_install01

とりあえず、Network Domain、Server Nameともに、localhost で進める

apache_install02

Typical で続行。

apache_install03

以上でインストール完了!サービスとして Apache Monitor がインストールされ、タスクバーから、開始、停止、再開が行えるようになる。http://localhost で It works! が表示される。

apache_install04

apache_install05

3.サンプルWebアプリケーションの作成

別になんでもよいのがだ、動作確認用に JBoss EE Web プロジェクトをデフォルトのまま作成する。

jboss_web_proj01

jboss-javaee6-webapp という名前のアプリケーションが作成される。

Eclipse上から、http://localhost:8080/jboss-javaee6-webapp で動作することを確認しておく。

jboss_web_proj02

4. mod_jk の設定

ここから、いよいよ mod_jk の設定。

4.1 mod_jk のダウンロードと配置

まず、モジュールをダウンロード

Apache Tomcat サイト http://tomcat.apache.org/ から、Connector ダウンロードサイトへ移動

http://tomcat.apache.org/download-connectors.cgi

mod_jk

Apache のバージョンにあった、Windows用のバイナリをダウンロード

http://apache.petsads.us/tomcat/tomcat-connectors/jk/binaries/windows/

解凍すると、mod_jk.so というファイルがあるので、

Apacheのインストールディレクトリのmodules へコピーする。

mod_jk_deploy

4.2 http.conf の編集

Apache インストールディレクトリ 配下の conf の http.conf ファイル末尾に、mod-jk.conf ファイルを読み込む記述を追記する。

Include conf/mod-jk.conf

4.3 mod-jk.conf の作成

同様にconf ディレクトリに mod-jk.conf ファイルを作成し、以下の様な内容を記述する。

JkMount に、先ほど作成した検証用Webアプリケーションのコンテキストパスを指定する。

LoadModule jk_module modules/mod_jk.so
JkWorkersFile conf/workers.properties
JkLogFile logs/mod_jk.log
JkLogLevel info
JkMount /jboss-javaee6-webapp/* loadbalancer
JkShmFile logs/jk.shm
ディレクティブ 内容
LoadModule mod_jk モジュールをロードする。mod_jk lib のファイル名を指定する。
JkMount ApacheにどのURLをmod_jkモジュールに転送すべきか伝える。

上記例では、/myapp/* のすべてのリクエストが mod_jk load-balancerに送られる。
JkWorkersFile worker ファイルは、workers.properties の場所を設定。

クラスタ設定とWebファーム(複数のWebサーバを組み合わせて1台のWebサーバーのように見せかけたシステム)の静的なノードのリストを含む。
JkLogFile ログの出力先
JkLogLevel debug/error/info でログレベルを指定

動作に問題があるときは、JkLogLevel を debug に設定し、Apache インストールディレクトリ logs の mod_jk.logを確認。

4.4 workers.properties の作成

上記 mod-jk.conf で参照する、workers.properties を記述する。

以下は、ロードバランスを行う例。

ローカルホストのポートオフセットを 150でクラスタリング設定をしている。

worker.list=loadbalancer,status
worker.worker1.type=ajp13
worker.worker1.host=localhost
worker.worker1.port=8009

worker.worker2.type=ajp13
worker.worker2.host=localhost
worker.worker2.port=8159

worker.loadbalancer.balance_workers=worker1,worker2
worker.loadbalancer.sticky_session=1

WildFly で Standalone で構成するなら、上記から、worker2 の箇所をコメントアウトすればOK

5. WildFly の設定

WildFly でAJPリクエストを受け付ける設定

管理コンソールの左ペインで、Webサブシステムを展開、HTTPを選択し、右ペインでAJPを選択し、Add。

wildfly_configure_ajp

AJPリスナー名と、ソケットバインディング ajp を設定

wildflg_ajp_add

Edit ボタンを押して、Enabled チェックボックスをチェック

(ドメインモードで、ha プロファイルで開始した場合、150のポートオフセットを指定)

wildfly_ajp_listener_edit

WildFly が 8009 を待ち受けているか確認。

PS C:\Users\piroto> netstat -an | Select-String "8009"

  TCP    127.0.0.1:8009         0.0.0.0:0              LISTENING
  TCP    127.0.0.1:8009         127.0.0.1:49238        ESTABLISHED
  TCP    127.0.0.1:49238        127.0.0.1:8009         ESTABLISHED

6. 実行

実際に動作しているか確認する。

Hyper-V の中で、80番ポートから作成したサンプルアプリケーションが動作するか確認する。

http://localhost/jboss-javaee6-webapp/index.jsf

mod_jk_success_on_hyperv

OK!

続いて、Hyper-V 上で動いている Apache に アクセスして、サンプルアプリケーションが動作するか確認。

http://192.168.0.120/jboss-javaee6-webapp/index.jsf

mod_jk_success

OK!

Apache 経由でアクセスすることが出来ました。

参考

  1. Java EE 7 検証環境構築(1) WildFly + JBoss Tools で EARプロジェクトを作成し Arquillian で ユニットテストをグリーンにするところまで
  2. Java EE 7 検証環境構築(2) WildFly に DataSourceを作成
  3. Java EE 7 検証環境構築(3) JPAからMySQLに接続するユニットテストをArquillianで実行
  4. Java EE 7 検証環境構築(4) Java EE での DI(Dependency Injection) および CDI(Contexts and Dependency Injection)をながめる
  5. Java EE 7 検証環境構築(5) JBoss Toolsが生成したサンプルソースのCDIを確認する
  6. Java EE 7 検証環境構築(6) JPA エンティティの作成と挿入
  7. Java EE 7 検証環境構築(7) JPA 問い合わせ(1) 名前付きクエリを使ってみる。テストでトランザクションも意識する
  8. Java EE 7 検証環境構築(8) JPA 問い合わせ(2) 動的クエリとCriteria API を試す
  9. Java EE 7 検証環境構築(9) jBatch 概要をおさえる
  10. Java EE 7 検証環境構築(10) JBoss Tools で作成した EARプロジェクトをJava EE 6 から 7 に変更する
  11. Java EE 7 検証環境構築(11) jBatch用 プロジェクトの作成を行う
  12. Java EE 7 検証環境構築(12) jBatch 簡易サンプル作成と Arquillian でユニットテスト
  13. Java EE 7 検証環境構築(13) jBatch REST サービス経由で実行する
  14. Java EE 7 検証環境構築(14) WildFly の管理をGUIで行う
  15. Java EE 7 検証環境構築(15) WildFly を サービスとして設定する(Windows/Linux)
  16. Java EE 7 検証環境構築(16) WildFly と Apache を mod_jk で連携させる(Widows)

前回に引き続き、WildFly周りを詰めていきたい。

どうも、WildFlyの公式サイト にはあまり細かい情報までは書かれていない(ような気がする)のと、ネット上にWildFlyの情報がまだ少ないのと、JBoss ASの情報はネットで結構見つかるが、WildFlyでは変わっているところがそこそこありそうなので、上記Kindle本を購入してみた。1,000円台と非常に安く読みやすい。おすすめ。

要点を今後のためにメモしながら、実際に設定してみる。

1. WildFly を Windows サービスとして設定する

WildFly は、サービスとしてインストール出来るだけでなく、起動時に開始するように設定出来る。そうするには、サーバーに含まれるいくつかのスクリプトファイルを使う必要がある。

1.1 Standalone

WildFly8をWindowsにサービスとしてインストールするのは、AS7/EAP6に対して非常に簡単となっている。サードパーティー製のネイティブライブラリは不要で、WildFly8にはすでに必要なものはすべて含まれている。

WildFly8をStandaloneモードでサービスとしてインストールしたい場合、%JBOSS_HOME%/bin/service フォルダで、

PS>.\service.bat install

これだけで、Windowsのサービス管理から設定ができるようになる。

wildfly_as_service_windows

また、サービスとして、開始(start)、終了(stop)、再開(restart)を同様に service.bat から行うことが1出来る。

PS>.\service.bat start

1.2 Domain モード

ドメインモードの場合、ドメインコントローラ(デフォルトでは、127.0.0.1 ポート 9990) およびホスト名(デフォルト master) の設定など、もう少し追加作業が必要。

PS>.\service.bat install/controller localhost:9990 /host master

2. WildFly を Linuxサービスとして設定する

CentOS に設定してみる。

WildFly + JBoss Tools で EARプロジェクトを作成し Arquillian で ユニットテストをグリーンにするところまで

2.1 ダウンロードとインストール

2.1.1 前提条件

JDK1.7 がインストールされていること。

CentOS に Oracle JDKをインストール

2.1.2 ダウンロードとインストール

http://wildfly.org/downloads/

上記からダウンロードして、適当なディレクトリに解凍する。

2.1.3 管理ユーザーの作成

JBOSS_HOME\bin\add-user.sh を起動して管理ユーザーを追加する

bin]$ ./add-user.sh 

What type of user do you wish to add? 
 a) Management User (mgmt-users.properties) 
 b) Application User (application-users.properties)
(a): a

Enter the details of the new user to add.
Using realm 'ManagementRealm' as discovered from the existing property files.
Username : admin

Password : 
JBAS015267: Password must have at least 1 non-alphanumeric symbol.
Are you sure you want to use the password entered yes/no? yes
Re-enter Password : 
What groups do you want this user to belong to? (Please enter a comma separated list, or leave blank for none)[  ]: 
About to add user 'admin' for realm 'ManagementRealm'
Is this correct yes/no? yes
Is this new user going to be used for one AS process to connect to another AS process? 
e.g. for a slave host controller connecting to the master or for a Remoting connection for server to server EJB calls.
yes/no? yes
To represent the user add the following to the server-identities definition < > value="bm9ydGgxMjM=">

2.2 サービスとして設定

2.1.1 設定ファイル

WildFlyをサービスとして開始させるために、JBOSS_HOME/bin/init.d ディレクトリを利用できる。

ファイル名 内容
wildfly-init-redhat.sh Red Hat Enterprise 系 Linux ディストリビューション(RHEL, CentOS) 用
wildfly-init-debian.sh Debian 系 Linuxディストリビューション
(Debian, Ubuntu) 用
wildfly.conf 上記initファイル用の設定

2.1.2 設定ファイルのコピーと編集

上記ファイルを必要に応じて、ディストリビューションごとのディレクトリにコピーする。

今回はCentOSなので、Red Hat 用を利用する

# cp wildfly-init-redhat.sh /etc/init.d/wildfly

同様に、設定ファイルをコピー

# cp wildfly.conf /etc/default/

wildfly.conf の内容を適切に編集する

# General configuration for the init.d scripts,
# not necessarily for JBoss AS itself.
# default location: /etc/default/wildfly

## Location of JDK
JAVA_HOME="/usr/java/jdk1.7.0_51"

## Location of WildFly
JBOSS_HOME="/home/piroto/opt/wildfly-8.1.0.Final"

## The username who should own the process.
JBOSS_USER=piroto

## The mode WildFly should start, standalone or domain
JBOSS_MODE=standalone

## Configuration for standalone mode
JBOSS_CONFIG=standalone.xml

## Configuration for domain mode
# JBOSS_DOMAIN_CONFIG=domain.xml
# JBOSS_HOST_CONFIG=host-master.xml

2.1.3 chkconfig に登録

# chkconfig --add wildfly

開始レベルの設定と確認

# chkconfig --level 2345 wildfly on
# chkconfig --list | grep wildfly                                                                 
wildfly         0:off   1:off   2:on    3:on    4:on    5:on    6:off

開始

# service wildfly start                                                                            
Starting wildfly:                                          [  OK  ]

ステータスの確認と終了

# service wildfly status
wildfly is running (pid 27748)
# service wildfly stop
Stopping wildfly:                                          [  OK  ]

以上特に問題なく完了。

1,000円台の出費でここまで捗るのはいいなぁKindle本。

和訳の書籍だと、値段が高すぎ(それでも買ってしまうのだが)なのと、情報が遅いのとで、結局英語のサイトをあさることになるのだが、確信情報へたどり着くまでのコストを考えると余裕で回収できそうだ。

この手の本を、制度が荒くてもいいからスピード重視で翻訳して和訳Kindle本出したら、そこそこ日本でもニーズがありそうだし、商売になるんじゃないかしら。

  1. Java EE 7 検証環境構築(1) WildFly + JBoss Tools で EARプロジェクトを作成し Arquillian で ユニットテストをグリーンにするところまで
  2. Java EE 7 検証環境構築(2) WildFly に DataSourceを作成
  3. Java EE 7 検証環境構築(3) JPAからMySQLに接続するユニットテストをArquillianで実行
  4. Java EE 7 検証環境構築(4) Java EE での DI(Dependency Injection) および CDI(Contexts and Dependency Injection)をながめる
  5. Java EE 7 検証環境構築(5) JBoss Toolsが生成したサンプルソースのCDIを確認する
  6. Java EE 7 検証環境構築(6) JPA エンティティの作成と挿入
  7. Java EE 7 検証環境構築(7) JPA 問い合わせ(1) 名前付きクエリを使ってみる。テストでトランザクションも意識する
  8. Java EE 7 検証環境構築(8) JPA 問い合わせ(2) 動的クエリとCriteria API を試す
  9. Java EE 7 検証環境構築(9) jBatch 概要をおさえる
  10. Java EE 7 検証環境構築(10) JBoss Tools で作成した EARプロジェクトをJava EE 6 から 7 に変更する
  11. Java EE 7 検証環境構築(11) jBatch用 プロジェクトの作成を行う
  12. Java EE 7 検証環境構築(12) jBatch 簡易サンプル作成と Arquillian でユニットテスト
  13. Java EE 7 検証環境構築(13) jBatch REST サービス経由で実行する
  14. Java EE 7 検証環境構築(14) WildFly の管理をGUIで行う
  15. Java EE 7 検証環境構築(15) WildFly を サービスとして設定する(Windows/Linux)
  16. Java EE 7 検証環境構築(16) WildFly と Apache を mod_jk で連携させる(Widows)

ここまで、WildFlyの管理コマンドラインインターフェース(CLI) を利用して、設定を行ってきたが、はっきり言って、使いやすいとは言いがたい。というのは、コマンドラインだから使いにくいというのではなく、

アプリケーションサーバーという、かなり複雑なアプリケーションを操作するのに、全体像およびどんな操作ができるのか把握していないと、どんな操作をしていいのかのイメージ自体がつかない。

かといって、Webインターフェースだと限定的なことしか出来なさそうだし。。。

と思っていたら、GUI版を起動させることができるみたいだ。

1. 管理コマンドラインGUIインターフェース

1.1 起動

-- gui パラメータを付与して起動する。

PS C:\Users\piroto> jboss-cli.bat --gui

jboss_cli_gui

おお、シンプルだが、全体が見渡せる。いい感じなのでは?

タブを見ると 「Command Builder」と「Output」に分かれていて、最上段にコマンドを打ち込めるようになっているので、あくまで、管理CLI を GUIにした物だということが見て取れる。

1.2 コマンドを作成

WildFlyの管理では、以下のようなことが出来るようだ。

Global Operations

操作 概要
read-resource 管理リソースの属性値に子リソースの情報も加えて読む。
read-attribute 個々の属性値を読む。nameパラメータ必須。
write-attribute 個々の属性値を書く。nameとvalueパラメータ必須。
unset-attribute 個々の属性値を許容されるなら、
undefined 値に設定する。nameパラメータ必須。
read-resource-description リソース属性の説明を返す。
read-operation-names リソースがサポートする操作リストを返す。
read-operation-description 操作の説明を返す。nameパラメータ必須。
read-children-types 子リソースのタイプリストを返す。
read-children-names タイプによる、子リソースの名前リストを返す。nameパラメータ必須。
read-children-resources タイプによる、子リソースについての情報リストを返す。child-typeパラメータ必須。

Standarad Operations

操作 概要
add 新しいリソースを作成
remove リソースを削除

GUI画面のリソースを右クリックすると、リソースがサポートする操作の一覧が表示される。

jboss_cli_gui03

操作をクリックすると、ダイアログが開き、パラメータ入力を促される。

入力してOK

jboss_cli_gui04

コマンドが、生成された!

/subsystem=undertow/:read-resource(recursive=true,recursive-depth=0)

1.3 実行

実行すると、Outputタブに結果が表示される。

jboss_cli_gui05

1.4 フィルタ

画面の最下部にはリソースフィルタを入力するテキストボックスがある。

jboss_cli_gui06

これは捗りそうだ。

  1. Java EE 7 検証環境構築(1) WildFly + JBoss Tools で EARプロジェクトを作成し Arquillian で ユニットテストをグリーンにするところまで
  2. Java EE 7 検証環境構築(2) WildFly に DataSourceを作成
  3. Java EE 7 検証環境構築(3) JPAからMySQLに接続するユニットテストをArquillianで実行
  4. Java EE 7 検証環境構築(4) Java EE での DI(Dependency Injection) および CDI(Contexts and Dependency Injection)をながめる
  5. Java EE 7 検証環境構築(5) JBoss Toolsが生成したサンプルソースのCDIを確認する
  6. Java EE 7 検証環境構築(6) JPA エンティティの作成と挿入
  7. Java EE 7 検証環境構築(7) JPA 問い合わせ(1) 名前付きクエリを使ってみる。テストでトランザクションも意識する
  8. Java EE 7 検証環境構築(8) JPA 問い合わせ(2) 動的クエリとCriteria API を試す
  9. Java EE 7 検証環境構築(9) jBatch 概要をおさえる
  10. Java EE 7 検証環境構築(10) JBoss Tools で作成した EARプロジェクトをJava EE 6 から 7 に変更する
  11. Java EE 7 検証環境構築(11) jBatch用 プロジェクトの作成を行う
  12. Java EE 7 検証環境構築(12) jBatch 簡易サンプル作成と Arquillian でユニットテスト
  13. Java EE 7 検証環境構築(13) jBatch REST サービス経由で実行する
  14. Java EE 7 検証環境構築(14) WildFly の管理をGUIで行う
  15. Java EE 7 検証環境構築(15) WildFly を サービスとして設定する(Windows/Linux)
  16. Java EE 7 検証環境構築(16) WildFly と Apache を mod_jk で連携させる(Widows)

EJBプロジェクトでのユニットテストができるようになったので、Webプロジェクトからの呼び出しのユニットテストを実施出来るようにしてみる。

1.Webプロジェクトでのユニットテスト

batch-web プロジェクトの pom.xml に以下の設定を追加する。

batch-web プロジェクトに REST Webサービスのエントリーポイントを作成し、そこからバッチを起動させる。

jbatch_web_structure

1.1 Java EE API の参照

まず、Webプロジェクトでは、EJBとjBatch の参照がされていないので、pom.xml の dependencies エレメント配下に以下を追記する。

<!-- Import the EJB API, we use provided scope as the API is included in JBoss AS 7 -->
<dependency>
    <groupId>org.jboss.spec.javax.ejb</groupId>
    <artifactId>jboss-ejb-api_3.2_spec</artifactId>
    <scope>provided</scope>
</dependency>
<dependency> 
    <groupId>javax.batch</groupId> 
    <artifactId>javax.batch-api</artifactId> 
    <version>1.0</version> 
    <scope>provided</scope>
</dependency> 

1.2 ユニットテスト用ライブラリ参照の追加

また、Arquillian でテストを実施するための依存関係も追記する。

<!-- Test scope dependencies -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <scope>test</scope>
</dependency>

<!-- Optional, but highly recommended -->
<!-- Arquillian allows you to test enterprise code such as EJBs and Transactional(JTA) 
    JPA from JUnit/TestNG -->
<dependency>
    <groupId>org.jboss.arquillian.junit</groupId>
    <artifactId>arquillian-junit-container</artifactId>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.jboss.arquillian.protocol</groupId>
    <artifactId>arquillian-protocol-servlet</artifactId>
    <scope>test</scope>
</dependency>

1.3 Maven プロファイル

Maven プロファイルの定義。WildFly を先に実行させておいて、テスト時にデプロイするプロファイル(arq-wildfly-remote) と、WildFly 停止状態からテストを実行するプロファイルを(arq-wildfly-managed)定義する。

どちらを利用するかは、プロジェクトのコンテキストメニューから、Maven ー Select Maven Profiles で選択する。

    <profile>
        <!-- The default profile skips all tests, though you can tune it to run 
            just unit tests based on a custom pattern -->
        <!-- Seperate profiles are provided for running all tests, including Arquillian 
            tests that execute in the specified container -->
        <id>default</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <build>
            <plugins>
                <plugin>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>${version.surefire.plugin}</version>
                    <configuration>
                        <skip>true</skip>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </profile>

    <profile>
        <!-- http://typea.info/blg/glob/2014/04/java-ee-7-1-wildfly-jboss-tools-ear-arquillian.html -->
        <id>arq-wildfly-managed</id>
        <dependencies>
            <dependency>
                <groupId>org.wildfly</groupId>
                <artifactId>wildfly-arquillian-container-managed</artifactId>
                <version>8.0.0.Final</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </profile>

    <profile>
        <!-- http://typea.info/blg/glob/2014/04/java-ee-7-1-wildfly-jboss-tools-ear-arquillian.html -->
        <id>arq-wildfly-remote</id>
        <dependencies>
            <dependency>
                <groupId>org.wildfly</groupId>
                <artifactId>wildfly-arquillian-container-remote</artifactId>
                <version>8.0.0.Final</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </profile>
</profiles>

2. REST サービス

作成した、バッチをRESTサービスから呼び出すコードを書く。

@Path("/simplejob")
@RequestScoped
public class SimpleJobRestService {
    @Inject
    private Logger log;
    
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public String execute() throws Exception {
        JobOperator jobOperator = BatchRuntime.getJobOperator();
        long executionId = jobOperator.start("simplejob", new Properties());
        
        BufferedReader reader = new BufferedReader(new FileReader("c:\\work\\test_out.txt"));
        StringBuilder buf = new StringBuilder();
        String line = null;
        while((line = reader.readLine()) != null) {
            buf.append(line).append("\n");
        }
        return buf.toString();
    }
}

3. Arquillian のテストケース

バッチを呼び出す、テスト対象のRESTサービスをInjectし、テストする。

@RunWith(Arquillian.class)
public class SimpleJobRestServiceTest {
    @Deployment
    public static WebArchive createDeployment() {
        WebArchive war = ShrinkWrap.create(WebArchive.class, "test.war")
                .addPackage("info.typea.tallarico.job.simplejob")
                .addPackage("info.typea.tallarico.rest")
                .addAsWebInfResource(EmptyAsset.INSTANCE, ArchivePaths.create("beans.xml"))
                .addAsResource("META-INF/batch-jobs/simpleJob.xml")
                .addClasses(Resources.class)
                .addClasses(WebResources.class)
                ;
        System.out.println(war.toString(true));
        return war;
    }
    
    @Inject
    Logger logger;
    
    @Inject
    SimpleJobRestService simpleJobService;
    
    @Test
    public void executeTest() throws Exception {
        assertNotNull(simpleJobService);
        
        logger.info("\n== BATCH REST SERVICE RESULT ==\n" 
                + simpleJobService.execute());
    }
}

4.テスト結果

OK!

jbatch_web_result_test

5.実際にバッチEARをテストする

Server から、WildFlyを選択し、コンテキストメニュー Add and Remove から、バッチEARを追加する。

jbatch_web_deploy

起動すると、以下のエラー。

  - Producer Method [Logger] with qualifiers [@Any @Default] declared as [[BackedAnnotatedMethod] @Produces public info.typea.tallarico.util.Resources.produceLog(InjectionPoint)]

たしかに、現状、Logger をインジェクションしたときに、EJBプロジェクトに含まれるLoggerと、EJBバッチプロジェクトに含まれるLoggerが曖昧になっている。

特定のBeanに対していくつかの実装が提供される場合(例えば、あるインターフェースに対して複数の実装クラスが存在する場合)、限定子を利用してどの実装をインジェクトするのかを特定できる

うーむ。Logger をProduceしており衝突しているどちらかの実装を削除すればよいのだが、せっかくなので、限定子を試してみる。

CDI関連のエラーのほとんどはデプロイ時に発生するようだ。

ちなみに、CDIについて、以下のスライドがわかりやすかった。

5.1 限定子を利用してみる

ということで、せっかくの機会なので限定子を利用してみる。

限定子アノテーションの宣言

package info.typea.tallarico.job.util;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import javax.inject.Qualifier;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.PARAMETER;
import java.lang.annotation.Target;

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface BatchResource {}

Produce メソッドをアノテートする

public class Resources {

    @Produces
    @BatchResource
    public Logger produceLog(InjectionPoint injectionPoint) {
        return Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
    }
}

インジェクション箇所をアノテートする

    @Inject
    @BatchResource
    Logger logger;

これでOKのはず

6.ブラウザからRESTサービスを直接呼び出す

バッチアプリケーションのコンテキストパスは、@ApplicationPath アノテーションで指定する。

@ApplicationPath("/batch")
public class JaxRsActivator extends Application {
    /* class body intentionally left blank */
}

RESTサービスのURIは、上記 2.RESTサービスで掲げたクラスのアノテーションで指定。

jbatch_web_success

OKブラウザから、バッチを呼び出すことが出来た!!

ちなみに、ここまで試したソースコード

https://github.com/pppiroto/Tallarico.git

  1. Java EE 7 検証環境構築(1) WildFly + JBoss Tools で EARプロジェクトを作成し Arquillian で ユニットテストをグリーンにするところまで
  2. Java EE 7 検証環境構築(2) WildFly に DataSourceを作成
  3. Java EE 7 検証環境構築(3) JPAからMySQLに接続するユニットテストをArquillianで実行
  4. Java EE 7 検証環境構築(4) Java EE での DI(Dependency Injection) および CDI(Contexts and Dependency Injection)をながめる
  5. Java EE 7 検証環境構築(5) JBoss Toolsが生成したサンプルソースのCDIを確認する
  6. Java EE 7 検証環境構築(6) JPA エンティティの作成と挿入
  7. Java EE 7 検証環境構築(7) JPA 問い合わせ(1) 名前付きクエリを使ってみる。テストでトランザクションも意識する
  8. Java EE 7 検証環境構築(8) JPA 問い合わせ(2) 動的クエリとCriteria API を試す
  9. Java EE 7 検証環境構築(9) jBatch 概要をおさえる
  10. Java EE 7 検証環境構築(10) JBoss Tools で作成した EARプロジェクトをJava EE 6 から 7 に変更する
  11. Java EE 7 検証環境構築(11) jBatch用 プロジェクトの作成を行う
  12. Java EE 7 検証環境構築(12) jBatch 簡易サンプル作成と Arquillian でユニットテスト
  13. Java EE 7 検証環境構築(13) jBatch REST サービス経由で実行する
  14. Java EE 7 検証環境構築(14) WildFly の管理をGUIで行う
  15. Java EE 7 検証環境構築(15) WildFly を サービスとして設定する(Windows/Linux)
  16. Java EE 7 検証環境構築(16) WildFly と Apache を mod_jk で連携させる(Widows)

jBatchの概要を押さえて、jBatch用の環境(Java EE7対応EARプロジェクトの生成)を行ったので、Java EE 7 チュートリアルから、バッチの簡単な例 を試してみる。(日本語訳サイトはこちら)

1.簡単な使用例

ジョブ定義言語(JSL)をXMLで作成する。XMLファイルは、META-INF/batch-jobs 配下に格納する。

今回の流れでは、batch-ejb プロジェクトの、META-INF/batch-jobs 配下に、simplejob.xml という名前で作成した。

batch_job_xml

チャンクステップとタスクステップのジョブ定義。input_file、output_file パラメータを具体的なファイルに設定してみる。

<?xml version="1.0" encoding="UTF-8"?>
<!-- http://docs.oracle.com/javaee/7/tutorial/doc/batch-processing003.htm -->
<job id="simplejob" xmlns="http://xmlns.jcp.org/xml/ns/javaee" 
                    version="1.0">
  <properties>
    <property name="input_file" value="c:\work\test.txt"/>
    <property name="output_file" value="c:\work\test_out.txt"/>
  </properties>
  <step id="mychunk" next="mytask">
    <chunk>
      <reader ref="MyReader"></reader>
      <processor ref="MyProcessor"></processor>
      <writer ref="MyWriter"></writer>
    </chunk>
  </step>
  <step id="mytask">
    <batchlet ref="MyBatchlet"></batchlet>
    <end on="COMPLETED"/>
  </step>
</job>

2.作成するクラス

ジョブ定義ファイルで参照するクラスを作成する。

batch_structure

クラス 役割
MyCheckpoint チェックポイント。処理済みの位置を保持する。
MyReader アイテムを読み込む。ジョブが再開された場合は、チェックポイントから読み込み位置を取得する。
MyProcesser 読み込んだアイテムを処理。
MyWriter 処理済みアイテムを出力する

2.1 チャンクステップ

2.1.1 Checkpoint クラス

  • 多くの場合、チャンク指向ステップのためにチェックポイントを実装すべき
  • 以下のクラスは、処理されたテキストファイルの行を保持する
@SuppressWarnings("serial")
public class MyCheckpoint implements Serializable {
    
    private long lineNum = 0;
    
    public void increase() { 
        lineNum++; 
    }
    public long getLineNum() { 
        return lineNum; 
    }
}

2.1.2 ItemReader クラス

@Dependent
@Named("MyReader")
public class MyReader implements javax.batch.api.chunk.ItemReader {
    private MyCheckpoint checkpoint;
    private BufferedReader breader;
    
    @Inject
    JobContext jobCtx;
    
    public MyReader() {}

    /*
     * Itemを読むためのReaderを準備する
     * 仮引数は、ジョブインスタンスの最終チェックポイント
     */
    @Override
    public void open(Serializable ckpt) throws Exception {
        if (ckpt == null) {
            checkpoint = new MyCheckpoint();
        } else {
            checkpoint = (MyCheckpoint) ckpt;
        }
        String fileName = jobCtx.getProperties()
                                .getProperty("input_file");
        breader = new BufferedReader(new FileReader(fileName));
        for (long i = 0; i < checkpoint.getLineNum(); i++) {
            breader.readLine();
        }
    }

    @Override
    public void close() throws Exception {
        breader.close();
    }

    @Override
    public Object readItem() throws Exception {
        String line = breader.readLine();
        return line;
    }

    /* 
     * Readerに対して、、現在のチェックポイントを返す
     */
    @Override
    public Serializable checkpointInfo() throws Exception {
        return checkpoint;
    }
}

2.1.3 ItemProcessor クラス

@Dependent
@Named("MyProcessor")
public class MyProcessor implements javax.batch.api.chunk.ItemProcessor {
    public MyProcessor() {}

    @Override
    public Object processItem(Object obj) throws Exception {
        String line = (String) obj;
        return line.toUpperCase();
    }
}

2.1.4 ItemWriter クラス

@Dependent
@Named("MyWriter")
public class MyWriter implements javax.batch.api.chunk.ItemWriter {
    private BufferedWriter bwriter;
    
    @Inject
    private JobContext jobCtx;

    @Override
    public void open(Serializable ckpt) throws Exception {
        String fileName = jobCtx.getProperties()
                                .getProperty("output_file");
        bwriter = new BufferedWriter(new FileWriter(fileName, 
                                                    (ckpt != null)));
    }

    @Override
    public void writeItems(List<Object> items) throws Exception {
        for (int i = 0; i < items.size(); i++) {
            String line = (String) items.get(i);
            bwriter.write(line);
            bwriter.newLine();
        }
    }

    @Override
    public Serializable checkpointInfo() throws Exception {
        return new MyCheckpoint();
    }

    @Override
    public void close() throws Exception {
        bwriter.close();
    }
}

2.2 タスクステップ

2.2.1 Batchlet

@Dependent
@Named("MyBatchlet")
public class MyBatchlet implements javax.batch.api.Batchlet {
    
    @Inject
    private JobContext jobCtx;
    
    @Override
    public String process() throws Exception {
        String fileName = jobCtx.getProperties()
                                .getProperty("output_file");
        System.out.println(""+(new File(fileName)).length());
        return "COMPLETED";
    }

    @Override
    public void stop() throws Exception {
        // TODO Auto-generated method stub
    }
}

2. Arquillian でユニットテストを行う

さて、チュートリアルに従い、実装したので、Arquillian によるユニットテストを行おう。。。と思う。

https://github.com/javaee-samples/javaee7-samples

Github に Java EE 7 のサンプルが上がっているので、それを参考にArquillian のテストケースを作成。

batch_test_structure

最終的には、上記のような構成になる。サンプルのテストケースを利用するには、BatchTestHelper クラスが必要なので、

https://github.com/javaee-samples/javaee7-samples/blob/master/util/src/main/java/org/javaee7/util/BatchTestHelper.java

をプロジェクトに組み込む。

2.1 BatchTestHelper

/**
 * @author Roberto Cortez
 * @see https://github.com/javaee-samples/javaee7-samples/blob/master/util/src/main/java/org/javaee7/util/BatchTestHelper.java
 */
public final class BatchTestHelper {
    private static final int MAX_TRIES = 10;
    private static final int THREAD_SLEEP = 100;

    private BatchTestHelper() {
        throw new UnsupportedOperationException();
    }

    /**
     * We need to keep the test running because JobOperator runs the batch job in an asynchronous way, so the
     * JobExecution can be properly updated with the running job status.
     *
     * @param jobExecution
     *         the JobExecution of the job that is being runned on JobOperator.
     *
     * @throws InterruptedException thrown by Thread.sleep.
     */
    public static void keepTestAlive(JobExecution jobExecution) throws InterruptedException {
        int maxTries = 0;
        while (!jobExecution.getBatchStatus().equals(BatchStatus.COMPLETED)) {
            if (maxTries < MAX_TRIES) {
                maxTries++;
                Thread.sleep(THREAD_SLEEP);
            } else {
                break;
            }
        }
    }

    /**
     * Convert the Metric array contained in StepExecution to a key-value map for easy access to Metric parameters.
     *
     * @param metrics
     *         a Metric array contained in StepExecution.
     *
     * @return a map view of the metrics array.
     */
    public static Map<metric.metrictype   , long> getMetricsMap(Metric[] metrics) {
        Map<metric.metrictype   , long> metricsMap = new HashMap<>();
        for (Metric metric : metrics) {
            metricsMap.put(metric.getType(), metric.getValue());
        }
        return metricsMap;
    }
}

2.2 テストケース

テストケースとは言うが、まずは、Arquillian 上でバッチが起動出来ればよい。

@Deployment アノテーションをつけたデプロイメソッドで、Webアーカイブを作成する。

@Test を付与した、テストメソッドでは、JobOperator から、ジョブ定義XMLで定義した、"simplejob"を呼び出しバッチを処理している。

@RunWith(Arquillian.class)
public class SimpleJobTest {

    @Deployment
    public static WebArchive createDeployment() {
        WebArchive war = ShrinkWrap.create(WebArchive.class, "test.war")
                .addClass(BatchTestHelper.class)
                .addPackage("info.typea.tallarico.job.simplejob")
                .addAsWebInfResource(EmptyAsset.INSTANCE, ArchivePaths.create("beans.xml"))
                .addAsResource("META-INF/batch-jobs/simpleJob.xml")
                .addClass(Resources.class)
                ;
        System.out.println(war.toString(true));
        return war;
    }
    
    @Inject
    Logger logger;
    
    @Test
    public void simpleJobTest() throws Exception {
        JobOperator jobOperator = BatchRuntime.getJobOperator();
        Long executionId = jobOperator.start("simplejob", new Properties());
        JobExecution jobExecution = jobOperator.getJobExecution(executionId);
        
        BatchTestHelper.keepTestAlive(jobExecution);
        
        List stepExecutions = jobOperator.getStepExecutions(executionId);
        for (StepExecution stepExecution : stepExecutions) {
            String stepName = stepExecution.getStepName();
            
            switch(stepName) {
            case "mychunk":
            case "mytask":
                Map<metric.metrictype   , Long> metricsMap 
                    = BatchTestHelper.getMetricsMap(stepExecution.getMetrics());
                
                logger.info("===== STEP NAME:" + stepName + "=====");
                logger.info("READ COUNT:" + metricsMap.get(Metric.MetricType.READ_COUNT).longValue());
                logger.info("WRITE COUNT:" + metricsMap.get(Metric.MetricType.WRITE_COUNT).longValue());
                logger.info("COMMIT COUNT:" + metricsMap.get(Metric.MetricType.COMMIT_COUNT).longValue());
                break;
            }
        }
    }
}

その他の設定は、

Java EE 7 検証環境構築(1) WildFly + JBoss Tools で EARプロジェクトを作成し Arquillian で ユニットテストをグリーンにするところまで

を参考に。

batch_test_green

テスト成功!出力ファイルも期待通り生成された。

  1. Java EE 7 検証環境構築(1) WildFly + JBoss Tools で EARプロジェクトを作成し Arquillian で ユニットテストをグリーンにするところまで
  2. Java EE 7 検証環境構築(2) WildFly に DataSourceを作成
  3. Java EE 7 検証環境構築(3) JPAからMySQLに接続するユニットテストをArquillianで実行
  4. Java EE 7 検証環境構築(4) Java EE での DI(Dependency Injection) および CDI(Contexts and Dependency Injection)をながめる
  5. Java EE 7 検証環境構築(5) JBoss Toolsが生成したサンプルソースのCDIを確認する
  6. Java EE 7 検証環境構築(6) JPA エンティティの作成と挿入
  7. Java EE 7 検証環境構築(7) JPA 問い合わせ(1) 名前付きクエリを使ってみる。テストでトランザクションも意識する
  8. Java EE 7 検証環境構築(8) JPA 問い合わせ(2) 動的クエリとCriteria API を試す
  9. Java EE 7 検証環境構築(9) jBatch 概要をおさえる
  10. Java EE 7 検証環境構築(10) JBoss Tools で作成した EARプロジェクトをJava EE 6 から 7 に変更する
  11. Java EE 7 検証環境構築(11) jBatch用 プロジェクトの作成を行う
  12. Java EE 7 検証環境構築(12) jBatch 簡易サンプル作成と Arquillian でユニットテスト
  13. Java EE 7 検証環境構築(13) jBatch REST サービス経由で実行する
  14. Java EE 7 検証環境構築(14) WildFly の管理をGUIで行う
  15. Java EE 7 検証環境構築(15) WildFly を サービスとして設定する(Windows/Linux)
  16. Java EE 7 検証環境構築(16) WildFly と Apache を mod_jk で連携させる(Widows)

jBatchの概要を押さえたし、jBatchが動くように、EARプロジェクトをJava EE 7 対応にしたので、jBatchプロジェクトを作成しようと思う。

コンセプトとしては、これまで作成してきたJPAによる永続化処理をバッチと共有し、同時に編集したい。

それぞれEARプロジェクトとし、それぞれ、EARをエクスポートする。

1.バッチEARプロジェクトの作成

ということで、末尾に-batch を付与した、EARプロジェクトを作成し、以下の構成とする。

tallarico、tallarico-ear、tallarico-ejb、tallarico-web がこれまで確認してきたプロジェクト群で、そこに、

tallarico-batch、tallarico-batch-ear、tallarico-batch-ejb、tallarico-batch-web が新たに追加される状態になる。

また、作成後、各pom.xml を 修正し、Java EE 6 → 7に変換する。

jbatch_package

2. 永続化処理(EJB)をバッチから参照可能にする

2.1 バッチ親プロジェクト(tallarico-batch)のpom.xml編集

永続化処理およびEJBをバッチから参照可能にするために、バッチの親プロジェクトのpom.xmlを開き、Dependencies タブのDependecy Management のAdd ボタンを押して表示されるダイアログに、Group Id および Artifact Id と Version を入力、Scope を compileとし、一旦OKで登録。登録された行を再度選択後、Properties ボタンを押下で再度ダイアログを開き、Type を jar から ejb に変更してOKする。

jbatch_main_project_pom

2.2 バッチEARプロジェクト(tallarico-batch-ear)の pom.xml

同様に、バッチのEARプロジェクトのpom.xmlファイルを開き、DependenciesタブのDependenciesにて、Add ボタンを押下。Group Id、Artifact Id を入力し、Scopeをcompileで一旦保存し、再度Propertiesで開いた後、Type を ejb として再度保存。

jbatch_ear_pom

2.3 バッチEJBプロジェクトの参照設定に永続化用EJBプロジェクトの参照を追加

jbatch_ejb_build_path

2.4 バッチEARを作成し中身を確認

一旦、バッチEARプロジェクトのコンテキストメニューから、ExportでEARファイルを作成。

中身に、永続化用EJB の JARファイルが含まれていることを確認。

jbatch_exported_ear

一旦ここまで。

  1. Java EE 7 検証環境構築(1) WildFly + JBoss Tools で EARプロジェクトを作成し Arquillian で ユニットテストをグリーンにするところまで
  2. Java EE 7 検証環境構築(2) WildFly に DataSourceを作成
  3. Java EE 7 検証環境構築(3) JPAからMySQLに接続するユニットテストをArquillianで実行
  4. Java EE 7 検証環境構築(4) Java EE での DI(Dependency Injection) および CDI(Contexts and Dependency Injection)をながめる
  5. Java EE 7 検証環境構築(5) JBoss Toolsが生成したサンプルソースのCDIを確認する
  6. Java EE 7 検証環境構築(6) JPA エンティティの作成と挿入
  7. Java EE 7 検証環境構築(7) JPA 問い合わせ(1) 名前付きクエリを使ってみる。テストでトランザクションも意識する
  8. Java EE 7 検証環境構築(8) JPA 問い合わせ(2) 動的クエリとCriteria API を試す
  9. Java EE 7 検証環境構築(9) jBatch 概要をおさえる
  10. Java EE 7 検証環境構築(10) JBoss Tools で作成した EARプロジェクトをJava EE 6 から 7 に変更する
  11. Java EE 7 検証環境構築(11) jBatch用 プロジェクトの作成を行う
  12. Java EE 7 検証環境構築(12) jBatch 簡易サンプル作成と Arquillian でユニットテスト
  13. Java EE 7 検証環境構築(13) jBatch REST サービス経由で実行する
  14. Java EE 7 検証環境構築(14) WildFly の管理をGUIで行う
  15. Java EE 7 検証環境構築(15) WildFly を サービスとして設定する(Windows/Linux)
  16. Java EE 7 検証環境構築(16) WildFly と Apache を mod_jk で連携させる(Widows)

Java EE 7 といいつつ、ここまでは、JBoss Tools を使って作成した、EARプロジェクト(Java EE 6)で確認してきたが、jBatch の確認を行うにあたって、EARプロジェクトをJava EE 7 に更新したい。

さてどうすればよいのかなと、JBoss Tools の最新のNightly ビルドとかインストールして 試してみるが、JBoss Central から作成される、EARプロジェクトは依然として Java EE 6 のままだった。

まぁ、JBoss Tools 自体とは別で提供されているだろうので、わずかな期待はしていたがしょうがない。

ということで、ググって、以下のサイトを発見!

WildFly 用の Java EE 7 maven アーキタイプだそう。

言われたとおりにしてみる。

Java EE 7 maven archetypes for WildFly

Using the Java EE EAR Project as a template you will need to do the following.

  1. In the parent POM modify the JBoss version properties for WildFly: version.jboss.maven.plugin : 8.0.0.Final, version.jboss.bom : 8.0.0.Final, version.jboss.as : 8.0.0.Final.
  2. Also in the parent POM dependency management you will need to replace the javaee-6.0 BOMs with the WildFly javaee-7.0 equivalents. Replace org.jboss.bom : jboss-javaee-6.0-with-tools : ${version.jboss.bom} : pom [import] with org.wildfly.bom : jboss-javaee-7.0-with-tools : ${version.jboss.bom} : pom [import]. Replace org.jboss.bom : jboss-javaee-6.0-with-hibernate : ${version.jboss.bom} : pom [import] with org.wildfly.bom : jboss-javaee-7.0-with-hibernate : ${version.jboss.bom} : pom [import].
  3. In the ejb module POM you will need to modify each of the managed dependencies to use the WildFly javaee-7.0 BOM managed versions. For example replace org.jboss.spec.javax.ejb : jboss-ejb-api_3.1_spec with org.jboss.spec.javax.ejb : jboss-ejb-api_3.2_spec.
  4. In the web module POM you will need to modify each of the managed dependencies to use the WildFly javaee-7.0 BOM managed versions. For example replace org.jboss.spec.javax.ws.rs : jboss-jaxrs-api_1.1_spec with org.jboss.resteasy : jaxrs-api.

You may find these links helpful: https://github.com/wildfly/wildfly/blob/master/spec-api/pom.xml, https://github.com/wildfly/boms/tree/master/jboss-javaee-7.0-with-tools,https://github.com/wildfly/boms/tree/master/jboss-javaee-7.0-with-hibernate.

I hope this helps.

プロジェクトの構成

project_structure

親POMの修正

上記プロジェクト構成例では、tallarico の pom.xml の以下の点を修正する。

WildFly用、JBoss version プロパティ

  • version.jboss.maven.plugin を 8.0.0.Final に修正
  • version.jboss.bom を 8.0.0.Final に 修正
  • version.jboss.as を 8.0.0.Final に修正

dependency management の groupId と artifactId をそれぞれ以下に

  • org.wildfly.bom : jboss-javaee-7.0-with-tools : ${version.jboss.bom}
  • org.wildfly.bom : jboss-javaee-7.0-with-hibernate : ${version.jboss.bom}

あと、上記サイトに記述はないが、maven.compiler のパラメータを、1.6 から 1.7 にしておく。

Java EE 7 では、Java SE 7 に対応している。

※修正箇所(および親要素)のみ抜粋(コメントアウトが初期値、コメントアウト行直下のが修正後の値)

<?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/maven-v4_0_0.xsd">
    <properties>
        <!-- <version.jboss.maven.plugin>7.4.Final</version.jboss.maven.plugin>  -->
        <version.jboss.maven.plugin>8.0.0.Final</version.jboss.maven.plugin>

        <!-- <version.jboss.bom>1.0.7.Final</version.jboss.bom>  -->
        <version.jboss.bom>8.0.0.Final</version.jboss.bom>
        
        <!-- version.jboss.as>7.2.0.Final</version.jboss.as>  -->
        <version.jboss.as>8.0.0.Final</version.jboss.as>
        <!-- 
        <maven.compiler.target>1.6</maven.compiler.target>
        <maven.compiler.source>1.6</maven.compiler.source>
        -->
        <maven.compiler.target>1.7</maven.compiler.target>
        <maven.compiler.source>1.7</maven.compiler.source>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <!-- 
                <artifactId>jboss-javaee-6.0-with-tools</artifactId>  
                <groupId>org.jboss.bom</groupId>
                -->
                <groupId>org.wildfly.bom</groupId>
                <artifactId>jboss-javaee-7.0-with-tools</artifactId>
                <version>${version.jboss.bom}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <dependency>
                <!-- 
                <groupId>org.jboss.bom</groupId>
                <artifactId>jboss-javaee-6.0-with-hibernate</artifactId>  
                -->
                <groupId>org.wildfly.bom</groupId>
                <artifactId>jboss-javaee-7.0-with-hibernate</artifactId>
                <version>${version.jboss.bom}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

EJBモジュールのPOM修正

dependency management の artifactId をそれぞれ以下に

  • org.jboss.spec.javax.ejb : jboss-ejb-api_3.2_spec
  • hibernate-jpa-2.1-api

※hibernateの記述は上記サイトにはない

※修正箇所(および親要素)のみ抜粋(コメントアウトが初期値、コメントアウト行直下のが修正後の値)

<?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/maven-v4_0_0.xsd">
    <dependencies>
        <dependency>
            <groupId>org.jboss.spec.javax.ejb</groupId>
            <!-- 
            <artifactId>jboss-ejb-api_3.1_spec</artifactId>
            -->
            <artifactId>jboss-ejb-api_3.2_spec</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.hibernate.javax.persistence</groupId>
            <!-- <artifactId>hibernate-jpa-2.0-api</artifactId>  -->
            <artifactId>hibernate-jpa-2.1-api</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>

WebモジュールPOM修正

dependency management の groupId と artifactId をそれぞれ以下に

  • org.jboss.resteasy : jaxrs-api
  • org.jboss.spec.javax.faces: jboss-jsf-api_2.2_spec
  • org.hibernate.javax.persistence : hibernate-jpa-2.1-api

※修正箇所(および親要素)のみ抜粋(コメントアウトが初期値、コメントアウト行直下のが修正後の値)

<?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/maven-v4_0_0.xsd">
   <dependencies>
      <dependency>
      	 <!-- 
         <groupId>org.jboss.spec.javax.ws.rs</groupId>
         <artifactId>jboss-jaxrs-api_1.1_spec</artifactId>
      	  -->
         <groupId>org.jboss.resteasy</groupId>
         <artifactId>jaxrs-api</artifactId>
         <scope>provided</scope>
      </dependency>
      <dependency>
         <groupId>org.jboss.spec.javax.faces</groupId>
         <!-- <artifactId>jboss-jsf-api_2.1_spec</artifactId>  -->
         <artifactId>jboss-jsf-api_2.2_spec</artifactId>
         <scope>provided</scope>
      </dependency>
      <dependency>
         <groupId>org.hibernate.javax.persistence</groupId>
            <!-- <artifactId>hibernate-jpa-2.0-api</artifactId>  -->
            <artifactId>hibernate-jpa-2.1-api</artifactId>
         <scope>provided</scope>
      </dependency>
   </dependencies>
</project>

上記編集後、プロジェクトのコンテキストメニューから、Maven - Update Project を実行する。

  1. Java EE 7 検証環境構築(1) WildFly + JBoss Tools で EARプロジェクトを作成し Arquillian で ユニットテストをグリーンにするところまで
  2. Java EE 7 検証環境構築(2) WildFly に DataSourceを作成
  3. Java EE 7 検証環境構築(3) JPAからMySQLに接続するユニットテストをArquillianで実行
  4. Java EE 7 検証環境構築(4) Java EE での DI(Dependency Injection) および CDI(Contexts and Dependency Injection)をながめる
  5. Java EE 7 検証環境構築(5) JBoss Toolsが生成したサンプルソースのCDIを確認する
  6. Java EE 7 検証環境構築(6) JPA エンティティの作成と挿入
  7. Java EE 7 検証環境構築(7) JPA 問い合わせ(1) 名前付きクエリを使ってみる。テストでトランザクションも意識する
  8. Java EE 7 検証環境構築(8) JPA 問い合わせ(2) 動的クエリとCriteria API を試す
  9. Java EE 7 検証環境構築(9) jBatch 概要をおさえる
  10. Java EE 7 検証環境構築(10) JBoss Tools で作成した EARプロジェクトをJava EE 6 から 7 に変更する
  11. Java EE 7 検証環境構築(11) jBatch用 プロジェクトの作成を行う
  12. Java EE 7 検証環境構築(12) jBatch 簡易サンプル作成と Arquillian でユニットテスト
  13. Java EE 7 検証環境構築(13) jBatch REST サービス経由で実行する
  14. Java EE 7 検証環境構築(14) WildFly の管理をGUIで行う
  15. Java EE 7 検証環境構築(15) WildFly を サービスとして設定する(Windows/Linux)
  16. Java EE 7 検証環境構築(16) WildFly と Apache を mod_jk で連携させる(Widows)

すこしずつ、Java EE 7 検証環境の構築を進めているが、実際のところ、ここまでは Java EE 6 で実現されていることで、Java EE 7 でなければならないものではない。

ただ、Java EE 7 は Java EE 6 の延長線上にあるものとのことなので、いずれにしても、Java EE 6 を理解しておく必要はあるのだが、Java EE 7 にこだわるのは、

Java EE 7から加わるバッチ仕様 Batch Aplications for the Java Platform

このjBatch を利用したみたいから。

しかし、残念ながら、まだ、Java EE 7 の適当な和書がない。

Java EE 7 Tutorial を適当に俺訳しながら、学ぼうかな~と思っていたら、

神サイトがありました。

The Java EE 7 TutorialのjBatchの章をテキトーに訳した

ということで、上記の3サイトを参考にしながら、jBatchの概要を把握していきたいと思う。

JPA周りをもっと突っ込んでいきたいが、時間的に先にjBatchを把握しておきたい。

1.概要

1.1.バッチジョブのステップ

ステップとは、バッチジョブの独立したシーケンシャルなフェーズ

以下のステップが含まれる

1.1.1 チャンク指向のステップ(Chank-oriented steps)

  • データソースから複数件読み込みによるデータ処理をし、それらのデータにビジネスロジックを適用、結果を保存する。
  • 一度に一件読み込み処理をしたのち、グループ化してチャンクに送る。
  • 結果はチャンクが設定サイズに達したときに保存。
  • 結果の保存を効率化し、トランザクション境界の管理を容易にする。

(1) チャンク指向のパート

  1. 入力・検索 : データソースから一度に一件読み込む
  2. ビジネスプロセス : ビジネスロジックにより一度に一件の処理
  3. 出力・書込み : 一度に一つのチャンク(処理された複数件)を保存

1.1.2 タスク指向のステップ(Task-oriented steps)

  • データソースのデータを使用する処理以外のタスク実行
  • 例えば、ディレクトリの作成や削除、ファイルの移動など
  • チャンクステップに比べるとたいてい短時間

1.2.並行処理

バッチジョブはしばしば巨大なデータもしくは計算量が高価になる処理を実行する。バッチアプリケーションは、以下の並行処理シナリオから利益を得ることが出来る。

  1. ステップはお互い依存しておらず、別のスレッドで実行することが出来る
  2. それぞれのアイテムが直前の処理結果に依存していない、チャンク指向ステップの場合、複数のスレッドで実行出来る

バッチフレームワークは開発者に、チャンク指向ステップを分割し、並行処理出来るように、独立したステップグループを定義するメカニズムを提供する。

1.3.ステータスと条件分岐

1.3.1 ステータス

  • バッチフレームワークはジョブでのステップごとにステータスを記録し続ける
  • ステータスはステップが実行中か完了したかを示す
  • ステップが完了した場合、ステータスは次にいずれかを意味する
    • 正常終了
    • 中断
    • エラー

1.3.2 条件分岐

  • バッチジョブには条件分岐を作成出来る
  • 直前のステップの完了ステータスを使用する

jeett_dt_059

Description of "Figure 55-2 Steps and Decision Elements in a Job"

1.4.バッチフレームワークの機能

以下のように共通機能を持つ

  • ジョブ、ステップ、条件分岐の定義とそれらの関係の定義
  • ステップグループの実行およびステップのパートを並行処理
  • ジョブとステップのステータス情報を維持管理
  • ジョブの起動と中断ジョブの再開
  • エラー処理

jbatch_function01

jbatch_function02

jbatch_function03

jBatch資料(PDF)

バッチフレームワークは、バッチアプリケーションに共通な要求のためのインフラを提供し、開発者をビジネスロジックに集中できるようにする。以下から構成。

  • ジョブとステップを記述するためのフォーマット
  • API
  • 実行を管理するサービス

2.Java EE でのバッチ処理

2.1 バッチ処理フレームワーク

以下の要素から構成

jbatch_elements

jBatch資料(PDF)

  • ジョブの実行を管理するバッチランタイム
  • XMLベースのジョブ定義言語
  • バッチランタイム操作用のJava API
  • ステップ、条件分岐、その他バッチあーティファクトを実装するためのJava API

Java EEのバッチアプリケーションはXMLファイルとJava クラスから構成される。

XMLファイルは、バッチアーティファクトを一単位とするジョブの構造とそれらを定義する

2.2 バッチジョブの作成

バッチアプリケーションを作成するための手順

  1. バッチアプリケーションの設計
    1. 入力データの入手元とフォーマット、要求処理結果、処理内容設計
    2. ジョブを、チャンク指向ステップ、タスク指向ステップ、条件分岐により構成し、依存関係を決定
    3. ステップ遷移を一単位とする実行順序を決定
    4. パラレル実行可能なステップと一つ以上のスレッドで実行可能なステップを特定
  2. ステップや条件分岐などをフレームワークが指定するインターフェースを実装したJavaクラスとしてバッチアーティファクトを作成。バッチアーティファクトはDIを使用してバッチランタイムからコンテキストオブジェクトを取得可能。
  3. ジョブ定義言語を使用してジョブとステップの実行フローをXMLに定義。

2.3 バッチジョブの要素

バッチジョブは以下の要素のうち、一つ以上から構成される

要素 内容
ステップ チャンク指向、タスク指向
フロー 一単位として実行されるステップのシーケンス
スプリット パラレルに実行するフローの組み合わせ。フローはそれぞれ別スレッドで実行され、すべて完了した時にスプリットは次へと遷移する
条件分岐 前のステップの完了ステータスをもって、継続終了判定

2.4 プロパティとパラメータ

  • ジョブとステップは、定義ファイルにプロパティを定義出来る。
  • バッチランタイムからコンテキスト経由でプロパティを取得できる。
  • バッチランタイムへジョブがサブミットされたとき、ジョブへパラメータを渡せる。

2.5 ジョブインスタンスと実行

  • ジョブ定義は複数のインスタンスと、インスタンスごとにパラメータを持てる
  • ジョブの実行とは、ジョブインスタンスを実行すること

2.6 バッチと完了ステータス

ジョブのステータス、ステップ、スプリット、フローはバッチランタイム上で文字列のバッチステータスとして表現される。

説明
STARTING ジョブがバッチランタイムへサブミット済み
STARTED ジョブが実行中
STOPPING ジョブが停止命令を受け取り済み
STOPPED ジョブが停止済み
FAILED ジョブがエラー終了
COMPLETED ジョブが正常終了
ABANDONED 破棄されたジョブ

  • JobOperator インターフェースを使用してジョブのバッチステータスを取得できる。
  • ジョブ定義ファイルは、ジョブ定義言語(JSL:Job Specification Language) を使用してバッチステータスを参照出来る。
  • バッチアーティファクトはコンテキストオブジェクトを使用してバッチステータスを取得できる。
  • フローではフローの最終ステップのバッチステータスを指す
  • スプリットでは以下となる
    • COMPLETED - すべてのフローが COMPLATED
    • FAILED - フローのいずれかが FAILED
    • STOPPED - フローのいずれかが STOPPEDであり、FAILEDは一つもない

バッチステータスはバッチランタイムにより設定される。

完了ステータス

  • バッチステータスベースのユーザー定義値である、完了ステータスを持つことが出来る。
  • 完了ステータスはジョブ定義ファイルもしくはバッチアーティファクトで設定可能

と、チュートリアルで概要の説明はここまで。

  1. Java EE 7 検証環境構築(1) WildFly + JBoss Tools で EARプロジェクトを作成し Arquillian で ユニットテストをグリーンにするところまで
  2. Java EE 7 検証環境構築(2) WildFly に DataSourceを作成
  3. Java EE 7 検証環境構築(3) JPAからMySQLに接続するユニットテストをArquillianで実行
  4. Java EE 7 検証環境構築(4) Java EE での DI(Dependency Injection) および CDI(Contexts and Dependency Injection)をながめる
  5. Java EE 7 検証環境構築(5) JBoss Toolsが生成したサンプルソースのCDIを確認する
  6. Java EE 7 検証環境構築(6) JPA エンティティの作成と挿入
  7. Java EE 7 検証環境構築(7) JPA 問い合わせ(1) 名前付きクエリを使ってみる。テストでトランザクションも意識する
  8. Java EE 7 検証環境構築(8) JPA 問い合わせ(2) 動的クエリとCriteria API を試す
  9. Java EE 7 検証環境構築(9) jBatch 概要をおさえる
  10. Java EE 7 検証環境構築(10) JBoss Tools で作成した EARプロジェクトをJava EE 6 から 7 に変更する
  11. Java EE 7 検証環境構築(11) jBatch用 プロジェクトの作成を行う
  12. Java EE 7 検証環境構築(12) jBatch 簡易サンプル作成と Arquillian でユニットテスト
  13. Java EE 7 検証環境構築(13) jBatch REST サービス経由で実行する
  14. Java EE 7 検証環境構築(14) WildFly の管理をGUIで行う
  15. Java EE 7 検証環境構築(15) WildFly を サービスとして設定する(Windows/Linux)
  16. Java EE 7 検証環境構築(16) WildFly と Apache を mod_jk で連携させる(Widows)

検索条件が静的なクエリは、名前付きクエリで対応出来るが、検索画面など複数の検索条件を動的に組み立てる場合には対応出来ないため、動的にクエリを組み立てる必要が出てくる。

SQL組み立て条件を、XMLで指定したりSQLの中に式を組み込んだりと、ORMマッパーの種類によりいろいろ対応方法があるが、JPAでは動的にクエリ文字列を組み立てる方法と、Criteria APIを利用して動的にクエリを組み立てる方法とがある。

1.動的クエリ

  • 動的クエリを作成するのに EntityManager.createQuery() を使用する
  • このメソッドはJPQLクエリを表す文字列を受け取る
    • Query query = em.createQuery(jpql);
  • 型付けを行いたい場合、TypedQueryを利用する
    • TypedQuery<Book> query = em.createQuery(jpql, Book.class);
  • 名前付きでパラメータを渡す場合、:parameterName で宣言し、query.setParameter("parameterName",parameterValue) で値を与える
  • 位置指定パラメータを渡す場合、?1 で宣言し、query.setParameter(1, parameterValue) で値を与える
  • JPQLクエリ文法

ということで実際に試してみる。まぁなんというか、昔ながらの愚直な方法ではある。

この例も、動的にくめるよねという視点のみで、動かすことのみを前提に作成しているので、実際にはもう少し機能の共通化(例えば、汎用的に条件を組み立てるとか、ページングとか)とリファクタリングが必要。

書き方も、JPQLとしての見通しがもう少しよくなるような工夫も必要だろう。

とはいえ、とりあえず意図したようには動く。また、ネイティブなSQLを組み立てて、それを実行することも当然出来る。

public List selectBooksByCondition(
        String title, 
        Float price, 
        String description,
        String isbn) {

    int condCnt = 0;
    Map parameterMap = new HashMap();
    StringBuilder buf = new StringBuilder();
    
    buf.append("select b from Book b");
    if (title != null ||
        price != null ||
        description != null || 
        isbn != null){
    
        buf.append(" where");
        
        if (title != null) {
            buf.append(((condCnt++ == 0)?"":" and"));
            buf.append(" title like :title");
            parameterMap.put("title", title + "%");
        }
        
        if (price != null) {
            buf.append(((condCnt++ == 0)?"":" and"));
            buf.append(" price = :price");
            parameterMap.put("price", price);
        }

        if (description != null) {
            buf.append(((condCnt++ == 0)?"":" and"));
            buf.append(" description like :description");
            parameterMap.put("description", "%" + description + "%");
        }

        if (isbn != null) {
            buf.append(((condCnt++ == 0)?"":" and"));
            buf.append(" isbn = :isbn");
            parameterMap.put("isbn", isbn);
        }
    
    }

    //Query query = em.createQuery(buf.toString());
    TypedQuery query = em.createQuery(buf.toString(), Book.class);
    if (parameterMap.size() > 0) {
        for(Map.Entry entry : parameterMap.entrySet()) {
            query.setParameter(entry.getKey(), entry.getValue());
        }
    }

    return query.getResultList();
}

2.Criteria API(オブジェクト指向クエリ)

  • 文字列で指定するクエリは間違えやすく、実行時に検出されるため原因の特定が困難となる
  • JPA2.0から Criteria API でオブジェクト指向のプログラミングで正しいクエリ構文を記述出来るようになっており、コンパイル時にエラーを検出出来る
  • Criteria APIはJPQLで出来ることがすべて出来るようになっている
  • チュートリアル

で、上記で動的にJPQLを作成してみたが、同様なことを、Criteriap API を使用して、タイプセーフに行うこともができる。

コードとしては、こちらの方がすっきりしてはいるが、やはり、このAPIの挙動を知る必要がある。ある程度複雑なSQLを、Criteria APIに翻訳して実装しなければいけないし、一旦翻訳すると、元のSQLはプロダクトコードではなくなってしまうし。。。

一概に、Criteria API の方が、動的クエリよりよいとは言いがたいところもある。

public List<book> selectBooksByCondition2(
        String title, 
        Float price, 
        String description,
        String isbn) {        

    CriteriaBuilder builder = em.getCriteriaBuilder();
    CriteriaQuery<book> query = builder.createQuery(Book.class);
    
    Root<book> book = query.from(Book.class);
    
    query.select(book);
   
    List<predicate> creteria = new ArrayList<predicate>();
        
    if (title != null) {
        creteria.add(builder.like(book.<string>get("title"), title + "%"));
    }
    
    if (price != null) {
        creteria.add(builder.equal(book.<float>get("price"), price));
    }

    if (description != null) {
        creteria.add(builder.like(book.<string>get("description"), "%"+description+"%"));
    }

    if (isbn != null) {
        creteria.add(builder.equal(book.<string>get("isbn"), isbn));
    }
    
    if (creteria.size() > 0) {
        query.where(builder.and(creteria.toArray(new Predicate[]{})));
    }
    
    return em.createQuery(query).getResultList();
}

タイプセーフなCriteria API

上記、Criteria API では、クエリの構築は確かにタイプセーフとなっているが、モデルのプロパティアクセスには、文字列を使っているためタイプセーフとは言いがたい。型も自らコーディングしている。

JPAでは、メタモデルを定義でき、以下の様にCriteria API を利用することが出来る。

http://relation.to/Bloggers/HibernateStaticMetamodelGeneratorAnnotationProcessor

エンティティ

@Entity public class Order {
    @Id 
    Integer id;
    @ManyToOne 
    Customer customer;
    @OneToMany 
    Set items;
    BigDecimal totalCost;
    ...
}

メタモデル

@StaticMetamodel(Order.class)
public class Order_ {
    public static volatile SingularAttribute id;
    public static volatile SingularAttribute customer;
    public static volatile SetAttribute items;
    public static volatile SingularAttribute totalCost;
}

クエリ

CriteriaBuilder cb = ordersEntityManager.getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery(Order.class);
SetJoin itemNode = cq.from(Order.class)
	.join(Order_.orderItems);
cq.where( cb.equal(itemNode.get(Item_.id), 5 ) ) 
	.distinct(true);

エンティティのプロパティの型指定と、型名の文字列をクエリで記述することが不要になっている。エンティティ+"_"がメタ情報を管理している。

例えば、このメタモデルのリフレクションを使用すると、クライアントからの検索条件をタイプセーフにクエリに落とし込む共通処理がかけそうだなーとか思いながら、メタモデルをEclipse+JBoss Tools 上で簡単に自動生成する手順がよくわからないので、今後の課題。

  1. Java EE 7 検証環境構築(1) WildFly + JBoss Tools で EARプロジェクトを作成し Arquillian で ユニットテストをグリーンにするところまで
  2. Java EE 7 検証環境構築(2) WildFly に DataSourceを作成
  3. Java EE 7 検証環境構築(3) JPAからMySQLに接続するユニットテストをArquillianで実行
  4. Java EE 7 検証環境構築(4) Java EE での DI(Dependency Injection) および CDI(Contexts and Dependency Injection)をながめる
  5. Java EE 7 検証環境構築(5) JBoss Toolsが生成したサンプルソースのCDIを確認する
  6. Java EE 7 検証環境構築(6) JPA エンティティの作成と挿入
  7. Java EE 7 検証環境構築(7) JPA 問い合わせ(1) 名前付きクエリを使ってみる。テストでトランザクションも意識する
  8. Java EE 7 検証環境構築(8) JPA 問い合わせ(2) 動的クエリとCriteria API を試す
  9. Java EE 7 検証環境構築(9) jBatch 概要をおさえる
  10. Java EE 7 検証環境構築(10) JBoss Tools で作成した EARプロジェクトをJava EE 6 から 7 に変更する
  11. Java EE 7 検証環境構築(11) jBatch用 プロジェクトの作成を行う
  12. Java EE 7 検証環境構築(12) jBatch 簡易サンプル作成と Arquillian でユニットテスト
  13. Java EE 7 検証環境構築(13) jBatch REST サービス経由で実行する
  14. Java EE 7 検証環境構築(14) WildFly の管理をGUIで行う
  15. Java EE 7 検証環境構築(15) WildFly を サービスとして設定する(Windows/Linux)
  16. Java EE 7 検証環境構築(16) WildFly と Apache を mod_jk で連携させる(Widows)

なんやかんやで、JPAを使ってエンティティを挿入することができたので、問い合わせを行う。

1.JPA問い合わせについて

1.1 JPQL (Java Persistence Query Language)

JPQLはリレーショナル・データベースに格納されているエンティティに問い合わせを実行するためにJPAで定義された言語。JPQLの構文はSQLとよく似ていますが、データベースのテーブルを直接操作するのではなく、エンティティオブジェクトを操作します。

JPQL 文法

  • JPAでは、SQLのかわりにJPQLを使用する 。
  • JPQLは動的、静的、ネイティブSQLも実行可能。
  • 静的クエリは、名前付きクエリ(Named Query)ともいい、アノテーション、XMLを使用して定義できる 。
  • JPQLではなく、DBMS ネイティブのSQLを指定することもできる (動的、静的)。

1.2 ネイティブクエリ

JPQLはデータベース間で移植可能で、あらゆる形式のエンティティも扱える多種多様な構文を持っています。しかし、その一方でJPAはデータベース固有の機能を利用できるようにネイティブクエリもサポートしています。ネイティブクエリはデータベース間での移植性は保証しません。JDBC直接呼び出しではなく、JPAのネイティブクエリを利用する主な理由は、クエリの結果がエンティティに自動変換されるためです。

1.3 名前付きクエリ(Named Query)

名前付きクエリは静的で変更できないという点で動的クエリとは異なります。名前付きクエリの静的な性質から、動的クエリのような柔軟性は実現できませんが、永続性プロバイダはアプリケーションの起動時にJPQL文字列をSQLに変換でき、クエリを実行するたびに毎回変換を行わないため、クエリの実行効率が高くなります。

  • 単独なら@NamedQuery、複数の場合@NamedQueriesでまとめる 。
  • または、対応するXMLディスクリプタ内のメタデータで定義する 。
  • 同様に@NamedNativeQuery で、ネイティブクエリも名前付きクエリとして定義できる。(@NamedNativeQueries で複数定義できる) 。
  • 一般的に、クエリ結果に直接対応するエンティティクラスに記述する。
  • クエリ名は永続性ユニットごとにスコープがあり、スコープ内で一意でなければならない(クエリ名の前にエンティティ名をつけるのが一般的)。
  • クエリ名文字列でタイプミスによる問題を軽減するために、クエリ名を定数を置き換えることもできる

2. 試す

ということで、まずは、この辺りまで(JPQL名前付きクエリ、ネイティブ名前付きクエリ)を試してみることにする。

動的JPQL、Criteria API(オブジェクト指向クエリ) などは別途確認する。

2.1 エンティティ

挿入の確認で作成した、エンティティに、名前付きクエリ(JPQL、ネイティブ) をアノテーションで付加する。Book エンティティにクエリ名を定数として持たせるのは若干違和感(Bookエンティティには本質的にクエリ名とか関係ないはず)がないではないが、メリットのほうが大きそうなので、定数化してみる。

@Entity
@NamedQueries({
    @NamedQuery(name=Book.QUERY_FIND_BY_ID,
                query="select b from Book b where b.id = :id"),
    @NamedQuery(name=Book.QUERY_SELECT_ALL,
                query="select b from Book b"),
    @NamedQuery(name=Book.QUERY_SELECT_BY_TITLE,
                query="select b from Book b where b.title like :title")})
@NamedNativeQuery(name=Book.QUERY_SELECT_MORE_EXPENSIVE,
                query="select * from book where price > :price",
                resultClass=Book.class)
public class Book {
    public static final String QUERY_FIND_BY_ID = "Book.findBookById";
    public static final String QUERY_SELECT_ALL = "Book.selectAllBooks";
    public static final String QUERY_SELECT_BY_TITLE = "Book.selectBooksByTitle";
    public static final String QUERY_SELECT_MORE_EXPENSIVE = "Book.selectMoreExpensiveBooks";

    public Book(){}
    
    public Book(String title, Float price, String description){
        this.title = title;
        this.price = price;
        this.description = description;
    }

   :省略
}

2.2 サービスを作成

それぞれの名前付きクエリを呼び出すサービスメソッドを定義してみる。

EntityManager の createNamedQueryは、Query を返し、Query のセッターは自分自身を返す。

メソッドチェイン(Method chaining) はSmalltalkで人気のあったプログラミングスタイルであり、他のJavaプログラミングスタイルに比べて読みにくくデバックがしにくいという批判もある。しかしながら、ほとんどの場合において、これは非常に便利である。 多くのJava開発者はSetterメソッドやadderメソッドは戻り値を返さないという意味でvoid型に宣言する。しかしこのプログラミングスタイルを適用する場合は、戻り値がオブジェクト自身を返すようにsetterメソッドやadderメソッドを宣言することになる。

なので、下記例では、Query のローカル変数を宣言していない。

@Stateless
public class BookService {

    @Inject
    private Logger log;

    @Inject
    private EntityManager em;
    
    public void insertBook(Book book) {
        em.persist(book);
    }
    
    public Book findBookById(Long id) {
        return (Book)em.createNamedQuery(Book.QUERY_FIND_BY_ID)
                .setParameter("id", id)
                .getSingleResult()
                ;
    }
    
    @SuppressWarnings("unchecked")
    public List selectAllBooks() {
        return em.createNamedQuery(Book.QUERY_SELECT_ALL).getResultList();
    }
    
    @SuppressWarnings("unchecked")
    public List selectBooksByTitle(String title) {
        return em.createNamedQuery(Book.QUERY_SELECT_BY_TITLE)
                .setParameter("title", title + "%")
                .getResultList()
                ;
    }
    
    @SuppressWarnings("unchecked")
    public List selectMoreExpensiveBooks(Float price) {
        return em.createNamedQuery(Book.QUERY_SELECT_MORE_EXPENSIVE)
                .setParameter("price", price)
                .getResultList()
                ;
    }
}

2.3 テストケース

Arquilian Java 永続化のテスト を参照する。Spring だと、@Rollback でテストメソッドのトランザクションを制御できた(デフォルトロールバック、@Rollback(false)でコミット)が。。。

@Inject UserTransaction utx; でユーザートランザクションはインジェクトでき(Mavenに依存関係を記述する必要 2.3.1 参照)、@Before が、テストメソッドの前、@After がテストメソッドの後に呼び出されるので、そこで、トランザクションの開始と、ロールバックをしてみる。

一応想定した、メソッドごとにトランザクション開始、ロールバックという動きはするのだが、もう少し簡単にできるとうれしい。

@RunWith(Arquillian.class)
public class BookServiceTest {
       @Deployment
        public static Archive<?> createTestArchive() {
            return ShrinkWrap.create(WebArchive.class, "test.war")
                    .addClass(Resources.class)
                    .addClasses(Book.class,BookService.class)
                    .addAsResource("META-INF/test-persistence.xml", "META-INF/persistence.xml")
                    .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
                    ;
        }

        @Inject
        Logger log;
        
        @Inject
        BookService bookService;

        @Inject
        UserTransaction utx;
        
        @Before
        public void beforeTest() throws Exception {
            utx.begin();
        }
        
        @After
        public void afterTest() throws Exception {
            utx.rollback();
        }
        
        @Test
        public void insertBookTest() {
            Book book = new Book("Insert Test",100f,"Test");
            bookService.insertBook(book);
            log.info(book.toString());
            assertNotNull(book.getId());
        }
        
        @Test
        public void findBookByIdTest() {
            createTestData();

            List allBooks = bookService.selectAllBooks();
            int pos = (int)(Math.random() * 100d) % allBooks.size(); 
            
            Book book1 = allBooks.get(pos);
            log.info(book1.toString());
            
            Book book2 = bookService.findBookById(book1.getId());
            log.info(book2.toString());
            
            assertEquals(book1, book2);
            assertTrue(book1 == book2);
        }
        
        
        @Test
        public void selectBooksByTitleTest() {
            createTestData();
            
            List books = bookService.selectBooksByTitle(SAMPLE_BOOK_TITLE_PREFIX);
            for (Book book : books) {
                log.info(book.toString());
                assertTrue(book.getTitle().startsWith(SAMPLE_BOOK_TITLE_PREFIX));
            }
        }
        
        @Test
        public void selectMoreExpensiveBooksTest() {
            createTestData();
            
            final float BASE_PRICE = 300f;
            
            List books = bookService.selectMoreExpensiveBooks(BASE_PRICE);
            for (Book book : books) {
                log.info(book.toString());
                assertTrue(book.getPrice() > BASE_PRICE);
            }
        }

        private final String SAMPLE_BOOK_TITLE_PREFIX = "SampleBook";
        private void createTestData() {
            for (int i=0; i<5; i++) {
                bookService.insertBook(
                    new Book(
                        String.format("%s%02d", SAMPLE_BOOK_TITLE_PREFIX, i),
                        i * 100f,
                        String.format("Description about %s%02d.", SAMPLE_BOOK_TITLE_PREFIX, i))
                );
            }
        }
}

2.3.1 UserTransaction を利用出来るようにする

pom.xml に以下を追記。

<dependency>
    <groupId>javax.transaction</groupId>
    <artifactId>jta</artifactId>
    <version>1.1</version>
    <scope>test</scope>
</dependency>
<\pre>

2.4 テスト結果

ここまで問題なく実行。

jpa_query_test

  1. Java EE 7 検証環境構築(1) WildFly + JBoss Tools で EARプロジェクトを作成し Arquillian で ユニットテストをグリーンにするところまで
  2. Java EE 7 検証環境構築(2) WildFly に DataSourceを作成
  3. Java EE 7 検証環境構築(3) JPAからMySQLに接続するユニットテストをArquillianで実行
  4. Java EE 7 検証環境構築(4) Java EE での DI(Dependency Injection) および CDI(Contexts and Dependency Injection)をながめる
  5. Java EE 7 検証環境構築(5) JBoss Toolsが生成したサンプルソースのCDIを確認する
  6. Java EE 7 検証環境構築(6) JPA エンティティの作成と挿入
  7. Java EE 7 検証環境構築(7) JPA 問い合わせ(1) 名前付きクエリを使ってみる。テストでトランザクションも意識する
  8. Java EE 7 検証環境構築(8) JPA 問い合わせ(2) 動的クエリとCriteria API を試す
  9. Java EE 7 検証環境構築(9) jBatch 概要をおさえる
  10. Java EE 7 検証環境構築(10) JBoss Tools で作成した EARプロジェクトをJava EE 6 から 7 に変更する
  11. Java EE 7 検証環境構築(11) jBatch用 プロジェクトの作成を行う
  12. Java EE 7 検証環境構築(12) jBatch 簡易サンプル作成と Arquillian でユニットテスト
  13. Java EE 7 検証環境構築(13) jBatch REST サービス経由で実行する
  14. Java EE 7 検証環境構築(14) WildFly の管理をGUIで行う
  15. Java EE 7 検証環境構築(15) WildFly を サービスとして設定する(Windows/Linux)
  16. Java EE 7 検証環境構築(16) WildFly と Apache を mod_jk で連携させる(Widows)

ここまでで、基本的に動作させることが出来るようになったので、JPA 周りの動きを見ていきたい。

CDIについての記述は物足りないにもほどがあったが、JPAについてはこの本の説明がわかりやすかった。

Beginning Java EE 6 GlassFish 3で始めるエンタープライズJava (Programmer's SELECTION)

環境を GlassFish から WildFly に変更したので、適宜読み替えながら、上記本の記述をかいつまみながら追っていこうと思う。

1.JPAの永続化を試してみる

1.1 エンティティの作成

だいたい、以下の様なJPAアノテーションを付加する。

  1. @Entity付与 でエンティティであることを認識させる
  2. @Id付与 で主キー設定
  3. @GeneratedValue で識別子の値を自動生成させる
  4. @Column でデフォルトのカラムマッピングをカスタマイズ

Bookエンティティを作成する。

@Entity
public class Book {

	@Id
	@GeneratedValue
	private Long id;
	
	@Column(nullable=false)
	private String title;
	private Float price;
	@Column(length=2000)
	private String description;
	private String isbn;
	private Integer nbOfPage;
	private Boolean illustrations;
	: コンストラクタ、ゲッター、セッター省略
}

1.2 サービスクラス(ステートレスセッションBean) を作成

Bookエンティティを操作するサービスクラスを作成してみる。

上記の本に書かれている訳ではない。

@Inject で、Logger と JPAの主役であるEntityManagerがインジェクトされているのは、CDIの仕組みによっている。

Java EE 7 検証環境構築(5) JBoss Toolsが生成したサンプルソースのCDIを確認する

@Stateless
public class BookService {

    @Inject
    private Logger log;

    @Inject
    private EntityManager em;
    
    public void insertBook(Book book) {
    	em.persist(book);
    }
}

1.3 Arquillian でユニットテストを実行

Java EE 7 検証環境構築(3) JPAからMySQLに接続するユニットテストをArquillianで実行

単純にBookエンティティを挿入してみる。

@RunWith(Arquillian.class)
public class BookServiceTest {
	   @Deployment
	    public static Archive<?> createTestArchive() {
	        return ShrinkWrap.create(WebArchive.class, "test.war")
	                .addClass(Resources.class)
	                .addClass(Book.class)
	                .addClass(BookService.class)
	                .addAsResource("META-INF/test-persistence.xml", "META-INF/persistence.xml")
	                .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
	                ;
	    }

	    @Inject
	    Logger log;

	    @Inject
	    BookService bookService;
	    
	    @Test
	    public void insertBookTest() {
	    	Book book = new Book();
	    	
	    	book.setTitle("Test Book");
	    	book.setPrice(150f);
	    	book.setDescription("this is test book.");
	    	
	    	bookService.insertBook(book);
	    	
	    }
}

JPAがはき出すSQL

10:28:44,068 INFO  [org.hibernate.tool.hbm2ddl.SchemaExport] (ServerService Thread Pool -- 20) HHH000227: Running hbm2ddl schema export
10:28:44,072 INFO  [stdout] (ServerService Thread Pool -- 20) Hibernate: drop table if exists Book
10:28:44,089 INFO  [stdout] (ServerService Thread Pool -- 20) Hibernate: drop table if exists hibernate_sequence
10:28:44,506 INFO  [stdout] (ServerService Thread Pool -- 20) Hibernate: create table Book (id bigint not null, description longtext, illustrations bit, isbn varchar(255), nbOfPage integer, price float, title varchar(255) not null, primary key (id))
10:28:45,221 INFO  [stdout] (ServerService Thread Pool -- 20) Hibernate: create table hibernate_sequence ( next_val bigint )
10:28:45,825 INFO  [stdout] (ServerService Thread Pool -- 20) Hibernate: insert into hibernate_sequence values ( 1 )
10:28:50,408 INFO  [stdout] (default task-1) Hibernate: select next_val as id_val from hibernate_sequence for update
10:28:50,423 INFO  [stdout] (default task-1) Hibernate: update hibernate_sequence set next_val= ? where next_val=?
10:28:50,574 INFO  [stdout] (default task-1) Hibernate: insert into Book (description, illustrations, isbn, nbOfPage, price, title, id) values (?, ?, ?, ?, ?, ?, ?)

テスト成功

jpa_junit01

1.4 MySQL に接続して、どのようなDDLが生成されたか見てみる

@Column で指定しないと、デフォルトの桁数などが使用されているのが見て取れる。

PS C:\Users\piroto> mysql -u root -p
Enter password: ********
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 12
Server version: 5.6.17 MySQL Community Server (GPL)

Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> use tallarico
Database changed
mysql> show tables;
+---------------------+
| Tables_in_tallarico |
+---------------------+
| book                |
| hibernate_sequence  |
| registrant          |
+---------------------+
3 rows in set (0.00 sec)

mysql> show create table book;
+-------+-------------------------------------
| Table | Create Table
+-------+-------------------------------------
| book  | CREATE TABLE `book` (
  `id` bigint(20) NOT NULL,
  `description` longtext,
  `illustrations` bit(1) DEFAULT NULL,
  `isbn` varchar(255) DEFAULT NULL,
  `nbOfPage` int(11) DEFAULT NULL,
  `price` float DEFAULT NULL,
  `title` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 
+-------+-------------------------------------
1 row in set (0.00 sec)

WildFly では、デフォルトのJPA実装は Hibernte ということ(HibernateがJPAインターフェースを備えている)で、少し古いが以下の本を購入してみた。読了はしていないが、前半部分だけでも、かなり良書。ORMとはなんたるかがよくわかる。

今までかなりORMを誤解していた部分があった。ORM結構面白そう。

  1. Java EE 7 検証環境構築(1) WildFly + JBoss Tools で EARプロジェクトを作成し Arquillian で ユニットテストをグリーンにするところまで
  2. Java EE 7 検証環境構築(2) WildFly に DataSourceを作成
  3. Java EE 7 検証環境構築(3) JPAからMySQLに接続するユニットテストをArquillianで実行
  4. Java EE 7 検証環境構築(4) Java EE での DI(Dependency Injection) および CDI(Contexts and Dependency Injection)をながめる
  5. Java EE 7 検証環境構築(5) JBoss Toolsが生成したサンプルソースのCDIを確認する
  6. Java EE 7 検証環境構築(6) JPA エンティティの作成と挿入
  7. Java EE 7 検証環境構築(7) JPA 問い合わせ(1) 名前付きクエリを使ってみる。テストでトランザクションも意識する
  8. Java EE 7 検証環境構築(8) JPA 問い合わせ(2) 動的クエリとCriteria API を試す
  9. Java EE 7 検証環境構築(9) jBatch 概要をおさえる
  10. Java EE 7 検証環境構築(10) JBoss Tools で作成した EARプロジェクトをJava EE 6 から 7 に変更する
  11. Java EE 7 検証環境構築(11) jBatch用 プロジェクトの作成を行う
  12. Java EE 7 検証環境構築(12) jBatch 簡易サンプル作成と Arquillian でユニットテスト
  13. Java EE 7 検証環境構築(13) jBatch REST サービス経由で実行する
  14. Java EE 7 検証環境構築(14) WildFly の管理をGUIで行う
  15. Java EE 7 検証環境構築(15) WildFly を サービスとして設定する(Windows/Linux)
  16. Java EE 7 検証環境構築(16) WildFly と Apache を mod_jk で連携させる(Widows)

CDI の参照実装 JBoss Weld

CDI の概要は何となく理解した。後は、どう使うのか。

まず、JBoss Tools で自動作成された EAR Project のサービスクラスを確認してみる。

@Stateless
public class MemberRegistration {

    @Inject
    private Logger log;

    @Inject
    private EntityManager em;

    @Inject
    private Event memberEventSrc;

    public void register(Member member) throws Exception {
        log.info("Registering " + member.getName());
        em.persist(member);
        memberEventSrc.fire(member);
    }
}

さて、ここでインジェクトされている、Logger や EntityManager はどこで登録されているのかしら?

事前定義 Bean

事前定義された、Bean。もしかして事前定義されている?

http://docs.oracle.com/javaee/7/tutorial/doc/cdi-adv004.htm#CJGHGDBA

事前定義 Resource or CDI Bean インジェクション例
UserTransaction Resource @Resource UserTransaction transaction;
Principal Resource @Resource Principal principal;
Validator Resource @Resource Validator validator;
ValidatorFactory Resource @Resource ValidatorFactory factory;
HttpServletRequest CDI Bean @Inject HttpServletRequest req;
HttpSession CDI Bean @Inject HttpSession session;
ServletContext CDI Bean @Inject ServletContext context;

の中には、ないようだ。

CDIが管理しているBeanをのぞいてみる

で、CDIが管理しているBeanの一覧を確認してみる。

BeanManager

Any bean may obtain an instance of BeanManager by injecting it:

 @Inject
 BeanManager manager;

Arquillian テストケースで、BeanManager が管理しているBean一覧を出力

@RunWith(Arquillian.class)
public class BeanManagerTest {
	
    @Deployment
    public static Archive<?> createTestArchive() {
        return ShrinkWrap.create(WebArchive.class, "test.war")
                .addClasses(Resources.class)
                .addAsResource("META-INF/test-persistence.xml", "META-INF/persistence.xml")
                .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
                ;
    }

    @Inject
    Logger log;
    
    @Inject
    BeanManager manager;
    
    @Test
    public void testListAllBeans() throws Exception {
        
     	@SuppressWarnings("serial")
		Set<?>> beans = manager.getBeans(Object.class, new AnnotationLiteral(){});
    	for (Bean<?> bean: beans) {
    		log.info(String.format(
    				"BeanName=%s,Scope=%s", 
    				bean.getBeanClass().getName(), 
    				bean.getScope().getName()));
    	}

    }
}

結果(Sort & Unique 済み)。

BeanName=com.sun.faces.application.view.ViewScopeExtension,Scope=javax.enterprise.context.ApplicationScoped
BeanName=com.sun.faces.application.view.ViewScopedCDIEventFireHelperImpl,Scope=javax.enterprise.context.Dependent
BeanName=com.sun.faces.flow.FlowCDIEventFireHelperImpl,Scope=javax.enterprise.context.Dependent
BeanName=com.sun.faces.flow.FlowCDIExtension,Scope=javax.enterprise.context.ApplicationScoped
BeanName=com.sun.faces.flow.FlowDiscoveryCDIExtension,Scope=javax.enterprise.context.ApplicationScoped
BeanName=com.sun.faces.flow.FlowDiscoveryCDIHelper,Scope=javax.enterprise.context.Dependent
BeanName=info.typea.tallarico.test.BeanManagerTest,Scope=javax.enterprise.context.Dependent
BeanName=info.typea.tallarico.util.Resources,Scope=javax.enterprise.context.Dependent
BeanName=javax.enterprise.inject.spi.Bean,Scope=javax.enterprise.context.Dependent
BeanName=javax.enterprise.inject.spi.Decorator,Scope=javax.enterprise.context.Dependent
BeanName=javax.enterprise.inject.spi.Interceptor,Scope=javax.enterprise.context.Dependent
BeanName=org.hibernate.validator.internal.cdi.ValidationExtension,Scope=javax.enterprise.context.ApplicationScoped
BeanName=org.hibernate.validator.internal.engine.ValidatorFactoryImpl,Scope=javax.enterprise.context.ApplicationScoped
BeanName=org.hibernate.validator.internal.engine.ValidatorImpl,Scope=javax.enterprise.context.ApplicationScoped
BeanName=org.jberet.creation.BatchBeanProducer,Scope=javax.enterprise.context.Dependent
BeanName=org.jberet.creation.BatchCDIExtension,Scope=javax.enterprise.context.ApplicationScoped
BeanName=org.jboss.arquillian.protocol.servlet.runner.ServletTestRunner,Scope=javax.enterprise.context.Dependent
BeanName=org.jboss.arquillian.testenricher.cdi.container.CDIExtension,Scope=javax.enterprise.context.ApplicationScoped
BeanName=org.jboss.resteasy.cdi.ResteasyCdiExtension,Scope=javax.enterprise.context.ApplicationScoped
BeanName=org.jboss.weld.bean.builtin.BeanManagerProxy,Scope=javax.enterprise.context.Dependent
BeanName=org.jboss.weld.bean.builtin.EventMetadataBean,Scope=javax.enterprise.context.Dependent
BeanName=org.jboss.weld.bean.builtin.InjectionPointBean,Scope=javax.enterprise.context.Dependent
BeanName=org.jboss.weld.bean.builtin.InstanceImpl,Scope=javax.enterprise.context.Dependent
BeanName=org.jboss.weld.bean.builtin.ee.HttpServletRequestBean,Scope=javax.enterprise.context.RequestScoped
BeanName=org.jboss.weld.bean.builtin.ee.HttpSessionBean,Scope=javax.enterprise.context.SessionScoped
BeanName=org.jboss.weld.bean.builtin.ee.PrincipalBean,Scope=javax.enterprise.context.Dependent
BeanName=org.jboss.weld.bean.builtin.ee.ServletContextBean,Scope=javax.enterprise.context.RequestScoped
BeanName=org.jboss.weld.bean.builtin.ee.UserTransactionBean,Scope=javax.enterprise.context.Dependent
BeanName=org.jboss.weld.context.conversation.ConversationImpl,Scope=javax.enterprise.context.RequestScoped
BeanName=org.jboss.weld.context.http.HttpSessionDestructionContext,Scope=javax.enterprise.context.Dependent
BeanName=org.jboss.weld.event.EventImpl,Scope=javax.enterprise.context.Dependent
BeanName=org.jboss.weld.manager.BeanManagerImpl,Scope=javax.enterprise.context.Dependent

Logger も、EntityManger も管理Beanにない。。。

んーどういう理屈?

    @Deployment
    public static Archive<?> createTestArchive() {
        return ShrinkWrap.create(WebArchive.class, "test.war")
                .addClasses(Resources.class)
                .addAsResource("META-INF/test-persistence.xml", "META-INF/persistence.xml")
                .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
                ;
    }

Arquillian テストクラスをもう一度よく見てみる。

beans.xml は、CDI を有効化するための、デプロイメント記述子。

ん、Resource.class は?ソースを確認。

/**
 * This class uses CDI to alias Java EE resources, such as the persistence context, to CDI beans
 * 
 * <p>
 * Example injection on a managed bean field:
 * </p>
 * 
 * <pre>
 * @Inject
 * private EntityManager em;
 * </pre>
 */
public class Resources {
    // use @SuppressWarnings to tell IDE to ignore warnings about field not being referenced directly
    @SuppressWarnings("unused")
    @Produces
    @PersistenceContext
    private EntityManager em;

    @Produces
    public Logger produceLog(InjectionPoint injectionPoint) {
        return Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
    }
}

おーあった、あった。Logger と EntityManager のファクトリメソッドをもった Resourceクラス(JBoss Tools のプロジェクトで自動生成されたクラス)は、BeanManagerに管理されてたわ。

納得。

  1. Java EE 7 検証環境構築(1) WildFly + JBoss Tools で EARプロジェクトを作成し Arquillian で ユニットテストをグリーンにするところまで
  2. Java EE 7 検証環境構築(2) WildFly に DataSourceを作成
  3. Java EE 7 検証環境構築(3) JPAからMySQLに接続するユニットテストをArquillianで実行
  4. Java EE 7 検証環境構築(4) Java EE での DI(Dependency Injection) および CDI(Contexts and Dependency Injection)をながめる
  5. Java EE 7 検証環境構築(5) JBoss Toolsが生成したサンプルソースのCDIを確認する
  6. Java EE 7 検証環境構築(6) JPA エンティティの作成と挿入
  7. Java EE 7 検証環境構築(7) JPA 問い合わせ(1) 名前付きクエリを使ってみる。テストでトランザクションも意識する
  8. Java EE 7 検証環境構築(8) JPA 問い合わせ(2) 動的クエリとCriteria API を試す
  9. Java EE 7 検証環境構築(9) jBatch 概要をおさえる
  10. Java EE 7 検証環境構築(10) JBoss Tools で作成した EARプロジェクトをJava EE 6 から 7 に変更する
  11. Java EE 7 検証環境構築(11) jBatch用 プロジェクトの作成を行う
  12. Java EE 7 検証環境構築(12) jBatch 簡易サンプル作成と Arquillian でユニットテスト
  13. Java EE 7 検証環境構築(13) jBatch REST サービス経由で実行する
  14. Java EE 7 検証環境構築(14) WildFly の管理をGUIで行う
  15. Java EE 7 検証環境構築(15) WildFly を サービスとして設定する(Windows/Linux)
  16. Java EE 7 検証環境構築(16) WildFly と Apache を mod_jk で連携させる(Widows)

JBoss Tools が作成した、サンプルエンタープライズアプリケーションの MemberRegistrationTest クラスの内容の大枠は把握できた。

今回の検証では、Spring から Java EE 7 への移行を狙っているのだが、SpringのDIとは異なり、Java EE のDIでは、アプリケーションサーバーなどのコンテナがオブジェクトを管理するためコンテナが必要となる。

ということは、依存性をユニットテスト実行時に解決するためには、必然的にコンテナが必要になるのだが、どうやって行うんだろう。。。と漠然と懸念していたのだが、Arquillian を利用するとArquillianがコンテナを管理したり、起動済みのコンテナに、パッケージを作成してデプロイしてくれるのでコンテナ上でのユニットテストが可能となる。すばらしい。

ではユニットテストの懸念は解決したので、次はサンプルプロジェクトでユニットテスト対象となっている、MemberRegistration サービスクラスのソースコードを見てみよう。

@Stateless
public class MemberRegistration {

    @Inject
    private Logger log;

    @Inject
    private EntityManager em;

    @Inject
    private Event memberEventSrc;

    public void register(Member member) throws Exception {
        log.info("Registering " + member.getName());
        em.persist(member);
        memberEventSrc.fire(member);
    }
}

ふむふむ。@Inject アノテーションにより、Logger や、EntityManagerの実装クラスがインジェクトされることは、容易に想像がつくが、一体どのように使用すべきで、どういう理屈で動いているだろう。

Loggerはのインスタンスはどこからやってくるのだ???

残念ながら(?) 直感で突っ切れるほど、Java EE は甘くない。

まず、Java EE 5 までの DI とJava EE 6 からの CDI では大きく内容が異なるようだ。

Java EE 6 のCDIとは何か?については、以下のサイトが非常にわかりやすい。サンプルも豊富ですばらしい。

まぁ、上記サイトを読めばいろいろ解決してしまう。。。とはいえ、自分でまとめないと覚えられないたちなので、以下にメモしていこうと思う。

1.Java EE5 までのDI

まず、Java EE 5 までのDI。本当に、この本 は、読みやすくわかりやすい。Java EE 7 版が出ていれば、この検証作業も本の内容をトレースするだけですみそうなのだが。。。早く出版されることを願いつつ、一部要約、抜粋する。

  • DIコンテナ(SpringフレームワークやSeasar2など) では任意のオブジェクトをインジェクション可能だが、Java EE 5 では、コンテナ管理オブジェクトのみが対応

  • Java EE 5 では、コンストラクタインジェクションに対応せず(フィールド、セッター)

1.1 インジェクション対象オブジェクト

Java EE 5 対応コンテナは、以下に示す「コンテナが管理するオブジェクト」をインジェクションする仕組みを持っている。

アノテーション インジェクション対象
@EJB セッションBean(EJB参照)
@Resource
  • データーソース
  • JMSのコネクションファクトリ
  • JMSのデスティネーション
  • Java Mailのセッション
  • JTAのユーザートランザクション
  • EJBコンテキスト
@PersistenceContext JPAのエンティティマネージャ
@PersistenceUnit JPAのエンティティマネージャファクトリ
@Timeout タイマーサービス

1.2 インジェクション先オブジェクト

Java EE 5 では、コンテナがライフサイクルを管理する以下のオブジェクトに対してインジェクション可能。

コンポーネント種類 オブジェクト
Web
  • サーブレット
  • フィルタ
  • リスナ
  • タグハンドラ
  • マネージドBean
EJB
  • セッションBean
  • インターセプタ
  • メッセージ駆動Bean
Webサービス サービス実装クラス

現在広く利用されているJava EE 5仕様でもDI機能は盛り込まれていましたが、非常に制限された形で仕様化されていました。インジェクションするオブジェクトも、インジェクションされるオブジェクトもその種類が制限されており、インジェクションに使用するアノーテーションも、インジェクションするオブジェクトの種類によって異なるアノーテーションクラスを使用する必要がありました(例えば、EJBのインジェクションには@EJBを使用し、データソースのインジェクションには@Resourceを使うなど)。そのため、Java EE 5仕様におけるインジェクションの機能は、汎用のDIと区別するため、一般に「リソース・インジェクション」と呼ばれています

ということらしい。

さてそれが、どう変わるのか?

2. Java EE 7 の CDI

では、Java EE 6 からの CDI!

といことで、Beginning Java EE 6 GlassFish 3で始めるエンタープライズJava (Programmer's SELECTION) を確認する。

こちらの本も全体として非常にわかりやすく、サンプルも豊富な良書だと思うのだが、なぜか、CDIについての記述がかなり少ない。出版の時期もあるのか?残念ながら @Inject が索引に出てきすらしないという状況。

ということで、Java EE 7 チュートリアルのCDIの箇所を斜め読みし、要点をまとめ(意訳&要約&感想)てみたい。(理解ミスがあったらごめんなさい)

あと、Redhat のサイトも参考になる

では。

2.1 概要

Java EE 7 の CDI では、さまざまなコンポーネントを疎結合かつタイプセーフな方法で柔軟に統合出来る。

CDIは、オブジェクトのライフサイクルを管理し依存性を自動的に提供する。

以下の様にメンバーを宣言することで、CDIランタイムにより自動的にインジェクトされる。

CDIランタイムは、インスタンスのスコープを知る必要がある。

@RequestScoped public class MessageB implements Message { ... }

CDI Bean は、CDIが管理、インジェクト出来るクラスでほとんどのJavaクラスが対象となる。

BeanアーカイブとはCDI Beanを含んだ、JAR、WAR ファイルを指す。

2.2 CDIが提供するサービス

最初の2つを基本として,大きく以下の様なことが可能となる。

  • ステートフルなコンポーネントのコンテキスト(定義されたライフサイクル)と相互作用させる
  • 依存性の注入(DI) タイプセーフ
  • EL言語との統合
  • インジェクトされたコンポーネントのデコレート
  • タイプセーフにインターセプターとコンポーネントをバインディング
  • イベント通知モデル
  • Web Conversation(会話)スコープ
  • SPI(Service Provider Interface) によりサードパーティとの統合を簡潔に

2.3 CDIのコンセプト

2.3.1 疎結合であること

  • サーバーとクライアントの分離(定義された型と限定子)。
  • 共同作業するコンポーネントのライフサイクル(コンテキスト依存コンポーネント、自動ライフサイクル管理)
  • ステートフルコンポーネントのサービス相互運用(純粋なメッセージパッシング、文字列による名称でのルックアップ削除)
  • イベントにより、メッセージプロデューサーをコンシューマーから完全に分離
  • インターセプターにより、直交する関心事を完全に分類

2.3.2 強力な型付け

  • アノテーションによる宣言ですべての設定が可能
    • XMLデプロイメント記述子のほとんどを削減
    • 開発ツールの提供容易性

2.4 Bean

プリミティブ型を含むほとんどのJava型が、Bean型。ライフサイクルコンテキストモデル(CDI仕様) に沿って、インスタンスのライフサイクルがコンテナ管理される、Java EE コンポーネントは Bean。

Bean は、限定子、スコープ、EL名(オプション)、インターセプタバインディング などの属性をもつ。

2.5 CDI 管理 Bean

トップレベルのクラス、以下に該当する場合は、CDI管理Bean

  • JSFの仕様に従っている場合
  • スタティックでないインナークラス
  • 具象クラス、または、@Decoratorがつけられたクラス
  • EJBコンポーネント定義アノテーションがつけられていない。(ejb-jar.xmlでEJBコンポーネントとして定義されていない)
  • 適切なコンストラクタ(パラメータを持たないコンストラクタ、@Injectでアノテートされたコンストラクタ)を持つ

2.6 インジェクト可能なオブジェクトとしてのBean

CDIはコンテナ管理ではない多くのオブジェクトもインジェクト可能

  • ほとんどのJavaクラス
  • セッション Bean
  • Java EE リソース(データソース、JMS)
  • 永続化コンテキスト(JPA EntityManager)
  • Producerフィールド
  • オブジェクトを返すProducerメソッド
  • Webサービスの参照
  • リモート EJBの参照

2.7 限定子の利用

特定のBeanに対していくつかの実装が提供される場合(例えば、あるインターフェースに対して複数の実装クラスが存在する場合)、限定子を利用してどの実装をインジェクトするのかを特定できる

限定子とはBeanに適用できるアノテーションのことで、限定子を定義するには以下を指定する。

  • @Qualifier
  • @Target({METHOD, FIELD, PARAMETER, TYPE}
  • @Retention(RUNTIME)

限定子の宣言例

@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface Informal {}

限定子を付与した実装クラスの例(1)

@Informal
public class InformalGreeting extends Greeting {
    public String greet(String name) {
        return "Hi, " + name + "!";
    }
}

限定子を付与した実装クラスの例(2)

@Default
public class Greeting {
    public String greet(String name) {
        return "Hello, " + name + ".";
    }
}

Beanにインジェクトする場合

限定子を付与しない場合、限定子@Default が暗黙で付与される。

@Inject Greeting greeting;

は、

@Inject @Default Greeting greeting;

と同じ意味となる。

限定子を付与する場合

@Inject @Informal Greeting greeting;

この様にすることで、Greeting インターフェースの実装が複数あり解決出来ない場合に、限定子により解決することが出来るようになる。

2.8 スコープの利用

WebアプリケーションにBeanをインジェクトして利用する場合、ステートを保持する必要がある

事前定義スコープ一覧

スコープ アノテーション 備考
Requet @RequestScoped HTTPリクエスト一回限り
Session @SessionScoped 複数回のHTTPリクエストにまたがる
Application @ApplicationScoped Webアプリケーションのすべてのユーザで共有
Dependent @Dependent
    • デフォルトのスコープ
    • 1オブジェクトは厳密に1クライアントに利用される
    • クライアントBeanと同じスコープを持つ
Conversation @ConversationScoped JSFアプリケーションに含まれ、開発者が境界を制御できる、複数のリクエストにまたがる長い会話

@Dependent を除いて、事前定義されたスコープはコンテキストスコープ。カスタムスコープを定義することも出来る。

スコープはオブジェクトに明確に定義されたライフサイクルコンテキストを提供する。スコープオブジェクトは、必要に応じて自動生成され破棄される。

Java EE コンポーネント(サーブレット、EJB) は、明確に定義されたスコープを持ず、次のいずれかとなる。

  • Singleton : すべてのクライアントでステートを共有
  • Stateless : クライアントが可視のステートを持たない
  • Stateful : クライアントにより明示的に生成と破棄

スコープアノテーションを付与し、CDI管理させる例

@RequestScoped
public class Printer {

    @Inject @Informal Greeting greeting;
    ...
}

2.9 Bean にEL名を付与

ELからCDI管理Beanを利用可能にするには、@Named 限定子を利用する必要がある。

@Named は Bean名(先頭小文字化)でのアクセスを可能にする。

@Named("MyPrinter") のように、名前を指定できる

2.10 Faceletから利用する

プロパティの参照

<h:inputText id="name" value="#{printer.name}"/>

メソッドの呼び出し

<h:commandButton value="Say Hello" action="#{printer.createSalutation}"/>

2.11 Producer メソッドを利用したオブジェクトインジェクション

ProducerメソッドはBeanでないオブジェクトをインジェクトする方法を提供する。

メソッドに対して@Produces アノテーションをつけると、その戻り値の型をインジェクト出来るようになるため、ファクトリークラスを簡単に作成できるようになる。

@Produces @Random int next() {
    return getRandom().nextInt(maxNumber);
}

@Random は上記で出てきた限定子。int (java.lang.Integer と同一視される) だと曖昧であるため、付与することで用途を明確にする

インジェクト例

@Inject @Random Instance randomInt;

2.12 CDIアプリケーションの構成

作成したBeanが、スコープでアノテートされている場合、サーバーがそれを理解するのでBeanアーカイブに追加の設定は不要。

2.12.1 bean.xml

bean.xml は、CDIが利用するオプションのデプロイメント記述子。

アノテーションでの行った設定と、bean.xmlに記述した設定が衝突する場合は、bean.xmlの設定が優先される。

アーカイブのばあい以下のディレクトリに含まれなければならない(CDIが有効にはならない)。

  • Webアプリケーションの場合、WEB-INF 配下
  • EJB,JARの場合、META-INF 配下

2.13 初期化と破棄のためのアノテーション

CDI管理クラスおよびそのスーパークラスの初期化と、破棄の準備のために、以下のコールバックメソッドが準備されている。

  • @PostConstruct :管理Beanの初期化
  • @PostDestroy :管理Beanの破棄準備

ここまで、見た限りでは、SpringのDIを置き換えるに足る機能は持っているのではないかという気がしないでもない。また面白そうな仕様であれこれ試してみたくなる。

  1. Java EE 7 検証環境構築(1) WildFly + JBoss Tools で EARプロジェクトを作成し Arquillian で ユニットテストをグリーンにするところまで
  2. Java EE 7 検証環境構築(2) WildFly に DataSourceを作成
  3. Java EE 7 検証環境構築(3) JPAからMySQLに接続するユニットテストをArquillianで実行
  4. Java EE 7 検証環境構築(4) Java EE での DI(Dependency Injection) および CDI(Contexts and Dependency Injection)をながめる
  5. Java EE 7 検証環境構築(5) JBoss Toolsが生成したサンプルソースのCDIを確認する
  6. Java EE 7 検証環境構築(6) JPA エンティティの作成と挿入
  7. Java EE 7 検証環境構築(7) JPA 問い合わせ(1) 名前付きクエリを使ってみる。テストでトランザクションも意識する
  8. Java EE 7 検証環境構築(8) JPA 問い合わせ(2) 動的クエリとCriteria API を試す
  9. Java EE 7 検証環境構築(9) jBatch 概要をおさえる
  10. Java EE 7 検証環境構築(10) JBoss Tools で作成した EARプロジェクトをJava EE 6 から 7 に変更する
  11. Java EE 7 検証環境構築(11) jBatch用 プロジェクトの作成を行う
  12. Java EE 7 検証環境構築(12) jBatch 簡易サンプル作成と Arquillian でユニットテスト
  13. Java EE 7 検証環境構築(13) jBatch REST サービス経由で実行する
  14. Java EE 7 検証環境構築(14) WildFly の管理をGUIで行う
  15. Java EE 7 検証環境構築(15) WildFly を サービスとして設定する(Windows/Linux)
  16. Java EE 7 検証環境構築(16) WildFly と Apache を mod_jk で連携させる(Widows)

ようやく、外堀が埋まってきた。

Java EE 7 の検証環境と言っているのは、EARに、JAX-RS と EJB と JSFをまとめたものをイメージしていたので、JBoss Tools の EAR プロジェクトでどんびしゃ。

Arquilian を使って、ユニットテストが緑になることを確認し、データソースもWildFlyに作成できたので、それを利用して、JPAのユニットテスト(もともと組み込まれているものだが)を成功させたい。

1.ユニットテストをWildFlyにデータソースを構成したMySQLにつなぐ

1.1 テスト用 persistence.xml の修正

まず、/src/test/resource/META-INF/test-persistence.xml を修正する。

(以下の1.以外は、Hibernate の動作確認)

  1. jta-data-source を作成した DataSource の JNDI名に合わせる
  2. hibernate.hbm2ddl.auto の値を create-drop から create にする(テスト後テーブル等削除されないように)
  3. hibernate.show_sql の値を false から true にし、SQLステートメントをコンソールに出力させる

Hibernate の設定値については、以下を参照

http://docs.jboss.org/hibernate/core/3.3/reference/en/html/session-configuration.html

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
   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_0.xsd">
   <persistence-unit name="primary">
      <jta-data-source>java:jboss/datasources/TallaricoDS</jta-data-source>
      <properties>
         <!-- Properties for Hibernate -->
         <property name="hibernate.hbm2ddl.auto" value="create" />
         <property name="hibernate.show_sql" value="true" />
      </properties>
   </persistence-unit>
</persistence>

1.2 テストクラスの修正

MemberRegistrationTest クラスを開いて、@Deploymentが付与されているメソッドを編集する。

@Deployment が付与されたメソッドは、Arquillianが、テスト用のアーカイブ(JAR、WAR)を作成するために、テスト実施前に呼び出される。

初期状態では、/src/test/resource/test-ds.xml に記述されているデータソースが、テスト実行時に、META-INF配下におかれ、記述されているJNDI名で登録され、そのJNDI名を、persistence.xml から参照するというテストの流れになっている。persistence.xml のデータソース名に、WildFlyに登録したデータソースJNDIを指定し、そちらを利用するため、以下を修正する。

// Deploy our test datasource の次の行、.addAsWebInfResource 行をコメントアウトする。

この行でテスト用のデータソースが test-ds.xmlから、読み込まれる、test-ds.xml にはH2データベース接続のデータソースの設定があり、JNDI名がコンテナに登録されるようになっている。

しなしながら、WildFly にデータソースを構成し、そちらをつかうため、不要。

@Deployment
public static Archive<?> createTestArchive() {
    return ShrinkWrap.create(WebArchive.class, "test.war")
            .addClasses(Member.class, MemberRegistration.class, Resources.class)
            .addAsResource("META-INF/test-persistence.xml", "META-INF/persistence.xml")
            .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
            // Deploy our test datasource
            //.addAsWebInfResource("test-ds.xml", "test-ds.xml")
            ;
}

2.ユニットテスト実行

成功!managed と remote いずれでも成功することを確認

datasource_unit_test_success01

3.MySQLを確認

MySQL Workbench で、テーブルが作成されたことを確認。

jpa_create_scheme01

よしよし。想定通り想定通り。

  1. Java EE 7 検証環境構築(1) WildFly + JBoss Tools で EARプロジェクトを作成し Arquillian で ユニットテストをグリーンにするところまで
  2. Java EE 7 検証環境構築(2) WildFly に DataSourceを作成
  3. Java EE 7 検証環境構築(3) JPAからMySQLに接続するユニットテストをArquillianで実行
  4. Java EE 7 検証環境構築(4) Java EE での DI(Dependency Injection) および CDI(Contexts and Dependency Injection)をながめる
  5. Java EE 7 検証環境構築(5) JBoss Toolsが生成したサンプルソースのCDIを確認する
  6. Java EE 7 検証環境構築(6) JPA エンティティの作成と挿入
  7. Java EE 7 検証環境構築(7) JPA 問い合わせ(1) 名前付きクエリを使ってみる。テストでトランザクションも意識する
  8. Java EE 7 検証環境構築(8) JPA 問い合わせ(2) 動的クエリとCriteria API を試す
  9. Java EE 7 検証環境構築(9) jBatch 概要をおさえる
  10. Java EE 7 検証環境構築(10) JBoss Tools で作成した EARプロジェクトをJava EE 6 から 7 に変更する
  11. Java EE 7 検証環境構築(11) jBatch用 プロジェクトの作成を行う
  12. Java EE 7 検証環境構築(12) jBatch 簡易サンプル作成と Arquillian でユニットテスト
  13. Java EE 7 検証環境構築(13) jBatch REST サービス経由で実行する
  14. Java EE 7 検証環境構築(14) WildFly の管理をGUIで行う
  15. Java EE 7 検証環境構築(15) WildFly を サービスとして設定する(Windows/Linux)
  16. Java EE 7 検証環境構築(16) WildFly と Apache を mod_jk で連携させる(Widows)

ユニットテストが通る環境がととのったが、データソースが、H2とかいうJavaのテスト用Databaseなので、MySQLに差し替えていく。

1. MySQL Connector/J のダウンロード

https://dev.mysql.com/downloads/connector/j/

Platform Independent

mysql-connector-java-5.1.30.zip

mysql_connector_j_download

解凍して、mysql-connector-java-5.1.30-bin.jar を取り出す。

2. MySQL JDBCドライバのデプロイ

まず、前提条件を,2.1 ~ 2.3 に簡単に記述する。

2.1 WildFlyの構成

まず、WildFlyの構成には以下の2種類がある

  1. スタンドアロン
    • スタンドアロンモードは単体のJVMプロセスでサーバーを構成する
    • $ ./standalone.sh で起動
  2. ドメイン
    • 複数のサーバーを単一の管理ポイントから一元管理
    • ドメインに対する設定はドメイン全体で共有される
    • $ ./domain.sh で起動

2.2 JDBC登録方法

JDBCの登録方法には、以下のデプロイとカスタムモジュールの2種類ある。今回はデプロイを選択

  1. デプロイ
    • 動的モジュール
    • JARファイルは動的モジュールとして即時に読み込まれデータソースから参照可能
    • ドメインによる一括管理可能
    • 単一JAR対応(複数JARの場合、別モジュールとして扱う)
  2. カスタムモジュール
    • 静的モジュール
    • $JBOSS_HOME/modules 配下にJARファイルを静的モジュールとして配置
    • ドメイン管理不可
    • 複数のJARからなるドライバも単一のものとして扱う

2.3 管理ツール

管理ツールにも、以下の2種類が存在する

  1. 管理コンソール
  2. 管理CLI (コマンドラインインターフェース)
    • 管理操作をコマンドラインから対話的に実行できる
    • $ ./jboss-cli.sh で起動する

2.4 JDBCのデプロイ

WildFlyを起動(standalone.bat)させておき、管理CLIを起動(jboss-cli.bat) して、接続(connect)し、デプロイ(deploy)する

管理コンソール画面からもJDBCのデプロイは可能だが、あくまで簡易版であり、管理CLIが推奨らしい

PS C:\Programs\wildfly-8.0.0.Final\bin> .\jboss-cli.bat
You are disconnected at the moment. Type 'connect' to connect to the server or 'help' for the list of supported commands
.
[disconnected /] connect
[standalone@localhost:9990 /] deploy c:\work\mysql-connector-java-5.1.30-bin.jar

WildFly管理コンソール(localhost:9990) Server - Manage Deployments から確認

check_mysql_driver_on_management_console_wildfly

管理CLI jdbc-driver-info コマンドで確認

[standalone@localhost:9990 /] jdbc-driver-info
NAME                                                                            SOURCE
h2                                                                              com.h2database.h2/main
mysql-connector-java-5.1.30-bin.jar_com.mysql.fabric.jdbc.FabricMySQLDriver_5_1 mysql-connector-java-5.1.30-bin.jar_com.mysql.fabric.jdbc.FabricMySQLDriver_5_1
mysql-connector-java-5.1.30-bin.jar_com.mysql.jdbc.Driver_5_1                   mysql-connector-java-5.1.30-bin.jar_com.mysql.jdbc.Driver_5_1

3.データソース

データソースは、データソースの作成、設定値の更新、有効化の手順で実施する。

3.1 定義の確認

現在の構成(MySQL構成前)を確認

[standalone@localhost:9990 /] cd subsystem=datasources
[standalone@localhost:9990 subsystem=datasources] :read-resource
{
    "outcome" => "success",
    "result" => {
        "data-source" => {"ExampleDS" => undefined},
        "jdbc-driver" => {"h2" => undefined},
        "xa-data-source" => undefined
    }
}

3.2 非XAデータソースの作成

非XAデータソース作成必須項目

項目 内容
connection-url 接続先DBのURL jdbc:mysql://127.0.0.1:3306/tallarico
driver-name 使用するJDBCドライバ
・デプロイ時は、JDBCドライバファイル名
・カスタムモジュール時はリソース名
mysql-connector-java-5.1.30-bin.jar_com.mysql.jdbc.Driver_5_1
jndi-name JNDI名(java:/もしくは、java:jboss:/ どちらかで始まる) java:jboss/datasources/TallaricoDS

data-source コマンドで作成

data-source コマンドのサブコマンド

[standalone@localhost:9990 /] data-source --help --commands
add
disable
enable
flush-all-connection-in-pool
flush-gracefully-connection-in-pool
flush-idle-connection-in-pool
flush-invalid-connection-in-pool
read-resource
remove
test-connection-in-pool
To read the description of a specific command execute 'data-source command_name --help'.

add コマンドで作成する

[standalone@localhost:9990 /] data-source add --name=TallaricoDS --connection-url=jdbc:mysql://127.0.0.1:3306/tallarico --jndi-name=java:jboss/datasources/TallaricoDS --driver-name=mysql-connector-java-5.1.30-bin.jar_com.mysql.jdbc.Driver_5_1

結果を確認する

[standalone@localhost:9990 subsystem=datasources] :read-resource
{
    "outcome" => "success",
    "result" => {
        "data-source" => {
            "ExampleDS" => undefined,
            "tallaricoDS" => undefined
        },
        "jdbc-driver" => {"h2" => undefined},
        "xa-data-source" => undefined
    }
}

設定可能項目を確認する

[standalone@localhost:9990 /] data-source read-resource --name=TallaricoDS --recursive=true
allocation-retry=n/a
allocation-retry-wait-millis=n/a
allow-multiple-users=false
background-validation=n/a
background-validation-millis=n/a
blocking-timeout-wait-millis=n/a
capacity-decrementer-class=n/a
capacity-decrementer-properties=n/a
capacity-incrementer-class=n/a
capacity-incrementer-properties=n/a
check-valid-connection-sql=n/a
connection-listener-class=n/a
connection-listener-property=n/a
connection-properties=n/a
connection-url=jdbc:mysql://127.0.0.1:3306/tallarico
datasource-class=n/a
driver-class=n/a
driver-name=mysql-connector-java-5.1.30-bin.jar_com.mysql.jdbc.Driver_5_1
enabled=true
exception-sorter-class-name=n/a
exception-sorter-properties=n/a
flush-strategy=n/a
idle-timeout-minutes=n/a
initial-pool-size=n/a
jndi-name=java:jboss/datasource/TallaricoDS
jta=true
max-pool-size=n/a
min-pool-size=n/a
new-connection-sql=n/a
password=n/a
pool-prefill=n/a
pool-use-strict-min=n/a
prepared-statements-cache-size=n/a
query-timeout=n/a
reauth-plugin-class-name=n/a
reauth-plugin-properties=n/a
security-domain=n/a
set-tx-query-timeout=false
share-prepared-statements=false
spy=false
stale-connection-checker-class-name=n/a
stale-connection-checker-properties=n/a
track-statements=NOWARN
transaction-isolation=n/a
url-delimiter=n/a
url-selector-strategy-class-name=n/a
use-ccm=true
use-fast-fail=false
use-java-context=true
use-try-lock=n/a
user-name=n/a
valid-connection-checker-class-name=n/a
valid-connection-checker-properties=n/a
validate-on-match=false
statistics=n/a

設定項目に値を設定する

--設定項目名=設定値 を半角スペースでつなげる

[standalone@localhost:9990 /] data-source --name=TallaricoDS --user-name=root --password=*****
operation-requires-reload: true
operation-requires-reload: true
process-state: reload-required

データソースの有効化

[standalone@localhost:9990 subsystem=datasources] data-source enable --name=TallaricoDS

管理コンソールから接続確認

connect_success

以上。

Ubuntu とか CentOSへのインストール は何度かやってきたけど、Windows へは初めてなので、手順をメモったり。

1.ダウンロード

http://dev.mysql.com/downloads/mysql/

mysql_msi_installer_download

Note: MySQL Installer is 32 bit, but will install both 32 bit and 64 bit binaries.

32bit 版のインストーラーで64bitもインストール出来るようだ。

 

インストールには、Oracle Web アカウントへのサインアップが必要。

mysql_need_to_signup

2.インストール

2.1 Install MySQL Products を選択

mysql_install01

2.2 ライセンスに同意

mysql_install02

チェックして Next

2.3 最新版のチェック

実施する場合、そのまま。スキップする場合、skip the check for updates にチェックし、Execute。

チェック完了後、Next にボタンがなるので、Next

mysql_install03

2.4 インストールタイプを選択

タイプ 内容
Developer Defult アプリケーション開発に必要なツールを含む。
・MySQL Server
・MySQL Workbench
・MySQL Visual Studio Plugin
・MySQL Connectors
・Examples and tutorials
・Documentation
Server only MySQL Serverのみ
Client only アプリケーション開発に必要なもの。
Developer Default から MySQL Serverを除いた物
Full すべてのプロダクト
Custom インストールするプロダクトを選択

 

mysql_install04

2.5 要件のチェック

事前にインストールしておかなければいけないソフトウェアがチェックされる。

OKなら、Next

mysql_install05

2.6 インストール内容の確認

インストール内容を確認しExecute。完了したらNext

mysql_install06

2.7 設定

MySQL Server の初期設定

mysql_install07

デフォルトでNext

  • 設定タイプ は Development Machine
  • 追加設定は行わない Show Advanced Options のチェックOFF

mysql_install08

このまま、Nextで進んでいくとインストール完了。

。。。してしまって、戻れなくなってしまった。

Show Advanced Options のチェックをONにして、確認しながら進むべきだった。

とりあえず、インストールされた、MySQL 5.6 Command Line Client を起動して、データベースの内容を確認する。

mysql> use mysql
Database changed
mysql> show create database mysql;
+----------+----------------------------------------------------------------+
| Database | Create Database                                                |
+----------+----------------------------------------------------------------+
| mysql    | CREATE DATABASE `mysql` /*!40100 DEFAULT CHARACTER SET utf8 */ |
+----------+----------------------------------------------------------------+
1 row in set (0.00 sec)

mysql_client

3.MySQL  Workbench

MySQL Workbenchは、データベースアーキテクト、開発者、DBAのための統合ビジュアルツールで、データモデリング、SQL開発、サーバー構成、ユーザー管理などの機能を提供 する。らしい。

なんかめちゃくちゃ高機能で便利そうに見えるじゃないですか。ちょっと使っていってみよ。

mysql_workbench01

mysql_workbench02

mysql_workbench03

MySQL Workbench、なんかかっちょいー! フリーのモデリングツールとしても使えるんじゃないか?

ということで、インストール完了。

4.データベースの作成

せっかくなので、MySQL Workbench から データベースを作成する。

メニューアイコンから、Create new scheme を選択する。

ちなみに、MySQL の場合、CREATE SCHEME は、CREATE DATABASE と同義

https://dev.mysql.com/doc/refman/5.1/ja/create-database.html

mysql_create_scheme

データベース名とコードセットを指定。

日本語環境の場合、utf8(utf8mb4)(デフォルト)、cp932、eucjpms のいづれかを選択することになるでしょう。

create_scheme_on_mysql

作成された!

mysql_scheme_created

  1. Java EE 7 検証環境構築(1) WildFly + JBoss Tools で EARプロジェクトを作成し Arquillian で ユニットテストをグリーンにするところまで
  2. Java EE 7 検証環境構築(2) WildFly に DataSourceを作成
  3. Java EE 7 検証環境構築(3) JPAからMySQLに接続するユニットテストをArquillianで実行
  4. Java EE 7 検証環境構築(4) Java EE での DI(Dependency Injection) および CDI(Contexts and Dependency Injection)をながめる
  5. Java EE 7 検証環境構築(5) JBoss Toolsが生成したサンプルソースのCDIを確認する
  6. Java EE 7 検証環境構築(6) JPA エンティティの作成と挿入
  7. Java EE 7 検証環境構築(7) JPA 問い合わせ(1) 名前付きクエリを使ってみる。テストでトランザクションも意識する
  8. Java EE 7 検証環境構築(8) JPA 問い合わせ(2) 動的クエリとCriteria API を試す
  9. Java EE 7 検証環境構築(9) jBatch 概要をおさえる
  10. Java EE 7 検証環境構築(10) JBoss Tools で作成した EARプロジェクトをJava EE 6 から 7 に変更する
  11. Java EE 7 検証環境構築(11) jBatch用 プロジェクトの作成を行う
  12. Java EE 7 検証環境構築(12) jBatch 簡易サンプル作成と Arquillian でユニットテスト
  13. Java EE 7 検証環境構築(13) jBatch REST サービス経由で実行する
  14. Java EE 7 検証環境構築(14) WildFly の管理をGUIで行う
  15. Java EE 7 検証環境構築(15) WildFly を サービスとして設定する(Windows/Linux)
  16. Java EE 7 検証環境構築(16) WildFly と Apache を mod_jk で連携させる(Widows)

GlassFish + Eclipse で Java EE 7 環境構築に疲れたので、WildFly + Eclipse で同じ頂を目指すことにする。

1.Eclipse Standard のダウンロードと展開

Eclipse Standard をダウンロードして適当なフォルダに展開。

https://www.eclipse.org/downloads/

jboss_tools_wildfly01

2.JBoss Tools のインストール

http://tools.jboss.org/downloads/

から、JBoss Tools ダウンロードへ

jboss_tools_download

Update Site のURLを取得

http://download.jboss.org/jbosstools/updates/stable/kepler/

jboss_tools_updatesite

上記 1. で展開したEclipseを起動して、Help - Install New Software で起動したウィンドウの、Add ボタンを押して、適当な名前と、上記Update SiteのURLを入力してOK

jboss_tools_install_eclipse

とりあえず、すべての項目にチェックしてインストール

jboss_tools_install_eclipse02

インストールされた。

jboss_tools_installed

3.WildFly のダウンロード

http://wildfly.org/downloads/

から、ダウンロードして、適当なディレクトリに展開。

wildfly_download

4.Eclipse と WildFly を連携させる

4.1 Servers に WildFly追加

Eclipse の メニュー Window - Show View - Other - Server - Servers ビューを開き、右クリックから、New - Server

add_server_to_eclipse

JBoss Community から WildFly を選択

add_server_to_eclipse02

WildFlyを展開したフォルダを指定。JREには、JDKを指定する必要があるため、JREになっている場合、「JRE」ボタンを押下して、JDKに設定する。

add_server_to_eclipse03

4.2 WildFlyの起動

一旦起動してみる。サーバーのコンテキストメニューからStart

start_wildfly_via_eclipse

http://127.0.0.1:8080/

で起動確認。

wildfly_is_running

http://127.0.0.1:9990/

で管理コンソールに接続できるのだが、ユーザー登録が必要。

4.3 WildFly ユーザー登録

WildFly インストールディレクトリ\bin\add-user.bat を実行して、管理ユーザーを登録する。

最初に、Management User の(a) を選択し、後はユーザー名、パスワードなどを入力する。

C:\Programs\wildfly-8.0.0.Final\bin> .\add-user.bat

What type of user do you wish to add?
 a) Management User (mgmt-users.properties)
 b) Application User (application-users.properties)
(a): a

Enter the details of the new user to add.
Using realm 'ManagementRealm' as discovered from the existing property files.
Username : admin
The username 'admin' is easy to guess
Are you sure you want to add user 'admin' yes/no? yes
Password recommendations are listed below. To modify these restrictions edit the add-user.properties configuration file.

 - The password should not be one of the following restricted values {root, admin, administrator}
 - The password should contain at least 8 characters, 1 alphanumeric character(s), 1 digit(s), 1 non-alphanumeric symbol
(s)
 - The password should be different from the username
Password :
JBAS015267: Password must have at least 1 non-alphanumeric symbol.
Are you sure you want to use the password entered yes/no? yes
Re-enter Password :
What groups do you want this user to belong to? (Please enter a comma separated list, or leave blank for none)[  ]:
About to add user 'admin' for realm 'ManagementRealm'
Is this correct yes/no? yes
Added user 'admin' to file 'C:\Programs\wildfly-8.0.0.Final\standalone\configuration\mgmt-users.properties'
Added user 'admin' to file 'C:\Programs\wildfly-8.0.0.Final\domain\configuration\mgmt-users.properties'
Added user 'admin' with groups  to file 'C:\Programs\wildfly-8.0.0.Final\standalone\configuration\mgmt-groups.properties
'
Added user 'admin' with groups  to file 'C:\Programs\wildfly-8.0.0.Final\domain\configuration\mgmt-groups.properties'
Is this new user going to be used for one AS process to connect to another AS process?
e.g. for a slave host controller connecting to the master or for a Remoting connection for server to server EJB calls.
yes/no? yes
To represent the user add the following to the server-identities definition

http://127.0.0.1:9990/

へアクセスすると、ユーザーおよびパスワードを求められるので、上記で追加したユーザーでログイン。

login_wildfly_as_management_user

管理コンソールにログイン出来た。

5.Java EE EAR プロジェクトの作成

プロジェクトエクスプローラーのコンテキストメニューから、New - Other - JBoss Central - Java EE EAR Project を作成する。

create_java_ee_ear_project

ウィザードの最初で、「Create a blank project」にチェックを入れると、ブランクのプロジェクトが生成されるが、チェックを入れない(デフォルト)だと、簡単なサンプルを含むプロジェクトが作成されるので、まずはそれを動かしてみる。

ear_blank_project

5.1 作成されたとたんにエラー

test-ds.xml などが以下の様なエラー

Referenced file contains errors (http://docs.jboss.org/ironjacamar/schema/datasources_1_0.xsd).

jboss_ear_project_initial_error

IronJacamar という、Java EE Connector Architecture 実装の設定XML様のXSDが見つからないため。

http://www.ironjacamar.org/documentation.html

ここに、スキーマのURLがあるので、2箇所以下の様に修正するとエラーが消える。

修正前

xsi:schemaLocation="http://www.jboss.org/ironjacamar/schema http://docs.jboss.org/ironjacamar/schema/datasources_1_0.xsd"

修正後

xsi:schemaLocation="http://www.ironjacamar.org/doc/schema http://www.ironjacamar.org/doc/schema/datasources_1_0.xsd"

6.サンプルプロジェクトを実行してみる

6.1 サーバーにプロジェクトを追加

WildFlyサーバーのコンテキストメニューから、Add and Remove を選択

add_server_to_project

プロジェクトを追加してFinish

add_server_to_project02

再度コンテキストメニューから、Publish して、サーバー起動。

6.2 サンプルプロジェクトの実行

起動した。メンバーの追加などが出来る。/rest/members など、RESTで呼び出すと、JSONの結果が帰ることが確認出来る。

sample_ear_project_is_running

7.JUnit の実行

7.1 JUnit を実行してみる

EJBプロジェクトの、src/test/java/ 以下に、上記で実行したメンバー登録用 EJB のテストケース(MemberRegistrationTest) が存在するので、コンテキストメニューから、Run As - JUnit Test を実行してみると、以下の様なエラー

org.jboss.arquillian.container.spi.client.container.DeploymentException: Cannot deploy: test.war

junit_error

7.2 Arquillian のいくつかの設定

Arquillian がそもそもどんなものかというのは、このあたりを読むとイメージがつかめる

ただ単にJUnit を走らすだけではだめなようだ。

まず、Arquillian が、テストを実行するには、managed と remote が存在する。

managed は、Arquillian が、コンテナを管理(起動)し、デプロイ、テストまでを行う。

また、remote は、起動済みのコンテナにデプロイし、テストを行う。

7.3 Arquillian managed のテスト実行

このテストを行うのために、pom.xml に maven のプロファイルを設定する。

プロジェクトが作成された段階で、arq-jbossas-managed という ID で、JBoss AS 用の設定はあるのだが、WildFly には対応していないよう(実行で例外)なので、WildFly 用の 設定記述を、

http://mvnrepository.com/artifact/org.wildfly/wildfly-arquillian-container-managed/8.0.0.Final

で確認して、行う。具体的には、以下の profile を EJBプロジェクト直下のpom.xmlに追記する。

<profile>
    <id>arq-wildfly-managed</id>
    <dependencies>
        <dependency>
            <groupId>org.wildfly</groupId>
            <artifactId>wildfly-arquillian-container-managed</artifactId>
            <version>8.0.0.Final</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</profile>

そして、EJBプロジェクト の src/test/resource/META-INF/arquillian.xml を開き、

jbossHome に WildFly のインストールパスを設定する。(JBOSS_HOME を環境変数に設定してもよい)

<container qualifier="jboss" default="true">
    <!-- By default, arquillian will use the JBOSS_HOME environment variable.  Alternatively, the configuration below can be uncommented. -->
    <configuration>
    <property name="jbossHome">C:\Programs\wildfly-8.0.0.Final</property>
    </configuration>
</container>

最後に、Mavanのプロファイルを、arq-wildfly-managed に切り替える。

EJBプロジェクトのコンテキストメニューから、Maven - Select Maven Profiles を選択し、今追加したプロファイルを選択しActivateしOK

select_maven_profiles

再度 JUnit 実行!

junit_success_management

OK!Greenだよ!

7.4 Arquillian remote テストの実行

http://mvnrepository.com/artifact/org.wildfly/wildfly-arquillian-container-remote

managed 同様に、以下の profile を EJBプロジェクト直下のpom.xmlに追記する。

<profile>
    <id>arq-wildfly-remote</id>
    <dependencies>
        <dependency>
            <groupId>org.wildfly</groupId>
            <artifactId>wildfly-arquillian-container-remote</artifactId>
            <version>8.0.0.Final</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</profile>

Mavanのプロファイルを今追加した、arq-wildfly-remote に切り替える。

maven_profile_remote

そして、WildFly を起動したのち、JUnit を実行

junit_remote_green

Greeeeeen だよ~

つかれたー。手順をまとめちゃうと、これだけだけど、トライ&エラーでここまでくるのに結構時間かかった。。。

これで、ようやく、EJBやら、JPAやら、本題に入れるよ。。。