Red Hat Training

A Red Hat training course is available for Red Hat JBoss Enterprise Application Platform

開発ガイド

Red Hat JBoss Enterprise Application Platform 7.0

Red Hat JBoss Enterprise Application Platform 7.0 向け

Red Hat Customer Content Services

概要

本書は、Red Hat JBoss Enterprise Application Platform 7.0 とそのパッチリリースを使用する Java EE の開発者向けのリファレンスや例を提供します。

第1章 アプリケーションの開発

1.1. はじめに

1.1.1. Red Hat JBoss Enterprise Application Platform 7 について

Red Hat JBoss Enterprise Application Platform 7 (JBoss EAP) は、オープンな標準に基いて構築され、Java Enterprise Edition 7 の仕様に準拠するミドルウェアプラットフォームです。メッセージングや高可用性クラスタリングなどの技術と WildFly Application Server 10 が統合されます。

JBoss EAP には、必要な場合にだけサービスを有効にできるモジュール構造が含まれ、サービスの起動時間が短縮されます。

管理コンソールと管理コマンドラインインターフェース (CLI) では、XML 設定ファイルの編集が不要になり、タスクをスクリプト化および自動化する機能が追加されました。

JBoss EAP は、JBoss EAP インスタンスに対してスタンドアロンサーバーと管理対象ドメインの 2 つの操作モードを提供します。スタンドアロンサーバー操作モードでは、実行している JBoss EAP を 1 つのサーバーインスタンスとして表します。管理対象ドメイン操作モードでは、1 つの制御ポイントから複数の JBoss EAP インスタンスを管理できます。

また、JBoss EAP には、セキュアでスケーラブルな Java EE アプリケーションの迅速な開発を可能にする API と開発フレームワークが含まれます。

1.2. Java Enterprise Edition 7 について

1.2.1. EE 7 プロファイルの概要

Java Enterprise Edition 7 (EE 7) には、複数のプロファイルのサポート (つまり、API のサブセット) が含まれます。EE 7 の仕様で定義されるプロファイルは、Full Profile と Web Profile の 2 つのみです。

EE 7 の Full Profile には、EE 7 の仕様に含まれるすべての API と仕様が含まれます。EE 7 の Web Profile には、Web 開発者に役立つよう設計された特別な API のサブセットが含まれます。

JBoss EAP は、Java Enterprise Edition 7 の Full Profile および Web Profile 仕様の認定実装です。

Java Enterprise Edition 7 の Web Profile

Web Profile は、Java Enterprise Edition 7 仕様で定義されている 2 つのプロファイルの 1 つであり、Web アプリケーション開発向けに設計されています。Web Profile は以下の API をサポートします。

  • Java EE 7 Web Profile の要件:

    • Java Platform、Enterprise Edition 7
  • Java Web テクノロジー:

    • Servlet 3.1 (JSR 340)
    • JSP 2.3
    • Expression Language (EL) 3.0
    • JavaServer Faces (JSF) 2.2 (JSR 344)
    • JSP 1.2 向け Java Standard Tag Library (JSTL)
    • 他言語のデバッグサポート 1.0 (JSR 45)
  • エンタープライズアプリケーションテクノロジー:

    • Contexts and Dependency Injection (CDI) 1.1 (JSR 346)
    • Dependency Injection for Java 1.0 (JSR 330)
    • Enterprise JavaBeans 3.2 Lite (JSR 345)
    • Java Persistence API 2.1 (JSR 338)
    • Java Platform 1.1 向けの共通アノテーション (JSR 250)
    • Java Transaction API (JTA) 1.2 (JSR 907)
    • Bean Validation 1.1 (JSR 349)

Java EE 7 の仕様で定義されている他のプロファイルは Full Profile であり、他の複数の API を含みます。

Java Enterprise Edition 7 の Full Profile

Java Enterprise Edition 7 (EE 7) の仕様により、プロファイルの概念が定義され、それらのプロファイルの 2 つが仕様の一部として定義されます。Full Profile は次の API と、Java Enterprise Edition 7 Web Profile でサポートされいている API をサポートします。

  • EE 7 Full Profile に含まれる API:

    • Batch 1.0
    • JSON-P 1.0
    • Concurrency 1.0
    • WebSocket 1.1
    • JMS 2.0
    • JPA 2.1
    • JCA 1.7
    • JAX-RS 2.0
    • JAX-WS 2.2
    • Servlet 3.1
    • JSF 2.2
    • JSP 2.3
    • EL 3.0
    • CDI 1.1
    • CDI Extensions
    • JTA 1.2
    • Interceptors 1.2
    • Common Annotations 1.1
    • Managed Beans 1.0
    • EJB 3.2
    • Bean Validation 1.1

1.3. 開発環境のセットアップ

1.3.1. JBoss Developer Studio のダウンロード

JBoss Developer Studio は Red Hat カスタマーポータルからダウンロードできます。

  1. Red Hat カスタマーポータルにログインします。
  2. ダウンロードをクリックします。
  3. 製品のダウンロードリストで Red Hat JBoss Developer Studio をクリックします。
  4. Version ドロップダウンメニューで希望のバージョンを選択します。

    注記

    JBoss Developer Studio バージョン 9.1 以上の使用が推奨されます。

  5. 表で Red Hat JBoss Developer Studio 9.x.x Stand-alone Installer を見つけ、Download をクリックします。
  6. JAR ファイルを希望のディレクトリーに保存します。

1.3.2. JBoss Developer Studio のインストール

  1. ターミナルを開き、ダウンロードした JAR ファイルが含まれるディレクトリーに移動します。
  2. 次のコマンドを実行して GUI インストールプログラムを起動します。

    $ java -jar jboss-devstudio-BUILD_VERSION-installer-standalone.jar
    注記

    または、JAR ファイルをダブルクリックしてインストールプログラムを起動することもできます。

  3. Next をクリックしてインストールプロセスを開始します。
  4. I accept the terms of this license agreement (ライセンス契約の内容に同意します) を選択し、Next をクリックします。
  5. インストールパスを調整し、Next をクリックします。

    注記

    インストールパスフォルダーが存在しない場合は、メッセージが表示されます。OK をクリックしてフォルダーを作成します。

  6. JVM を選択するか、デフォルトの JVM を選択したままにし、Next をクリックします。
  7. プラットフォームとサーバーの選択を要求されたら、Next をクリックします。
  8. インストールの詳細を確認し、Next をクリックします。
  9. インストールプロセスが完了したら Next をクリックします。
  10. JBoss Developer Studio のデスクトップショートカットを設定し、Next をクリックします。
  11. Done をクリックします。

1.3.3. JBoss Developer Studio の起動

JBoss Developer Studio を起動するには、インストール中に作成されたデスクトップショートカットをダブルクリックするか、コマンドラインから起動します。コマンドラインを使用して JBoss Developer Studio を起動するには、以下の手順に従います。

  1. ターミナルを開き、JBoss Developer Studio インストールディレクトリーに移動します。
  2. 次のコマンドを実行して JBoss Developer Studio を起動します。

    $ ./jbdevstudio
    注記

    Windows Server の場合は jbdevstudio.bat ファイルを使用します。

1.3.4. JBoss EAP サーバーを JBoss Developer Studio へ追加

この手順では、JBoss EAP サーバーが JBoss Developer Studio に追加されていないことを前提とします。以下の手順に従い、Define New Server ウィザードを使用して JBoss EAP サーバーを追加します。

  1. Servers タブを開きます。

    注記

    Servers タブが表示されていない場合は、WindowShow ViewServers と選択してパネルに追加します。

  2. No servers are available. Click this link to create a new server (使用できるサーバーがありません。このリンクをクリックして新しいサーバーを作成してください) のリンクをクリックします。

    図1.1 新しいサーバーの追加

    The *Servers* tab when no servers are available.
  3. Red Hat JBoss Middleware を展開し、JBoss Enterprise Application Platform 7.0 を選択します。サーバー名 (例: JBoss EAP 7.0) を入力し、Next をクリックします。

    図1.2 新しいサーバーの定義

    The *Define a New Server* window.
  4. サーバーアダプターを作成し、サーバーの起動と停止を管理します。デフォルトの値のままにし、Next をクリックします。

    図1.3 新しいサーバーアダプターの作成

    The *Create a New Server Adapter* window.
  5. 名前 (例: JBoss EAP 7.0 Runtime) を入力します。Home Directory の横にある Browse をクリックし、JBoss EAP のインストールディレクトリーに移動します。次に、Next をクリックします。

    図1.4 新しいサーバーランタイム環境の追加

    The *JBoss Runtime* window.
    注記

    一部のクイックスタートでは、異なるプロファイルまたは追加の引数を使用してサーバーを起動する必要があります。たとえば、full プロファイルが必要なクイックスタートをデプロイするには、新しいサーバーを定義し、Configuration file フィールドで standalone-full.xml を指定する必要があります。新しいサーバーにはその内容を表す名前を付けてください。

  6. 新しいサーバーの既存プロジェクトを設定します。この時点ではプロジェクトは存在しないため Finish をクリックします。

    図1.5 新しいサーバーのリソースの変更

    The *Add and Remove Resources* window.

JBoss EAP 7.0 サーバーが Servers タブにリストされます。

図1.6 サーバーリスト

The *Servers* tab when the `JBoss EAP 7.0` server is listed.

1.4. クイックスタートサンプルの使用

1.4.1. Maven について

Apache Maven は、ソフトウェアプロジェクトの作成、管理、および構築を行う Java アプリケーションの開発で使用される分散型ビルド自動化ツールです。Maven は Project Object Model (POM) と呼ばれる標準の設定ファイルを利用して、プロジェクトの定義や構築プロセスの管理を行います。POM はモジュールやコンポーネントの依存関係、ビルドの順番、結果となるプロジェクトパッケージングのターゲットを定義し、XML ファイルを使用して出力します。この結果、プロジェクトが適切かつ統一された状態で構築されるようになります。

Maven は、リポジトリーを使用してアーカイブを行います。Maven リポジトリーには Java ライブラリー、プラグイン、およびその他のビルドアーティファクトが格納されています。デフォルトのパブリックリポジトリーは Maven 2 Central Repository ですが、複数の開発チームの間で共通のアーティファクトを共有する目的で、社内のプライベートおよび内部リポジトリーとすることが可能です。また、サードパーティーのリポジトリーも利用できます。詳細については、Apache Maven プロジェクトおよび Introduction to Repositories ガイドを参照してください。

JBoss EAP には、Java EE 開発者が JBoss EAP 6 でアプリケーションを構築する際に一般的に使用する要件の多くを含む Maven リポジトリーが含まれます。

詳細については、Using Maven with JBoss EAP を参照してください。

1.4.1.1. クイックスタートを用いた Maven の使用

アプリケーションをビルドし、JBoss EAP 7 にデプロイするのに必要なアーティファクトと依存関係はパブリックリポジトリーでホストされます。JBoss EAP 7 のクイックスタートでは、Maven settings.xml ファイルを設定して、クイックスタートをビルドするときにこれらのリポジトリーを使用する必要がなくなりました。Maven リポジトリーはクイックスタートプロジェクト POM ファイルに設定されるようになりました。この設定方法は、クイックスタートを容易に使えるようにするために提供されますが、ビルドの処理が遅くなる可能性があるため、通常は本番プロジェクトでの使用は推奨されません。

Red Hat JBoss Developer Studio には Maven が含まれるため、個別にダウンロードおよびインストールする必要はありません。JBoss Developer Studio バージョン 9.1 以上を使用することが推奨されます。

Maven コマンドラインを使用してアプリケーションをビルドおよびデプロイする場合は、最初に Apache Maven プロジェクトから Maven をダウンロードし、Maven のドキュメントに記載されている手順に従ってインストールします。

1.4.2. クイックスタートコードサンプルのダウンロードおよび実行

1.4.2.1. クイックスタートのダウンロード

JBoss EAP には、さまざまな Java EE 7 の技術を使用してアプリケーションを作成するのに役立つ包括的なクイックスタートコードサンプルセットが含まれています。クイックスタートは Red Hat カスタマーポータルからダウンロードできます。

  1. Red Hat カスタマーポータルにログインします。
  2. ダウンロードをクリックします。
  3. 製品のダウンロードリストで Red Hat JBoss Enterprise Application Platform をクリックします。
  4. Version ドロップダウンメニューで希望のバージョンを選択します。
  5. 表で Red Hat JBoss Enterprise Application Platform 7.0.0 Quickstarts を見つけ、Download をクリックします。
  6. ZIP ファイルを希望のディレクトリーに保存します。
  7. Zip ファイルを展開します。

1.4.2.2. JBoss Developer Studio でのクイックスタートの実行

クイックスタートがダウンロードされたら、JBoss Developer Studio にインポートし、JBoss EAP にデプロイできます。

クイックスタートの JBoss Developer Studio へのインポート

各クイックスタートには、プロジェクトおよび設定情報が含まれる POM ファイルが同梱されています。この POM ファイルを使用すると、簡単にクイックスタートを JBoss Developer Studio にインポートできます。

重要

JBoss Developer Studio へのインポート時にクイックスタートプロジェクトフォルダーが IDE ワークスペース内にある場合は、IDE により無効なプロジェクト名と WAR アーカイブ名が生成されます。作業を開始する前に、クイックスタートプロジェクトフォルダーが IDE ワークスペースの外部にあることを確認してください。

  1. JBoss Developer Studio を起動します。
  2. FileImport と選択します。
  3. MavenExisting Maven Projects と選択し、Next をクリックします。

    図1.7 既存の Maven プロジェクトのインポート

    The *Import* window.
  4. 希望のクイックスタートのディレクトリー (helloworld など) を参照し、OK をクリックします。Projects リストボックスに、選択したクイックスタートプロジェクトの pom.xml ファイルが示されます。

    図1.8 Maven プロジェクトの選択

    The *Maven Projects* selection window.
  5. Finish をクリックします。

helloworld クイックスタートの実行

helloworld クイックスタートを実行すると、JBoss EAP サーバーが適切に設定および実行されたことを簡単に検証できます。

  1. サーバーを定義していない場合は、JBoss EAP サーバーを JBoss Developer Studio へ追加します。
  2. Project Explorer タブの jboss-helloworld プロジェクトを右クリックし、Run AsRun on Server と選択します。

    図1.9 Run As - Run on Server

    The *Run As* -> *Run on Server* screen capture.
  3. リストから JBoss EAP 7.0 を選択し、Next をクリックします。

    図1.10 Run on Server

    The *Run on Server* window.
  4. jboss-helloworld クイックスタートはすでにリストされ、サーバー上で設定できる状態です。Finish をクリックしてクイックスタートをデプロイします。

    図1.11 サーバーで設定されたリソースの変更

    The *Add and Remove Resources* window.
  5. 結果を検証します。

    • Server タブで、JBoss EAP 7.0 サーバーの状態が Started に変わります。
    • Console タブに、JBoss EAP サーバーの起動と helloworld クイックスタートのデプロイメントに関するメッセージが表示されます。

      WFLYUT0021: Registered web context: /jboss-helloworld
      WFLYSRV0010: Deployed "jboss-helloworld.war" (runtime-name : "jboss-helloworld.war")
    • helloworld アプリケーションは http://localhost:8080/jboss-helloworld で使用でき、Hello World! というテキストが表示されます。

bean-validation クイックスタートの実行

bean-validation などの一部のクイックスタートは、機能のデモを行うためにユーザーインターフェースレイヤーの代わりに Arquillian テストを提供します。

  1. bean-validation クイックスタートを JBoss Developer Studio にインポートします。
  2. Servers サーバータブでサーバーを右クリックし、Start を選択して JBoss EAP サーバーを起動します。Servers タブが表示されない場合や、サーバーが定義されていない場合は、JBoss EAP サーバーを Red Hat JBoss Developer Studio へ追加してください。
  3. Project Explorer タブで jboss-bean-validation プロジェクトを右クリックし、Run AsMaven Build と選択します。
  4. 以下の内容を Goals 入力フィールドに入力し、Run を実行します。

    clean test -Parq-wildfly-remote

    図1.12 設定の編集

    The *Edit Configuration* window.
  5. 結果を検証します。

    Console タブに bean-validation Arquillian テストの結果が表示されます。

    -------------------------------------------------------
     T E S T S
    -------------------------------------------------------
    Running org.jboss.as.quickstarts.bean_validation.test.MemberValidationTest
    Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.189 sec
    
    Results :
    
    Tests run: 5, Failures: 0, Errors: 0, Skipped: 0
    
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------

1.4.2.3. コマンドラインでのクイックスタートの実行

Maven を使用すると、コマンドラインから簡単にクイックスタートをビルドおよびデプロイできます。Maven がインストールされていない場合は Apache Maven プロジェクトを参照し、ダウンロードとインストールを行ってください。

README.md ファイルは、システム要件、Maven の設定、ユーザーの追加、およびクイックスタートの実行に関する一般的な情報が含まれるクイックスタートのルートディレクトリーにあります。

各クイックスタートには、クイックスタートを実行するための特定の手順と Maven コマンドを提供する独自の README.md ファイルも含まれます。

コマンドラインでの helloworld クイックスタートの実行

  1. helloworld クイックスタートのルートディレクトリーにある README.md ファイルを確認します。
  2. JBoss EAP サーバーを起動します。

    $ EAP_HOME/bin/standalone.sh
  3. helloworld クイックスタートディレクトリーへ移動します。
  4. クイックスタートの README.md ファイルにある Maven コマンドを使用して、クイックスタートをビルドおよびデプロイします。

    mvn clean install wildfly:deploy
  5. helloworld アプリケーションは http://localhost:8080/jboss-helloworld で使用でき、Hello World! というテキストが表示されます。

1.4.3. クイックスタートチュートリアルの確認

1.4.3.1. helloworld クイックスタート

helloworld クイックスタートは JBoss EAP に単純なサーブレットをデプロイする方法を示します。ビジネスロジックは CDI (Contexts and Dependency Injection: コンテキストと依存関係の挿入) Bean として提供されるサービスにカプセル化され、サーブレットに挿入されます。このクイックスタートに基づいて、サーバーを適切に設定および起動することができます。

コマンドラインを使用してこのクイックスタートをビルドしデプロイする手順の詳細については、helloworld クイックスタートディレクトリーのルートにある README.html ファイルを参照してください。このトピックでは、Red Hat JBoss Developer Studio を使用してクイックスタートを実行する方法を説明します (Red Hat JBoss Developer Studio がインストールされ、Maven が設定された状態で helloworld クイックスタートがインポートされ、正常に実行されたことを前提とします)。

前提条件
ディレクトリー構造の確認

helloworld クイックスタートのコードは QUICKSTART_HOME/helloworld ディレクトリーにあります。helloworld クイックスタートはサーブレットと CDI Bean によって構成されます。また、バージョン番号が 1.1 であり、bean-discovery-modeall であるアプリケーションの WEB-INF ディレクトリーに beans.xml ファイルが含まれます。このマーカーファイルにより、WAR が Bean アーカイブとして識別され、JBoss EAP がこのアプリケーションで Bean を検索し、CDI をアクティベートするよう指示されます。

src/main/webapp/ ディレクトリーには、クイックスタートのファイルが含まれます。このサンプルのすべての設定ファイルは、src/main/webapp/ 内の WEB-INF/ ディレクトリー (beans.xml ファイルが含まれる) にあります。また、src/main/webapp/ ディレクトリーには、http://localhost:8080/jboss-helloworld/HelloWorld にあるサーブレットにユーザーのブラウザーをリダイレクトするために単純なメタ更新を使用する index.html ファイルも含まれます。クイックスタートは web.xml ファイルを必要としません。

コードの確認

パッケージの宣言とインポートはこれらのリストには含まれていません。完全なリストはクイックスタートのソースコードにあります。

  1. HelloWorldServlet コードを確認します。

    HelloWorldServlet.java ファイルは src/main/java/org/jboss/as/quickstarts/helloworld/ ディレクトリーにあります。このサーブレットが情報をブラウザーに送ります。

    HelloWorldServlet クラスコードサンプル

    42 @SuppressWarnings("serial")
    43 @WebServlet("/HelloWorld")
    44 public class HelloWorldServlet extends HttpServlet {
    45
    46     static String PAGE_HEADER = "<html><head><title>helloworld</title></head><body>";
    47
    48     static String PAGE_FOOTER = "</body></html>";
    49
    50     @Inject
    51	   HelloService helloService;
    52
    53     @Override
    54     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    55         resp.setContentType("text/html");
    56         PrintWriter writer = resp.getWriter();
    57         writer.println(PAGE_HEADER);
    58         writer.println("<h1>" + helloService.createHelloMessage("World") + "</h1>");
    59         writer.println(PAGE_FOOTER);
    60         writer.close();
    61     }
    62
    63 }

    表1.1 HelloWorldServlet の詳細

    注記

    43

    必要な作業は @WebServlet アノテーションを追加し、サーブレットにアクセスするために使用する URL にマッピングを提供するだけです。

    46〜48

    各 Web ページには適切な形式の HTML が必要になります。本クイックスタートは静的な文字列を使用して最低限のヘッダーとフッターの出力を書き出します。

    50〜51

    これらの行は、実際のメッセージを生成する HelloService CDI Bean を挿入します。HelloService の API を変更しない限り、ビューレイヤーを変更せずに HelloService の実装を後で変更することが可能です。

    58

    この行はサービスを呼び出し、「Hello World」というメッセージを生成して HTTP 要求へ書き出します。

  2. HelloService コードを確認します。

    HelloService.java ファイルは src/main/java/org/jboss/as/quickstarts/helloworld/ ディレクトリーにあります。このサービスは単にメッセージを返します。XML やアノテーションの登録は必要ありません。

    HelloService クラスコードサンプル

    public class HelloService {
    
        String createHelloMessage(String name) {
            return "Hello " + name + "!";
        }
    }

1.4.3.2. numberguess クイックスタート

numberguess クイックスタートは単純な非永続アプリケーションを作成し、JBoss EAP にデプロイする方法を示します。情報は JSF ビューを使用して表示され、ビジネスロジックは 2 つの CDI Bean にカプセル化されます。numberguess クイックスタートでは 1 から 100 までの数字を当てるチャンスが 10 回与えられます。数字を選択した後、その数字が正解の数字よりも大きいかまたは小さいかが表示されます。

numberguess クイックスタートのコードは QUICKSTART_HOME/numberguess ディレクトリーにあります (QUICKSTART_HOME は JBoss EAP クイックスタートをダウンロードし、展開したディレクトリー)。numberguess クイックスタートは複数の Bean、設定ファイル、および Facelets (JSF) ビューによって構成され、 WAR モジュールとしてパッケージ化されます。

コマンドラインを使用してこのクイックスタートをビルドしデプロイする手順の詳細については、numberguess クイックスタートディレクトリーのルートにある README.html ファイルを参照してください。以下の例では、Red Hat JBoss Developer Studio を使用してクイックスタートを実行します。

前提条件
設定ファイルの確認

この例のすべての設定ファイルは、クイックスタートの QUICKSTART_HOME/numberguess/src/main/webapp/WEB-INF/ ディレクトリーにあります。

  1. faces-config.xml ファイルを確認します。

    本クイックスタートは faces-config.xml ファイル名の JSF 2.2 バージョンを使用します。Facelets の標準的なバージョンが JSF 2.2 のデフォルトのビューハンドラーであるため、設定は必要ありません。このファイルはルート要素のみで構成され、JSF をアプリケーションで有効にする必要があることを示すマーカーファイルにすぎません。

    <faces-config version="2.2"
       xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
          http://xmlns.jcp.org/xml/ns/javaee
          http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
    
    </faces-config>
  2. beans.xml ファイルを確認します。

    beans.xml ファイルには、1.1 のバージョン番号と allbean-discovery-mode が含まれます。このファイルは、WAR を Bean アーカイブとして識別し、JBoss EAP がこのアプリケーションで Bean を検索し、CDI をアクティベートするよう指示するマーカーファイルです。

    <beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="
          http://xmlns.jcp.org/xml/ns/javaee
          http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
        bean-discovery-mode="all">
    </beans>
注記

このクイックスタートは web.xml ファイルを必要としません。

1.4.3.2.1. JSF コードの確認

JSF はソースファイルに .xhtml ファイル拡張子を使用しますが、レンダリングされたビューは .jsf 拡張子で提供されます。home.xhtml ファイルは src/main/webapp/ ディレクトリーにあります。

JSF ソースコード

19<html xmlns="http://www.w3.org/1999/xhtml"
20	xmlns:ui="http://java.sun.com/jsf/facelets"
21	xmlns:h="http://java.sun.com/jsf/html"
22	xmlns:f="http://java.sun.com/jsf/core">
23
24	<head>
25	<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
26	<title>Numberguess</title>
27	</head>
28
29	<body>
30	<div id="content">
31		<h1>Guess a number...</h1>
32		<h:form id="numberGuess">
33
34		<!-- Feedback for the user on their guess -->
35	<div style="color: red">
36		<h:messages id="messages" globalOnly="false" />
37		<h:outputText id="Higher" value="Higher!"
38 		  rendered="#{game.number gt game.guess and game.guess ne 0}" />
39		<h:outputText id="Lower" value="Lower!"
40		   rendered="#{game.number lt game.guess and game.guess ne 0}" />
41	</div>
42
43	<!-- Instructions for the user -->
44	<div>
45	I'm thinking of a number between <span
46	id="numberGuess:smallest">#{game.smallest}</span> and <span
47	id="numberGuess:biggest">#{game.biggest}</span>. You have
48	#{game.remainingGuesses} guesses remaining.
49	</div>
50
51	<!-- Input box for the users guess, plus a button to submit, and reset -->
52	<!-- These are bound using EL to our CDI beans -->
53	<div>
54	Your guess:
55	<h:inputText id="inputGuess" value="#{game.guess}"
56		required="true" size="3"
57		disabled="#{game.number eq game.guess}"
58		validator="#{game.validateNumberRange}" />
59		<h:commandButton id="guessButton" value="Guess"
60			action="#{game.check}"
61			disabled="#{game.number eq game.guess}" />
62	</div>
63	<div>
64	<h:commandButton id="restartButton" value="Reset"
65	action="#{game.reset}" immediate="true" />
66	</div>
67	</h:form>
68
69	</div>
70
71	<br style="clear: both" />
72
73	</body>
74</html>

以下の行番号は、JBoss Developer Studio でファイルを表示するときに示されるものに対応します。

表1.2 JSF の詳細

注記

36〜40

これらはユーザーに送信できるメッセージ、「Higher」(より大きい) と「Lower」(より小さい) です。

45〜48

ユーザーが数を選択するごとに数字の範囲が狭まります。有効な数の範囲が分かるようにこの文章は変更されます。

55〜58

この入力フィールドは値式を使用して Bean プロパティーにバインドされます。

58

ユーザーが誤って範囲外の数字を入力しないようにバリデーターのバインディングが使用されます。バリデーターがないと、ユーザーが範囲外の数字を使用する可能性があります。

59〜61

ユーザーの選択した数字をサーバーに送る方法がなければなりません。ここでは、Bean 上のアクションメソッドをバインドします。

1.4.3.2.2. クラスファイルの確認

numberguess クイックスタートのソースファイルはすべて QUICKSTART_HOME/numberguess/src/main/java/org/jboss/as/quickstarts/numberguess/ ディレクトリーにあります。パッケージの宣言とインポートはリストには含まれていません。完全なリストはクイックスタートのソースコードにあります。

  1. Random.java 修飾子コードの検証

    型に基づき挿入の対象となる 2 つの Bean を明確に区別するために修飾子が使用されます。修飾子の詳細については、修飾子を使用したあいまいな挿入の解決を参照してください。 ランダムな数字を挿入するには @Random 修飾子が使用されます。

    @Target({ TYPE, METHOD, PARAMETER, FIELD })
    @Retention(RUNTIME)
    @Documented
    @Qualifier
    public @interface Random {
    
    }
  2. MaxNumber.java 修飾子コードの検証

    @MaxNumber qualifier は最大許可数の挿入に使用されます。

    @Target({ TYPE, METHOD, PARAMETER, FIELD })
    @Retention(RUNTIME)
    @Documented
    @Qualifier
    public @interface MaxNumber {
    }
  3. Generator.java コードの検証

    Generator クラスは、producer メソッドを介して乱数を作成し、producer メソッドを介して最大可能数を公開します。このクラスはアプリケーションスコープであるため、毎回異なる乱数になることはありません。

    @SuppressWarnings("serial")
    @ApplicationScoped
    public class Generator implements Serializable {
    
        private java.util.Random random = new java.util.Random(System.currentTimeMillis());
    
        private int maxNumber = 100;
    
        java.util.Random getRandom() {
            return random;
        }
    
        @Produces
        @Random
        int next() {
            // a number between 1 and 100
            return getRandom().nextInt(maxNumber - 1) + 1;
        }
    
        @Produces
        @MaxNumber
        int getMaxNumber() {
            return maxNumber;
        }
    }
  4. Game.java コードの検証

    セッションスコープのクラス Game は、アプリケーションのプライマリーエントリーポイントであり、ゲームの設定や再設定、ユーザーが選択する数字のキャプチャーや検証、FacesMessage によるユーザーへのフィードバック提供を行います。コンストラクト後の lifecycle メソッドを使用し、@Random Instance`<Integer>` Bean から乱数を取得することによりゲームを初期化します。

    このクラスの @Named アノテーションを見てください。このアノテーションは式言語 (EL) を使用して Bean が JSF ビューにアクセスできるようにしたい場合のみ必要です。この場合 #{game} が EL になります。

    @SuppressWarnings("serial")
    @Named
    @SessionScoped
    public class Game implements Serializable {
    
        /**
         * The number that the user needs to guess
         */
        private int number;
    
        /**
         * The users latest guess
         */
        private int guess;
    
        /**
         * The smallest number guessed so far (so we can track the valid guess range).
         */
        private int smallest;
    
        /**
         * The largest number guessed so far
         */
        private int biggest;
    
        /**
         * The number of guesses remaining
         */
        private int remainingGuesses;
    
        /**
         * The maximum number we should ask them to guess
         */
        @Inject
        @MaxNumber
        private int maxNumber;
    
        /**
         * The random number to guess
         */
        @Inject
        @Random
        Instance<Integer> randomNumber;
    
        public Game() {
        }
    
        public int getNumber() {
            return number;
        }
    
        public int getGuess() {
            return guess;
        }
    
        public void setGuess(int guess) {
            this.guess = guess;
        }
    
        public int getSmallest() {
            return smallest;
        }
    
        public int getBiggest() {
            return biggest;
        }
    
        public int getRemainingGuesses() {
            return remainingGuesses;
        }
    
        /**
         * Check whether the current guess is correct, and update the biggest/smallest guesses as needed. Give feedback to the user
         * if they are correct.
         */
        public void check() {
            if (guess > number) {
                biggest = guess - 1;
            } else if (guess < number) {
                smallest = guess + 1;
            } else if (guess == number) {
                FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Correct!"));
            }
            remainingGuesses--;
        }
    
        /**
         * Reset the game, by putting all values back to their defaults, and getting a new random number. We also call this method
         * when the user starts playing for the first time using {@linkplain PostConstruct @PostConstruct} to set the initial
         * values.
         */
        @PostConstruct
        public void reset() {
            this.smallest = 0;
            this.guess = 0;
            this.remainingGuesses = 10;
            this.biggest = maxNumber;
            this.number = randomNumber.get();
        }
    
        /**
         * A JSF validation method which checks whether the guess is valid. It might not be valid because there are no guesses left,
         * or because the guess is not in range.
         *
         */
        public void validateNumberRange(FacesContext context, UIComponent toValidate, Object value) {
            if (remainingGuesses <= 0) {
                FacesMessage message = new FacesMessage("No guesses left!");
                context.addMessage(toValidate.getClientId(context), message);
                ((UIInput) toValidate).setValid(false);
                return;
            }
            int input = (Integer) value;
    
            if (input < smallest || input > biggest) {
                ((UIInput) toValidate).setValid(false);
    
                FacesMessage message = new FacesMessage("Invalid guess");
                context.addMessage(toValidate.getClientId(context), message);
            }
        }
    }

1.5. デフォルトの Welcome Web アプリケーションの設定

JBoss EAP には、デフォルトでポート 8080 のルートコンテキストで表示される Welcome アプリケーションが含まれます。

このデフォルトの Welcome アプリケーションは、独自の Web アプリケーションで置き換えることができます。これは、以下の 2 つのいずれかの方法で設定できます。

Welcome コンテンツを無効にすることもできます。

welcome-content ファイルハンドラーの変更

新しいデプロイメントを参照する、既存の welcome-content ファイルハンドラーのパスを変更します。

/subsystem=undertow/configuration=handler/file=welcome-content:write-attribute(name=path,value="/path/to/content")
注記

または、サーバーのルートにより使用される異なるファイルハンドラーを作成することもできます。

/subsystem=undertow/configuration=handler/file=NEW_FILE_HANDLER:add(path="/path/to/content")
/subsystem=undertow/server=default-server/host=default-host/location=\/:write-attribute(name=handler,value=NEW_FILE_HANDLER)

変更を反映するためにサーバーをリロードします。

reload

default-web-module の変更

デプロイされた Web アプリケーションをサーバーのルートにマップします。

/subsystem=undertow/server=default-server/host=default-host:write-attribute(name=default-web-module,value=hello.war)

変更を反映するためにサーバーをリロードします。

reload

デフォルトの Welcome Web アプリケーションの無効化

default-hostlocation エントリー (/) を削除して welcome アプリケーションを無効にします。

/subsystem=undertow/server=default-server/host=default-host/location=\/:remove

変更を反映するためにサーバーをリロードします。

reload

第2章 JBoss EAP での Maven の使用

2.1. Maven について

2.1.1. Maven リポジトリー

Apache Maven は、ソフトウェアプロジェクトの作成、管理、および構築を行う Java アプリケーションの開発で使用される分散型ビルド自動化ツールです。Maven は Project Object Model (POM) と呼ばれる標準の設定ファイルを利用して、プロジェクトの定義や構築プロセスの管理を行います。POM はモジュールやコンポーネントの依存関係、ビルドの順番、結果となるプロジェクトパッケージングのターゲットを定義し、XML ファイルを使用して出力します。この結果、プロジェクトが適切かつ統一された状態で構築されるようになります。

Maven は、リポジトリーを使用してアーカイブを行います。Maven リポジトリーには Java ライブラリー、プラグイン、その他のビルドアーティファクトが格納されています。デフォルトのパブリックリポジトリーは Maven 2 Central Repository ですが、複数の開発チームの間で共通のアーティファクトを共有する目的で、社内のプライベートおよび内部リポジトリーとすることが可能です。また、サードパーティーのリポジトリーもあります。JBoss EAP には、Java EE 開発者が JBoss EAP 6 でアプリケーションを構築する際に利用する要件の多くが含まれる Maven リポジトリーが含まれます。このリポジトリーを使用するようプロジェクトを設定するには、JBoss EAP Maven リポジトリーの設定を参照してください。

Maven の詳細については、Welcome to Apache Maven を参照してください。

Maven リポジトリーの詳細については、Apache Maven Project - Introduction to Repositories を参照してください。

2.1.2. Maven POM ファイル

プロジェクトオブジェクトモデル (POM) ファイルはプロジェクトをビルドするために Maven で使用する設定ファイルです。POM ファイルは XML のファイルであり、プロジェクトの情報やビルド方法を含みます。これには、ソース、テスト、およびターゲットのディレクトリーの場所、プロジェクトの依存関係、プラグインリポジトリー、実行できるゴールが含まれます。また、バージョン、説明、開発者、メーリングリスト、ライセンスなどのプロジェクトに関する追加情報も含まれます。pom.xml ファイルでは一部の設定オプションを設定する必要があり、他のすべてのオプションはデフォルト値に設定されます。

pom.xml ファイルのスキーマは http://maven.apache.org/maven-v4_0_0.xsd にあります。

POM ファイルの詳細については、Apache Maven Project POM Reference を参照してください。

Maven POM ファイルの最低要件

pom.xml ファイルの最低要件は次のとおりです。

  • プロジェクトルート
  • modelVersion
  • groupId - プロジェクトのグループの ID
  • artifactId - アーティファクト (プロジェクト) の ID
  • version - 指定したグループ下のアーティファクトのバージョン

例: サンプル pom.xml ファイル

基本的な pom.xml ファイルは次のようになります。

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.jboss.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1</version>
</project>

2.1.3. Maven 設定ファイル

Maven の settings.xml ファイルには Maven に関するユーザー固有の設定情報が含まれています。開発者の ID、プロキシ情報、ローカルリポジトリーの場所など、 pom.xml ファイルで配布されてはならないユーザー固有の情報が含まれています。

settings.xml が存在する場所は 2 つあります。

  • Maven インストール: 設定ファイルは $M2_HOME/conf/ ディレクトリーにあります。これらの設定は global 設定と呼ばれます。デフォルトの Maven 設定ファイルはコピー可能なテンプレートであり、これを基にユーザー設定ファイルを設定することが可能です。
  • ユーザーのインストール: 設定ファイルは ${user.home}/.m2/ ディレクトリーにあります。 Maven とユーザーの settings.xml ファイルが存在する場合、内容はマージされます。重複する内容がある場合は、ユーザーの settings.xml ファイルが優先されます。

例: Maven 設定ファイル

<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
  <profiles>
    <!-- Configure the JBoss EAP Maven repository -->
    <profile>
      <id>jboss-eap-maven-repository</id>
      <repositories>
        <repository>
          <id>jboss-eap</id>
          <url>file:///path/to/repo/jboss-eap-7.0.0.GA-maven-repository/maven-repository</url>
          <releases>
            <enabled>true</enabled>
          </releases>
          <snapshots>
            <enabled>false</enabled>
          </snapshots>
        </repository>
      </repositories>
      <pluginRepositories>
        <pluginRepository>
          <id>jboss-eap-maven-plugin-repository</id>
          <url>file:///path/to/repo/jboss-eap-7.0.0.GA-maven-repository/maven-repository</url>
          <releases>
            <enabled>true</enabled>
          </releases>
          <snapshots>
            <enabled>false</enabled>
          </snapshots>
        </pluginRepository>
      </pluginRepositories>
    </profile>
  </profiles>
  <activeProfiles>
    <!-- Optionally, make the repository active by default -->
    <activeProfile>jboss-eap-maven-repository</activeProfile>
  </activeProfiles>
</settings>

settings.xml ファイルのスキーマは http://maven.apache.org/xsd/settings-1.0.0.xsd にあります。

2.1.4. Maven リポジトリーマネージャー

リポジトリーマネージャーは、 Maven リポジトリーを容易に管理できるようにするツールです。リポジトリーマネージャーには、次のような利点があります。

  • ユーザーの組織のリポジトリーとリモート Maven リポジトリーとの間のプロキシを設定する機能を提供します。これには、デプロイメントの高速化や効率化、Maven によるダウンロード対象を制御するレベルの向上など、さまざまな利点があります。
  • 独自に生成したアーティファクトのデプロイ先を提供し、組織内の異なる開発チーム間におけるコラボレーションを可能にします。

Maven リポジトリーマネージャーの詳細については、Best Practice - Using a Repository Manager を参照してください。

一般的に使用される Maven リポジトリーマネージャー
Sonatype Nexus
Nexus の詳細については、Sonatype Nexus documentation を参照してください。
Artifactory
Artifactory の詳細については、JFrog Artifactory ドキュメンテーションを参照してください。
Apache Archiva
Apache Archiva の詳細については、Apache Archiva: The Build Artifact Repository Manager を参照してください。
注記

通常リポジトリーマネージャーが使用されるエンタープライズ環境では、Maven は、このマネージャーを使用してすべてのプロジェクトに対してすべてのアーティファクトを問い合わせる必要があります。Maven は、宣言されたすべてのリポジトリーを使用して不明なアーティファクトを見つけるため、探しているものが見つからない場合に、centralリポジトリー (組み込みの親 POM で定義されます) で検索を試行します。この central の場所をオーバーライドするには、central で定義を追加してデフォルトの central リポジトリーがリポジトリーマネージャーになるようにします。これは、確立されたプロジェクトには適切ですが、クリーンな、または「新しい」プロジェクトの場合は、周期的な依存関係が作成されるため、問題が発生します。

2.2. Maven と JBoss EAP Maven リポジトリーのインストール

2.2.1. Maven のダウンロードとインストール

Maven コマンドラインを使用してアプリケーションをビルドし、JBoss EAP にデプロイする場合は、Maven をダウンロードし、インストールする必要があります。Red Hat JBoss Developer Studio を使用してアプリケーションをビルドおよびデプロイする場合、Maven は Red Hat JBoss Developer Studio で配布されるため、この手順を省略できます。

  1. Apache Maven Project - Download Maven にアクセスし、ご使用のオペレーティングシステムに対応する最新のディストリビューションをダウンロードします。
  2. ご使用のオペレーシングシステムに Apache Maven をダウンロードおよびインストールする方法については、Maven のドキュメントを参照してください。

2.2.2. JBoss EAP の Maven リポジトリーのインストール

JBoss EAP Maven リポジトリーをインストールする方法は 3 つあります。

注記

JBoss EAP Maven リポジトリーはオンラインで利用したり、示された 3 つのいずれかの方法でダウンロードし、ローカルにインストールしたりできます。

2.2.3. JBoss EAP Maven リポジトリーのローカルインストール

この例では、ローカルのファイルシステムへ JBoss EAP Maven リポジトリーをダウンロードする手順を取り上げます。このオプションは簡単に設定できます。このオプションを使用すると、ローカルマシンですぐに使用を開始できます。開発で Maven の使用方法を理解するのに役に立ちますが、チームによる実稼働環境での使用には推奨されません。

JBoss EAP Maven リポジトリーをダウンロードし、ローカルファイルシステムにインストールするには、以下の手順に従ってください。

  1. Web ブラウザーを開き、URL https://access.redhat.com/jbossnetwork/restricted/listSoftware.html?product=appplatform にアクセスします。
  2. リストに Red Hat JBoss Enterprise Application Platform 7.0 Maven Repository があることを確認します。
  3. ダウンロード ボタンをクリックし、リポジトリーが含まれる .zip ファイルをダウンロードします。
  4. ローカルファイルシステム上の Zip 形式のファイルを希望のディレクトリーで展開します。

    これにより、maven-repository/ という名前のサブディレクトリーに Maven レポジトリーが含まれる新しい jboss-eap-7.0.0.GA-maven-repository/ ディレクトリーが作成されます。

重要

古いローカルリポジトリーを引き続き使用する場合は、そのリポジトリーを Maven settings.xml 設定ファイルで個別に設定する必要があります。各ローカルリポジトリーは、独自の <repository> タグ内で設定する必要があります。

重要

新しい Maven リポジトリーをダウンロードする場合は、使用する前に、.m2/ ディレクトリーにあるキャッシュされた repository/ サブディレクトリーを削除してください。

2.2.4. Apache httpd で使用する JBoss EAP Maven レポジトリーのインストール

この例では、Apache httpd で使用する JBoss EAP Maven リポジトリーをダウンロードする手順を示します。Web サーバーにアクセスできる開発者は Maven リポジトリーにもアクセスできるため、このオプションはマルチユーザーの開発環境や複数のチームにまたがる開発環境に適しています。

注記

最初に Apache httpd を設定する必要があります。手順については、Apache HTTP Server Project ドキュメンテーションを参照してください。

  1. Web ブラウザーを開き、URL https://access.redhat.com/jbossnetwork/restricted/listSoftware.html?product=appplatform にアクセスします。
  2. リストに Red Hat JBoss Enterprise Application Platform 7.0 Maven Repository があることを確認します。
  3. ダウンロード ボタンをクリックし、リポジトリーが含まれる .zip ファイルをダウンロードします。
  4. Apache サーバー上で Web にアクセス可能なディレクトリーに Zip 形式のファイルを展開します。
  5. 作成されたディレクトリーで読み取りアクセスとディレクトリーの閲覧を許可するよう Apache を設定します。

    この設定により、マルチユーザー環境が Apache httpd 上で Maven リポジトリーにアクセスできるようになります。

2.3. Maven リポジトリーの使用

2.3.1. JBoss EAP Maven リポジトリーの設定

概要

プロジェクトで JBoss EAP Maven リポジトリーを使用するよう Maven に指示する方法は 2 つあります。

Maven 設定を使用した JBoss EAP Maven リポジトリーの設定

これは推奨される方法です。リポジトリーマネージャーや共有サーバー上のリポジトリーを使用して Maven を設定すると、プロジェクトの制御および管理を行いやすくなります。また、代替のミラーを使用してプロジェクトファイルを変更せずにリポジトリーマネージャーに特定のリポジトリーのルックアップ要求をすべてリダイレクトすることも可能になります。ミラーの詳細については、http://maven.apache.org/guides/mini/guide-mirror-settings.html を参照してください。

プロジェクトの POM ファイルにリポジトリー設定が含まれていない場合、この設定方法はすべての Maven プロジェクトに対して適用されます。

この項では、Maven の設定方法について説明します。Maven インストールグローバル設定またはユーザーのインストール設定を指定できます。

Maven 設定ファイルの指定

  1. 使用しているオペレーションシステムの Maven settings.xml ファイルを見つけます。通常、このファイルは ${user.home}/.m2/ ディレクトリーにあります。

    • Linux または Mac の場合、これは ~/.m2/ になります。
    • Windows の場合、これは \Documents and Settings\.m2\ または \Users\.m2\ になります。
  2. settings.xml ファイルが見つからない場合は、${user.home}/.m2/conf/ ディレクトリーの settings.xml ファイルを ${user.home}/.m2/ ディレクトリーへコピーします。
  3. 以下の XML を <profiles> element of the settings.xml ファイルにコピーします。JBoss EAP リポジトリーの URL を調べJBOSS_EAP_REPOSITORY_URL をその URL に置き換えます。

    <!-- Configure the JBoss Enterprise Maven repository -->
    <profile>
      <id>jboss-enterprise-maven-repository</id>
      <repositories>
        <repository>
          <id>jboss-enterprise-maven-repository</id>
          <url>JBOSS_EAP_REPOSITORY_URL</url>
          <releases>
            <enabled>true</enabled>
          </releases>
          <snapshots>
            <enabled>false</enabled>
          </snapshots>
        </repository>
      </repositories>
      <pluginRepositories>
        <pluginRepository>
          <id>jboss-enterprise-maven-repository</id>
          <url>JBOSS_EAP_REPOSITORY_URL</url>
          <releases>
            <enabled>true</enabled>
          </releases>
          <snapshots>
            <enabled>false</enabled>
          </snapshots>
        </pluginRepository>
      </pluginRepositories>
    </profile>

    以下に、オンラインの JBoss EAP Maven リポジトリーにアクセする設定例を示します。

    <!-- Configure the JBoss Enterprise Maven repository -->
    <profile>
      <id>jboss-enterprise-maven-repository</id>
      <repositories>
        <repository>
          <id>jboss-enterprise-maven-repository</id>
          <url>https://maven.repository.redhat.com/ga/</url>
          <releases>
            <enabled>true</enabled>
          </releases>
          <snapshots>
            <enabled>false</enabled>
          </snapshots>
        </repository>
      </repositories>
      <pluginRepositories>
        <pluginRepository>
          <id>jboss-enterprise-maven-repository</id>
          <url>https://maven.repository.redhat.com/ga/</url>
          <releases>
            <enabled>true</enabled>
          </releases>
          <snapshots>
            <enabled>false</enabled>
          </snapshots>
        </pluginRepository>
      </pluginRepositories>
    </profile>
  4. 次の XML を settings.xml ファイルの <activeProfiles> 要素へコピーします。

    <activeProfile>jboss-enterprise-maven-repository</activeProfile>
  5. Red Hat JBoss Developer Studio の実行中に settings.xml ファイルを変更する場合は、ユーザー設定を更新する必要があります。

    1. メニューで Window → Preferences と選択します。
    2. Preferences ウィンドウで Maven を展開し、User Settings を設定します。
    3. Update Settings ボタンをクリックし、Red Hat JBoss Developer Studio で Maven のユーザー設定を更新します。

      Maven ユーザー設定の更新 のスクリーンショット

      Maven ユーザー設定の更新 のスクリーンショット

重要

Maven リポジトリーに古いアーティファクトが含まれる場合は、プロジェクトをビルドまたはデプロイしたときに以下のいずれかの Maven エラーメッセージが表示されることがあります。

  • Missing artifact ARTIFACT_NAME
  • [ERROR] Failed to execute goal on project PROJECT_NAME; Could not resolve dependencies for PROJECT_NAME

この問題を解決するには、最新の Maven アーティファクトをダウンロードするためにローカルリポジトリーのキャッシュバージョンを削除します。キャッシュバージョンは ${user.home}/.m2/repository/ に存在します。

プロジェクト POM を使用した JBoss EAP Maven リポジトリーの設定
警告

この設定方法は、設定されたプロジェクトのグローバルおよびユーザー Maven 設定を上書きするため、回避する必要があります。

プロジェクト POM ファイルを使用してリポジトリーを設定する場合は、慎重に計画する必要があります。このような設定では、推移的に含まれた POM が問題になります。Maven は、外部リポジトリーで不明なアーティファクトを問い合わせ、これによりビルド処理に時間がかかるようになるためです。また、アーティファクトの抽出元を制御できなくなることもあります。

注記

リポジトリーの URL はリポジトリーの場所 (ファイルシステムまたは Web サーバー) によって異なります。リポジトリーのインストール方法については、JBoss EAP の Maven リポジトリーのインストールを参照してください。各インストールオプションの例は次のとおりです。

ファイルシステム
file:///path/to/repo/jboss-eap-maven-repository
Apache Web Server
http://intranet.acme.com/jboss-eap-maven-repository/
Nexus リポジトリーマネージャー
https://intranet.acme.com/nexus/content/repositories/jboss-eap-maven-repository

プロジェクトの POM ファイルの設定

  1. テキストエディターでプロジェクトの pom.xml ファイルを開きます。
  2. 次のリポジトリー設定を追加します。すでにファイルに <repositories> 設定が存在する場合は <repository> 要素を追加します。必ず <url> を実際のリポジトリーの場所に変更するようにしてください。
<repositories>
   <repository>
      <id>jboss-eap-repository-group</id>
      <name>JBoss EAP Maven Repository</name>
      <url>JBOSS_EAP_REPOSITORY_URL</url>
      <layout>default</layout>
      <releases>
         <enabled>true</enabled>
         <updatePolicy>never</updatePolicy>
      </releases>
      <snapshots>
         <enabled>true</enabled>
         <updatePolicy>never</updatePolicy>
      </snapshots>
   </repository>
</repositories>
  1. 次のプラグインリポジトリー設定を追加します。すでにファイルに <pluginRepositories> 設定が存在する場合は <pluginRepository> 要素を追加します。
<pluginRepositories>
   <pluginRepository>
      <id>jboss-eap-repository-group</id>
      <name>JBoss EAP Maven Repository</name>
      <url>JBOSS_EAP_REPOSITORY_URL</url>
      <releases>
         <enabled>true</enabled>
      </releases>
      <snapshots>
         <enabled>true</enabled>
      </snapshots>
   </pluginRepository>
</pluginRepositories>
JBoss EAP リポジトリーの URL の確認

リポジトリーの URL は、リポジトリーが存在する場所によって異なります。以下のいずれかのリポジトリーの場所を使用するよう Maven を設定できます。

  • オンラインの JBoss EAP Maven リポジトリーを使用するには、URL https://maven.repository.redhat.com/ga/ を指定します。
  • ローカルファイルシステムにインストールされた JBoss EAP Maven リポジトリーを使用するには、リポジトリーをダウンロードし、URL のローカルファイルパスを使用する必要があります (例: file:///path/to/repo/jboss-eap-7.0-maven-repository/maven-repository/)。
  • Apache Web Server にリポジトリーをインストールする場合、リポジトリーの URL は http://intranet.acme.com/jboss-eap-7.0-maven-repository/maven-repository/ のようになります。
  • Nexus リポジトリーマネージャーを使用して JBoss EAP Maven リポジトリーをインストールする場合、URL は https://intranet.acme.com/nexus/content/repositories/jboss-eap-7.0-maven-repository/maven-repository/ のようになります。
注記

リモートリポジトリーへのアクセスには、HTTP サーバーのリポジトリー用の http:// やファイルサーバーのリポジトリー用の file:// などの一般的なプロトコルが使用されます。

2.3.2. Red Hat JBoss Developer Studio で使用する Maven の設定

アプリケーションをビルドし、Red Hat JBoss Enterprise Application Platform にデプロイするのに必要なアーティファクトと依存関係は、パブリックリポジトリーでホストされます。アプリケーションをビルドするときにこのリポジトリーを使用するよう Maven を設定する必要があります。このトピックでは、Red Hat JBoss Developer Studio を使用してアプリケーションをビルドおよびデプロイする場合に Maven を設定する手順について説明します。

Maven は Red Hat JBoss Developer Studio で配布されるため、個別にインストールする必要がありません。ただし、JBoss EAP へのデプロイメントのために Java EE Web Project ウィザードで使用する Maven を設定する必要があります。以下の手順は、Red Hat JBoss Developer Studio 内から Maven 設定ファイルを編集して JBoss EAP で使用する Maven を設定する方法を示しています。

Red Hat JBoss Developer Studio での Maven の設定

  1. Window → Preferences をクリックし、JBoss Tools を展開して、JBoss Maven Integration を選択します。

    Preferences ウィンドウの JBoss Maven 統合ペイン

    Preferences ウィンドウの JBoss Maven 統合ペイン

  2. Configure Maven Repositories をクリックします。
  3. Add Repository をクリックして JBoss Enterprise Maven リポジトリーを設定します。Add Maven Repository ダイアログで以下の手順を実行します。

    1. Profile IDRepository ID、および Repository Name の値を jboss-ga-repository に設定します。
    2. Repository URL の値を http://maven.repository.redhat.com/ga に設定します。
    3. Active by default チェックボックスをクリックして Maven リポジトリーを有効にします。
    4. OK をクリックします。

      Maven リポジトリーの追加

      Maven リポジトリーの追加

  4. リポジトリーを確認して、終了 をクリックします。

    Maven リポジトリーの確認

    Maven リポジトリーの確認

  5. "Are you sure you want to update the file MAVEN_HOME/settings.xml? というメッセージが表示されます。Yes をクリックして設定を更新します。OK をクリックしてダイアログを閉じます。

JBoss EAP Maven リポジトリーが Red Hat JBoss Developer Studio での使用向けに設定されます。

2.3.3. プロジェクト依存関係の管理

このトピックでは、Red Hat JBoss Enterprise Application Platform 向けの BOM (Bill of Materials) POM の使用方法について説明します。

BOM は、指定モジュールに対するすべてのランタイム依存関係のバージョンを指定する Maven pom.xml (POM) ファイルです。バージョン依存関係は、ファイルの依存関係管理セクションにリストされています。

プロジェクトは、groupId:artifactId:version (GAV) をプロジェクト pom.xml ファイルの依存関係管理セクションに追加し、<scope>import</scope> および <type>pom</type> 要素の値を指定して、BOM を使用します。

注記

多くの場合、プロジェクト POM ファイルの依存関係によって provided スコープが使用されます。これは、これらのクラスが実行時にアプリケーションサーバーによって提供され、ユーザーアプリケーションとともにパッケージ化する必要がないためです。

サポート対象の Maven アーティファクト

製品のビルドプロセスの一部として、JBoss EAP のすべてのランタイムコンポーネントは制御された環境でソースからビルドされます。これにより、バイナリーアーティファクトに悪意のあるコードが含まれないようにし、製品のライフサイクルが終了するまでサポートを提供できるようにします。これらのアーティファクトは、1.0.0-redhat-1 のように使用される -redhat バージョン修飾子によって簡単に識別可能です。

サポートされるアーティファクトをビルド設定 pom.xml ファイルに追加すると、ローカルビルドおよびテスト向けの適切なバイナリーアーティファクトがビルドで使用されるようになります。-redhat バージョンのアーティファクトは、サポートされるパブリック API の一部とは限らず、今後の改訂で変更されることがあります。サポートされるパブリック API の詳細については、本リリースに同梱されている JavaDoc ドキュメントを参照してください。

たとえば、サポートされているバージョンの Hibernate を使用するには、ビルド設定に以下のようなコードを追加します。

<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-core</artifactId>
  <version>5.0.1.Final-redhat-1</version>
  <scope>provided</scope>
</dependency>

上記の例には、<version/> の値が含まれていることに注意してください。ただし、依存関係バージョンの設定には、Maven の依存関係管理を使用することが推奨されます。

依存関係管理

Maven には、ビルド全体で直接的および推移的な依存関係のバージョンを管理するメカニズムが含まれています。依存関係管理の使用に関する一般的な情報については、Apache Maven Project の Introduction to the Dependency Mechanism を参照してください。

サポートされる Red Hat の依存関係を 1 つ以上ビルドに直接使用しても、ビルドの推移的な依存関係がすべて Red Hat アーティファクトによって完全にサポートされるとは限りません。Maven のビルドでは、Maven の中央リポジトリーおよびその他の Maven リポジトリーから複数のアーティファクトソースの組み合わせが使用することが一般的です。

JBoss EAP Maven リポジトリーには、サポートされるすべての JBoss EAP バイナリーアーティファクトを指定する依存関係管理 BOM が含まれています。この BOM は、ビルドの直接的および推移的依存関係に対して、サポートされる JBoss EAP 依存関係の優先順位を決定するためにビルドで使用できます。つまり、推移的な依存関係が、サポートされる正しい依存関係バージョン (該当する場合) に対して管理されます。この BOM のバージョンは、JBoss EAP リリースのバージョンと一致します。

<dependencyManagement>
  <dependencies>
    ...
    <dependency>
      <groupId>org.jboss.bom</groupId>
      <artifactId>eap-runtime-artifacts</artifactId>
      <version>7.0.0.GA</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
    ...
  </dependencies>
</dependencyManagement>
注記

JBoss EAP 7 では、この BOM の名前が eap6-supported-artifacts から eap-runtime-artifacts に変更されました。この変更の目的は、この POM のアーティファクトが JBoss EAP ランタイムの一部であるが、必ずしもサポートされるパブリック API の一部ではないことを明確にすることです。一部の jar には、リリースごとに異なる場合がある 内部 API と機能が含まれます。

JBoss EAP Java EE 仕様の BOM

jboss-javaee-7.0 BOM には、JBoss EAP によって使用される Java EE 仕様の API JAR が含まれています。

この BOM をプロジェクトで使用するには、JSP のバージョンが含まれる GAV に対する依存関係と、アプリケーションのビルドおよびデプロイに必要なサーブレット API JAR を追加します。

以下の例では、1.0.3.Final-redhat-1 バージョンの jboss-javaee-7.0 BOM が使用されています。

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.jboss.spec</groupId>
      <artifactId>jboss-javaee-7.0</artifactId>
      <version>1.0.3.Final-redhat-1</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
    ...
  </dependencies>
</dependencyManagement>

<dependencies>
  <dependency>
    <groupId>org.jboss.spec.javax.servlet</groupId>
    <artifactId>jboss-servlet-api_3.1_spec</artifactId>
    <scope>provided</scope>
  </dependency>
  <dependency>
    <groupId>org.jboss.spec.javax.servlet.jsp</groupId>
    <artifactId>jboss-jsp-api_2.3_spec</artifactId>
    <scope>provided</scope>
  </dependency>
  ...
</dependencies>
JBoss EAP BOM とクイックスタート

クイックスタートは、Maven リポジトリーの主要なユースケース例を提供します。下表に、クイックスタートによって使用される Maven BOM を示します。

表2.1 クイックスタートによって使用される JBoss BOM

BOM アーティファクト IDユースケース

jboss-eap-javaee7

サポートされる JBoss EAP JavaEE 7 API と追加の JBoss EAP API jar

jboss-eap-javaee7-with-spring3

jboss-eap-javaee7 および推奨される Spring 3 バージョン

jboss-eap-javaee7-with-spring4

jboss-eap-javaee7 および推奨される Spring 4 バージョン

jjboss-eap-javaee7-with-tools

jboss-eap-javaee7 および Arquillian などの開発ツール

注記

ほとんどのユースケースに対して使用方法を単純にするために、JBoss EAP 6 のこれらの BOM は少ない数の BOM に統合されました。Hibernate、ロギング、トランザクション、メッセージング、および他のパブリック API jar は jboss-javaee7-eap に含まれるようになり、各ユースケースで個別の BOM が必要なくなりました。

以下の例では、7.0.0.GA バージョンの jboss-eap-javaee7 BOM が使用されています。

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.jboss.bom</groupId>
      <artifactId>jboss-eap-javaee7</artifactId>
      <version>7.0.0.GA</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
    ...
  </dependencies>
</dependencyManagement>

<dependencies>
  <dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <scope>provided</scope>
  </dependency>
  ...
</dependencies>
JBoss EAP クライアント BOM

クライアント BOM は、依存関係管理セクションを作成したり、依存関係を定義したりしません。クライアント BOM は他の BOM の集合体であり、リモートクライアントのユースケースに必要な依存関係のセットをパッケージ化するために使用されます。

wildfly-ejb-client-bom および wildfly-jms-client-bom の BOM は jboss-eap-javaee7 BOM により管理されるため、プロジェクト依存関係でバージョンを管理する必要はありません。

以下に wildfly-ejb-client-bom および wildfly-jms-client-bom クライアント BOM 依存関係をプロジェクトに追加する方法の例を示します。

<dependencyManagement>
  <dependencies>
    <!-- jboss-eap-javaee7: JBoss stack of the Java EE APIs and related components.  -->
    <dependency>
      <groupId>org.jboss.bom</groupId>
      <artifactId>jboss-eap-javaee7</artifactId>
      <version>7.0.0.GA</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
  ...
</dependencyManagement>

<dependencies>
  <dependency>
    <groupId>org.jboss.eap</groupId>
    <artifactId>wildfly-ejb-client-bom</artifactId>
    <type>pom</type>
  </dependency>
  <dependency>
    <groupId>org.jboss.eap</groupId>
    <artifactId>wildfly-jms-client-bom</artifactId>
    <type>pom</type>
  </dependency>
  ...
</dependencies>

Maven 依存関係および BOM POM ファイルの詳細については、Apache Maven Project - Introduction to the Dependency Mechanism を参照してください。

第3章 クラスローディングとモジュール

3.1. はじめに

3.1.1. クラスロードとモジュールの概要

JBoss EAP は、デプロイされたアプリケーションのクラスパスを制御するためにモジュール形式のクラスロードシステムを使用します。このシステムは、階層クラスローダーの従来のシステムよりも、柔軟性があり、より詳細に制御できます。開発者は、アプリケーションで利用可能なクラスに対して粒度の細かい制御を行い、アプリケーションサーバーで提供されるクラスを無視して独自のクラスを使用するようデプロイメントを設定できます。

モジュール形式のクラスローダーにより、すべての Java クラスはモジュールと呼ばれる論理グループに分けられます。各モジュールは、独自のクラスパスに追加されたモジュールからクラスを取得するために、他のモジュールの依存関係を定義できます。デプロイされた各 JAR および WAR ファイルはモジュールとして扱われるため、開発者はモジュール設定をアプリケーションに追加してアプリケーションのクラスパスの内容を制御できます。

3.1.2. モジュール

モジュールは、クラスローディングおよび依存関係管理に使用されるクラスの論理グループです。JBoss EAP は、静的モジュールと動的モジュールの 2 つの種類のモジュールを識別します。この 2 つの種類のモジュールの主な違いは、パッケージ化方法です。

静的モジュール

静的モジュールは、アプリケーションサーバーの EAP_HOME/modules/ ディレクトリーで定義されます。各モジュールは EAP_HOME/modules/com/mysql/ のようにサブディレクトリーとして存在します。各モジュールには、module.xml 設定ファイルとすべての必要な JAR ファイルが含まれるスロットサブディレクトリー (デフォルトでは main) が含まれます。アプリケーションサーバーにより提供される API は、Java EE API と他の API を含む静的モジュールとして提供されます。

MySQL JDBC ドライバー module.xml ファイルの例

<?xml version="1.0" ?>
<module xmlns="urn:jboss:module:1.1" name="com.mysql">
  <resources>
    <resource-root path="mysql-connector-java-5.1.36-bin.jar"/>
  </resources>
  <dependencies>
    <module name="javax.api"/>
    <module name="javax.transaction.api"/>
  </dependencies>
</module>

モジュール名 (com.mysql) は、モジュールのディレクトリー構造 (スロット名 (main) を除く) に一致する必要があります。

カスタム静的モジュールの作成は、同じサードパーティーライブラリーを使用する同じサーバー上に多くのアプリケーションがデプロイされる場合に役立ちます。これらのライブラリーを各アプリケーションとバンドルする代わりに、管理者はこれらのライブラリーが含まれるモジュールを作成およびインストールできます。アプリケーションは、カスタム静的モジュールで明示的な依存関係を宣言できます。

JBoss EAP ディストリビューションで提供されるモジュールは、EAP_HOME/modules ディレクトリー内の system ディレクトリーにあります。このため、サードパーティーによって提供されるモジュールから分離されます。また、JBoss EAP 上で使用する、Red Hat により提供されるすべての製品によって、system ディレクトリー内にモジュールがインストールされます。

各モジュールに 1 つのディレクトリーを使用して、カスタムモジュールが EAP_HOME/modules ディレクトリーにインストールされるようにする必要があります。こうすると、同梱されたバージョンではなく、system ディレクトリーに存在するカスタムバージョンのモジュールがロードされるようになります。これにより、ユーザー提供のモジュールがシステムモジュールよりも優先されます。

JBOSS_MODULEPATH 環境変数を使用して JBoss EAP がモジュールを検索する場所を変更する場合は、指定された場所の 1 つで system サブディレクトリー構造を探します。system 構造は、JBOSS_MODULEPATH で指定された場所のどこかに存在する必要があります。

動的モジュール

動的モジュールは、各 JAR または WAR デプロイメント (または、EAR 内のサブデプロイメント) に対してアプリケーションサーバーによって作成およびロードされます。動的モジュールの名前は、デプロイされたアーカイブの名前から派生されます。デプロイメントはモジュールとしてロードされるため、依存関係を設定し、他のデプロイメントで依存関係として使用することが可能です。

モジュールは必要な場合にのみロードされます。通常、モジュールは、明示的または暗黙的な依存関係があるアプリケーションがデプロイされる場合にのみロードされます。

3.1.3. モジュールの依存関係

モジュール依存関係は、あるモジュールに他の 1 つまたは複数のモジュールのクラスが必要になるという宣言です。JBoss EAP がモジュールをロードするときに、モジュール形式のクラスローダーがモジュールの依存関係を解析し、各依存関係のクラスをクラスパスに追加します。指定の依存関係が見つからない場合、モジュールはロードできません。

注記

モジュールとモジュール形式のクラスロードシステムに関する完全な詳細については、節モジュールを参照してください。

デプロイされたアプリケーション (JAR や WAR など) は動的モジュールとしてロードされ、依存関係を利用して JBoss EAP によって提供される API にアクセスします。

依存関係には明示的暗黙的の 2 つのタイプがあります。

明示的な依存関係
明示的な依存関係は開発者が設定ファイルで宣言します。静的モジュールでは、依存関係を module.xml ファイルに宣言できます。動的モジュールでは、デプロイメントの MANIFEST.MF または jboss-deployment-structure.xml デプロイメント記述子に依存関係を宣言できます。
暗黙的な依存関係

暗黙的な依存関係は、デプロイメントで特定の状態やメタデータが見つかったときに自動的に追加されます。JBoss EAP に同梱される Java EE 7 API は、デプロイメントで暗黙的な依存関係が検出されたときに追加されるモジュールの例になります。

jboss-deployment-structure.xml デプロイメント記述子ファイルを使用して、特定の暗黙的な依存関係を除外するようデプロイメントを設定することも可能です。これは、JBoss EAP が暗黙的な依存関係として追加しようとする特定バージョンのライブラリーをアプリケーションがバンドルする場合に役に立つことがあります。

jboss-deployment-structure.xml デプロイメント記述子の使用の詳細については、節デプロイメントへの明示的なモジュール依存関係の追加を参照してください。

オプションの依存関係

明示的な依存関係は、オプションとして指定できます。オプションの依存関係をロードできなくても、モジュールのロードは失敗しません。ただし、依存関係は後で使用できるようになっても、モジュールのクラスパスには追加されません。依存関係はモジュールがロードされるときに利用可能である必要があります。

依存関係のエクスポート

モジュールのクラスパスには独自のクラスとその直接の依存関係のクラスのみが含まれます。モジュールは 1 つの依存関係の依存関係クラスにはアクセスできませんが、暗黙的な依存関係のエクスポートを指定できます。エクスポートされた依存関係は、エクスポートするモジュールに依存するモジュールへ提供されます。

たとえば、モジュール A はモジュール B に依存し、モジュール B はモジュール C に依存します。モジュール A はモジュール B のクラスにアクセスでき、モジュール B はモジュール C のクラスにアクセスできます。モジュール Aは以下のいずれかの条件を満たさない限り、モジュール C のクラスにアクセスできません。

  • モジュール A が、モジュール C に対する明示的な依存関係を宣言する
  • モジュール B がモジュール C の依存関係をエクスポートする
グローバルモジュール

グローバルモジュールは、JBoss EAP が各アプリケーションへの依存関係として提供するモジュールです。このモジュールをグローバルモジュールの JBoss EAP のリストへ追加すると、モジュールをグローバルモジュールにすることができます。モジュールへの変更は必要ありません。

詳細については、JBoss EAP Configuration Guide の節 Define Global Modules を参照してください。

3.1.3.1. 管理 CLI を使用したモジュールの依存関係の表示

以下の管理操作を使用すると、特定のモジュールとその依存関係に関する情報を表示できます。

/core-service=module-loading:module-info(name=$MODULE_NAME)

module-info 出力の例

[standalone@localhost:9990 /] /core-service=module-loading:module-info(name=org.jboss.logmanager
{
    "outcome" => "success",
    "result" => {
        "name" => "org.jboss.logmanager:main",
        "main-class" => undefined,
        "fallback-loader" => undefined,
        "dependencies" => [
            {
                "dependency-name" => "ModuleDependency",
                "module-name" => "javax.api:main",
                "export-filter" => "Reject",
                "import-filter" => "multi-path filter {exclude children of \"META-INF/\", exclude equals \"META-INF\", default accept}",
                "optional" => false
            },
            {
                "dependency-name" => "ModuleDependency",
                "module-name" => "org.jboss.modules:main",
                "export-filter" => "Reject",
                "import-filter" => "multi-path filter {exclude children of \"META-INF/\", exclude equals \"META-INF\", default accept}",
                "optional" => false
            }
        ],
        "local-loader-class" => undefined,
        "resource-loaders" => [
            {
                "type" => "org.jboss.modules.JarFileResourceLoader",
                "paths" => [
                    "",
                    "org/jboss/logmanager",
                    "META-INF/services",
                    "org",
                    "META-INF/maven/org.jboss.logmanager/jboss-logmanager",
                    "org/jboss",
                    "org/jboss/logmanager/errormanager",
                    "org/jboss/logmanager/formatters",
                    "META-INF",
                    "org/jboss/logmanager/filters",
                    "org/jboss/logmanager/config",
                    "META-INF/maven",
                    "org/jboss/logmanager/handlers",
                    "META-INF/maven/org.jboss.logmanager"
                ]
            },
            {
                "type" => "org.jboss.modules.NativeLibraryResourceLoader",
                "paths" => undefined
            }
        ]
    }
}

3.1.4. デプロイメントでのクラスローディング

JBoss EAP では、クラスローディングを行うために、デプロイメントはすべてモジュールとして処理されます。このようなデプロイメントは動的モジュールと呼ばれます。クラスローディングの動作はデプロイメントの種類によって異なります。

WAR デプロイメント
WAR デプロイメントは 1 つのモジュールとして考慮されます。WEB-INF/lib ディレクトリーのクラスは WEB-INF/classes ディレクトリーにあるクラスと同じように処理されます。WAR にパッケージされているクラスはすべて、同じクラスローダーでロードされます。
EAR デプロイメント

EAR デプロイメントは複数のモジュールで構成され、以下のルールに従って定義されます。

  1. EAR の lib/ ディレクトリーは親モジュールと呼ばれる 1 つのモジュールです。
  2. また、EAR 内の各 WAR デプロイメントは 1 つのモジュールです。
  3. 同様に、EAR 内の EJB JAR デプロイメントも 1 つのモジュールです。

サブデプロイメントモジュール (EAR 内の WAR および JAR デプロイメント) は、自動的に親モジュールに依存しますが、サブデプロイメントモジュール同士が自動的に依存するわけではありません。これは、サブデプロイメントの分離 (subdeployment isolation) と呼ばれ、デプロイメントごとまたはアプリケーションサーバー全体で無効にすることができます。

サブデプロイメントモジュール間の明示的な依存関係は、他のモジュールと同じ方法で追加することが可能です。

3.1.5. クラスローディングの優先順位

JBoss EAP のモジュール形式クラスローダーは、優先順位を決定してクラスローディングの競合が発生しないようにします。

デプロイメントに、パッケージとクラスの完全なリストがデプロイメントごとおよび依存関係ごとに作成されます。このリストは、クラスローディングの優先順位のルールに従って順序付けされます。実行時にクラスをロードすると、クラスローダーはこのリストを検索し、最初に一致したものをロードします。こうすることで、デプロイメントクラスパス内の同じクラスやパッケージの複数のコピーが競合しないようになります。

クラスローダーは上から順にクラスをロードします。

  1. 暗黙的な依存関係: これらの依存関係 (JAVA EE API など) は JBoss EAP によって自動的に追加されます。これらの依存関係には一般的な機能や JBoss EAP によって提供される API が含まれるため、これらの依存関係のクラスローダー優先順位は最も高くなります。

    暗黙的な各依存関係の完全な詳細については、暗黙的なモジュール依存関係 を参照してください。

  2. 明示的な依存関係: これらの依存関係は、アプリケーションの MANIFEST.MF ファイルや新しいオプションの JBoss デプロイメント記述子 jboss-deployment-structure.xml ファイルを使用してアプリケーション設定に手動で追加されます。

    明示的な依存関係の追加方法については、デプロイメントへの明示的なモジュール依存関係の追加を参照してください。

  3. ローカルリソース: これらはデプロイメント内にパッケージ化されるクラスファイル (例: WAR ファイルの WEB-INF/classes または WEB-INF/lib ディレクトリー内) です。
  4. デプロイメント間の依存関係: これらは EAR デプロイメントにある他のデプロイメントに対する依存関係です。これには、EAR の lib ディレクトリーにあるクラスや他の EJB jar で定義されたクラスが含まれることがあります。

3.1.6. 動的モジュールの命名規則

JBoss EAP では、すべてのデプロイメントが、以下の規則に従って名前が付けられたモジュールとしてロードされます。

  • WAR および JAR ファイルのデプロイメントは次の形式で名前が付けられます。

    deployment.DEPLOYMENT_NAME

    たとえば、inventory.warstore.jar のモジュール名はそれぞれ deployment.inventory.wardeployment.store.jar になります。

  • エンタープライズアーカイブ (EAR) 内のサブデプロイメントは次の形式で名前が付けられます。

    deployment.EAR_NAME.SUBDEPLOYMENT_NAME

    たとえば、エンタープライズアーカイブ accounts.ear 内にある reports.war のサブデプロイメントのモジュール名は deployment.accounts.ear.reports.war になります。

3.1.7. jboss-deployment-structure.xml

jboss-deployment-structure.xml は JBoss EAP のオプションのデプロイメント記述子です。このデプロイメント記述子を使用すると、デプロイメントでクラスローディングを制御できます。

このデプロイメント記述子の XML スキーマは、/docs/schema/jboss-deployment-structure-1_2.xsd にあります。

3.2. デプロイメントへの明示的なモジュール依存関係の追加

明示的なモジュール依存関係をアプリケーションに追加すると、これらのモジュールのクラスをデプロイメント時にアプリケーションのクラスパスに追加することができます。

注記

JBoss EAP では、依存関係がデプロイメントに自動的に追加されます。詳細については、暗黙的なモジュール依存関係を参照してください。

前提条件

  1. モジュールの依存関係を追加するソフトウェアプロジェクト。
  2. 依存関係として追加するモジュールの名前を知っている必要があります。JBoss EAP に含まれる静的モジュールのリストについては、含まれるモジュールを参照してください。モジュールが他のデプロイメントである場合は、動的モジュールの名前付けを参照してモジュール名を判断してください。

依存関係を設定するには、以下の 2 つの方法があります。

  • デプロイメントの MANIFEST.MF ファイルにエントリーを追加します。
  • jboss-deployment-structure.xml デプロイメント記述子にエントリーを追加します。

MANIFEST.MF への依存関係設定の追加

MANIFEST.MF ファイルの必要な依存関係エントリーを作成するよう Maven プロジェクトを設定できます。

  1. MANIFEST.MF という名前のファイルを作成します (プロジェクトにない場合)。Web アプリケーション (WAR) では、このファイルを META-INF ディレクトリーに追加します。EJB アーカイブ (JAR) では、このファイルを META-INF ディレクトリーに追加します。
  2. 依存関係モジュール名をコンマで区切り、依存関係エントリーを MANIFEST.MF ファイルへ追加します。

    Dependencies: org.javassist, org.apache.velocity, org.antlr
    • 依存関係をオプションにするには、依存関係エントリーのモジュール名に optional を付けます。

      Dependencies: org.javassist optional, org.apache.velocity
    • 依存関係エントリーのモジュール名に export を付けると、依存関係をエクスポートすることができます。

      Dependencies: org.javassist, org.apache.velocity export
    • annotations フラグは、EJB インターセプターを宣言するときなど、アノテーションのスキャン中に処理する必要があるアノテーションがモジュールの依存関係に含まれる場合に必要になります。この設定を行わないと、モジュールに宣言された EJB インターセプターをデプロイメントで使用できません。アノテーションのスキャンが関係するその他の状況でも、この設定が必要になる場合があります。

      Dependencies: org.javassist, test.module annotations
    • デフォルトでは、依存関係の META-INF 内のアイテムにはアクセスできません。services 依存関係により META-INF/services のアイテムにアクセスできるようになり、モジュール内の services をロードできるようになります。

      Dependencies: org.javassist, org.hibernate services
    • beans.xml ファイルをスキャンし、生成される Bean をアプリケーションが利用できるようにするために、meta-inf 依存関係を使用できます。

      Dependencies: org.javassist, test.module meta-inf

jboss-deployment-structure.xml への依存関係設定の追加

  1. jboss-deployment-structure.xml という名前の新しいファイルを作成し (アプリケーションにない場合)、プロジェクトに追加します。このファイルは <jboss-deployment-structure> がルート要素の XML ファイルです。

    <jboss-deployment-structure>
    
    </jboss-deployment-structure>

    Web アプリケーション (WAR) では、このファイルを WEB-INF ディレクトリーに追加します。EJB アーカイブ (JAR) では、このファイルを META-INF ディレクトリーに追加します。

  2. <deployment> 要素をドキュメントルート内に作成し、その中に <dependencies> 要素を作成します。
  3. <dependencies> ノード内に各モジュールの依存関係に対するモジュール要素を追加します。name 属性をモジュールの名前に設定します。

    <module name="org.javassist" />
    • 値が true のモジュールエントリーに optional 属性を追加することにより依存関係をオプションにすることができます。この属性のデフォルト値は false です。

      <module name="org.javassist" optional="true" />
    • 値が true のモジュールエントリーに export 属性を追加することにより依存関係をエクスポートできます。この属性のデフォルト値は false です。

      <module name="org.javassist" export="true" />
    • アノテーションのスキャン中に処理する必要があるアノテーションがモジュール依存関係に含まれる場合は、annotations フラグが使用されます。

      <module name="test.module" annotations="true" />
    • Services 依存関係は、この依存関係にある services を使用するかどうか、およびどのように使用するかを指定します。デフォルト値は none です。この属性に import の値を指定することは、依存関係モジュールの META-INF/services パスを含むインポートフィルターリストの最後にフィルターを追加することと同じです この属性に export の値を設定することは、エクスポートフィルターリストに対して同じアクションを実行することと同じです。

      <module name="org.hibernate" services="import" />
    • META-INF 依存関係は、この依存関係の META-INF エントリーを使用するかどうか、およびどのように使用するかを指定します。デフォルト値は none です。この属性に import の値を指定することは、依存関係モジュールの META-INF/** パスを含むインポートフィルターリストの最後にフィルターを追加することと同じです この属性に export の値を設定することは、エクスポートフィルターリストに対して同じアクションを実行することと同じです。

      <module name="test.module" meta-inf="import" />

例: 2 つの依存関係がある jboss-deployment-structure.xml

<jboss-deployment-structure>
   <deployment>
      <dependencies>
         <module name="org.javassist" />
         <module name="org.apache.velocity" export="true" />
      </dependencies>
   </deployment>
</jboss-deployment-structure>

JBoss EAP では、デプロイ時に、指定されたモジュールからアプリケーションのクラスパスにクラスが追加されます。

Jandex インデックスの作成

annotations フラグを使用するには、モジュールに Jandex インデックスが含まれる必要があります。JBoss EAP 7.0 では、これは自動的に生成されます。ただし、このインデックスを手動で追加する場合は、後方互換性を確保するために、モジュールに追加する新しい「index JAR」を作成します。Jandex JAR を使用してインデックスをビルドした後、新しい JAR ファイルに挿入します。

Jandex インデックスの作成:

  1. インデックスを作成します。

    java -jar modules/system/layers/base/org/jboss/jandex/main/jandex-jandex-2.0.0.Final-redhat-1.jar $JAR_FILE
  2. 一時作業領域を作成します。

    mkdir /tmp/META-INF
  3. インデックスファイルをワーキングディレクトリーへ移動します。

    mv $JAR_FILE.ifx /tmp/META-INF/jandex.idx
    1. オプション 1: インデックスを新しい JAR ファイルに含めます。

      jar cf index.jar -C /tmp META-INF/jandex.idx

      JAR をモジュールディレクトリーに置き、 module.xml を編集してリソースルートへ追加します。

    2. オプション 2: インデックスを既存の JAR に追加します。

      java -jar /modules/org/jboss/jandex/main/jandex-1.0.3.Final-redhat-1.jar -m $JAR_FILE
  4. アノテーションインデックスを使用するようモジュールインポートに指示し、アノテーションのスキャンでアノテーションを見つけられるようにします。

    1. オプション 1: MANIFEST.MF を使用してモジュールの依存関係を追加する場合は、annotations をモジュール名の後に追加します。たとえば、

      Dependencies: test.module, other.module

      を以下のように変更します。

      Dependencies: test.module annotations, other.module
    2. オプション 2: jboss-deployment-structure.xml を使用してモジュールの依存関係を追加する場合は、モジュールの依存関係に annotations="true" を追加します。

      注記

      静的モジュール内のクラスで定義されたアノテーション付き Java EE コンポーネントをアプリケーションで使用する場合は、アノテーションインデックスが必要です。JBoss EAP 7.0 では、静的モジュールのアノテーションインデックスは自動的に生成されるため、作成する必要がありません。ただし、MANIFEST.MF または jboss-deployment-structure.xml ファイルのいずれかに依存関係を追加して、アノテーションを使用するようモジュールインポートに指示する必要があります。

3.3. Maven を使用した MANIFEST.MF エントリーの生成

Maven JAR、EJB、または WAR パッケージングプラグインを使用する Maven プロジェクトでは、Dependencies エントリーを持つ MANIFEST.MF ファイルを生成することができます。この場合、依存関係の一覧は自動的に生成されず、pom.xml に指定された詳細が含まれる MANIFEST.MF ファイルのみが作成されます。

Maven を使用して MANIFEST.MF エントリーを生成する前に、以下のものが必要になります。

  • JAR、EJB、または WAR プラグイン (maven-jar-pluginmaven-ejb-plugin、または maven-war-plugin) のいずれかを使用している Maven プロジェクト。
  • プロジェクトのモジュール依存関係の名前を知っている必要があります。JBoss EAP に含まれる静的モジュールのリストについては、含まれるモジュールを参照してください。モジュールが他のデプロイメントである場合は、動的モジュールの名前付けを参照してモジュール名を判断してください。

モジュール依存関係が含まれる MANIFEST.MF ファイルの生成

  1. プロジェクトの pom.xml ファイルにあるパッケージングプラグイン設定に次の設定を追加します。

    <configuration>
       <archive>
          <manifestEntries>
             <Dependencies></Dependencies>
          </manifestEntries>
       </archive>
    </configuration>
  2. モジュール依存関係のリストを <Dependencies> 要素に追加します。MANIFEST.MF ファイルに依存関係を追加するときと同じ形式を使用します。

    <Dependencies>org.javassist, org.apache.velocity</Dependencies>

    ここでは、optional 属性と export 属性を使用することもできます。

    <Dependencies>org.javassist optional, org.apache.velocity export</Dependencies>
  3. Maven アセンブリーゴールを使用してプロジェクトをビルドします。

    [Localhost ]$ mvn assembly:single

    アセンブリーゴールを使用してプロジェクトをビルドすると、指定のモジュール依存関係を持つ MANIFEST.MF ファイルが最終アーカイブに含まれます。

    例: pom.xml で設定されたモジュール依存関係

    注記

    この例は WAR プラグインの例になりますが、JAR や EJB プラグイン (maven-jar-plugin や maven-ejb-plugin) でも動作します。

    <plugins>
       <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-war-plugin</artifactId>
          <configuration>
             <archive>
                <manifestEntries>
                   <Dependencies>org.javassist, org.apache.velocity</Dependencies>
                </manifestEntries>
             </archive>
          </configuration>
       </plugin>
    </plugins>

3.4. モジュールが暗黙的にロードされないようにする

暗黙的な依存関係がロードされないようデプロイ可能なアプリケーションを設定できます。これは、アプリケーションサーバーにより提供される暗黙的な依存関係とは異なるバージョンのライブラリーやフレームワークがアプリケーションに含まれる場合に役に立つことがあります。

前提条件

  • 暗黙的な依存関係を除外するソフトウェアプロジェクト。
  • 除外するモジュール名を知っている必要があります。暗黙的な依存関係のリストや状態については暗黙的なモジュール依存関係を参照してください。

jboss-deployment-structure.xml への依存関係除外設定の追加

  1. jboss-deployment-structure.xml という名前の新しいファイルを作成し (アプリケーションにない場合)、プロジェクトに追加します。このファイルは <jboss-deployment-structure> がルート要素の XML ファイルです。

    <jboss-deployment-structure>
    
    </jboss-deployment-structure>

    Web アプリケーション (WAR) では、このファイルを WEB-INF ディレクトリーに追加します。EJB アーカイブ (JAR) では、このファイルを META-INF ディレクトリーに追加します。

  2. <deployment> 要素をドキュメントルート内に作成し、その中に <exclusions> 要素を作成します。

    <deployment>
       <exclusions>
    
       </exclusions>
    </deployment>
  3. exclusions 要素内で、除外する各モジュールに対して <module> 要素を追加します。 name 属性をモジュールの名前に設定します。

    <module name="org.javassist" />

    例: 2 つのモジュールの除外

    <jboss-deployment-structure>
       <deployment>
          <exclusions>
             <module name="org.javassist" />
             <module name="org.dom4j" />
          </exclusions>
       </deployment>
    </jboss-deployment-structure>

3.5. サブシステムをデプロイメントから除外

サブシステムの除外は、サブシステムの削除と同じ効果がありますが、単一のデプロイメントにのみ適用されます。jboss-deployment-structure.xml 設定ファイルを編集することにより、デプロイメントからサブシステムを除外できます。

サブシステムの除外

  1. jboss-deployment-structure.xml ファイルを編集します。
  2. <deployment> タグ内に以下の XML を追加します。

    <exclude-subsystems>
      <subsystem name="SUBSYSTEM_NAME" />
    </exclude-subsystems>
  3. jboss-deployment-structure.xml ファイルを保存します。

サブシステムのデプロイメントユニットプロセッサーがデプロイメント上で実行されなくなります。

例: サンプル jboss-deployment-structure.xml ファイル

<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.2">
  <ear-subdeployments-isolated>true</ear-subdeployments-isolated>
  <deployment>
    <exclude-subsystems>
      <subsystem name="jaxrs" />
    </exclude-subsystems>
    <exclusions>
      <module name="org.javassist" />
    </exclusions>
    <dependencies>
      <module name="deployment.javassist.proxy" />
      <module name="deployment.myjavassist" />
      <module name="myservicemodule" services="import"/>
    </dependencies>
    <resources>
      <resource-root path="my-library.jar" />
    </resources>
  </deployment>
  <sub-deployment name="myapp.war">
    <dependencies>
      <module name="deployment.myear.ear.myejbjar.jar" />
    </dependencies>
    <local-last value="true" />
  </sub-deployment>
  <module name="deployment.myjavassist" >
    <resources>
     <resource-root path="javassist.jar" >
       <filter>
         <exclude path="javassist/util/proxy" />
       </filter>
     </resource-root>
    </resources>
  </module>
  <module name="deployment.javassist.proxy" >
    <dependencies>
      <module name="org.javassist" >
        <imports>
          <include path="javassist/util/proxy" />
          <exclude path="/**" />
        </imports>
      </module>
    </dependencies>
  </module>
</jboss-deployment-structure>

3.6. デプロイメントでのプログラムを用いたクラスローダーの使用

3.6.1. デプロイメントでのプログラムによるクラスおよびリソースのロード

プログラムを用いて、アプリケーションコードでクラスやリソースを検索またはロードできます。

Class.forName() メソッドを使用したクラスのロード

Class.forName() メソッドを使用すると、プログラムでクラスをロードおよび初期化できます。このメソッドには 2 つのシグネチャーがあります。

  • Class.forName(String className): このシグネチャーは、1 つのパラメーター (ロードする必要があるクラスの名前) のみを取ります。このメソッドシグネチャーを使用すると、現在のクラスのクラスローダーによってクラスがロードされ、デフォルトで新たにロードされたクラスが初期化されます。
  • Class.forName(String className, boolean initialize, ClassLoader loader): このシグネチャーは、クラス名、クラスを初期化するかどうかを指定するブール値、およびクラスをロードする ClassLoader の 3 つのパラメーターを想定します。

プログラムでクラスをロードする場合は、3 つの引数のシグネチャーを用いる方法が推奨されます。このシグネチャーを使用すると、ロード時に目的のクラスを初期化するかどうかを制御できます。また、JVM はコールスタックをチェックして、使用するクラスローダーを判断する必要がないため、クラスローダーの取得および提供がより効率的になります。コードが含まれるクラスの名前が CurrentClass である場合は、CurrentClass.class.getClassLoader() メソッドを使用してクラスのクラスローダーを取得できます。

例: ロードするクラスローダーを提供し、TargetClass を初期化する

以下は、ロードするクラスローダーを提供し、TargetClass クラスを初期化する例になります。

Class<?> targetClass = Class.forName("com.myorg.util.TargetClass", true, CurrentClass.class.getClassLoader());

名前ですべてのリソースを検索

リソースの名前とパスがわかり、直接そのリソースをロードする場合は、標準的な Java 開発キットクラスまたは ClassLoader API を使用するのが最良の方法です。

  • 単一リソースをロードする: ご使用のクラスと同じディレクトリーまたはデプロイメントの他のクラスと同じディレクトリーにある単一のリソースをロードする場合は、Class.getResourceAsStream() メソッドを使用できます。

    InputStream inputStream = CurrentClass.class.getResourceAsStream("targetResourceName");
  • 単一リソースのすべてのインスタンスをロードする: デプロイメントのクラスローダーが見える単一リソースのすべてのインスタンスをロードするには、Class.getClassLoader().getResources(String resourceName) メソッドを使用します。ここで、resourceName はリソースの完全修飾パスに置き換えます。このメソッドは、指定の名前でクラスローダーがアクセスできるリソースに対し、すべての URL オブジェクトの列挙を返します。その後、URL の配列で繰り返し処理し、openStream() メソッドを使用して各ストリームを開くことができます。

    例: リソースのすべてのインスタンスをロードし、結果で繰り返し処理を行う

    Enumeration<URL> urls = CurrentClass.class.getClassLoader().getResources("full/path/to/resource");
    while (urls.hasMoreElements()) {
        URL url = urls.nextElement();
        InputStream inputStream = null;
        try {
            inputStream = url.openStream();
            // Process the inputStream
            ...
        } catch(IOException ioException) {
            // Handle the error
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (Exception e) {
                    // ignore
                }
            }
        }
    }

URL インスタンスはローカルストレージからロードされるため、openConnection() や他の関連するメソッドを使用する必要はありません。ストリームは非常に簡単に使用でき、ストリームを使用することにより、コードの複雑さが最小限に抑えられます。

  • クラスローダーよりクラスファイルをロードする: クラスがすでにロードされている場合は、以下の構文を使用して、そのクラスに対応するクラスファイルをロードできます。

    例: ロードされたクラスのクラスファイルをロードする

    InputStream inputStream = CurrentClass.class.getResourceAsStream(TargetClass.class.getSimpleName() + ".class");

    クラスがロードされていない場合は、クラスローダーを使用し、パスを変換する必要があります。

    例: ロードされていないクラスのクラスファイルをロードする

    String className = "com.myorg.util.TargetClass"
    InputStream inputStream = CurrentClass.class.getClassLoader().getResourceAsStream(className.replace('.', '/') + ".class");

3.6.2. デプロイメントでのプログラムによるリソースの繰り返し

JBoss Modules ライブラリーは、すべてのデプロイメントリソースを繰り返し処理するために複数の API を提供します。JBoss Modules API の JavaDoc は http://docs.jboss.org/jbossmodules/1.3.0.Final/api/ にあります。これらの API を使用するには、以下の依存関係を MANIFEST.MF に追加する必要があります。

依存関係: org.jboss.modules

これらの API により柔軟性が向上しますが、直接のパスルックアップよりも動作がかなり遅くなることに注意してください。

このトピックでは、アプリケーションコードでプログラムを用いてリソースを繰り返す方法を説明します。

  • デプロイメント内およびすべてのインポート内のリソースをリストする: 場合によっては、正確なパスでリソースをルックアップできないことがあります。たとえば、正確なパスがわからなかったり、指定のパスで複数のファイルをチェックしたりする必要がある場合などです。このような場合、JBoss Modules ライブラリーはすべてのデプロイメントを繰り返し処理するための API を複数提供します。2 つのメソッドのいずれかを使用すると、デプロイメントでリソースを繰り返し処理できます。

    • 単一のモジュールで見つかったすべてのリソースを繰り返し処理する: ModuleClassLoader.iterateResources() メソッドは、このモジュールクラスローダー内のすべてのリソースを繰り返し処理します。このメソッドは、検索を開始するディレクトリーの名前と、サブディレクトリーで再帰的に処理するかどうかを指定するブール値の 2 つの引数を取ります。

      以下の例は、ModuleClassLoader の取得方法と、bin/ ディレクトリーにあるリソースのイテレーターの取得方法 (サブディレクトリーを再帰的に検索) を示しています。

例: サブディレクトリーを再帰的に検索し、bin ディレクトリーのリソースを検索する

ModuleClassLoader moduleClassLoader = (ModuleClassLoader) TargetClass.class.getClassLoader();
Iterator<Resource> mclResources = moduleClassLoader.iterateResources("bin",true);

取得されたイテレーターは、一致した各リソースをチェックし、名前とサイズのクエリー (可能な場合) を行うために使用できます。また、読み取り可能ストリームを開いたり、リソースの URL を取得するために使用できます。

  • 単一モジュールで見つかったすべてのリソースとインポートされたリソースを繰り返し処理する: Module.iterateResources() メソッドは、このモジュールクラスローダー内のすべてのリソース (モジュールにインポートされたリソースを含む) を繰り返し処理します。このメソッドは、前述のメソッドよりもはるかに大きなセットを返します。このメソッドには、特定パターンの結果を絞り込むフィルターである引数が必要になります。代わりに、PathFilters.acceptAll() を指定してセット全体を返すことも可能です。

例: このモジュールで、インポートを含むすべてのリソースを検索する

ModuleClassLoader moduleClassLoader = (ModuleClassLoader) TargetClass.class.getClassLoader();
Module module = moduleClassLoader.getModule();
Iterator<Resource> moduleResources = module.iterateResources(PathFilters.acceptAll());
  • パターンと一致するすべてのリソースを検索する: デプロイメント内またはデプロイメントの完全なインポートセット内で特定のリソースのみを見つける必要がある場合は、リソースの繰り返しをフィルターする必要があります。JBoss Modules のフィルター API は、リソースの繰り返しをフィルターする複数のツールを提供します。

    • 依存関係の完全なセットをチェックする: 依存関係の完全なセットをチェックする必要がある場合は、Module.iterateResources() メソッドの PathFilter パラメーターを使用して、一致する各リソースの名前を確認できます。
    • デプロイメント依存関係を確認する: デプロイメント内のみを検索する必要がある場合は、ModuleClassLoader.iterateResources() メソッドを使用しますが、追加のメソッドを使用して結果となるイテレーターをフィルターする必要があります。PathFilters.filtered() メソッドは、リソースイテレーターのフィルターされたビューを提供できます。PathFilters クラスには、さまざまな関数を実行するフィルターを作成する多くの静的メソッドが含まれています。これには、子パスや完全一致の検索、Ant 形式の「glob」パターンの一致などが含まれます。
  • リソースのフィルターに関する追加コード例: 以下の例は、異なる基準を基にリソースをフィルターする方法を示しています。

例: デプロイメントで messages.properties という名前のファイルをすべて検索する

ModuleClassLoader moduleClassLoader = (ModuleClassLoader) TargetClass.class.getClassLoader();
Iterator<Resource> mclResources = PathFilters.filtered(PathFilters.match("**/messages.properties"), moduleClassLoader.iterateResources("", true));

例: デプロイメントおよびインポートで messages.properties という名前のファイルをすべて検索する

ModuleClassLoader moduleClassLoader = (ModuleClassLoader) TargetClass.class.getClassLoader();
Module module = moduleClassLoader.getModule();
Iterator<Resource> moduleResources = module.iterateResources(PathFilters.match("**/message.properties"));

例: デプロイメントで my-resources という名前のディレクトリー内にあるすべてのファイルを検索する

ModuleClassLoader moduleClassLoader = (ModuleClassLoader) TargetClass.class.getClassLoader();
Iterator<Resource> mclResources = PathFilters.filtered(PathFilters.match("**/my-resources/**"), moduleClassLoader.iterateResources("", true));

例: デプロイメントおよびインポートで message または errors という名前のファイルをすべて検索する

ModuleClassLoader moduleClassLoader = (ModuleClassLoader) TargetClass.class.getClassLoader();
Module module = moduleClassLoader.getModule();
Iterator<Resource> moduleResources = module.iterateResources(PathFilters.any(PathFilters.match("**/messages"), PathFilters.match("**/errors"));

例: デプロイメントで特定パッケージにあるすべてのファイルを検索する

ModuleClassLoader moduleClassLoader = (ModuleClassLoader) TargetClass.class.getClassLoader();
Iterator<Resource> mclResources = moduleClassLoader.iterateResources("path/form/of/packagename", false);

3.7. クラスローディングとサブデプロイメント

3.7.1. エンタープライズアーカイブのモジュールおよびクラスロード

エンタープライズアーカイブ (EAR) は、JAR または WAR デプロイメントのように、単一モジュールとしてロードされません。これらは、複数の一意のモジュールとしてロードされます。

以下のルールによって、EAR に存在するモジュールが決定されます。

  • EAR アーカイブのルートにある lib/ ディレクトリーの内容はモジュールです。これは、親モジュールと呼ばれます。
  • 各 WAR および EJB JAR サブデプロイメントはモジュールです。これらのモジュールの動作は、他のモジュールおよび親モジュールの暗黙的な依存関係と同じです。
  • サブデプロイメントでは、親モジュールとすべての他の非 WAR サブデプロイメントに暗黙的な依存関係が存在します。

JBoss EAP では、サブデプロイメントクラスローダーの分離がデフォルトで無効になるため、非 WAR サブデプロイメントの暗黙的な依存関係が発生します。

重要

サブデプロイメントでは、WAR サブデプロイメントに暗黙的な依存関係が存在しません。他のモジュールと同様に、サブデプロイメントは、別のサブデプロイメントの明示的な依存関係で設定できます。

サブデプロイメントクラスローダーの分離は、厳密な互換性が必要な場合に有効にできます。これは、単一の EAR デプロイメントまたはすべての EAR デプロイメントに対して有効にできます。Java EE 6 の仕様では、依存関係が各サブデプロイメントの MANIFEST.MF ファイルの Class-Path エントリーとして明示的に宣言されている場合を除き、移植可能なアプリケーションがお互いにアクセスできるサブデプロイメントに依存しないことが推奨されます。

3.7.2. サブデプロイメントクラスローダーの分離

エンタープライズアーカイブ (EAR) の各サブデプロイメントは独自のクラスローダーを持つ動的モジュールです。デフォルトでは、サブデプロイメントは他のサブデプロイメントのリソースにアクセスできます。

サブデプロイメントが他のサブデプロイメントのリソースにアクセスすることが許可されていない場合は、厳格なサブデプロイメントの分離を有効にできます。

3.7.3. EAR 内のサブデプロイメントクラスローダーの分離を有効にする

このタスクでは、EAR の特別なデプロイメント記述子を使用して EAR デプロイメントのサブデプロイメントクラスローダーの分離を有効にする方法を示します。アプリケーションサーバーを変更する必要はなく、他のデプロイメントは影響を受けません。

重要

サブデプロイメントクラスローダーの分離が無効であっても、WAR を依存関係として追加することはできません。

  1. デプロイメント記述子ファイルを追加する: jboss-deployment-structure.xml デプロイメント記述子ファイルを EAR の META-INF ディレクトリーへ追加し (このファイルが存在しない場合)、次の内容を追加します。

    <jboss-deployment-structure>
    
    </jboss-deployment-structure>
  2. <ear-subdeployments-isolated> 要素を追加する: <ear-subdeployments-isolated> 要素を jboss-deployment-structure.xml ファイルへ追加し (この要素が存在しない場合)、内容が true となるようにします。

    <ear-subdeployments-isolated>true</ear-subdeployments-isolated>

結果

この EAR デプロイメントに対してサブデプロイメントクラスローダーの分離が有効になります。つまり、EAR のサブデプロイメントは WAR ではないサブデプロイメントごとに自動的な依存関係を持ちません。

3.7.4. エンタープライズアーカイブのサブデプロイメント間で共有するセッションの設定

JBoss EAP では、EAR に含まれる WAR モジュールサブデプロイメント間でセッションを共有するようエンタープライズアーカイブ (EAR) を設定する機能が提供されます。この機能はデフォルトで無効になり、EAR の META-INF/jboss-all.xml ファイルで明示的に有効にする必要があります。

重要

この機能は標準的サーブレット機能ではないため、この機能が有効な場合はアプリケーションを移植できいないことがあります。

EAR 内の WAR 間で共有するセッションを有効にするには、EAR の META-INF/jboss-all.xmlshared-session-config 要素を宣言する必要があります。

META-INF/jboss-all.xml の例

<jboss umlns="urn:jboss:1.0">
  ...
  <shared-session-config xmlns="urn:jboss:shared-session-config:1.0">
  </shared-session-config>
  ...
</jboss>

shared-session-config 要素は、EAR 内のすべての WAR に対して共有セッションマネージャーを設定するために使用されます。shared-session-config 要素が存在する場合は、EAR 内のすべての WAR で同じセッションマネージャーが共有されます。ここで行われる変更は、EAR 内に含まれる すべての WAR に影響します。

3.7.4.1. 共有セッション設定オプションのリファレンス

shared-session-config 要素の構造は以下のとおりです。

  • shared-session-config

    • max-active-sessions
    • session-config

      • session-timeout
      • cookie-config

        • name
        • domain
        • path
        • comment
        • http-only
        • secure
        • max-age
      • tracking-mode
    • replication-config

      • cache-name
      • replication-granularity

META-INF/jboss-all.xml の例

<jboss umlns="urn:jboss:1.0">
  <shared-session-config xmlns="urn:jboss:shared-session-config:1.0">
    <max-active-sessions>10</max-active-sessions>
    <session-config>
      <session-timeout>0</session-timeout>
      <cookie-config>
        <name>JSESSIONID</name>
        <domain>domainName</domain>
        <path>/cookiePath</path>
        <comment>cookie comment</comment>
        <http-only>true</http-only>
        <secure>true</secure>
        <max-age>-1</max-age>
      </cookie-config>
    </session-config>
    <tracking-mode>COOKIE</tracking-mode>
  </shared-session-config>
  <replication-config>
    <cache-name>web</cache-name>
    <replication-granularity>SESSION</replication-granularity>
  </replication-config>
</jboss>

shared-session-config

共有セッション設定のルート要素。この要素が META-INF/jboss-all.xml に存在しない場合は、EAR に含まれるすべてのデプロイ済み WAR で単一のセッションマネージャーが共有されます。

max-active-sessions

許可される最大セッション数。

session-config

EAR に含まれるすべてのデプロイ済み WAR に対するセッション設定パラメーターを含みます。

session-timeout

EAR に含まれるデプロイ済み WAR で作成されたすべてのセッションに対するデフォルトのセッションタイムアウト間隔を定義します。指定されたタイムアウトは、分単位の整数で表記する必要があります。タイムアウトが 0 またはそれよりも小さい値である場合は、コンテナーにより、セッションのデフォルトの動作がタイムアウトしなくなります。この要素が指定されない場合は、コンテナーでデフォルトのタイムアウト期間を設定する必要があります。

cookie-config

EAR に含まれるデプロイ済み WAR により作成されたセッション追跡クッキーを含みます。

name

EAR に含まれるデプロイ済み WAR により作成されたセッション追跡クッキーに割り当てられる名前。デフォルト値は JSESSIONID です。

domain

EAR に含まれるデプロイ済み WAR により作成されたセッション追跡クッキーに割り当てられるドメイン名。

path

EAR に含まれるデプロイ済み WAR により作成されたセッション追跡クッキーに割り当てられるパス。

comment

EAR に含まれるデプロイ済み WAR により作成されたセッション追跡クッキーに割り当てられるコメント。

http-only

EAR に含まれるデプロイ済み WAR により作成されたセッション追跡クッキーを HttpOnly とマークするかどうかを指定します。

secure

対応するセッションを開始した要求が HTTPS ではなくプレーンな HTTP を使用している場合であっても、EAR に含まれるデプロイ済み WAR により作成されたセッション追跡クッキーをセキュアとマークするかどうかを指定します。

max-age

EAR に含まれるデプロイ済み WAR により作成されたセッション追跡クッキーに割り当てられる有効期間 (秒単位)。デフォルト値は -1 です。

tracking-mode

EAR に含まれるデプロイ済み WAR により作成されたセッションの追跡モードを定義します。

replication-config

HTTP セッションクラスタリング設定を含みます。

cache-name

このオプションは、クラスタリング専用です。セッションデータを格納する Infinispan コンテナーとキャッシュの名前を指定します。デフォルト値 (明示的に設定されていない場合) は、アプリケーションサーバーによって決定されます。キャッシュコンテナー内で特定のキャッシュを使用するには、 container.cache という形式 (たとえば、web.dist) を使用します。名前が修飾されてない場合は、指定されたコンテナーのデフォルトのキャッシュが使用されます。

replication-granularity

このオプションはクラスタリング専用です。セッションレプリケーションの粒度を決定します。可能な値は SESSIONATTRIBUTE であり、デフォルト値は SESSION です。

SESSION 粒度が使用される場合は、すべてのセッション属性がレプリケートされます (要求のスコープ内でいずれかのセッション属性が変更された場合)。このポリシーは、オブジェクト参照が複数のセッション属性で共有されるときに必要になります。ただし、セッション属性が非常に大きい場合や頻繁に変更されない場合は非効率になることがあります。これは、属性が変更されたかどうかに関係なく、すべての属性をレプリケートする必要があるためです。

ATTRIBUTE 粒度が使用される場合は、要求のスコープ内で変更された属性のみがレプリケートされます。オブジェクト参照が複数のセッション属性で共有される場合、このポリシーは適切ではありません。セッション属性が非常に大きい場合や頻繁に変更されない場合は SESSION よりも効率的になることがあります。

3.8. カスタムモードでのタグライブラリー記述子 (TLD) のデプロイ

共通のタグライブラリー記述子 (TLD) を使用する複数のアプリケーションがある場合、アプリケーションから TLD を分離し、一元的で一意な場所に置くと有用であることがあります。これにより、TLD を使用するアプリケーションごとに更新を行う必要がなくなり、TLD への追加や更新が簡単になります。

これを行うには、TLD JAR が含まれるカスタム JBoss EAP モジュールを作成し、アプリケーションでそのモジュールの依存関係を宣言します。

注記

少なくとも 1 つの JAR に TLD が含まれ、TLD が META-INF に含まれるようにします。

カスタムモジュールでの TLD のデプロイ

  1. 管理 CLI を使用して、JBoss EAP インスタンスへ接続し、以下のコマンドを実行して TLD JAR が含まれるカスタムモジュールを作成します。

    module add --name=MyTagLibs --resources=/path/to/TLDarchive.jar
    注記

    module 管理 CLI コマンドを使用したモジュールの追加および削除は、技術プレビューとしてのみ提供されています。本番稼働環境では、モジュールは手動で追加および削除する必要があります。詳細については、JBoss EAP Configuration Guide の項 Create a Custom Module Manually を参照してください。

    TLD が依存関係を必要とするクラスとともにパッケージ化されている場合は、--dependencies= オプションを使用して、カスタムモジュールの作成時にこれらの依存関係を指定するようにします。

    モジュールを作成するときに、システムのファイルシステム固有の区切り文字を使用して複数の JAR リソースを指定できます。

    • Linux の場合 - : 例、--resources=<path-to-jar>:<path-to-another-jar>
    • Windows の場合 - : 例、--resources=<path-to-jar>;<path-to-another-jar>

      注記
      --resources
      これは --module-xml を使用しない限り必要です。ファイルシステム固有のパス区切り文字 (たとえば、java.io.File.pathSeparatorChar) で区切られたファイルシステムパス (通常は JAR ファイル) をリストします。指定されたファイルは作成されたモジュールのディレクトリーにコピーされます。
      --resource-delimiter
      これは、リソース引数のオプションのユーザー定義パス区切り文字です。この引数が存在する場合、コマンドパーサーはファイルシステム固有のパス区切り文字の代わりにその値を使用します。これにより、modules コマンドをクロスプラットフォームのスクリプトで使用できるようになります。
  2. ご使用のアプリケーションで、デプロイメントへの明示的なモジュール依存関係の追加で説明されているいずれかの方法を使用して新しい MyTagLibs カスタムモジュールの依存関係を宣言します。
重要

依存関係を宣言するときは必ず META-INF もインポートしてください。たとえば、MANIFEST.MF の場合は以下のようになります。

Dependencies: com.MyTagLibs meta-inf

jboss-deployment-structure.xml の場合は、meta-inf 属性を使用してください。

3.9. 参考情報

3.9.1. 暗黙的なモジュール依存関係

以下の表には、依存関係としてデプロイメントに自動的に追加されるモジュールと、依存関係をトリガーする条件が記載されています。

表3.1 暗黙的なモジュール依存関係

依存関係を追加するサブシステム常に追加されるパッケージ依存関係条件的に追加されるパッケージ依存関係依存関係の追加を引き起こす条件

アプリケーションクライアント

  • org.omg.api
  • org.jboss.xnio
  

Batch

  • javax.batch.api
  • org.jberet.jberet-core
  • org.wildfly.jberet
  

Bean の検証

  • org.hibernate.validator
  • javax.validation.api
  

コアサーバー

  • javax.api
  • sun.jdk
  • org.jboss.vfs
  • ibm.jdk
  

DriverDependenciesProcessor

 
  • javax.transaction.api
 

EE

  • org.jboss.invocation (org.jboss.invocation.proxy.classloading を除く)
  • org.jboss.as.ee (org.jboss.as.ee.component.serializationorg.jboss.as.ee.concurrentorg.jboss.as.ee.concurrent.handle を除く)
  • org.wildfly.naming
  • javax.annotation.api
  • javax.enterprise.concurrent.api
  • javax.interceptor.api
  • javax.json.api
  • javax.resource.api
  • javax.rmi.api
  • javax.xml.bind.api
  • javax.api
  • org.glassfish.javax.el
  • org.glassfish.javax.enterprise.concurrent
  

EJB 3

  • javax.ejb.api
  • javax.xml.rpc.api
  • org.jboss.ejb-client
  • org.jboss.iiop-client
  • org.jboss.as.ejb3
  • org.wildfly.iiop-openjdk
 

IIOP

  • org.omg.api
  • javax.rmi.api
  • javax.orb.api
  

JAX-RS (RESTEasy)

  • javax.xml.bind.api
  • javax.ws.rs.api
  • javax.json.api
  • org.jboss.resteasy.resteasy-atom-provider
  • org.jboss.resteasy.resteasy-crypto
  • org.jboss.resteasy.resteasy-validator-provider-11
  • org.jboss.resteasy.resteasy-jaxrs
  • org.jboss.resteasy.resteasy-jaxb-provider
  • org.jboss.resteasy.resteasy-jackson2-provider
  • org.jboss.resteasy.resteasy-jsapi
  • org.jboss.resteasy.resteasy-json-p-provider
  • org.jboss.resteasy.resteasy-multipart-provider
  • org.jboss.resteasy.resteasy-yaml-provider
  • org.codehaus.jackson.jackson-core-asl
  • org.jboss.resteasy.resteasy-cdi

デプロイメントに JAX-RS アノテーションが存在すること。

JCA

  • javax.resource.api
  • javax.jms.api
  • javax.validation.api
  • org.jboss.ironjacamar.api
  • org.jboss.ironjacamar.impl
  • org.hibernate.validator

リソースアダプター (RAR) アーカイブのデプロイメント。

JPA (Hibernate)

  • javax.persistence.api
  • org.jboss.as.jpa
  • org.jboss.as.jpa.spi
  • org.javassist

@PersistenceUnit または @PersistenceContext アノテーションが存在するか、デプロイメント記述子に <persistence-unit-ref> または <persistence-context-ref> 要素が存在すること。

JBoss EAP は永続プロバイダー名をモジュール名にマップします。persistence.xml ファイルで特定のプロバイダーに名前を付けると、適切なモジュールに対して依存関係が追加されます。これが希望の挙動ではない場合は、jboss-deployment-structure.xml を使用して除外できます。

JSF (Java Server Faces)

 
  • javax.faces.api
  • com.sun.jsf-impl
  • org.jboss.as.jsf
  • org.jboss.as.jsf-injection

EAR アプリケーションに追加されます。

値が trueorg.jboss.jbossfaces.WAR_BUNDLES_JSF_IMPLcontext-paramweb.xml ファイルで指定しない場合のみ WAR アプリケーションに追加されます。

JSR-77

  • javax.management.j2ee.api
  

ロギング

  • org.jboss.logging
  • org.apache.commons.logging
  • org.apache.log4j
  • org.slf4j
  • org.jboss.logging.jul-to-slf4j-stub
  

メール

  • javax.mail.api
  • javax.activation.api
  

メッセージング

  • javax.jms.api
  • org.wildfly.extension.messaging-activemq
 

PicketLink Federation

 
  • org.picketlink
 

Pojo

  • org.jboss.as.pojo
  

SAR

 
  • org.jboss.modules
  • org.jboss.as.system-jmx
  • org.jboss.common-beans

jboss-service.xml を含む SAR アーカイブのデプロイメント。

Seam2

 
  • org.jboss.vfs

.

セキュリティー

  • org.picketbox
  • org.jboss.as.security
  • javax.security.jacc.api
  • javax.security.auth.message.api
  

ServiceActivator

 
  • org.jboss.msc
 

トランザクション

  • javax.transaction.api
  • org.jboss.xts
  • org.jboss.jts
  • org.jboss.narayana.compensations
 

Undertow

  • javax.servlet.jstl.api
  • javax.servlet.api
  • javax.servlet.jsp.api
  • javax.websocket.api
  • io.undertow.core
  • io.undertow.servlet
  • io.undertow.jsp
  • io.undertow.websocket
  • io.undertow.js
  • org.wildfly.clustering.web.api
 

Web Services

  • javax.jws.api
  • javax.xml.soap.api
  • javax.xml.ws.api
  • org.jboss.ws.api
  • org.jboss.ws.spi

アプリケーションクライアントタイプでない場合は、条件付き依存関係が追加されます。

Weld (CDI)

  • javax.enterprise.api
  • javax.inject.api
  • javax.persistence.api
  • org.javassist
  • org.jboss.as.weld
  • org.jboss.weld.core
  • org.jboss.weld.probe
  • org.jboss.weld.api
  • org.jboss.weld.spi
  • org.hibernate.validator.cdi

デプロイメントに beans.xml ファイルが存在すること。

3.9.2. 含まれるモジュール

含まれるモジュールの完全なリストとこれらのモジュールがサポートされているかについては、Red Hat カスタマーポータルの Red Hat JBoss Enterprise Application Platform 7 Included Modules を参照してください。

3.9.3. JBoss デプロイメント構造のデプロイメント記述子

このデプロイメント記述子を使用して実行できる主なタスクは次のとおりです。

  • 明示的なモジュール依存関係を定義する。
  • 特定の暗黙的な依存関係がロードされないようにする。
  • デプロイメントのリソースより追加モジュールを定義する。
  • EAR デプロイメントのサブデプロイメント分離の挙動を変更する。
  • EAR のモジュールに追加のリソースルートを追加する。

第4章 ロギング

4.1. ロギング

ロギングとはアクティビティの記録 (ログ) を提供するアプリケーションからのメッセージ群を記録することです。

ログメッセージは、アプリケーションをデバッグする開発者や実稼働環境のアプリケーションを維持するシステム管理者に対して重要な情報を提供します。

ほとんどの最新の Java のロギングフレームワークには、正確な時間やメッセージの発信元などの詳細も含まれます。

4.1.1. サポート対象のアプリケーションロギングフレームワーク

JBoss LogManager は次のロギングフレームワークをサポートします。

JBoss LogManager では以下の API がサポートされます。

  • JBoss Logging
  • commons-logging
  • SLF4J
  • Log4j
  • java.util.logging

JBoss LogManager では以下の SPI もサポートされます。

  • java.util.logging Handler
  • Log4j Appender
注記

Log4j APILog4J Appender を使用している場合、オブジェクトは渡される前に string に変換されます。

4.2. JJBoss Logging Framework を用いたロギング

4.2.1. JBoss Logging について

JBoss Logging は、JBoss EAP に含まれるアプリケーションロギングフレームワークです。JBoss Logging を使用すると、簡単にロギングをアプリケーションに追加できます。また、フレームワークを使用するアプリケーションにコードを追加し、定義された形式でログメッセージを送信できます。アプリケーションサーバーにアプリケーションがデプロイされると、これらのメッセージをサーバーでキャプチャーしたり、サーバーの設定に基づいて表示したり、ファイルに書き込んだりできます。

JBoss Logging では次の機能が提供されます。

  • 革新的で使いやすい型指定されたロガー。型指定されたロガーは org.jboss.logging.annotations.MessageLogger でアノテートされたロガーインターフェースです。例については、国際化されたロガー、メッセージ、例外の作成を参照してください。
  • 国際化およびローカリゼーションの完全なサポート。翻訳者は properties ファイルのメッセージバンドルを、開発者はインターフェースやアノテーションを使い作業を行います。詳細については、国際化と現地語化を参照してください。
  • 実稼働用の型指定されたロガーを生成し、開発用の型指定されたロガーを実行時に生成する構築時ツール。

4.2.2. JBoss Logging を使用したアプリケーションへのロギングの追加

この手順では、JBoss Logging を使用してアプリケーションにロギングを追加する方法を示します。

重要

Maven を使用してプロジェクトをビルドする場合は、JBoss EAP Maven リポジトリーを使用するよう Maven を設定する必要があります。詳細については、JBoss EAP Maven リポジトリーの設定を参照してください。

  1. JBoss Logging JAR ファイルがアプリケーションのビルドパスに指定されている必要があります。

    • Red Hat JBoss Developer Studio を使用してビルドする場合は、Project メニューから Properties を選択し、Targeted Runtimes を選択して JBoss EAP のランタイムにチェックが付けられていることを確認します。
    • Maven を使用してプロジェクトをビルドする場合は、JBoss Logging フレームワークにアクセスするために jboss-logging 依存関係をプロジェクトの pom.xml ファイルに追加します。

      <dependency>
      	<groupId>org.jboss.logging</groupId>
      	<artifactId>jboss-logging</artifactId>
      	<version>3.3.0.Final-redhat-1</version>
      	<scope>provided</scope>
      </dependency>

      jboss-javaee-7.0 BOM は jboss-logging のバージョンを管理します。詳細については、プロジェクト依存関係の管理を参照してください。アプリケーションでのロギングの例については、logging クイックスタートを参照してください。

    JAR は、JBoss EAP がデプロイされたアプリケーションに提供するため、ビルドされたアプリケーションに含める必要はありません。

  2. ロギングを追加する各クラスに対して、以下の手順を実行します。

    1. 使用する JBoss Logging クラスネームスペースに対して import ステートメントを追加します。少なくとも、以下の import ステートメントが必要です。

      import org.jboss.logging.Logger;
    2. org.jboss.logging.Logger のインスタンスを作成し、静的メソッド Logger.getLogger(Class) を呼び出して初期化します。各クラスに対してこれを単一のインスタンス変数として作成することが推奨されます。

      private static final Logger LOGGER = Logger.getLogger(HelloWorld.class);
  3. ログメッセージを送信するコードの Logger オブジェクトメソッドを呼び出します。

    Logger には、異なるタイプのメッセージに対して異なるパラメーターを持つさまざまなメソッドがあります。以下のメソッドを使用して対応するログレベルのログメッセージと message パラメーターを文字列として送信します。

    LOGGER.debug("This is a debugging message.");
    LOGGER.info("This is an informational message.");
    LOGGER.error("Configuration file not found.");
    LOGGER.trace("This is a trace message.");
    LOGGER.fatal("A fatal error occurred.");

    JBoss Logging メソッドの完全なリストについては、Logging API ドキュメンテーションを参照してください。

JBoss Logging の例

次の例では、プロパティーファイルからアプリケーションのカスタマイズされた設定がロードされます。指定されたファイルが見つからない場合は、ERROR レベルログメッセージが記録されます。

import org.jboss.logging.Logger;
public class LocalSystemConfig
{
   private static final Logger LOGGER = Logger.getLogger(LocalSystemConfig.class);

   public Properties openCustomProperties(String configname) throws CustomConfigFileNotFoundException
   {
      Properties props = new Properties();
      try
      {
         LOGGER.info("Loading custom configuration from "+configname);
         props.load(new FileInputStream(configname));
      }
      catch(IOException e) //catch exception in case properties file does not exist
      {
         LOGGER.error("Custom configuration file ("+configname+") not found. Using defaults.");
         throw new CustomConfigFileNotFoundException(configname);
      }

      return props;
   }
}

4.3. デプロイメントごとのロギング

デプロイメントごとのロギングを使用すると、開発者はアプリケーションのロギング設定を事前に設定できます。アプリケーションがデプロイされると、定義された設定に従ってロギングが開始されます。この設定によって作成されたログファイルにはアプリケーションの動作に関する情報のみが含まれます。

注記

デプロイメントごとのロギング設定が行われない場合、すべてのアプリケーションとサーバーには logging サブシステムの設定が使用されます。

この方法では、システム全体のロギングを使用する利点と欠点があります。利点は、JBoss EAP インスタンスの管理者がサーバーロギング以外のロギングを設定する必要がないことです。欠点は、デプロイメントごとのロギング設定はサーバーの起動時に読み取り専用であるため、実行時に変更できないことです。

4.3.1. デプロイメントごとのロギングをアプリケーションに追加

アプリケーションへのデプロイメントごとのロギングを設定するには、logging.properties 設定ファイルをデプロイメントに追加します。この設定ファイルは、JBoss Log Manager が基礎となるログマネージャーであるどのロギングファサードとも使用できるため、推奨されます。

設定ファイルが追加されるディレクトリーは、デプロイメント方法によって異なります。

  • EAR デプロイメントの場合は、ロギング設定ファイルを META-INF ディレクトリーにコピーします。
  • WAR または JAR デプロイメントの場合は、ロギング設定ファイルを WEB-INF/classes ディレクトリーにコピーします。
注記

Simple Logging Facade for Java (SLF4J) または Apache log4j を使用している場合は、logging.properties 設定ファイルが適しています。Apache log4j アペンダーを使用している場合は、log4j.properties 設定ファイルが必要になります。jboss-logging.properties 設定ファイルはレガシーデプロイメントのみでサポートされます。

logging.properties の設定

logging.properties ファイルはサーバーが起動し、logging サブシステムが起動するまで使用されます。logging サブシステムが設定に含まれない場合、サーバーはこのファイルの設定をサーバー全体のロギング設定として使用します。

JBoss ログマネージャーの設定オプション

ロガーオプション

  • loggers=<category>[,<category>,…​] - 設定するロガーカテゴリーのコンマ区切りのリストを指定します。ここにリストされていないカテゴリーは、以下のプロパティーから設定されません。
  • logger.<category>.level=<level> - カテゴリーのレベルを指定します。このレベルは有効なレベルのいずれかになります。指定されない場合は、最も近い親のレベルが継承されます。
  • logger.<category>.handlers=<handler>[,<handler>,…​] - このロガーに割り当てるハンドラー名のコンマ区切りのリストを指定します。ハンドラーは、同じプロパティーファイルに設定する必要があります。
  • logger.<category>.filter=<filter> - カテゴリーのフィルターを指定します。
  • logger.<category>.useParentHandlers=(true|false) - ログメッセージを親ハンドラーにカスケードするかどうかを指定します。デフォルト値は true です。

ハンドラーオプション

  • handler.<name>=<className> - インスタンス化するハンドラーのクラス名を指定します。このオプションは必須です。
  • handler.<name>.level=<level> - このハンドラーのレベルを制限します。指定されない場合は、ALL のデフォルト値が保持されます。
  • handler.<name>.encoding=<encoding> - 文字エンコーディングを指定します (このハンドラータイプによりサポートされている場合)。指定されない場合は、ハンドラー固有のデフォルト値が使用されます。
  • handler.<name>.errorManager=<name> - 使用するエラーマネージャーの名前を指定します。エラーマネージャーは同じプロパティーファイルで設定する必要があります。指定されない場合は、エラーマネージャーが設定されません。
  • handler.<name>.filter=<name> - カテゴリーのフィルターを指定します。フィルターの定義の詳細については、フィルター式を参照してください。
  • handler.<name>.formatter=<name> - 使用するフォーマッターの名前を指定します (このハンドラータイプによりサポートされている場合)。フォーマッターは同じプロパティーファイルで設定する必要があります。指定されない場合、ほとんどのハンドラータイプのメッセージはログに記録されません。
  • handler.<name>.properties=<property>[,<property>,…​] - 追加的に設定する JavaBean 形式のプロパティーを指定します。該当するプロパティーが適切に変換されるように、基本的なタイプイントロスペクションが行われます。
  • handler.<name>.constructorProperties=<property>[,<property>,…​] - 構築パラメーターとして使用する必要があるプロパティーのリストを指定します。該当するプロパティーが適切に変換されるように、基本的なタイプイントロスペクションが行われます。
  • handler.<name>.<property>=<value> - 名前付きプロパティーの値を設定します。

詳細については、JBoss EAP 設定ガイドログハンドラー属性を参照してください。

エラーマネージャーオプション

  • errorManager.<name>=<className> - インスタンス化するエラーマネージャーのクラス名を指定します。このオプションは必須です。
  • errorManager.<name>.properties=<property>[,<property>,…​] - 追加的に設定する JavaBean 形式のプロパティーを指定します。該当するプロパティーが適切に変換されるように、基本的なタイプイントロスペクションが行われます。
  • errorManager.<name>.<property>=<value> - 名前付きプロパティーの値を設定します。

フォーマッターオプション

  • formatter.<name>=<className> - インスタンス化するフォーマッターのクラス名を指定します。このオプションは必須です。
  • formatter.<name>.properties=<property>[,<property>,…​] - 追加的に設定する JavaBean 形式のプロパティーを指定します。該当するプロパティーが適切に変換されるように、基本的なタイプイントロスペクションが行われます。
  • formatter.<name>.constructorProperties=<property>[,<property>,…​] - 構築パラメーターとして使用する必要があるプロパティーのリストを指定します。該当するプロパティーが適切に変換されるように、基本的なタイプイントロスペクションが行われます。
  • formatter.<name>.<property>=<value> - 名前付きプロパティーの値を設定します。

以下の例は、コンソールにログ記録する logging.properties ファイルの最低限の設定を示しています。

# Additional logger names to configure (root logger is always configured)
# loggers=

# Root logger level
logger.level=INFO

# Root logger handlers
logger.handlers=CONSOLE

# Console handler configuration
handler.CONSOLE=org.jboss.logmanager.handlers.ConsoleHandler
handler.CONSOLE.properties=autoFlush
handler.CONSOLE.autoFlush=true
handler.CONSOLE.formatter=PATTERN

# Formatter pattern configuration
formatter.PATTERN=org.jboss.logmanager.formatters.PatternFormatter
formatter.PATTERN.properties=pattern
formatter.PATTERN.pattern=%K{level}%d{HH:mm:ss,SSS} %-5p %C.%M(%L) [%c] %s%e%n

4.4. ロギングプロファイル

ロギングプロファイルは、デプロイされたアプリケーションに割り当てることができる独立したロギング設定のセットです。通常の logging サブシステム同様にロギングプロファイルはハンドラー、カテゴリー、およびルートロガーを定義できますが、他のプロファイルや主要な logging サブシステムを参照できません。設定が容易である点でロギングプロファイルは logging サブシステムと似ています。

ログインプロファイルを使用すると、管理者は他のロギング設定に影響を与えずに 1 つ以上のアプリケーションに固有なロギング設定を作成することができます。各プロファイルはサーバー設定で定義されるため、影響を受けるアプリケーションを再デプロイせずに、ロギング設定を変更できます。ただし、ロギングプロファイルは管理コンソールを使用して設定できません。詳細については、JBoss EAP Configuration GuideConfigure a Logging Profile を参照してください。

各ロギングプロファイルには以下の項目を設定できます。

  • 一意な名前 (必須)
  • 任意の数のログハンドラー
  • 任意の数のログカテゴリー
  • 最大 1 つのルートロガー

アプリケーションでは Logging-Profile 属性を使用して、MANIFEST.MF ファイルで使用するロギングプロファイルを指定できます。

4.4.1. アプリケーションでのロギングプロファイルの指定

アプリケーションでは、使用するロギングプロファイルを MANIFEST.MF ファイルで指定できます。

注記

このアプリケーションが使用するサーバー上に設定されたロギングプロファイルの名前を知っている必要があります。

ロギングプロファイル設定をアプリケーションに追加するには、MANIFEST.MF ファイルを編集します。

  • アプリケーションに MANIFEST.MF ファイルがない場合は、ロギングプロファイル名を指定する以下の内容が含まれるファイルを作成します。

    Manifest-Version: 1.0
    Logging-Profile: LOGGING_PROFILE_NAME
  • アプリケーションに MANIFEST.MF ファイルがすでにある場合は、ロギングプロファイル名を指定する以下の行を追加します。

    Logging-Profile: LOGGING_PROFILE_NAME
注記

Maven および maven-war-plugin を使用している場合は、MANIFEST.MF ファイルを src/main/resources/META-INF/ に置き、次の設定を pom.xml ファイルに追加します。

<plugin>
  <artifactId>maven-war-plugin</artifactId>
  <configuration>
    <archive>
      <manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile>
    </archive>
  </configuration>
</plugin>

アプリケーションがデプロイされると、ログメッセージに対して指定されたロギングプロファイルの設定が使用されます。

ロギングプロファイルとアプリケーションの設定方法の例については、JBoss EAP Configuration GuideExample Logging Profile Configuration を参照してください。

4.5. 国際化と現地語化

4.5.1. はじめに

4.5.1.1. 国際化

国際化とは、技術的な変更を行わずに異なる言語や地域に対してソフトウェアを適合させるソフトウェア設計のプロセスのことです。

4.5.1.2. 多言語化

多言語化とは、特定の地域や言語に対してロケール固有のコンポーネントやテキストの翻訳を追加することで、国際化されたソフトウェアを適合させるプロセスのことです。

4.5.2. JBoss Logging Tools の国際化および現地語化

JBoss Logging Tools は、ログメッセージ、例外メッセージ、および汎用文字列の国際化や現地語化のサポートを提供する Java API です。JBoss Logging Tools は翻訳のメカニズムを提供するだけでなく、各ログメッセージに対して一意な識別子のサポートも提供します。

国際化されたメッセージと例外は、org.jboss.logging.annotations アノテーションが付けられたインターフェース内でメソッド定義として作成されます。インターフェースを実装する必要はありません。JBoss Logging Tools がコンパイル時にインターフェースを実装します。定義すると、これらのメソッドを使用してコードでメッセージをログに記録したり、例外オブジェクトを取得したりできます。

JBoss Logging Tools によって作成される国際化されたロギングインターフェースや例外インターフェースは、特定の言語や地域に対する翻訳が含まれる各バンドルのプロパティーファイルを作成して現地語化されます。JBoss Logging Tools は、トランスレーターが編集できる各バンドル対してテンプレートプロパティーファイルを生成できます。

JBoss Logging Tools は、プロジェクトの対象翻訳プロパティーファイルごとに各バンドルの実装を作成します。必要なのはバンドルに定義されているメソッドを使用することのみで、JBoss Logging Tools は現在の地域設定に対して正しい実装が呼び出されるようにします。

メッセージ ID とプロジェクトコードは各ログメッセージの前に付けられる一意の識別子です。この一意の識別子をドキュメントで使用すると、ログメッセージの情報を簡単に検索することができます。適切なドキュメントでは、メッセージが書かれた言語に関係なく、ログメッセージの意味を識別子から判断できます。

JBoss Logging Tools には次の機能のサポートが含まれます。

MessageLogger
org.jboss.logging.annotations パッケージ内のこのインターフェースは、国際化されたログメッセージを定義するために使用されます。メッセージロガーインターフェースは @MessageLogger でアノテートされます。
MessageBundle
このインターフェースは、翻訳可能な汎用メッセージと国際化されたメッセージが含まれる例外オブジェクトを定義するために使用できます。メッセージバンドルインターフェースは、@MessageBundle でアノテートされます。
国際化されたログメッセージ

これらのログメッセージは、MessageLogger のメソッドを定義して作成されます。メソッドは @LogMessage アノテーションと @Message アノテーションを付け、@Message の値属性を使用してログメッセージを指定する必要があります。国際化されたログメッセージはプロパティーファイルで翻訳を提供することによりローカライズされます。

JBoss Logging Tools はコンパイル時に各翻訳に必要なロギングクラスを生成し、ランタイム時に現ロケールに対して適切なメソッドを呼び出します。

国際化された例外
国際化された例外は、MessageBundle で定義されたメソッドから返された例外オブジェクトです。これらのメッセージバンドルは、デフォルトの例外メッセージを定義するためにアノテートできます。デフォルトのメッセージは、現在のロケールと一致するプロパティーファイルに翻訳がある場合にその翻訳に置き換えられます。国際化された例外にも、プロジェクトコードとメッセージ ID を割り当てることができます。
国際化されたメッセージ
国際化されたメッセージは、MessageBundle で定義されたメソッドから返された文字列です。Java String オブジェクトを返すメッセージバンドルメソッドは、その文字列のデフォルトの内容 (メッセージと呼ばれます) を定義するためにアノテートできます。デフォルトのメッセージは、現在のロケールと一致するプロパティーファイルに翻訳がある場合にその翻訳に置き換えられます。
翻訳プロパティーファイル
翻訳プロパティーファイルは、1 つのロケール、国、バリアントに対する 1 つのインターフェースのメッセージの翻訳が含まれる Java プロパティーファイルです。翻訳プロパティーファイルは、メッセージを返すクラスを生成するために JBoss Logging Tools によって使用されます。
JBoss Logging Tools のプロジェクトコード

プロジェクトコードはメッセージのグループを識別する文字列です。プロジェクトコードは各ログメッセージの最初に表示され、メッセージ ID の前に付けられます。プロジェクトコードは @MessageLogger アノテーションの projectCode 属性で定義されます。

注記

新しいログメッセージプロジェクトコード接頭辞の完全なリストについては、JBoss EAP 7.0 で使用されているプロジェクトコードを参照してください。

JBoss Logging Tools のメッセージ ID
メッセージ ID はプロジェクトコードと組み合わせてログメッセージを一意に識別する数字です。メッセージ ID は各ログメッセージの最初に表示され、メッセージのプロジェクトコードの後に付けられます。メッセージ ID は @Message アノテーションの ID 属性で定義されます。

JBoss EAP に同梱される logging-tools クイックスタートは、JBoss Logging Tools の多くの機能の例を提供する単純な Maven プロジェクトです。以降のコード例は、logging-tools クイックスタートから取得されました。

4.5.3. 国際化されたロガー、メッセージ、例外の作成

4.5.3.1. 国際化されたログメッセージの作成

JBoss Logging Tools を使用して MessageLogger インターフェースを作成することにより、国際化されたログメッセージを作成できます。

注記

このトピックでは、ログメッセージのすべてのオプション機能または国際化について説明しません。

  1. JBoss EAP Maven レポジトリーを使用するよう Maven を設定します (まだそのように設定していない場合)。

    詳細については、Maven 設定を使用した JBoss EAP Maven リポジトリーの設定を参照してください。

  2. JBoss Logging Tools を使用するようプロジェクトの pom.xml ファイルを設定します。

    詳細については、JBoss Logging Tools の Maven 設定を参照してください。

  3. ログメッセージ定義を含めるために Java インターフェースをプロジェクトに追加して、メッセージロガーインターフェースを作成します。

    定義するログメッセージの内容がわかるようインターフェースに名前を付けます。ログメッセージインターフェースの要件は次のとおりです。

    • @org.jboss.logging.annotations.MessageLogger でアノテートする必要があります。
    • オプションで、org.jboss.logging.BasicLogger を拡張できます。
    • インターフェースと同じ型のメッセージロガーであるフィールドをインターフェースで定義する必要があります。これは、@org.jboss.logging.LoggergetMessageLogger() を使用して行います。

      ロガーインターフェースのコード例

      package com.company.accounts.loggers;
      
      import org.jboss.logging.BasicLogger;
      import org.jboss.logging.Logger;
      import org.jboss.logging.annotations.MessageLogger;
      
      @MessageLogger(projectCode="")
      interface AccountsLogger extends BasicLogger {
         AccountsLogger LOGGER = Logger.getMessageLogger(
               AccountsLogger.class,
               AccountsLogger.class.getPackage().getName() );
      }

  4. 各ログメッセージのインターフェースにメソッド定義を追加します。

    ログメッセージの各メソッドにその内容を表す名前を付けます。各メソッドの要件は次のとおりです。

    • メソッドは void を返す必要があります。
    • @org.jboss.logging.annotation.LogMessage アノテーションでアノテートする必要があります。
    • @org.jboss.logging.annotations.Message アノテーションでアノテートする必要があります。
    • デフォルトのログレベルは INFO です。
    • @org.jboss.logging.annotations.Message の値属性にはデフォルトのログインメッセージが含まれます。このメッセージは翻訳がない場合に使用されます。

      @LogMessage
      @Message(value = "Customer query failed, Database not available.")
      void customerQueryFailDBClosed();
  5. メッセージをログに記録する必要があるコードで呼び出しをインターフェースメソッドに追加してメソッドを呼び出します。

    インターフェースの実装を作成する必要はありません。これは、プロジェクトがコンパイルされる時にアノテーションプロセッサーにより行われます。

    AccountsLogger.LOGGER.customerQueryFailDBClosed();

    カスタムのロガーは BasicLogger からサブクラス化されるため、BasicLogger のロギングメソッドを使用することもできます。国際化されていないメッセージをログに記録するために他のロガーを作成する必要はありません。

    AccountsLogger.LOGGER.error("Invalid query syntax.");
  6. プロジェクトで、現地語化できる 1 つ以上の国際化されたロガーがサポートされるようになります。
注記

JBoss EAP に同梱される logging-tools クイックスタートは、JBoss Logging Tools の使用例を提供する単純な Maven プロジェクトです。

4.5.3.2. 国際化されたメッセージの作成と使用

この手順では、国際化された例外を作成および使用する方法を示します。

注記

本項では、これらのメッセージの現地語化に関するすべてのオプション機能またはプロセスについて説明しません。

  1. JBoss EAP Maven レポジトリーを使用するよう Maven を設定します (まだそのように設定していない場合)。詳細については、Maven 設定を使用した JBoss EAP Maven リポジトリーの設定を参照してください。
  2. JBoss Logging Tools を使用するようプロジェクトの pom.xml ファイルを設定します。詳細については、JBoss Logging Tools の Maven 設定を参照してください。
  3. 例外のインターフェースを作成します。JBoss Logging Tools はインターフェースで国際化されたメッセージを定義します。含まれるメッセージのインターフェースにその内容を表す名前を付けます。インターフェースの要件は以下のとおりです。

    • public として宣言する必要があります。
    • @org.jboss.logging.annotations.MessageBundle でアノテートする必要があります。
    • インターフェースと同じ型のメッセージバンドルであるフィールドをインターフェースが定義する必要があります。

      インターフェースのコード例

      @MessageBundle(projectCode="")
      public interface GreetingMessageBundle {
         GreetingMessageBundle MESSAGES = Messages.getBundle(GreetingMessageBundle.class);
      }

      注記

      Messages.getBundle(GreetingMessagesBundle.class) を呼び出すのは Messages.getBundle(GreetingMessagesBundle.class, Locale.getDefault()) を呼び出すのと同等です。

      Locale.getDefault() は、Java Virtual Machine のこのインスタンスのデフォルトロケールに対する現在の値を取得します。起動時に、ホストの環境に基づいて Java Virtual Machine によりデフォルトのロケールが設定されます。これは、ロケールが明示的に指定されない場合に、ロケールに関連する多くのメソッドにより使用され、setDefault メソッドを使用して変更できます。

  4. 各メッセージのインターフェースにメソッド定義を追加します。メッセージに対する各メソッドにその内容を表す名前を付けます。各メソッドの要件は以下のとおりです。

    • String のオブジェクトを返す必要があります。
    • @org.jboss.logging.annotations.Message アノテーションでアノテートする必要があります。
    • デフォルトメッセージに @org.jboss.logging.annotations.Message の値属性を設定する必要があります。翻訳がない場合にこのメッセージが使用されます。

      メソッド定義のコード例

      @Message(value = "Hello world.")
      String helloworldString();

  5. メッセージを取得する必要があるアプリケーションでインターフェースメソッドを呼び出します。

    メソッド呼び出しのコード例

    System.out.println(helloworldString());

プロジェクトで、現地語化できる国際化されたメッセージ文字列がサポートされるようになります。

注記

使用できる完全な例については、JBoss EAP に同梱される logging-tools クイックスタートを参照してください。

4.5.3.3. 国際化された例外の作成

JBoss Logging Tools を使用して、国際化された例外を作成および使用できます。

以下の手順では、Red Hat JBoss Developer Studio または Maven のいずれかを使用してビルドされた既存のソフトウェアプロジェクトに、国際化された例外を追加することを前提としています。

注記

このトピックでは、これらの例外の国際化に関するすべてのオプション機能またはプロセスについて説明しません。

  1. JBoss Logging Tools を使用するようプロジェクトの pom.xml ファイルを設定します。詳細については、JBoss Logging Tools の Maven 設定を参照してください。
  2. 例外のインターフェースを作成します。JBoss Logging Tools はインターフェースで国際化されたメッセージを定義します。定義される例外のインターフェースにその内容を表す名前を付けます。インターフェースの要件は以下のとおりです。

    • public として宣言する必要があります。
    • @MessageBundle でアノテートする必要があります。
    • インターフェースと同じ型のメッセージバンドルであるフィールドをインターフェースが定義する必要があります。

      @MessageBundle(projectCode="")
      public interface ExceptionBundle {
         ExceptionBundle EXCEPTIONS = Messages.getBundle(ExceptionBundle.class);
      }
  3. 各例外のインターフェースにメソッド定義を追加します。例外に対する各メソッドにその内容を表す名前を付けます。各メソッドの要件は以下のとおりです。

    • Exception オブジェクトまたは Exception のサブタイプを返す必要があります。
    • @org.jboss.logging.annotations.Message アノテーションでアノテートする必要があります。
    • デフォルトの例外メッセージに @org.jboss.logging.annotations.Message の値属性を設定する必要があります。このメッセージは翻訳がない場合に使用されます。
    • メッセージ文字列の他にパラメーターを必要とするコンストラクターが返される例外にある場合は、@Param アノテーションを使用してこれらのパラメーターをメソッド定義に提供する必要があります。パラメーターは、例外のコンストラクターと同じ型および順番である必要があります。

      @Message(value = "The config file could not be opened.")
      IOException configFileAccessError();
      
      @Message(id = 13230, value = "Date string '%s' was invalid.")
      ParseException dateWasInvalid(String dateString, @Param int errorOffset);
  4. 例外を取得する必要があるコードでインターフェースメソッドを呼び出します。メソッドによって例外はスローされませんが、スローできる例外オブジェクトがメソッドによって返されます。

    try {
       propsInFile=new File(configname);
       props.load(new FileInputStream(propsInFile));
    }
    catch(IOException ioex) {
      //in case props file does not exist
       throw ExceptionBundle.EXCEPTIONS.configFileAccessError();
    }

プロジェクトで、現地語化できる国際化された例外がサポートされるようになります。

注記

使用できる完全な例については、JBoss EAP に同梱される logging-tools クイックスタートを参照してください。

4.5.4. 国際化されたロガー、メッセージ、例外の現地語化

4.5.4.1. Maven での新しい翻訳プロパティーファイルの作成

Maven で構築されたプロジェクトでは、含まれる MessageLoggerMessageBundle それぞれに対して空の翻訳プロパティーファイルを生成できます。これらのファイルは新しい翻訳プロパティーファイルとして使用することができます。

新しい翻訳プロパティーファイルを生成するよう Maven プロジェクトを設定する手順は次のとおりです。

前提条件

  • 作業用の Maven プロジェクトがすでに存在している必要があります。
  • JBoss Logging Tools に対してプロジェクトが設定されていなければなりません。
  • 国際化されたログメッセージや例外を定義する 1 つ以上のインターフェースがプロジェクトに含まれていなければなりません。

翻訳プロパティーファイルの生成

  1. -AgenereatedTranslationFilePath コンパイラー引数を Maven コンパイラープラグイン設定に追加し、新しいファイルが作成されるパスを割り当てます。

    この設定では、Maven プロジェクトの target/generated-translation-files ディレクトリーに新しいファイルが作成されます。

    <plugin>
       <groupId>org.apache.maven.plugins</groupId>
       <artifactId>maven-compiler-plugin</artifactId>
       <version>2.3.2</version>
       <configuration>
          <source>1.6</source>
          <target>1.6</target>
          <compilerArgument>
          -AgeneratedTranslationFilesPath=${project.basedir}/target/generated-translation-files
          </compilerArgument>
          <showDeprecation>true</showDeprecation>
       </configuration>
    </plugin>
  2. Maven を使用してプロジェクトをビルドします。

    $ mvn compile

    @MessageBundle または @MessageLogger でアノテートされた各インターフェースに対して 1 つのプロパティーファイルが作成されます。

    • 各インターフェースが宣言された Java パッケージに対応するサブディレクトリーに新しいファイルが作成されます。
    • 新しい各ファイルには、以下のパターンで名前が付けられます。ここで、INTERFACE_NAME はファイルを生成するために使用するインターフェースの名前です。

      INTERFACE_NAME.i18n_locale_COUNTRY_VARIANT.properties

生成されたファイルは新しい翻訳の基礎としてプロジェクトにコピーできます。

注記

使用できる完全な例については、JBoss EAP に同梱される logging-tools クイックスタートを参照してください。

4.5.4.2. 国際化されたロガー、例外、またはメッセージの翻訳

プロパティーファイルは、JBoss Logging Tools を使用してインターフェースで定義されたロギングおよび例外メッセージの翻訳を提供します。

次の手順は、翻訳プロパティーファイルの作成方法と使用方法を示しています。この手順では、国際化された例外またはログメッセージに対して 1 つ以上のインターフェースがすでに定義されているプロジェクトが存在することを前提にしています。

前提条件

  • 作業用の Maven プロジェクトがすでに存在している必要があります。
  • JBoss Logging Tools に対してプロジェクトが設定されていなければなりません。
  • 国際化されたログメッセージや例外を定義する 1 つ以上のインターフェースがプロジェクトに含まれていなければなりません。
  • テンプレート翻訳プロパティーファイルを生成するようプロジェクトが設定されている必要があります。

国際化されたロガー、例外、またはメッセージの翻訳

  1. 以下のコマンドを実行して、テンプレート翻訳プロパティーファイルを作成します。

    $ mvn compile
  2. 翻訳したいインターフェースのテンプレートを、テンプレートが作成されたディレクトリーからプロジェクトの src/main/resources ディレクトリーにコピーします。プロパティーファイルは翻訳するインターフェースと同じパッケージに存在する必要があります。
  3. GreeterLogger.i18n_fr_FR.properties のように、含まれる言語を示すように、コピーされたテンプレートファイルの名前を変更します。
  4. 新しい翻訳プロパティーファイルの内容を編集し、適切な翻訳が含まれるようにします。

    # Level: Logger.Level.INFO
    # Message: Hello message sent.
    logHelloMessageSent=Bonjour message envoyé.
  5. テンプレートをコピーし、バンドルの各翻訳のために変更するプロセスを繰り返します。

プロジェクトに 1 つ以上のメッセージバンドルまたはロガーバンドルに対する翻訳が含まれるようになります。プロジェクトをビルドすると、提供された翻訳が含まれるログメッセージに対して適切なクラスが生成されます。JBoss Logging Tools は、アプリケーションサーバーの現在のロケールに合わせて適切なクラスを自動的に使用するため、明示的にメソッドを呼び出したり、特定言語のパラメーターを提供したりする必要はありません。

生成されたクラスのソースコードは target/generated-sources/annotations/ で確認できます。

4.5.5. 国際化されたログメッセージのカスタマイズ

4.5.5.1. ログメッセージへのメッセージ ID とプロジェクトコードの追加

この手順は、メッセージ ID とプロジェクトコードを JBoss Logging Tools を使用して作成された国際化済みログメッセージへ追加する方法を示しています。ログメッセージがログで表示されるようにするには、プロジェクトコードとメッセージ ID の両方が必要です。メッセージにプロジェクトコードとメッセージ ID の両方がない場合は、どちらも表示されません。

前提条件

  1. 国際化されたログメッセージが含まれるプロジェクトが存在する必要があります。国際化されたログメッセージの作成を参照してください。
  2. 使用するプロジェクトコードを知っている必要があります。プロジェクトコードを 1 つ使用することも、各インターフェースに異なる複数のコードを定義することも可能です。

ログメッセージへのメッセージ ID とプロジェクトコードの追加

  1. カスタムのロガーインターフェースに付けられる @MessageLogger アノテーションの projectCode 属性を使用してプロジェクトコードを指定します。インターフェースに定義されるすべてのメッセージがこのプロジェクトコードを使用します。

    @MessageLogger(projectCode="ACCNTS")
    interface AccountsLogger extends BasicLogger {
    
    }
  2. メッセージを定義するメソッドに付けられる @Message アノテーションの id 属性を使用して、各メッセージのメッセージ ID を指定します。

    @LogMessage
    @Message(id=43, value = "Customer query failed, Database not available.")  void customerQueryFailDBClosed();
  3. メッセージ ID とプロジェクトコードの両方が関連付けられたログメッセージでは、メッセージ ID とプロジェクトコードがログに記録されたメッセージの前に付けられます。

    10:55:50,638 INFO  [com.company.accounts.ejb] (MSC service thread 1-4) ACCNTS000043: Customer query failed, Database not available.

4.5.5.2. メッセージのログレベル設定

JBoss Logging Tools のインターフェースによって定義されるメッセージのデフォルトのログレベルは INFO です。ロギングメソッドに付けられた @LogMessage アノテーションの level 属性を用いて異なるログレベルを指定することが可能です。異なるログレベルを指定するには、以下の手順を実行します。

  1. ログメッセージメソッド定義の @LogMessage アノテーションに level 属性を追加します。
  2. level 属性を使用してこのメッセージにログレベルを割り当てます。level の有効値は org.jboss.logging.Logger.Level で定義された DEBUGERRORFATALINFOTRACE、および WARN の 6 つの列挙定数です。

    import org.jboss.logging.Logger.Level;
    
    @LogMessage(level=Level.ERROR)
    @Message(value = "Customer query failed, Database not available.")
    void customerQueryFailDBClosed();

上記の例のロギングメソッドを呼び出すと、ERROR レベルのログメッセージが作成されます。

10:55:50,638 ERROR  [com.company.app.Main] (MSC service thread 1-4)
 Customer query failed, Database not available.

4.5.5.3. パラメーターによるログメッセージのカスタマイズ

カスタムのログインメソッドはパラメーターを定義できます。これらのパラメーターを使用してログメッセージに表示される追加情報を渡すことが可能です。ログメッセージでパラメーターが表示される場所は、明示的なインデクシングか通常のインデクシングを使用してメッセージ自体に指定されます。

パラメーターによるログメッセージのカスタマイズ

  1. すべての型のパラメーターをメソッド定義に追加します。型に関係なくパラメーターの String 表現がメッセージに表示されます。
  2. ログメッセージにパラメーター参照を追加します。参照は明示的なインデックスまたは通常のインデックスを使用できます。

    • 通常のインデックスを使用するには、各パラメーターを表示したいメッセージ文字列に %s 文字を挿入します。%s の最初のインスタンスにより最初のパラメーターが挿入され、2 番目のインスタンスにより 2 番目のパラメーターが挿入されます。
    • 明示的なインデックスを使用するには、文字 %#$s をメッセージに挿入します。ここで、# は表示したいパラメーターの数を示します。

明示的なインデックスを使用すると、メッセージのパラメーター参照の順番がメソッドで定義される順番とは異なるようになります。これは、異なるパラメーターの順番が必要になる可能性がある翻訳済みメッセージで重要になります。

重要

指定されたメッセージでは、パラメーターの数とパラメーターへの参照の数が同じでなければなりません。同じでないとコードがコンパイルされません。@Cause アノテーションが付けられたパラメーターはパラメーターの数には含まれません。

以下に、通常のインデックスを使用したメッセージパラメーターの例を示します。

@LogMessage(level=Logger.Level.DEBUG)
@Message(id=2, value="Customer query failed, customerid:%s, user:%s")
void customerLookupFailed(Long customerid, String username);

以下に、明示的なインデックスを使用したメッセージパラメーターの例を示します。

@LogMessage(level=Logger.Level.DEBUG)
@Message(id=2, value="Customer query failed, user:%2$s, customerid:%1$s")
void customerLookupFailed(Long customerid, String username);

4.5.5.4. 例外をログメッセージの原因として指定

JBoss Logging Tools では、カスタムログインメソッドのパラメーターの 1 つをメッセージの原因として定義することができます。定義するには、このパラメーターを Throwable 型またはいずれかのサブクラスにし、@Cause アノテーションを付ける必要があります。このパラメーターは、他のパラメーターのようにログメッセージで参照することはできず、ログメッセージの後に表示されます。

次の手順は、@Cause パラメーターを使用して「原因となる」例外を示し、ロギングメソッドを更新する方法を表しています。この機能に追加したい国際化されたロギングメッセージがすでに作成されていることを前提とします。

例外をログメッセージの原因として指定

  1. Throwable 型のパラメーターまたはサブクラスをメソッドに追加します。

    @LogMessage
    @Message(id=404, value="Loading configuration failed. Config file:%s")
    void loadConfigFailed(Exception ex, File file);
  2. パラメーターに @Cause アノテーションを追加します。

    import org.jboss.logging.annotations.Cause
    
    @LogMessage
    @Message(value = "Loading configuration failed. Config file: %s")
    void loadConfigFailed(@Cause Exception ex, File file);
  3. メソッドを呼び出します。コードでメソッドが呼び出されると、正しい型のオブジェクトが渡され、ログメッセージの後に表示されます。

    try
    {
       confFile=new File(filename);
       props.load(new FileInputStream(confFile));
    }
    catch(Exception ex) //in case properties file cannot be read
    {
         ConfigLogger.LOGGER.loadConfigFailed(ex, filename);
    }

コードによって FileNotFoundException 型の例外が発生した場合、上記コード例の出力は次のようになります。

10:50:14,675 INFO [com.company.app.Main] (MSC service thread 1-3) Loading configuration failed. Config file: customised.properties
java.io.FileNotFoundException: customised.properties (No such file or directory)
   at java.io.FileInputStream.open(Native Method)
   at java.io.FileInputStream.<init>(FileInputStream.java:120)
   at com.company.app.demo.Main.openCustomProperties(Main.java:70)
   at com.company.app.Main.go(Main.java:53)
   at com.company.app.Main.main(Main.java:43)

4.5.6. 国際化された例外のカスタマイズ

4.5.6.1. メッセージ ID およびプロジェクトコードの例外メッセージへの追加

メッセージ ID およびプロジェクトコードは、国際化された例外によって表示される各メッセージの前に付けられる一意の識別子です。これらの識別コードによって、アプリケーションのすべての例外メッセージの参照を作成できるため、理解できない言語で書かれた例外メッセージの意味を検索できます。

以下の手順は、JBoss Logging Tools を使用して作成された国際化済み例外メッセージにメッセージ ID とプロジェクトコードを追加する方法を示しています。

前提条件

  1. 国際化された例外が含まれるプロジェクトが存在する必要があります。詳細については、国際化された例外の作成を参照してください。
  2. 使用するプロジェクトコードを知っている必要があります。プロジェクトコードを 1 つ使用することも、各インターフェースに異なる複数のコードを定義することも可能です。

メッセージ ID およびプロジェクトコードの例外メッセージへの追加

  1. 例外バンドルインターフェースに付けられる @MessageBundle アノテーションの projectCode 属性を使用して、プロジェクトコードを指定します。インターフェースに定義されるすべてのメッセージがこのプロジェクトコードを使用します。

    @MessageBundle(projectCode="ACCTS")
    interface ExceptionBundle
    {
       ExceptionBundle EXCEPTIONS = Messages.getBundle(ExceptionBundle.class);
    }
  2. 例外を定義するメソッドに付けられる @Message アノテーションの id 属性を使用して、各例外に対してメッセージ ID を指定します。

    @Message(id=143, value = "The config file could not be opened.")
    IOException configFileAccessError();
重要

プロジェクトコードとメッセージ ID を両方持つメッセージでは、メッセージの前にプロジェクトコードとメッセージ ID が表示されます。プロジェクトコードとメッセージ ID の両方がない場合は、どちらも表示されません。

国際化された例外の例

以下の例外バンドルインターフェースの例では、プロジェクトコードが "ACCTS" であり、ID が "143" の例外メソッドが 1 つあります。

@MessageBundle(projectCode="ACCTS")
interface ExceptionBundle
{
    ExceptionBundle EXCEPTIONS = Messages.getBundle(ExceptionBundle.class);

    @Message(id=143, value = "The config file could not be opened.")
    IOException configFileAccessError();
}

次のコードを使用すると、例外オブジェクトを取得およびスローできます。

throw ExceptionBundle.EXCEPTIONS.configFileAccessError();

これにより、次のような例外メッセージが表示されます。

Exception in thread "main" java.io.IOException: ACCTS000143: The config file could not be opened.
at com.company.accounts.Main.openCustomProperties(Main.java:78)
at com.company.accounts.Main.go(Main.java:53)
at com.company.accounts.Main.main(Main.java:43)

4.5.6.2. パラメーターによる例外メッセージのカスタマイズ

例外を定義する例外バンドルメソッドでは、パラメーターを指定して例外メッセージに表示される追加情報を渡すことが可能です。例外メッセージでのパラメーターの正確な位置は、明示的なインデックスまたは通常のインデックスを使用してメッセージ自体に指定されます。

パラメーターによる例外メッセージのカスタマイズ

  1. すべての型のパラメーターをメソッド定義に追加します。型に関係なくパラメーターの String 表現がメッセージに表示されます。
  2. 例外メッセージにパラメーター参照を追加します。参照は明示的なインデックスまたは通常のインデックスを使用できます。

    • 通常のインデックスを使用するには、各パラメーターを表示したいメッセージ文字列に %s 文字を挿入します。%s の最初のインスタンスにより最初のパラメーターが挿入され、2 番目のインスタンスにより 2 番目のパラメーターが挿入されます。
    • 明示的なインデックスを使用するには、文字 %#$s をメッセージに挿入します。ここで、# は表示したいパラメーターの数を示します。

明示的なインデックスを使用すると、メッセージのパラメーター参照の順番がメソッドで定義される順番とは異なるようになります。これは、異なるパラメーターの順番が必要になる可能性がある翻訳済みメッセージで重要になります。

重要

指定されたメッセージでは、パラメーターの数とパラメーターへの参照の数が同じでなければなりません。同じでないとコードがコンパイルされません。@Cause アノテーションが付けられたパラメーターはパラメーターの数には含まれません。

以下に、通常のインデックスを使用したメッセージパラメーターの例を示します。

@Message(id=2, value="Customer query failed, customerid:%s, user:%s")
void customerLookupFailed(Long customerid, String username);

以下に、明示的なインデックスを使用したメッセージパラメーターの例を示します。

@Message(id=2, value="Customer query failed, user:%2$s, customerid:%1$s")
void customerLookupFailed(Long customerid, String username);

4.5.6.3. 別の例外の原因として 1 つの例外を指定

例外バンドルメソッドより返された例外に対し、他の例外を基礎となる原因として指定することができます。指定するには、パラメーターをメソッドに追加し、パラメーターに @Cause アノテーションを付けます。このパラメーターを使用して原因となる例外を渡します。このパラメーターを例外メッセージで参照することはできません。

次の手順は、@Cause パラメーターを使用して原因となる例外を示し、例外バンドルよりメソッドを更新する方法を表しています。この機能に追加したい国際化された例外バンドルがすでに作成されていることを前提とします。

  1. Throwable 型のパラメーターまたはサブクラスをメソッドに追加します。

    @Message(id=328, value = "Error calculating: %s.")
    ArithmeticException calculationError(Throwable cause, String msg);
  2. パラメーターに @Cause アノテーションを追加します。

    import org.jboss.logging.annotations.Cause
    
    @Message(id=328, value = "Error calculating: %s.")
    ArithmeticException calculationError(@Cause Throwable cause, String msg);
  3. 例外オブジェクトを取得するため、インターフェースメソッドを呼び出します。キャッチした例外を原因として使用し、キャッチブロックより新しい例外を発生させるのが最も一般的なユースケースになります。

    try
    {
       ...
    }
    catch(Exception ex)
    {
       throw ExceptionBundle.EXCEPTIONS.calculationError(
                                        ex, "calculating payment due per day");
    }

以下に、例外を別の例外の原因として指定する例を示します。この例外バンドルでは、ArithmeticException 型の例外を返す単一のメソッドを定義します。

@MessageBundle(projectCode = "TPS")
interface CalcExceptionBundle
{
    CalcExceptionBundle EXCEPTIONS = Messages.getBundle(CalcExceptionBundle.class);

    @Message(id=328, value = "Error calculating: %s.")
    ArithmeticException calcError(@Cause Throwable cause, String value);
}

このコード例では、整数のゼロ除算を実行しようとすると例外が発生する操作が実行されます。例外が捕捉され、その最初の例外を原因として使用して新しい例外が作成されます。

int totalDue = 5;
int daysToPay = 0;
int amountPerDay;

try
{
   amountPerDay = totalDue/daysToPay;
}
catch (Exception ex)
{
   throw CalcExceptionBundle.EXCEPTIONS.calcError(ex, "payments per day");
}

以下は、例外メッセージの例です。

Exception in thread "main" java.lang.ArithmeticException: TPS000328: Error calculating: payments per day.
    at com.company.accounts.Main.go(Main.java:58)
    at com.company.accounts.Main.main(Main.java:43)
Caused by: java.lang.ArithmeticException: / by zero
    at com.company.accounts.Main.go(Main.java:54)
    ... 1 more

4.5.7. 参考資料

4.5.7.1. JBoss Logging Tools の Maven 設定

以下の手順では、国際化のために JBoss Logging と JBoss Logging Tools を使用するよう Maven プロジェクトを設定します。

  1. JBoss EAP レポジトリーを使用するよう Maven を設定します (まだそのように設定していない場合)。詳細については、Maven 設定を使用した JBoss EAP Maven リポジトリーの設定を参照してください。

    pom.xml ファイルの <dependencyManagement> セクションに jboss-eap-javaee7 BOM を含めます。

    <dependencyManagement>
      <dependencies>
        <!-- JBoss distributes a complete set of Java EE APIs including
          a Bill of Materials (BOM). A BOM specifies the versions of a "stack" (or
          a collection) of artifacts. We use this here so that we always get the correct versions of artifacts.
          Here we use the jboss-javaee-7.0 stack (you can
          read this as the JBoss stack of the Java EE APIs). You can actually
          use this stack with any version of JBoss EAP that implements Java EE. -->
        <dependency>
          <groupId>org.jboss.bom</groupId>
           <artifactId>jboss-eap-javaee7</artifactId>
           <version>${version.jboss.bom.eap}</version>
           <type>pom</type>
           <scope>import</scope>
        </dependency>
      <dependencies>
    <dependencyManagement>
  2. Maven 依存関係をプロジェクトの pom.xml ファイルに追加します。

    1. JBoss Logging フレームワークにアクセスするために jboss-logging 依存関係を追加します。
    2. JBoss Logging Tools を使用する場合は、jboss-logging-processor 依存関係も追加します。

      これら両方の依存関係は、前の手順で追加された JBoss EAP BOM で利用できます。したがって、各依存関係のスコープ要素は示されているように provided に設定できます。

      <!-- Add the JBoss Logging Tools dependencies -->
      <!-- The jboss-logging API -->
      <dependency>
         <groupId>org.jboss.logging</groupId>
         <artifactId>jboss-logging</artifactId>
         <scope>provided</scope>
      </dependency>
      <!-- Add the jboss-logging-tools processor if you are using JBoss Tools  -->
      <dependency>
         <groupId>org.jboss.logging</groupId>
         <artifactId>jboss-logging-processor</artifactId>
         <scope>provided</scope>
      </dependency>
  3. maven-compiler-plugin のバージョンは 3.1 以上であり、1.8 のターゲットソースおよび生成されたソースに対して設定する必要があります。

    <plugin>
       <groupId>org.apache.maven.plugins</groupId>
       <artifactId>maven-compiler-plugin</artifactId>
       <version>3.1</version>
       <configuration>
          <source>1.8</source>
          <target>1.8</target>
       </configuration>
    </plugin>
注記

JBoss Logging Tools を使用するよう設定された pom.xml ファイルの完全な例については、JBoss EAP に同梱される logging-tools クイックスタートを参照してください。

4.5.7.2. 翻訳プロパティーファイルの形式

JBoss Logging Tools でのメッセージの翻訳に使用されるプロパティーファイルは標準的な Java プロパティーファイルです。このファイルの形式は、java.util.Properties クラスドキュメンテーションに記載されている単純な行指向の key=value ペア形式です。

ファイル名の形式は次のようになります。

InterfaceName.i18n_locale_COUNTRY_VARIANT.properties
  • InterfaceName は翻訳が適用されるインターフェースの名前です。
  • localeCOUNTRY、および VARIANT は翻訳が適用される地域設定を識別します。
  • localeCOUNTRY は ISO-639 および ISO-3166 言語および国コードを使用して言語と国を指定します。COUNTRY は任意です。
  • VARIANT は特定のオペレーティングシステムまたはブラウザーのみに適用される翻訳を識別するために使用できる任意の識別子です。

翻訳ファイルに含まれるプロパティーは翻訳されるインターフェースのメソッドの名前です。プロパティーに割り当てられた値が翻訳になります。メソッドがオーバーロードされる場合、これはドットとパラメーターの数を名前に付加することによって示されます。翻訳のメソッドは、異なる数のパラメーターを提供することによってのみオーバーロードできます。

翻訳プロパティーファイルの例

ファイル名: GreeterService.i18n_fr_FR_POSIX.properties

# Level: Logger.Level.INFO
# Message: Hello message sent.
logHelloMessageSent=Bonjour message envoyé.

4.5.7.3. JBoss Logging Tools のアノテーションに関するリファレンス

JBoss Logging では、ログメッセージや文字列、例外の国際化や現地語化に使用する以下のアノテーションが定義されています。

表4.1 JBoss Logging Tools のアノテーション

アノテーションターゲット説明属性

@MessageBundle

インターフェース

インターフェースをメッセージバンドルとして定義します。

projectCode

@MessageLogger

インターフェース

インターフェースをメッセージロガーとして定義します。

projectCode

@Message

メソッド

メッセージバンドルとメッセージロガーで使用できます。メッセージバンドルでは、現地語化された String または Exception オブジェクトを返すメソッドとして定義されます。メッセージロガーでは、現地語化されたロガーとしてメソッドが定義されます。

valueid

@LogMessage

メソッド

メッセージロガーのメソッドをロギングメソッドとして定義します。

level (デフォルト値は INFO)

@Cause

パラメーター

ログメッセージまたは他の例外が発生したときに例外を渡すパラメーターとして定義します。

-

@Param

パラメーター

例外のコンストラクターへ渡されるパラメーターとして定義します。

-

4.5.7.4. JBoss EAP で使用されるプロジェクトコード

以下の表は、 JBoss EAP 7.0 で使用されるすべてのプロジェクトコードとそのプロジェクトコードが属す Maven モジュールの一覧です。

表4.2 JBoss EAP で使用されるプロジェクトコード

Maven モジュールプロジェクトコード

appclient

WFLYAC

batch/extension-jberet

WFLYBATCH

batch/extension

WFLYBATCH-DEPRECATED

batch/jberet

WFLYBAT

bean-validation

WFLYBV

controller-client

WFLYCC

controller

WFLYCTL

clustering/common

WFLYCLCOM

clustering/ejb/infinispan

WFLYCLEJBINF

clustering/infinispan/extension

WFLYCLINF

clustering/jgroups/extension

WFLYCLJG

clustering/server

WFLYCLSV

clustering/web/infinispan

WFLYCLWEBINF

connector

WFLYJCA

deployment-repository

WFLYDR

deployment-scanner

WFLYDS

domain-http

WFLYDMHTTP

domain-management

WFLYDM

ee

WFLYEE

ejb3

WFLYEJB

embedded

WFLYEMB

host-controller

WFLYDC

host-controller

WFLYHC

iiop-openjdk

WFLYIIOP

io/subsystem

WFLYIO

jaxrs

WFLYRS

jdr

WFLYJDR

jmx

WFLYJMX

jpa/hibernate5

JIPI

jpa/spi/src/main/java/org/jipijapa/JipiLogger.java

JIPI

jpa/subsystem

WFLYJPA

jsf/subsystem

WFLYJSF

jsr77

WFLYEEMGMT

launcher

WFLYLNCHR

legacy

WFLYORB

legacy

WFLYMSG

legacy

WFLYWEB

logging

WFLYLOG

mail

WFLYMAIL

management-client-content

WFLYCNT

messaging-activemq

WFLYMSGAMQ

mod_cluster/extension

WFLYMODCLS

naming

WFLYNAM

network

WFLYNET

patching

WFLYPAT

picketlink

WFLYPL

platform-mbean

WFLYPMB

pojo

WFLYPOJO

process-controller

WFLYPC

protocol

WFLYPRT

remoting

WFLYRMT

request-controller

WFLYREQCON

rts

WFLYRTS

sar

WFLYSAR

security-manager

WFLYSM

security

WFLYSEC

server

WFLYSRV

system-jmx

WFLYSYSJMX

threads

WFLYTHR

transactions

WFLYTX

undertow

WFLYUT

webservices/server-integration

WFLYWS

weld

WFLYWELD

xts

WFLYXTS

第5章 リモート JNDI ルックアップ

5.1. JNDI へのオブジェクトの登録

Java Naming and Directory Interface (JNDI) は、Java ソフトウェアクライアントが名前でオブジェクトを検出およびルックアップすることを可能にするディレクトリーサービスの Java API です。

JNDI に登録されるオブジェクトがリモート JNDI クライアント (つまり、別の JVM で実行されるクライアント) によりルックアップされる場合は、オブジェクトを java:jboss/exported コンテキストで登録する必要があります。

たとえば、messaging-activemq サブシステムの JMS キューをリモート JNDI クライアントに公開する必要がある場合は、 JMS キューを java:jboss/exported/jms/queue/myTestQueue のように JNDI に登録する必要があります。リモート JNDI クライアントは、名前 jms/queue/myTestQueue で JMS キューをルックアップできます。

例: standalone-full(-ha).xml のキューの設定

<subsystem xmlns="urn:jboss:domain:messaging-activemq:1.0">
  <server name="default">
    ...
    <jms-queue name="myTestQueue" entries="java:jboss/exported/jms/queue/myTestQueue"/>
    ...
  </server>
</subsystem>

5.2. リモート JNDI の設定

リモート JNDI クライアントは接続し、JNDI からの名前でオブジェクトをルックアップできます。jboss-client.jar がクラスパスに指定されている必要があります。jboss-client.jarEAP_HOME/bin/client/jboss-client.jar で利用できます。

以下の例は、リモート JNDI クライアントの JNDI から myTestQueue キューをルックアップする方法を示しています。

例: MDB リソースアダプターの設定

Properties properties = new Properties();
properties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
properties.put(Context.PROVIDER_URL, "http-remoting://<hostname>:8080");
context = new InitialContext(properties);
Queue myTestQueue = (Queue) context.lookup("jms/queue/myTestQueue");

第6章 Web アプリケーションのクラスター化

6.1. セッションレプリケーション

6.1.1. HTTP セッションレプリケーション

セッションレプリケーションは、配布可能なアプリケーションのクライアントセッションが、クラスター内のノードのフェイルオーバーによって中断されないようにします。クラスター内の各ノードは実行中のセッションの情報を共有するため、ノードが消滅してもセッションを引き継くことができます。

セッションレプリケーションは、mod_cluster、mod_jk、mod_proxy、ISAPI、および NSAPI クラスターにより高可用性を確保する仕組みのことです。

6.1.2. アプリケーションにおけるセッションレプリケーションの有効化

JBoss EAP の高可用性 (HA) 機能を利用し、Web アプリケーションのクラスタリングを有効にするには、アプリケーションが配布可能になるよう設定する必要があります。

アプリケーションを配布可能にする
  1. アプリケーションが配布可能であることを示します。アプリケーションが配布可能とマークされていない場合は、セッションが配布されません。アプリケーションの web.xml 記述子ファイルの <web-app> タグ内に <distributable/> 要素を追加します。

    例: 配布可能なアプリケーションの最低限の設定

    <?xml version="1.0"?>
    <web-app  xmlns="http://java.sun.com/xml/ns/j2ee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
                                  http://java.sun.com/xml/ns/j2ee/web-app_3_0.xsd"
              version="3.0">
    
          <distributable/>
    
    </web-app>

  2. 次に、必要な場合はデフォルトのレプリケーションの動作を変更します。セッションレプリケーションに影響する値を変更する場合は、アプリケーションの WEB-INF/jboss-web.xml ファイルにある <jboss-web> 内の <replication-config> 要素内でこれらの値をオーバーライドできます。該当する要素で、デフォルト値をオーバーライドする場合のみ値を含めます。

    例: <replication-config> 値

    <jboss-web xmlns="http://www.jboss.com/xml/ns/javaee"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-web_10_0.xsd">
       <replication-config>
          <replication-granularity>SESSION</replication-granularity>
        </replication-config>
    </jboss-web>

<replication-granularity> パラメーターは、レプリケートされるデータの粒度を決定します。デフォルト値は SESSION ですが、ATTRIBUTE を設定すると、ほとんどの属性は変更されずにセッションのパフォーマンスを向上させることができます。

<replication-granularity> の有効値は以下のとおりです。

  • SESSION: デフォルト値です。属性がダーティーである場合に、セッションオブジェクト全体がレプリケートされます。このポリシーは、オブジェクト参照が複数のセッション属性で共有される 場合に必要です。共有されるオブジェクト参照は、1 つのユニットでセッション全体がシリアライズされるため、リモートノードで維持されます。
  • ATTRIBUTE: これは、セッションのダーティーな属性と一部のセッションデータ (最後にアクセスされたタイムスタンプなど) にのみに使用できる値です。
変更不能なセッション属性

JBoss EAP7 の場合、セッションレプリケーションはセッションが変更された場合、またはセッションの変更可能な属性がアクセスされた場合にトリガーされます。以下のいずれかの条件に該当しない限り、セッション属性は変更可能であると見なされます。

  • 値が既知の変更不能な値である

    • null
    • java.util.Collections.EMPTY_LISTEMPTY_MAPEMPTY_SET
  • 値の型がまたは既知の変更不能な型である、または既知の変更不能な型を実装する

    • java.lang.BooleanCharacterByteShortIntegerLongFloatDouble
    • java.lang.ClassEnumStackTraceElementString
    • java.io.Filejava.nio.file.Path
    • java.math.BigDecimalBigIntegerMathContext
    • java.net.Inet4AddressInet6AddressInetSocketAddressURIURL
    • java.security.Permission
    • java.util.CurrencyLocaleTimeZoneUUID
    • java.time.ClockDurationInstantLocalDateLocalDateTimeLocalTimeMonthDayPeriodYearYearMonthZoneIdZoneOffsetZonedDateTime
    • java.time.chrono.ChronoLocalDateChronologyEra
    • java.time.format.DateTimeFormatterDecimalStyle
    • java.time.temporal.TemporalFieldTemporalUnitValueRangeWeekFields
    • java.time.zone.ZoneOffsetTransitionZoneOffsetTransitionRuleZoneRules
  • 値の型が以下のアノテートでアノテートされる

    • @org.wildfly.clustering.web.annotation.Immutable
    • @net.jcip.annotations.Immutable

6.2. HTTP セッションパッシベーションおよびアクティベーション

6.2.1. HTTP セッションパッシベーションおよびアクティベーション

パッシベーションとは、比較的利用されていないセッションをメモリーから削除し、永続ストレージへ保存することでメモリーの使用量を制御するプロセスのことです。

アクティベーションとは、パッシベートされたデータを永続ストレージから取得し、メモリーに戻すことです。

パッシベーションは HTTP セッションのライフタイムの異なるタイミングで実行されます。

  • コンテナーが新規セッションの作成を要求するときに現在アクティブなセッションの数が設定上限を超えている場合、サーバーはセッションの一部をパッシベートして新規セッションのスペースを作成しようとします。
  • Web アプリケーションがデプロイされ、他のサーバーでアクティブなセッションのバックアップコピーが、新たにデプロイされる Web アプリケーションのセッションマネージャーによって取得された場合、セッションはパッシベートされることがあります。

アクティブなセッションが設定可能な最大数を超えると、セッションはパッシベートされます。

セッションは常に LRU (Least Recently Used) アルゴリズムを使ってパッシベートされます。

6.2.2. アプリケーションでの HTTP セッションパッシベーションの設定

HTTP セッションパッシベーションは、アプリケーションの WEB-INF/jboss-web.xml および META-INF/jboss-web.xml ファイルで設定されます。

例: jboss-web.xml ファイル

<jboss-web xmlns="http://www.jboss.com/xml/ns/javaee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-web_10_0.xsd">

   <max-active-sessions>20</max-active-sessions>
</jboss-web>

<max-active-sessions/> 要素により、許可されるアクティブなセッションの最大数が設定され、セッションパッシベーションが有効になります。セッションの作成によって、アクティブなセッションの数が <max-active-sessions/> を超える場合は、新しいセッションのスペースを作成するために、セッションマネージャーが認識する最も古いセッションがパッシベートされます。

注記

メモリーのセッションの合計数には、このノードでアクセスされていない他のクラスターノードからレプリケートされたセッションが含まれます。これを考慮して <max-active-sessions> を設定してください。また、他のノードからレプリケートされるセッションの数は、REPL または DIST キャッシュモードが有効であるかどうかによっても異なります。REPL キャッシュモードでは、各セッションは各ノードにレプリケートされます。DIST キャッシュモードでは、各セッションは、owners パラメーターによって指定された数のノードにのみレプリケートされます。セッションキャッシュモードの設定については、JBoss EAP Config GuideConfigure the Cache Mode を参照してください。たとえば、各ノードが 100 ユーザーからの要求を処理する 8 つのノードクラスターがあるとします。この場合、REPL キャッシュモードでは、各ノードのメモリーに 800 のセッションが格納されます。DIST キャッシュモードが有効であり、デフォルトの owners 設定が 2 であるときは、各ノードのメモリーには 200 のセッションが格納されます。

6.3. クラスタリングサービスのパブリック API

JBoss EAP 7 には、アプリケーションが使用する改善されたパブリッククラスタリング API が導入されました。新しいサービスは、ライトウェイトで簡単に挿入できるよう設計されています。外部の依存関係は必要ありません。

org.wildfly.clustering.group.Group

グループサービスは、JGroups チャネルのクラスタートポロジーを参照し、トポロジーの変更時に通知するメカニズムを提供します。

@Resource(lookup = "java:jboss/clustering/group/channel-name")
private Group channelGroup;
org.wildfly.clustering.dispatcher.CommandDispatcher

CommandDispatcherFactory サービスは、クラスター内のノードでコマンドを実行するためにディスパッチャーを作成するメカニズムを提供します。結果として得られる CommandDispatcher は、以前の JBoss EAP リリースのリフレクションベースの GroupRpcDispatcher に類似したコマンドパターンです。

@Resource(lookup = "java:jboss/clustering/dispatcher/channel-name")
private CommandDispatcherFactory factory;

public void foo() {
    String context = "Hello world!";
    try (CommandDispatcher<String> dispatcher = this.factory.createCommandDispatcher(context)) {
        dispatcher.executeOnCluster(new StdOutCommand());
    }
}

public static class StdOutCommand implements Command<Void, String> {
    @Override
    public Void execute(String context) {
        System.out.println(context);
        return null;
    }
}

6.4. HA シングルトンサービス

クラスター化されたシングルトンサービス (高可用性 (HA) シングルトンとも呼ばれます) は、クラスターの複数のノードにデプロイされるサービスです。このサービスはいずれかのノードでのみ提供されます。シングルトンサービスが実行されているノードは、通常マスターノードと呼ばれます。

マスターノードが失敗またはシャットダウンした場合に、残りのノードから別のマスターが選択され、新しいマスターでサービスが再開されます。マスターが停止し、別のマスターが引き継ぐまでの短い間を除き、サービスは 1 つのノードのみによって提供されます。

HA シングルトン ServiceBuilder API

JBoss EAP 7 では、プロセスを大幅に簡略化するシングルトンサービスを構築するための新しいパブリック API が導入されます。

SingletonServiceBuilder 実装により、サービスは非同期的に開始されるようインストールされ、Modular Service Container (MSC) のデッドロックが回避されます。

HA シングルトンサービス選択ポリシー

ノードが ha-singleton を起動する優先度がある場合は、ServiceActivator クラスで選択ポリシーを設定できます。

JBoss EAP は、以下の 2 つの選択ポリシーを提供します。

  1. 単純な選択ポリシー

    単純な選択ポリシーでは、相対的な経過時間に基づいてマスターノードが選択されます。必要な経過時間は、利用可能なノードのリストのインデックスである position プロパティーで設定されます。この場合は、以下のように設定されます。

    • position = 0 – 最も古いノードを参照する (デフォルト)
    • position = 1 – 2 番目に古いノードを参照する

      position をマイナスにして最も新しいノードを示すこともできます。

    • position = -1 – 最も新しいノードを参照する
    • position = -2 – 2 番目に新しいノードを参照する
  2. ランダムな選択ポリシー

    ランダムな選択ポリシーでは、シングルトンサービスのプロバイダーとなるランダムなメンバーが選択されます。

HA シングルトンサービスアプリケーションの作成

以下に、アプリケーションを作成し、クラスター全体シングルトンサービスとしてデプロイするのに必要な手順の簡単な例を示します。この例のサービスにより、クラスターで 1 度だけ開始されるスケジュールタイマーがアクティブ化されます。

  1. org.jboss.msc.service.Service インターフェースを実装し、getValue()start()、および stop() メソッドを含む HATimerService サービスを作成します。

    サービスクラスコード例

    public class HATimerService implements Service<String> {
        private static final Logger LOGGER = Logger.getLogger(HATimerService.class.toString());
        public static final ServiceName SINGLETON_SERVICE_NAME = ServiceName.JBOSS.append("quickstart", "ha", "singleton", "timer");
    
        /**
         * A flag whether the service is started.
         */
        private final AtomicBoolean started = new AtomicBoolean(false);
    
        /**
         * @return the name of the server node
         */
        public String getValue() throws IllegalStateException, IllegalArgumentException {
            LOGGER.info(String.format("%s is %s at %s", HATimerService.class.getSimpleName(), (started.get() ? "started" : "not started"), System.getProperty("jboss.node.name")));
            return System.getProperty("jboss.node.name");
        }
    
        public void start(StartContext arg0) throws StartException {
            if (!started.compareAndSet(false, true)) {
                throw new StartException("The service is still started!");
            }
            LOGGER.info("Start HASingleton timer service '" + this.getClass().getName() + "'");
    
            final String node = System.getProperty("jboss.node.name");
            try {
                InitialContext ic = new InitialContext();
                ((Scheduler) ic.lookup("global/jboss-cluster-ha-singleton-service/SchedulerBean!org.jboss.as.quickstarts.cluster.hasingleton.service.ejb.Scheduler"))
                    .initialize("HASingleton timer @" + node + " " + new Date());
            } catch (NamingException e) {
                throw new StartException("Could not initialize timer", e);
            }
        }
    
        public void stop(StopContext arg0) {
            if (!started.compareAndSet(true, false)) {
                LOGGER.warning("The service '" + this.getClass().getName() + "' is not active!");
            } else {
                LOGGER.info("Stop HASingleton timer service '" + this.getClass().getName() + "'");
                try {
                    InitialContext ic = new InitialContext();
                    ((Scheduler) ic.lookup("global/jboss-cluster-ha-singleton-service/SchedulerBean!org.jboss.as.quickstarts.cluster.hasingleton.service.ejb.Scheduler")).stop();
                } catch (NamingException e) {
                    LOGGER.info("Could not stop timer:" + e.getMessage());
                }
            }
        }
    }

  2. org.jboss.msc.service.ServiceActivator インターフェースを実装し、activate() メソッドで HATimerService をクラスタ化されたシングルトンとしてインストールするサービスアクティベーターを作成します。この例では、node1 がシングルトンサービスを開始するよう指定します。

    サービスアクティベーターコード例

    public class HATimerServiceActivator implements ServiceActivator {
        private final Logger log = Logger.getLogger(this.getClass().toString());
    
        @Override
        public void activate(ServiceActivatorContext context) {
            log.info("HATimerService will be installed!");
    
            HATimerService service = new HATimerService();
            ServiceName factoryServiceName = SingletonServiceName.BUILDER.getServiceName("server", "default");
            ServiceController<?> factoryService = context.getServiceRegistry().getRequiredService(factoryServiceName);
            SingletonServiceBuilderFactory factory = (SingletonServiceBuilderFactory) factoryService.getValue();
            ServiceName ejbComponentService = ServiceName.of("jboss", "deployment", "unit", "jboss-cluster-ha-singleton-service.jar", "component", "SchedulerBean", "START");
            factory.createSingletonServiceBuilder(HATimerService.SINGLETON_SERVICE_NAME, service)
                .electionPolicy(new PreferredSingletonElectionPolicy(new SimpleSingletonElectionPolicy(), new NamePreference("node1/singleton")))
                .build(new DelegatingServiceContainer(context.getServiceTarget(), context.getServiceRegistry()))
                .setInitialMode(ServiceController.Mode.ACTIVE)
                .addDependency(ejbComponentService)
                .install();
        }
    }

  3. アプリケーションの META-INF/services/ ディレクトリーで org.jboss.msc.service.ServiceActivator という名前のファイルを作成し、前の手順で作成された ServiceActivator クラスの完全修飾名を含む行を追加します。

    META-INF/services/org.jboss.msc.service.ServiceActivator ファイル例

    org.jboss.as.quickstarts.cluster.hasingleton.service.ejb.HATimerServiceActivator

  4. initialize() および stop() メソッドを含む Scheduler インターフェースを作成します。

    スケジューラーインターフェースコード例

    public interface Scheduler {
    
        void initialize(String info);
    
        void stop();
    
    }

  5. Scheduler インターフェースを実装する Singleton Bean を作成します。この Bean はクラスター全体のシングルトンタイマーとして使用されます。

    重要

    Singleton Bean はリモートインターフェースを持たないようにし、すべてのアプリケーションの別の EJB からローカルインターフェースを参照しないようにする必要があります。これにより、クライアントや他のコンポーネントをルックアップできなくなり、HATimerServiceSingleton を完全に制御するようになります。

    シングルトン Bean コード例

    @Singleton
    public class SchedulerBean implements Scheduler {
        private static Logger LOGGER = Logger.getLogger(SchedulerBean.class.toString());
        @Resource
        private TimerService timerService;
    
        @Timeout
        public void scheduler(Timer timer) {
            LOGGER.info("HASingletonTimer: Info=" + timer.getInfo());
        }
    
        @Override
        public void initialize(String info) {
            ScheduleExpression sexpr = new ScheduleExpression();
            // set schedule to every 10 seconds for demonstration
            sexpr.hour("*").minute("*").second("0/10");
            // persistent must be false because the timer is started by the HASingleton service
            timerService.createCalendarTimer(sexpr, new TimerConfig(info, false));
        }
    
        @Override
        public void stop() {
            LOGGER.info("Stop all existing HASingleton timers");
            for (Timer timer : timerService.getTimers()) {
                LOGGER.fine("Stop HASingleton timer: " + timer.getInfo());
                timer.cancel();
            }
        }
    }

このアプリケーションの完全な稼働サンプルについては、JBoss EAP に同梱される cluster-ha-singleton クイックスタートを参照してください。クイックスタートは、アプリケーションをビルドおよびデプロイする詳細な手順を提供します。

6.5. HA シングルトンデプロイメント

JBoss EAP 7 には、該当するアプリケーションをシングルトンデプロイメントとしてデプロイする機能が追加されます。

クラスタ化されたサーバーのグループにデプロイされる場合、シングルトンデプロイメントでは該当するタイミングで単一のノードにのみデプロイされます。デプロイメントがアクティブなノードが停止または失敗すると、デプロイメントは別のノードで自動的に開始されます。

HA シングルトンの動作を制御するポリシーは、新しいシングルトンサブシステムによって管理されます。デプロイメントは特定のシングルトンポリシーを指定するか、デフォルトのサブシステムポリシーを使用します。デプロイメントは、デプロイメントオーバーレイとして既存のデプロイメントに最も簡単に適用される /META-INF/singleton-deployment.xml デプロイメント記述子を使用してシングルトンデプロイメントとして識別されます。また、必要なシングルトン設定を既存の jboss-all.xml 内に組み込むこともできます。

シングルトンデプロイメントの定義または選択

  • デプロイメントをシングルトンデプロイメントとして定義するには、アプリケーションアーカイブに /META-INF/singleton-deployment.xml 記述子を含めます。

    シングルトンデプロイメント記述子の例:

    <?xml version="1.0" encoding="UTF-8"?>
    <singleton-deployment xmlns="urn:jboss:singleton-deployment:1.0"/>

    特定のシングルトンポリシーを使用したシングルトンデプロイメント記述子の例:

    <?xml version="1.0" encoding="UTF-8"?>
    <singleton-deployment policy="my-new-policy" xmlns="urn:jboss:singleton-deployment:1.0"/>
  • または、singleton-deployment 要素を jboss-all.xml 記述子に追加することもできます。

    jboss-all.xmlsingleton-deployment 要素の例:

    <?xml version="1.0" encoding="UTF-8"?>
    <jboss xmlns="urn:jboss:1.0">
        <singleton-deployment xmlns="urn:jboss:singleton-deployment:1.0"/>
    </jboss>

    特定のシングルトンポリシーを使用した jboss-all.xmlsingleton-deployment 要素の例:

    <?xml version="1.0" encoding="UTF-8"?>
    <jboss xmlns="urn:jboss:1.0">
        <singleton-deployment policy="my-new-policy" xmlns="urn:jboss:singleton-deployment:1.0"/>
    </jboss>

シングルトンデプロイメントの作成

JBoss EAP は、以下の 2 つの選択ポリシーを提供します。

  • 単純な選択ポリシー

    simple-election-policyposition 属性で示された特定のメンバーを選択します (該当するアプリケーションがデプロイされます)。position 属性は、降順の経過時間でソートされた候補のリストから選択するノードのインデックスを決定します (0 は最も古いノード、1 は 2 番目に古いノード、-1 は最も新しいノード、-2 は 2 番目に新しいノードを示します)。指定された位置が候補の数を超えると、モジュロ演算が適用されます。

    管理 CLI で simple-election-policy と位置を -1 に設定した状態で新しいシングルトンポリシーを作成する例:

    batch
    /subsystem=singleton/singleton-policy=my-new-policy:add(cache-container=server)
    /subsystem=singleton/singleton-policy=my-new-policy/election-
    policy=simple:add(position=-1)
    run-batch
    注記

    新しく作成されたポリシー my-new-policy をデフォルトとして設定するには、以下のコマンドを実行します。

    /subsystem=singleton:write-attribute(name=default, value=my-new-policy)

    standalone-ha.xml を使用して位置が -1 に設定された状態で simple-election-policy を設定する例:

    <subsystem xmlns="urn:jboss:domain:singleton:1.0">
       <singleton-policies default="my-new-policy">
          <singleton-policy name="my-new-policy" cache-container="server">
             <simple-election-policy position="-1"/>
          </singleton-policy>
       </singleton-policies>
    </subsystem>
  • ランダムな選択ポリシー

    random-election-policy は、該当するアプリケーションがデプロイされるランダムなメンバーを選択します。

    管理 CLI で random-election-policy を使用して新しいシングルトンポリシーを作成する例:

    batch
    /subsystem=singleton/singleton-policy=my-other-new-policy:add(cache-container=server)
    /subsystem=singleton/singleton-policy=my-other-new-policy/election-policy=random:add()
    run-batch

    standalone-ha.xml を使用して random-election-policy を設定する例:

    <subsystem xmlns="urn:jboss:domain:singleton:1.0">
       <singleton-policies default="my-other-new-policy">
          <singleton-policy name="my-other-new-policy" cache-container="server">
             <random-election-policy/>
          </singleton-policy>
       </singleton-policies>
    </subsystem>
    注記

    ポリシーを追加する前に、cache-containerdefault-cache 属性を定義する必要があります。定義しないと、カスタムキャッシュコンテナーを使用する場合に、エラーメッセージが表示されることがあります。

設定

また、シングルトン選択ポリシーを使用して、クラスターの 1 人または複数のメンバーの優先順位を指定することもできます。優先順位は、ノード名または送信ソケットバインド名を使用して定義できます。ノード優先順位は、常に選択ポリシーの結果よりも優先されます。

管理 CLI で既存のシングルトンポリシーの優先順位を指定する例:

/subsystem=singleton/singleton-policy=foo/election-policy=simple:list-add(name=name-preferences, value=nodeA)

/subsystem=singleton/singleton-policy=bar/election-policy=random:list-add(name=socket-binding-preferences, value=binding1)

管理 CLI で simple-election-policyname-preferences を使用して新しいシングルトンポリシーを作成する例:

batch
/subsystem=singleton/singleton-policy=my-new-policy:add(cache-container=server)
/subsystem=singleton/singleton-policy=my-new-policy/election-policy=simple:add(name-preferences=[node1, node2, node3, node4])
run-batch
注記

新しく作成されたポリシー my-new-policy をデフォルトとして設定するには、以下のコマンドを実行します。

/subsystem=singleton:write-attribute(name=default, value=my-new-policy)

standalone-ha.xmlsocket-binding-preferences を使用して random-election-policy を設定する例:

<subsystem xmlns="urn:jboss:domain:singleton:1.0">
   <singleton-policies default="my-other-new-policy">
      <singleton-policy name="my-other-new-policy" cache-container="server">
         <random-election-policy>
            <socket-binding-preferences>binding1 binding2 binding3 binding4</socket-binding-preferences>
         </random-election-policy>
      </singleton-policy>
   </singleton-policies>
</subsystem>
クォーラム

ネットワークパーティションは、シングルトンデプロイメントに対して特に問題となります。これは、同時に実行する同じデプロイメントに対して複数のシングルトンプロバイダーをトリガーできるためです。このシナリオを回避するために、シングルトンポリシーにより、シングルトンプロバイダー選択が行われる前に、存在する必要があるノードの最小数を規定するクォーラムを定義できます。通常のデプロイメントシナリオでは、N/2 + 1 のクォーラムが使用されます (ここで、N は予想されるクラスターサイズです)。この値は実行時に更新でき、各シングルトンポリシーを使用するすべてのシングルトンデプロイメントにすぐに影響を与えます。

standalone-ha.xml ファイルでのクォーラム宣言の例:

<subsystem xmlns="urn:jboss:domain:singleton:1.0">
   <singleton-policies default="default">
      <singleton-policy name="default" cache-container="server" quorum="4">
         <simple-election-policy/>
      </singleton-policy>
   </singleton-policies>
</subsystem>

管理 CLI を使用したクォーラム宣言の例:

/subsystem=singleton/singleton-policy=foo:write-attribute(name=quorum, value=3)

6.6. Apache mod_cluster-manager アプリケーション

6.6.1. mod_cluster-manager アプリケーション

mod_cluster-manager アプリケーションは、Apache HTTP サーバーで利用可能な管理 Web ページです。接続されたワーカーノードを監視し、コンテキストの有効化または無効化やクラスター内のワーカーノードの負荷分散プロパティーの設定などのさまざまな管理タスクを実行するために使用されます。

mod_cluster-manager アプリケーションの使用

mod_cluster-manager アプリケーションは、ワーカーノードでさまざまな管理タスクを実行するために使用されます。

mod_cluster 管理 Web ページ 図 - mod_cluster 管理 Web ページ

  • [1] mod_cluster/1.3.1.Final: mod_cluster ネイティブライブラリーのバージョン。
  • [2] ajp://192.168.122.204:8099: 使用されるプロトコル (AJP、HTTP、または HTTPS)、ワーカーノードのホスト名または IP アドレス、およびポート。
  • [3] jboss-eap-7.0-2: ワーカーノードの JVMRoute。
  • [4] Virtual Host 1: ワーカーノードで設定された仮想ホスト。
  • [5] Disable: 特定のコンテキストで新しいセッションの作成を無効にするために使用できる管理オプション。ただし、現在のセッションは無効にされず、そのまま処理されます。
  • [6] Stop: コンテキストへのセッション要求のルーティングを停止するために使用できる管理オプション。sticky-session-force プロパティーが true に設定されない限り、残りのセッションは別のノードにフェイルオーバーされます。
  • [7] Enable Contexts Disable Contexts Stop Contexts: ノード全体で実行できる操作。これらのいずれかのオプションを選択すると、すべての仮想ホストのノードのコンテキストすべてが影響を受けます。
  • [8] Load balancing group (LBGroup): すべてのワーカーノードをカスタム負荷分散グループにグループ化するために、 load-balancing-group プロパティーは JBoss EAP 設定の modcluster サブシステムで設定されます。負荷分散グループ (LBGroup) は、設定されたすべての負荷分散グループに関する情報を提供する情報フィールドです。このフィールドが設定されていないと、すべてのワーカーノードは単一のデフォルト負荷分散グループにグループ化されます。

    注記

    これは唯一の情報フィールドであるため、load-balancing-group プロパティーの設定に使用できません。このプロパティーは JBoss EAP 設定の modcluster サブシステムで設定する必要があります。

  • [9] Load (value): ワーカーノードの負荷係数。負荷係数は以下のように評価されます。

    -load > 0 : 負荷係数の値が 1 であると、ワーカーノードがオーバーロードされます。負荷係数が 100 の場合は、負荷がないノードであることを意味します。
    -load = 0 : 負荷係数の値が 0 であると、ワーカーノードはスタンドバイモードになります。これは、他のノードが使用できなくなるまで (および他のノードが利用不可でない限り) セッション要求がこのノードにルーティングされないことを意味します。
    -load = -1 : 負荷係数の値が -1 の場合は、ワーカーノードがエラー状態にあることを示します。
    -load = -2 : 負荷係数の値が -2 の場合は、ワーカーノードが CPing/CPong を実行中であり、遷移状態にあることを示します。
    
注記

JBoss EAP 7.0 の場合は、ロードバランサーとして Undertow を使用することもできます。

第7章 コンテキストおよび依存関係の挿入 (CDI)

7.1. CDI の概要

7.1.1. コンテキストと依存関係の注入 (CDI)

Contexts and Dependency Injection (CDI) は、Enterprise Java Beans (EJB) 3 コンポーネントを Java Server Faces (JSF) 管理対象 Bean として使用できるよう設計された仕様であり、2 つのコンポーネントモデルを統合し、Java を使用した Web ベースのアプリケーション向けプログラミングモデルを大幅に簡略化します。CDI 1.2 リリースは、1.1 のメンテナンスリリースとして扱われます。 CDI 1.1 の詳細については、JSR 346: Contexts and Dependency Injection for Java™ EE 1.1 を参照してください。

JBoss EAP には、JSR-346:Contexts and Dependency Injection for Java™ EE 1.1 の参照実装である Weld が含まれます。

CDI の利点

CDI には以下のような利点があります。

  • 多くのコードをアノテーションに置き換えることにより、コードベースが単純化および削減されます。
  • 柔軟であり、インジェクションおよびイベントを無効または有効にしたり、代替の Bean を使用したり、非 CDI オブジェクトを簡単にインジェクトしたりできます。
  • デフォルト値と異なるよう設定をカスタマイズする必要がある場合に、オプションで、beans.xmlMETA-INF/ または WEB-INF/ ディレクトリーに含めることができます。
  • パッケージ化とデプロイメントが簡略化され、デプロイメントに追加する必要がある XML の量が減少します。
  • コンテキストを使用したライフサイクル管理が提供されます。インジェクションを要求、セッション、会話、またはカスタムコンテキストに割り当てることができます。
  • 文字列ベースのインジェクションよりも安全かつ簡単にデバッグを行える、タイプセーフな依存関係の注入が提供されます。
  • インターセプターと Bean が切り離されます。
  • 複雑なイベント通知が提供されます。

7.1.2. Weld、Seam 2、および JavaServer Faces 間の関係

Weld は JSR 346: Contexts and Dependency Injection for Java™ EE 1.1 で定義されている CDI の参照実装です。Weld は、Seam 2 と他の依存関係注入フレームワークの影響を受けており、JBoss EAP 6 に含まれています。

Seam 2 の目的は、Enterprise Java Bean と JavaServer Faces 管理対象 Bean を統合することでした。

JavaServer Faces 2.2 では、JSR-344: JavaServer™ Faces 2.2 が実装されます。これは、サーバーサイドユーザーインターフェースをビルドするための API です。

7.2. CDI の有効化

CDI は、JBoss EAP の中核的なテクノロジーの 1 つであり、デフォルトで有効になります。CDI は、設定ファイルの該当するセクションをコメントアウトまたは削除することにより無効になっている場合があります。有効にする必要がある場合は、以下の手順を実行します。

JBoss EAP での CDI の有効化

  1. JBoss EAP を停止します。

    JBoss EAP により実行中に設定ファイルが変更されるため、設定ファイルを直接編集する前にサーバーを停止します。

  2. 適切な設定ファイルを編集します。

    スタンドアロンサーバーの場合は EAP_HOME/standalone/configuration/standalone.xml、管理対象ドメインの場合は EAP_HOME/domain/configuration/domain.xml を編集します。

  3. CDI 拡張機能を追加します。

    org.jboss.as.weld 拡張機能がコメントアウトされた場合は、コメント解除します。全体が削除された場合は、以下の行をファイルの </extensions> タグのすぐ上に新しい行で追加することにより復元します。

    <extension module="org.jboss.as.weld"/>
  4. CDI サブシステムを追加します。

    weld サブシステムがコメントアウトされた場合は、コメント解除します。全体が削除された場合は、以下の行を <profiles> セクションの該当するプロファイルに追加することにより復元します。

    <subsystem xmlns="urn:jboss:domain:weld:3.0"/>
  5. 更新された設定で JBoss EAP を起動します。

JBoss EAP が起動するとき、CDI サブシステムは有効になります。

7.3. CDI を使用したアプリケーションの開発

コンテキストと依存関係の注入 (CDI: Contexts and Dependency Injection) を使用すると、アプリケーションの開発、コードの再利用、デプロイメント時または実行時のコードの調整、およびユニットテストを非常に柔軟に実行できます。JBoss EAP には、CDI の参照実装である Weld が含まれます。これらのタスクは、エンタープライズアプリケーションで CDI を使用する方法を示しています。

7.3.1. デフォルトの Bean 検出モード

bean アーカイブのデフォルトの bean 検出モードは annotated です。このような bean アーカイブは implicit bean archive と呼ばれます。

bean 検出モードが annotated の場合:

  • bean defining annotation がなく、セッション bean の bean クラスでない bean クラスが検出されません。
  • セッション bean 上になく、bean クラスが bean を定義するアノテーションを持たないプロデューサーメソッドが検出されません。
  • セッション bean 上になく、bean クラスが bean を定義するアノテーションを持たないプロデューサーフィールドが検出されません。
  • セッション bean 上になく、bean クラスが bean を定義するアノテーションを持たないディスポーザーメソッドが検出されません。
  • セッション bean 上になく、bean クラスが bean を定義するアノテーションを持たないオブザーバーメソッドが検出されません。
重要

CDI セクションのすべての例は、検出モードが all に設定された場合にのみ有効です。

bean を定義するアノテーション

bean クラスは bean defining annotation を持つことがあり、Bean アーカイブで定義されたようにアプリケーションのどこにでも配置することができます。bean を定義するアノテーションを持つ bean クラスは暗黙的な bean と呼ばれます。

bean を定義するアノテーションのセットには以下のものが含まれます。

  • @ApplicationScoped@SessionScoped@ConversationScoped、および @RequestScoped アノテーション
  • その他すべての通常スコープタイプ
  • @Interceptor および @Decorator アノテーション
  • @Stereotype アノテーションが付けられた stereotype アノテーションすべて
  • @Dependent スコープアノテーション

これらのアノテーションのいずれかが bean クラスで宣言された場合、その bean クラスは bean 定義アノテーションを持っていることになります。たとえば、以下の依存スコープ bean は bean 定義アノテーションを持っています。

@Dependent
public class BookShop
        extends Business
        implements Shop<Book> {
    ...
}
注記

他の JSR-330 実装との互換性を確保するために、@Dependent を除くすべての pseudo-scope アノテーションは bean 定義アノテーションではありません。ただし、pseudo-scope アノテーションを含む stereotype アノテーションは bean 定義アノテーションです。

7.3.2. スキャンプロセスからの Bean の除外

除外フィルターは、Bean アーカイブの beans.xml ファイルの <exclude> 要素によって <scan> 要素の子として定義されます。デフォルトでは、除外フィルターはアクティブです。定義に以下のものが含まれる場合、除外フィルターは非アクティブになります。

  • name 属性を含む、<if-class-available> という名前の子要素。Bean アーカイブのクラスローダーはこの名前のクラスをロードできません。
  • name 属性を含む、<if-class-not-available> という名前の子要素。Bean アーカイブのクラスローダーはこの名前のクラスをロードできます。
  • name 属性を含む、<if-system-property> という名前の子要素。この名前に対して定義されたシステムプロパティーはありません。
  • name 属性と値属性を含む、<if-system-property> という名前の子要素。この名前とこの値に対して定義されたシステムプロパティーはありません。

フィルターがアクティブな場合、タイプは検出から除外され、以下のいずれかの状態になります。

  • 検出されるタイプの完全修飾名が、除外フィルターの名前属性の値に一致します。
  • 検出されるタイプのパッケージ名が、除外フィルターの接尾辞 ".*" を含む名前属性の値に一致します。
  • 検出されるタイプのパッケージ名が、除外フィルターの接尾辞 ".*" を含む名前属性の値で始まります。

たとえば、以下の beans.xml ファイルを考えてみます。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee">

    <scan>
        <exclude name="com.acme.rest.*" /> 1

        <exclude name="com.acme.faces.**"> 2
            <if-class-not-available name="javax.faces.context.FacesContext"/>
        </exclude>

        <exclude name="com.acme.verbose.*"> 3
            <if-system-property name="verbosity" value="low"/>
        </exclude>

        <exclude name="com.acme.ejb.**"> 4
            <if-class-available name="javax.enterprise.inject.Model"/>
            <if-system-property name="exclude-ejbs"/>
        </exclude>
    </scan>

</beans>
1
最初の除外フィルターにより、com.acme.rest パッケージ内のすべてのクラスが除外されます。
2
2 番目の除外フィルターにより、com.acme.faces パッケージとすべてのサブパッケージ内のすべてのクラスが除外されます (JSF が利用可能でない場合のみ)。
3
3 番目の除外フィルターにより、システムプロパティー verbosity が値 low を持つ場合に、com.acme.verbose パッケージ内のすべてのクラスが除外されます。
4
4 番目の除外フィルターにより、システムプロパティー exclude-ejbs が任意の値で設定され、javax.enterprise.inject.Model クラスがクラスローダーでも利用可能な場合に、com.acme.ejb パッケージとすべてのサブパッケージ内のすべてのクラスが除外されます。
注記

Java EE コンポーネントが Bean と見なされないように、Java EE コンポーネントは @Vetoed でアノテートすることが安全です。イベントは @Vetoed でアノテートされたタイプに対して、または @Vetoed でアノテートされたパッケージで発生しません。詳細については、@Vetoed を参照してください。

7.3.3. インジェクションを使用した実装の拡張

インジェクションを使用して、既存のコードの機能を追加または変更できます。

この例では、既存のクラスに翻訳機能を追加します。メソッド buildPhrase を持つ Welcome クラスがすでにあることを前提とします。buildPhrase メソッドは、都市の名前を引数として取得し、"Welcome to Boston!" などのフレーズを出力します。

例: Translator Bean を Welcome クラスにインジェクトする

以下のコードにより、想像上の Translator オブジェクトが Welcome クラスにインジェクトされます。Translator オブジェクトは、文をある言語から別の言語に翻訳できる EJB ステートレス Bean または別のタイプの Bean になります。この例では、Translator は挨拶全体を翻訳するために使用され、元の Welcome クラスは変更されません。Translator は、buildPhrase メソッドが呼び出される前にインジェクトされます。

public class TranslatingWelcome extends Welcome {

    @Inject Translator translator;

    public String buildPhrase(String city) {
        return translator.translate("Welcome to " + city + "!");
    }
    ...
}

7.4. あいまいな依存関係または満たされていない依存関係

コンテナーが 1 つの Bean への注入を解決できない場合、依存関係があいまいとなります。

コンテナーがいずれの Bean に対しても注入の解決をできない場合、依存関係が満たされなくなります。

コンテナーは以下の手順を踏み、依存関係の解決をはかります。

  1. インジェクションポイントの Bean 型を実装する全 Bean にある修飾子アノテーションを解決します。
  2. 無効となっている Bean をフィルタリングします。無効な Bean とは、明示的に有効化されていない @Alternative Bean のことです。

依存関係があいまいな場合、あるいは満たされない場合は、コンテナーはデプロイメントを中断して例外を発生させます。

あいまいな依存関係を修正するには、修飾子を使用したあいまいなインジェクションの解決を参照してください。

7.4.1. 修飾子

修飾子は、コンテナーが複数の Bean を解決できるときにあいまいな依存関係を回避するために使用されるアノテーションであり、インジェクションポイントに含められます。インジェクションポイントで宣言された修飾子は、同じ修飾子を宣言する有効な Bean セットを提供します。

修飾子は、以下の例で示されたように Retention と Target を使用して宣言する必要があります。

例: @Synchronous 修飾子と @Asynchronous 修飾子の定義

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

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

例: @Synchronous 修飾子と @Asynchronous 修飾子の使用

@Synchronous
public class SynchronousPaymentProcessor implements PaymentProcessor {
   public void process(Payment payment) { ... }
}

@Asynchronous
public class AsynchronousPaymentProcessor implements PaymentProcessor {
   public void process(Payment payment) { ... }
}
'@Any'

Bean またはインジェクションポイントにより修飾子が明示的に宣言されない場合、コンテナーにより修飾子は @Default と見なされます。場合によっては、修飾子を指定せずにインジェクションポイントを宣言する必要があります。この場合も修飾子が存在します。すべての Bean には修飾子 @Any が含まれます。したがって、インジェクションポイントで @Any を明示的に指定することにより、インジェクションが可能な Bean を制限せずにデフォルトの修飾子を抑制できます。

これは、特定の Bean タイプを持つすべての Bean に対して繰り返し処理を行う場合に特に役に立ちます。

import javax.enterprise.inject.Instance;
...

@Inject

void initServices(@Any Instance<Service> services) {

   for (Service service: services) {

      service.init();

   }

}

各 Bean は修飾子 @Any を持ちます (この修飾子が明示的に指定されていない場合でも)。

各イベントも修飾子 @Any を持ちます (この修飾子を明示的に宣言せずにイベントが発生した場合でも)。

@Inject @Any Event<User> anyUserEvent;

@Any 修飾子により、インジェクションポイントが特定の Bean タイプのすべての Bean またはイベントを参照できます。

@Inject @Delegate @Any Logger logger;

7.4.2. 修飾子を使用したあいまいなインジェクションの解決

修飾子を使用してあいまいなインジェクションを解決できます。あいまいなインジェクションについては、あいまいな依存関係または満たされていない依存関係をお読みください。

以下の例はあいまいであり、Welcome の 2 つの実装 (翻訳を行う 1 つと翻訳を行わないもう 1 つ) を含みます。翻訳を行う Welcome を使用するには、インジェクションを指定する必要があります。

例: あいまいなインジェクション

public class Greeter {
  private Welcome welcome;

  @Inject
  void init(Welcome welcome) {
    this.welcome = welcome;
  }
  ...
}

修飾子を使用したあいまいなインジェクションの解決
  1. あいまいなインジェクションを解決するには、@Translating という名前の修飾子アノテーションを作成します。

    @Qualifier
    @Retention(RUNTIME)
    @Target({TYPE,METHOD,FIELD,PARAMETERS})
    public @interface Translating{}
  2. @Translating アノテーションを使用して、翻訳を行う Welcome をアノテートします。

    @Translating
    public class TranslatingWelcome extends Welcome {
        @Inject Translator translator;
        public String buildPhrase(String city) {
            return translator.translate("Welcome to " + city + "!");
        }
        ...
    }
  3. インジェクションで翻訳を行う Welcome を要求します。ファクトリーメソッドパターンの場合と同様に、修飾された実装を明示的に要求する必要があります。あいまいさはインジェクションポイントで解決されます。

    public class Greeter {
      private Welcome welcome;
      @Inject
      void init(@Translating Welcome welcome) {
        this.welcome = welcome;
      }
      public void welcomeVisitors() {
        System.out.println(welcome.buildPhrase("San Francisco"));
      }
    }

7.5. 管理 Bean

Java EE では管理対象 Bean 仕様の共通定義が確立されています。管理対象 Bean は、プログラミングの制限が最小限であるコンテナー管理オブジェクトとして定義され、POJO (Plain Old Java Object) として知られるようになりました。管理対象 Bean はリソースのインジェクション、ライフサイクルコールバック、インターセプターなどの基本サービスの小さなセットをサポートします。EJBや CDI などのコンパニオン仕様は、この基本モデルに基づいて構築されます。

ごくわずかな例外を除き、パラメーターのないコンストラクター (または @Inject アノテーションが指定されたコンストラクター) を持つ具象 Java クラスは Bean になります。これには、すべての Java Bean と EJB セッション Bean が含まれます。

7.5.1. Bean であるクラスのタイプ

管理対象 Bean は Java クラスです。管理対象 Bean の基本的なライフサイクルやセマンティクスは、管理対象 Bean の仕様で定義されています。Bean クラス @ManagedBean をアノテートすることで明示的に管理対象 Bean を宣言できますが、CDI ではその必要はありません。この仕様によると、CDI コンテナーでは、以下の条件を満たすクラスはすべて管理対象 Bean として扱われます。

  • 非静的な内部クラスではないこと。
  • 具象クラス、あるいは @Decorator アノテーションが付与されている。
  • EJB コンポーネントを定義するアノテーションが付与されていないこと、あるいは ejb-jar.xml で EJB Bean クラスとして宣言されていること。
  • インターフェース javax.enterprise.inject.spi.Extension が実装されていないこと。
  • パラメーターのないコンストラクターか、@Inject アノテーションが付与されたコンストラクターがあること。
  • アノテートされた @Vetoed でないこと、または @Vetoed でアノテートされたパッケージ内にないこと。

管理対象 Bean の Bean 型で無制限のものには、直接的あるいは間接的に実装する Bean クラス、全スーパークラス、および全インターフェースが含まれます。

管理対象 Bean にパブリックフィールドがある場合は、デフォルトの @Dependent スコープが必要です。

@Vetoed

CDI 1.1 には、新しいアノテーションである @Vetoed が導入されました。このアノテーションを追加することにより、Bean をインジェクションから除外できます。

@Vetoed
public class SimpleGreeting implements Greeting {
    ...
}

このコードでは、SimpleGreeting Bean はインジェクションの対象となりません。

パッケージ内のすべての Bean をインジェクションから除外できます。

@Vetoed
package org.sample.beans;

import javax.enterprise.inject.Vetoed;

org.sample.beans パッケージ内の package-info.java のこのコードにより、このパッケージ内のすべての Bean がインジェクションから除外されます。

ステートレス EJB や JAX-RS リソースエンドポイントなどの Java EE コンポーネントは、Bean と見なされないように @Vetoed でマークできます。@Vetoed アノテーションをすべての永続エンティティーに追加すると、BeanManager がエンティティーを CDI Bean として管理することを回避できます。エンティティーが @Vetoed とアノテートされた場合は、インジェクションが行われません。この理由は、JPA プロバイダーが破損する原因となる操作を BeanManager が実行することを防ぐことです。

7.5.2. CDI を用いたオブジェクトの Bean へのインジェクト

CDI コンポーネントがアプリケーションで検出されると、CDI は自動的にアクティベートされます。デフォルト値と異なるよう設定をカスタマイズする場合は、デプロイメントアーカイブに META-INF/beans.xml または WEB-INF/beans.xml を含めることができます。

他のオブジェクトにオブジェクトをインジェクトする
  1. クラスのインスタンスを取得するには、Bean 内で @Inject を使用してフィールドをアノテートします。

    public class TranslateController {
       @Inject TextTranslator textTranslator;
       ...
  2. インジェクトしたオブジェクトのメソッドを直接使用します。TextTranslator にメソッド translate があることを前提とします。

    // in TranslateController class
    
    public void translate() {
       translation = textTranslator.translate(inputText);
    }
  3. Bean のコンストラクターでインジェクションを使用します。ファクトリーやサービスロケーターを使用して作成する代わりに、Bean のコンストラクターへオブジェクトをインジェクトできます。

    public class TextTranslator {
    
       private SentenceParser sentenceParser;
       private Translator sentenceTranslator;
    
       @Inject
       TextTranslator(SentenceParser sentenceParser, Translator sentenceTranslator) {
          this.sentenceParser = sentenceParser;
          this.sentenceTranslator = sentenceTranslator;
       }
    
       // Methods of the TextTranslator class
       ...
    }
  4. Instance(<T>) インターフェースを使用してインスタンスをプログラムにより取得します。Bean 型でパラメーター化されると、Instance インターフェースは TextTranslator のインスタンスを返すことができます。

    @Inject Instance<TextTranslator> textTranslatorInstance;
    ...
    public void translate() {
       textTranslatorInstance.get().translate(inputText);
    }

オブジェクトを Bean にインジェクトすると、Bean は全オブジェクトのメソッドとプロパティーを使用できるようになります。Bean のコンストラクターにインジェクトするときに、インジェクションがすでに存在するインスタンスを参照する場合以外は、Bean のコンストラクターが呼び出されるとインジェクトされたオブジェクトのインスタンスが作成されます。たとえば、セッションの存続期間内にセッションスコープの Bean をインジェクトしても、新しいインスタンスは作成されません。

7.6. コンテキストおよびスコープ

CDI では、特定のスコープに関連付けられた Bean のインスタンスを保持するストレージ領域をコンテキストと呼びます。

スコープは Bean とコンテキスト間のリンクです。スコープとコンテキストの組み合わせは特定のライフサイクルを持つことがあります。事前定義された複数のスコープが存在し、独自のスコープを作成できます。事前定義されたスコープの例は @RequestScoped@SessionScoped、および @ConversationScope です。

表7.1 利用可能なスコープ

範囲説明

@Dependent

Bean は、参照を保持する Bean のライフサイクルにバインドされます。インジェクション Bean のデフォルトのスコープは @Dependent です。

@ApplicationScoped

Bean はアプリケーションのライフサイクルにバインドされます。

@RequestScoped

Bean はリクエストのライフサイクルにバインドされます。

@SessionScoped

Bean はセッションのライフサイクルにバインドされます。

@ConversationScoped

Bean は会話のライフサイクルにバインドされます。会話スコープは、リクエストの長さとセッションの間であり、アプリケーションによって制御されます。

カスタムスコープ

上記のコンテキストで対応できない場合は、カスタムスコープを定義できます。

7.7. 名前付き Bean

Bean には、@Named アノテーションを使用して名前を付けることができます。Bean を命名することにより、Bean を Java Server Faces (JSF) と Expression Language (EL) で直接使用できるようになります。

@Named アノテーションは、Bean 名であるオプションパラメーターを取ります。このパラメーターが省略された場合、Bean 名はデフォルトで最初の文字が小文字に変換された Bean のクラス名に設定されます。

7.7.1. 名前付き Bean の使用

@Named Annotation を使用した Bean 名の設定
  1. @Named アノテーションを使用して名前を Bean に割り当てます。

    @Named("greeter")
    public class GreeterBean {
      private Welcome welcome;
    
      @Inject
      void init (Welcome welcome) {
        this.welcome = welcome;
      }
    
      public void welcomeVisitors() {
        System.out.println(welcome.buildPhrase("San Francisco"));
      }
    }

    上記の例では、名前が指定されていない場合、デフォルトの名前は greeterBean になります。

  2. JSF ビューで名前付き Bean を使用します。

    <h:form>
      <h:commandButton value="Welcome visitors" action="#{greeter.welcomeVisitors}"/>
    </h:form>

7.8. Bean ライフサイクル

このタスクは、リクエストの残存期間の間 Bean を保存する方法を示しています。

インジェクトされた Bean のデフォルトのスコープは @Dependent です。つまり、Bean のライフサイクルは、参照を保持する Bean のライフサイクルに依存します。他の複数のスコープが存在し、独自のスコープを定義できます。詳細については、コンテキストおよびスコープを参照してください。

Bean ライフサイクルの管理

  1. 必要なスコープで Bean をアノテートします。

    @RequestScoped
    @Named("greeter")
    public class GreeterBean {
      private Welcome welcome;
      private String city; // getter & setter not shown
      @Inject   void init(Welcome welcome) {
        this.welcome = welcome;
      }
      public void welcomeVisitors() {
        System.out.println(welcome.buildPhrase(city));
      }
    }
  2. Bean が JSF ビューで使用されると、Bean は状態を保持します。

    <h:form>
      <h:inputText value="#{greeter.city}"/>
      <h:commandButton value="Welcome visitors" action="#{greeter.welcomeVisitors}"/>
    </h:form>

Bean は、指定するスコープに関連するコンテキストに保存され、スコープが適用される限り存続します。

7.8.1. プロデューサーメソッドの使用

プロデューサーメソッドは、Bean インスタンスのソースとして動作するメソッドです。指定されたコンテキストにインスタンスが存在しない場合は、メソッド宣言自体で Bean が定義され、コンテナーによって Bean のインスタンスを取得するメソッドが呼び出されます。プロデューサーメソッドにより、アプリケーションは Bean インスタンス化プロセスを完全に制御できるようになります。

このタスクは、インジェクション用の Bean ではないさまざまなオブジェクトを生成するプロデューサーメソッドを使用する方法を示しています。

例: 代替の代わりにプロデューサーメソッドを使用してデプロイメント後のポリモーフィズムを可能にする

例の @Preferred アノテーションは、修飾子アノテーションです。修飾子の詳細については、修飾子を参照してください。

@SessionScoped
public class Preferences implements Serializable {
   private PaymentStrategyType paymentStrategy;
   ...
   @Produces @Preferred
   public PaymentStrategy getPaymentStrategy() {
       switch (paymentStrategy) {
           case CREDIT_CARD: return new CreditCardPaymentStrategy();
           case CHECK: return new CheckPaymentStrategy();
           default: return null;
       }
   }
}

以下のインジェクションポイントは、プロデューサーメソッドと同じタイプおよび修飾子アノテーションを持つため、通常の CDI インジェクションルールを使用してプロデューサーメソッドに解決されます。プロデューサーメソッドは、このインジェクションポイントを処理するインスタンスを取得するためにコンテナーにより呼び出されます。

@Inject @Preferred PaymentStrategy paymentStrategy;

例: スコープをプロデューサーメソッドに割り当てる

プロデューサーメソッドのデフォルトのスコープは @Dependent です。スコープを Bean に割り当てた場合、スコープは適切なコンテキストにバインドされます。この例のプロデューサーメソッドは、1 つのセッションあたり一度だけ呼び出されます。

@Produces @Preferred @SessionScoped
public PaymentStrategy getPaymentStrategy() {
   ...
}

例: プロデューサーメソッド内部でのインジェクションの使用

アプリケーションにより直接インスタンス化されたオブジェクトは、依存関係の注入を利用できず、インターセプターを持ちません。ただし、プロデューサーメソッドへの依存関係の注入を使用して Bean インスタンスを取得できます。

@Produces @Preferred @SessionScoped
public PaymentStrategy getPaymentStrategy(CreditCardPaymentStrategy ccps,
                                          CheckPaymentStrategy cps ) {
   switch (paymentStrategy) {
      case CREDIT_CARD: return ccps;
      case CHEQUE: return cps;
      default: return null;
   }
}

リクエストスコープの Bean をセッションスコープのプロデューサーにインジェクトする場合は、プロデューサーメソッドにより、現在のリクエストスコープのインスタンスがセッションスコープにプロモートされます。これは、適切な動作ではないため、プロデューサーメソッドをこのように使用する場合は注意してください。

注記

プロデューサーメソッドのスコープは、プロデューサーメソッドを宣言する Bean から継承されません。

プロデューサーメソッドを使用して、Bean ではないオブジェクトをインジェクトし、コードを動的に変更できます。

7.9. 代替の Bean

実装が特定のクライアントモジュールまたはデプロイメントシナリオに固有である Bean が代替となります。

デフォルトでは、@Alternative Bean が無効になります。これらは、beans.xml ファイルを編集することにより、特定の Bean アーカイブに対して有効になります。ただし、このアクティベーションは、そのアーカイブの Bean に対してのみ適用されます。CDI 1.1 以降、代替の Bean は、@Priority アノテーションを使用してアプリケーション全体に対して有効にできます。

代替の定義例

この代替により、@Synchronous 代替と@Asynchronous 代替を使用して PaymentProcessor クラスの実装が定義されます。

@Alternative @Synchronous @Asynchronous

public class MockPaymentProcessor implements PaymentProcessor {

   public void process(Payment payment) { ... }

}

beans.xml@Alternative を有効にする例

<beans
   xmlns="http://xmlns.jcp.org/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="
      http://xmlns.jcp.org/xml/ns/javaee
      http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd">
   <alternatives>
         <class>org.mycompany.mock.MockPaymentProcessor</class>
   </alternatives>
</beans>

選択された代替の宣言

@Priority アノテーションにより、アプリケーション全体に対して代替を有効にすることができるようになります。代替にはアプリケーションの優先度を割り当てることができます。

  • 管理対象 Bean またはセッション Bean の Bean クラスに @Priority アノテーションを置く、または
  • プロデューサーメソッド、フィールド、またはリソースを宣言する Bean クラスに @Priority アノテーションを置く

7.9.1. 代替を用いたインジェクションのオーバーライド

代替の Bean を使用すると、既存の Bean をオーバーライドできます。これらは、同じ役割を満たすクラスをプラグインする方法として考慮できますが、動作が異なります。代替の Bean はデフォルトで無効になります。

このタスクは、代替を指定し、有効にする方法を示しています。

インジェクションのオーバーライド

このタスクでは、プロジェクトに TranslatingWelcome クラスがすでにあることを前提としています。ただし、これを "mock" TranslatingWelcome クラスでオーバーライドするとします。これは、実際の Translator Bean を使用できないテストデプロイメントのケースに該当します。

  1. 代替を定義します。

    @Alternative
    @Translating
    public class MockTranslatingWelcome extends Welcome {
      public String buildPhrase(string city) {
        return "Bienvenue à " + city + "!");
      }
    }
  2. 置換実装をアクティベートするために、完全修飾クラス名を META-INF/beans.xml または WEB-INF/beans.xml ファイルに追加します。

    <beans>
      <alternatives>
        <class>com.acme.MockTranslatingWelcome</class>
      </alternatives>
    </beans>

元の実装の代わりに代替実装が使用されます。

7.10. ステレオタイプ

多くのシステムでは、アーキテクチャーパターンを使用して繰り返し発生する Bean ロールのセットを生成します。ステレオタイプを使用すると、このようなロールを指定し、中心的な場所で、このロールを持つ Bean に対する共通メタデータを宣言できます。

ステレオタイプにより、以下のいずれかの組み合わせがカプセル化されます。

  • デフォルトスコープ
  • インターセプターバインディングのセット

また、ステレオタイプにより、以下の 2 つのいずれかを指定できます。

  • ステレオタイプがデフォルトの Bean EL 名であるすべての Bean
  • ステレオタイプが代替であるすべての Bean

Bean は、ステレオタイプを 0 個以上宣言できます。ステレオタイプは、他の複数のアノテーションをパッケージ化する @Stereotype アノテーションです。ステレオタイプアノテーションは、Bean クラス、プロデューサーメソッド、またはフィールドに適用できます。

ステレオタイプからスコープを継承するクラスは、そのステレオタイプをオーバーライドし、Bean で直接スコープを指定できます。

また、ステレオタイプが @Named アノテーションを持つ場合、配置された Bean はデフォルトの Bean 名を持ちます。この Bean は、@Named アノテーションが Bean で直接指定された場合に、この名前をオーバーライドできます。名前付き Bean の詳細については、名前付き Beanを参照してください。

7.10.1. ステレオタイプの使用

ステレオタイプを使用しないと、アノテーションが煩雑になる可能性があります。このタスクは、ステレオタイプを使用して煩雑さとコードを減らす方法を示しています。

例: アノテーションの煩雑さ

@Secure
@Transactional
@RequestScoped
@Named
public class AccountManager {
  public boolean transfer(Account a, Account b) {
    ...
  }
}

ステレオタイプの定義および使用
  1. ステレオタイプを定義します。

    @Secure
    @Transactional
    @RequestScoped
    @Named
    @Stereotype
    @Retention(RUNTIME)
    @Target(TYPE)
    public @interface BusinessComponent {
     ...
    }
  2. ステレオタイプを使用します。

    @BusinessComponent
    public class AccountManager {
      public boolean transfer(Account a, Account b) {
        ...
      }
    }

7.11. オブザーバーメソッド

オブザーバーメソッドは、イベント発生時に通知を受け取ります。

また、CDI は、イベントが発生したトランザクションの完了前または完了後フェーズ中にイベント通知を受け取るトランザクションオブザーバーメソッドを提供します。

7.11.1. イベントの発生と確認

例: イベントの発生

以下のコードは、メソッドでインジェクトおよび使用されるイベントを示しています。

public class AccountManager {
  @Inject Event<Withdrawal> event;

  public boolean transfer(Account a, Account b) {
    ...
    event.fire(new Withdrawal(a));
  }
}

例: 修飾子を使用したイベントの発生

修飾子を使用すると、より具体的にイベントのインジェクションにアノテーションを付けられます。修飾子の詳細については、修飾子を参照してください。

public class AccountManager {
  @Inject @Suspicious Event <Withdrawal> event;

  public boolean transfer(Account a, Account b) {
    ...
    event.fire(new Withdrawal(a));
  }
}

例: イベントの確認

イベントを確認するには、@Observes アノテーションを使用します。

public class AccountObserver {
  void checkTran(@Observes Withdrawal w) {
    ...
  }
}

修飾子を使用すると、特定の種類のイベントのみを確認できます。

public class AccountObserver {
  void checkTran(@Observes @Suspicious Withdrawal w) {
    ...
  }
}

7.11.2. トランザクションオブザーバー

トランザクションオブザーバーは、イベントが発生したトランザクションの完了フェーズ前または完了フェーズ後にイベント通知を受け取ります。トランザクションオブザーバーは、単一のアトミックトランザクションよりも状態が保持される期間が長いため、トランザクションオブザーバーはステートフルオブジェクトモデルで重要になります。

トラザクションオブザーバーには 5 つの種類があります。

  • IN_PROGRESS: デフォルトではオブザーバーは即座に呼び出されます。
  • AFTER_SUCCESS: トランザクションが正常に完了する場合のみ、オブザーバーはトランザクションの完了フェーズの後に呼び出されます。
  • AFTER_FAILURE: トランザクションの完了に失敗する場合のみ、オブザーバーはトランザクションの完了フェーズの後に呼び出されます。
  • AFTER_COMPLETION: オブザーバーはトランザクションの完了フェーズの後に呼び出されます。
  • BEFORE_COMPLETION: オブザーバーはトランザクションの完了フェーズの前に呼び出されます。

以下のオブザーバーメソッドは、カテゴリーツリーを更新するトランザクションが正常に実行される場合のみアプリケーションコンテキストにキャッシュされたクエリー結果セットを更新します。

public void refreshCategoryTree(@Observes(during = AFTER_SUCCESS) CategoryUpdateEvent event) { ... }

アプリケーションスコープで JPA クエリー結果セットをキャッシュしたことを仮定します。

import javax.ejb.Singleton;
import javax.enterprise.inject.Produces;

@ApplicationScoped @Singleton

public class Catalog {
   @PersistenceContext EntityManager em;
   List<Product> products;
   @Produces @Catalog
   List<Product> getCatalog() {
      if (products==null) {
         products = em.createQuery("select p from Product p where p.deleted = false")
            .getResultList();
      }
      return products;
   }
}

Product はときどき作成および削除されます。Product が作成または削除されると Product カタログを更新する必要がありますが、トランザクションが正常に完了した後に更新を行う必要があります。

以下は、イベントを引き起こす Products を作成および削除する Bean の例になります。

import javax.enterprise.event.Event;

@Stateless

public class ProductManager {
   @PersistenceContext EntityManager em;
   @Inject @Any Event<Product> productEvent;
   public void delete(Product product) {
      em.delete(product);
      productEvent.select(new AnnotationLiteral<Deleted>(){}).fire(product);
   }

   public void persist(Product product) {
      em.persist(product);
      productEvent.select(new AnnotationLiteral<Created>(){}).fire(product);
   }
   ...
}

トランザクションが正常に完了した後に、Catalog がイベントを監視できるようになりました。

import javax.ejb.Singleton;

@ApplicationScoped @Singleton
public class Catalog {
   ...
   void addProduct(@Observes(during = AFTER_SUCCESS) @Created Product product) {
      products.add(product);
   }

   void removeProduct(@Observes(during = AFTER_SUCCESS) @Deleted Product product) {
      products.remove(product);
   }

}

7.12. インターセプター

インターセプターを使用すると、Bean のメソッドを直接変更せずに Bean のビジネスメソッドに機能を追加できます。インターセプターは、Bean のビジネスメソッドの前に実行されます。インターセプターは、JSR 318: Enterprise JavaBeans™ 3.1 仕様の一部として定義されています。

CDI により、インターセプターと Bean をバインドするアノテーションを利用できるため、この機能が強化されます。

インターセプションポイント

  • ビジネスメソッドインターセプション: ビジネスメソッドのインターセプターは、Bean のクライアントによる Bean のメソッド呼び出しに適用されます。
  • ライフサイクルコールバックインターセプション: ライフサイクルコールバックインターセプションは、コンテナーによるライフサイクルコールバックの呼び出しに適用されます。
  • タイムアウトメソッドインターセプター: タイムアウトメソッドインターセプターは、コンテナーによる EJB タイムアウトメソッドの呼び出しに適用されます。

インターセプターの有効化

デフォルトでは、すべてのインターセプターが無効になります。インターセプターは、Bean アーカイブの beans.xml 記述子を使用して有効にすることができます。ただし、このアクティベーションは、そのアーカイブの Bean に対してのみ適用されます。CDI 1.1 以降、インターセプターは、@Priority アノテーションを使用してアプリケーション全体に対して有効にできます。

<beans
   xmlns="http://xmlns.jcp.org/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="
      http://xmlns.jcp.org/xml/ns/javaee
      http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd">
   <interceptors>
      <class>org.mycompany.myapp.TransactionInterceptor</class>
   </interceptors>
</beans>

XML 宣言を使用すると、以下の 2 つのことが可能になります。

  • システムでインターセプターの順序を指定して、決定論的な動作を得ることができます。
  • デプロイメント時にインターセプタークラスを有効または無効にすることができます。

@Priority を使用して有効にされたインターセプターは、beans.xml ファイルを使用して有効にされたインターセプターよりも前に呼び出されます。

注記

インターセプターが @Priority により有効にされ、同時に beans.xml により呼び出されると、移植不可能な動作になります。したがって、異なる CDI 実装で整合性のある動作を維持するには、この組み合わせの有効化を回避する必要があります。

7.12.1. CDI とのインターセプターの使用

CDI により、インターセプターコードが単純化され、ビジネスコードへの適用が簡単になります。

CDI がない場合、インターセプターには 2 つの問題があります。

  • Bean は、インターセプター実装を直接指定する必要があります。
  • アプリケーションの各 Bean は、インターセプターの完全なセットを適切な順序で指定する必要があります。この場合、アプリケーション全体でインターセプターを追加または削除するには時間がかかり、エラーが発生する傾向があります。

例: CDI のないインターセプター

@Interceptors({
  SecurityInterceptor.class,
  TransactionInterceptor.class,
  LoggingInterceptor.class
})
@Stateful public class BusinessComponent {
  ...
}

CDI とのインターセプターの使用
  1. インターセプターバインディングタイプを定義します。

    @InterceptorBinding
    @Retention(RUNTIME)
    @Target({TYPE, METHOD})
    public @interface Secure {}
  2. インターセプター実装をマークします。

    @Secure
    @Interceptor
    public class SecurityInterceptor {
      @AroundInvoke
      public Object aroundInvoke(InvocationContext ctx) throws Exception {
        // enforce security ...
        return ctx.proceed();
        }
    }
  3. ビジネスコードでインターセプターを使用します。

    @Secure
    public class AccountManager {
      public boolean transfer(Account a, Account b) {
        ...
      }
    }
  4. インターセプターを META-INF/beans.xml または WEB-INF/beans.xml に追加することにより、インターセプターをデプロイメントで有効にします。

    <beans>
      <interceptors>
        <class>com.acme.SecurityInterceptor</class>
        <class>com.acme.TransactionInterceptor</class>
      </interceptors>
    </beans>

インターセプターは、リストされた順序で適用されます。

7.13. デコレーター

デコレーターは、特定の Java インターフェースからの呼び出しをインターセプトし、そのインターフェースに割り当てられたすべてのセマンティクスを認識します。デコレーターは、何らかの業務をモデル化するのに役に立ちますが、インターセプターの一般性を持ちません。デコレーターは Bean または抽象クラスであり、デコレートするタイプを実装し、@Decorator アノテーションが付けられます。CDI アプリケーションでデコレーターを呼び出すには、beans.xml ファイルで指定する必要があります。

beans.xml でデコレーターを呼び出す例

<beans
   xmlns="http://xmlns.jcp.org/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="
      http://xmlns.jcp.org/xml/ns/javaee
      http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd">
   <decorators>
         <class>org.mycompany.myapp.LargeTransactionDecorator</class>
   </decorators>
</beans>

この宣言には 2 つの主な目的があります。

  • システムでデコレーターの順序を指定して、決定論的な動作を得ることができます。
  • デプロイメント時にデコレータークラスを有効または無効にすることができます。

デコレートされたオブジェクトへの参照を取得するために、デコレーターには @Delegate インジェクションポイントが 1 つ必要になります。

デコレーターの例

@Decorator
public abstract class LargeTransactionDecorator implements Account {

   @Inject @Delegate @Any Account account;
   @PersistenceContext EntityManager em;

   public void withdraw(BigDecimal amount) {
      ...
   }

   public void deposit(BigDecimal amount);
      ...
   }
}

CDI 1.1 以降、デコレーターは、@Priority アノテーションを使用してアプリケーション全体に対して有効にできます。

@Priority を使用して有効にされたデコレーターは、beans.xml を使用して有効にされたデコレーターよりも前に呼び出されます。低い優先度値が最初に呼び出されます。

注記

デコレーターが @Priority により有効にされ、同時に beans.xml により呼び出されると、移植不可能な動作になります。したがって、異なる CDI 実装で整合性のある動作を維持するには、この組み合わせの有効化を回避する必要があります。

7.14. 移植可能な拡張機能

CDI は、フレームワーク、拡張機能、および他のテクノロジーとの統合の基礎となることを目的としています。したがって、CDI は、移植可能な CDI の拡張機能の開発者が使用する SPI のセットを公開します。

拡張機能は、以下のような種類の機能を提供できます。

  • ビジネスプロセス管理エンジンとの統合
  • Spring、Seam、GWT、Wicket などのサードパーティーフレームワークとの統合
  • CDI プログラミングモデルに基づく新しいテクノロジー

JSR-346 仕様に基づいて、移植可能な拡張機能は次の方法でコンテナーと統合できます。

  • 独自の Bean、インターセプター、およびデコレーターをコンテナーに提供します。
  • 依存関係注入サービスを使用した独自のオブジェクトへの依存関係のインジェクション
  • カスタムスコープのコンテキスト実装を提供します。
  • アノテーションベースのメタデータを別のソースからのメタデータで拡大またはオーバーライドします。

7.15. Bean プロキシ

通常、インジェクトされた Bean のクライアントは Bean インスタンスへの直接参照を保持しません。Bean が依存オブジェクト (スコープ @Dependent) でない場合、コンテナーはプロキシオブジェクトを使用して、インジェクトされたすべての参照を Bean にリダイレクトする必要があります。

この Bean プロキシはクライアントプロキシと呼ばれ、メソッド呼び出しを受け取る Bean インスタンスが、現在のコンテキストと関連するインスタンスになるようにします。またクライアントプロキシは、他のインジェクトされた Bean を再帰的にシリアライズせずに、セッションコンテキストなどのコンテキストにバインドされる Bean をディスクへシリアライズできるようにします。

Java の制限により、コンテナーによるプロキシの作成が不可能な Java の型があります。これらの型の 1 つで宣言されたインジェクションポイントが、@Dependent 以外のスコープを持つ Bean に解決すると、コンテナーがデプロイメントをアボートします。

特定の Java の型ではコンテナーによってプロキシを作成できません。これらの型には次のようなものがあります。

  • パラメーターのない非プライベートコンストラクターを持たないクラス
  • final が宣言されたクラスまたは final メソッドを持つクラス
  • 配列およびプリミティブ型

7.16. インジェクションでのプロキシの使用

各 Bean のライフサイクルが異なる場合に、インジェクションにプロキシが使用されます。プロキシは起動時に作成された Bean のサブクラスで、Bean クラスのプライベートメソッド以外のメソッドをすべて上書きします。プロキシは実際の Bean インスタンスへ呼び出しを転送します。

この例では、PaymentProcessor インスタンスは直接 Shop へインジェクトされません。その代わりにプロキシがインジェクトされ、processPayment() メソッドが呼び出されるとプロキシが現在の PaymentProcessor Bean インスタンスをルックアップし、processPayment() メソッドを呼び出します。

例: プロキシインジェクション

@ConversationScoped
class PaymentProcessor
{
  public void processPayment(int amount)
  {
    System.out.println("I'm taking $" + amount);
  }
}

@ApplicationScoped
public class Shop
{

  @Inject
  PaymentProcessor paymentProcessor;

  public void buyStuff()
  {
    paymentProcessor.processPayment(100);
  }
}

第8章 JBoss EAP MBean サービス

管理対象 Bean (単に MBean と呼ばれることもあります) は、依存関係インジェクションで作成された JavaBean の型です。MBean サービスは JBoss EAP サーバーの中心的な要素です。

8.1. JBoss MBean Service の記述

JBoss サービスに依存するカスタム MBean サービスを記述するには、サービスインターフェースメソッドパターンが必要です。JBoss MBean のサービスインターフェースメソッドパターンは createstartstop、および destroy が実行可能である場合に MBean サービスに通知する複数のライフサイクル操作で構成されます。

以下の方法を使用すると依存関係の状態を管理できます。

  • MBean で特定のメソッドを呼び出したい場合は、これらのメソッドを MBean インターフェースで宣言します。この方法では、MBean 実装で JBoss 固有クラスの依存関係を回避できます。
  • JBoss 固有クラスの依存関係を気にしない場合は、MBean インターフェースで ServiceMBean インターフェースおよび ServiceMBeanSupport クラスを拡張できます。ServiceMBeanSupport クラスは create、start、および stop などのサービスライフサイクルメソッドの実装を提供します。start() イベントなどの特定のイベントを処理するには、ServiceMBeanSupport クラスによって提供される startService() メソッドをオーバーライドする必要があります。

8.1.1. 標準の MBean の例

本項では、サービスアーカイブ (.sar) で一緒にパッケージ化される 2 つの MBean 例の開発について説明します。

ConfigServiceMBean インターフェースは startgetTimeout、および stop などの特定のメソッドを宣言し、JBoss 固有のクラスを使用せずに MBean に対して starthold、および stop を実行します。ConfigService クラスは ConfigServiceMBean インターフェースを実装した後、このインターフェース内で使用されたメソッドを実装します。

PlainThread クラスは ServiceMBeanSupport クラスを拡張し、PlainThreadMBean インターフェースを実装します。PlainThread はスレッドを開始し、ConfigServiceMBean.getTimeout() を使用してスレッドのスリープ時間を決定します。

MBean サービスの例

package org.jboss.example.mbean.support;
public interface ConfigServiceMBean {
    int getTimeout();
    void start();
    void stop();
}
package org.jboss.example.mbean.support;
public class ConfigService implements ConfigServiceMBean {
    int timeout;
    @Override
    public int getTimeout() {
        return timeout;
    }
    @Override
    public void start() {
        //Create a random number between 3000 and 6000 milliseconds
        timeout = (int)Math.round(Math.random() * 3000) + 3000;
        System.out.println("Random timeout set to " + timeout + " seconds");
    }
    @Override
    public void stop() {
        timeout = 0;
    }
}

package org.jboss.example.mbean.support;
import org.jboss.system.ServiceMBean;
public interface PlainThreadMBean extends ServiceMBean {
    void setConfigService(ConfigServiceMBean configServiceMBean);
}

package org.jboss.example.mbean.support;
import org.jboss.system.ServiceMBeanSupport;
public class PlainThread extends ServiceMBeanSupport implements PlainThreadMBean {
    private ConfigServiceMBean configService;
    private Thread thread;
    private volatile boolean done;
    @Override
    public void setConfigService(ConfigServiceMBean configService) {
        this.configService = configService;
    }
    @Override
    protected void startService() throws Exception {
        System.out.println("Starting Plain Thread MBean");
        done = false;
        thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    while (!done) {
                        System.out.println("Sleeping....");
                        Thread.sleep(configService.getTimeout());
                        System.out.println("Slept!");
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        });
        thread.start();
    }
    @Override
    protected void stopService() throws Exception {
        System.out.println("Stopping Plain Thread MBean");
        done = true;
    }
}

jboss-service.xml 記述子は、inject タグを使用して ConfigService クラスが PlainThread クラスにインジェクトされる方法を示します。inject タグは PlainThreadMBeanConfigServiceMBean 間の依存関係を確立し、PlainThreadMBean が簡単に ConfigServiceMBean を使用できるようにします。

JBoss-service.xml サービス記述子

<server xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="urn:jboss:service:7.0 jboss-service_7_0.xsd"
        xmlns="urn:jboss:service:7.0">
 <mbean code="org.jboss.example.mbean.support.ConfigService" name="jboss.support:name=ConfigBean"/>
 <mbean code="org.jboss.example.mbean.support.PlainThread" name="jboss.support:name=ThreadBean">
  <attribute name="configService">
   <inject bean="jboss.support:name=ConfigBean"/>
  </attribute>
 </mbean>
</server>

MBean の例を記述した後、クラスと jboss-service.xml 記述子をサービスアーカイブ (.sar) の META-INF でパッケージ化できます。

8.2. JBoss MBean サービスのデプロイ

管理対象ドメインでのサンプル MBean のデプロイおよびテスト

管理対象ドメインでサンプル MBean (ServiceMBeanTest.sar) をデプロイするには、以下のコマンドを使用します。

deploy ~/Desktop/ServiceMBeanTest.sar --all-server-groups

スタンドアロンサーバーでのサンプル MBean のデプロイおよびテスト

スタンドアロンサーバーでサンプル MBean (ServiceMBeanTest.sar) をビルドおよびデプロイするには、以下のコマンドを使用します。

deploy ~/Desktop/ServiceMBeanTest.sar

サンプル MBean のデプロイ解除

サンプル MBean をデプロイ解除するには、以下のコマンドを使用します。

ServiceMBeanTest.sar のデプロイ解除

第9章 コンカレンシーユーティリティー

コンカレンシーユーティリティーは、Java SE コンカレンシーユーティリティーを Java EE アプリケーション環境仕様で使用できるようにする API であり、JSR 236: Concurrency Utilities for Java™ EE で定義されています。JBoss EAP では、EE コンカレンシーユーティリティーのインスタンスを作成、編集、および削除でき、その結果、これらのインスタンスがアプリケーションで使用できるようになります。

コンカレンシーユーティリティーを使用すると、既存のコンテキストのアプリケーションスレッドをプルし、独自のスレッドで使用することにより、呼び出しコンテキストを拡張できるようになります。呼び出しコンテキストのこの拡張には、デフォルトでクラスローディング、JNDI、およびセキュリティーコンテキストが含まれます。

コンカレンシーユーティリティーのタイプには以下のものが含まれます。

  • コンテキストサービス
  • 管理対象スレッドファクトリー
  • 管理対象エグゼキューターサービス
  • 管理対象スケジュール済みエグゼキューターサービス

standalone.xml のコンカレンシーユーティリティー

<subsystem xmlns="urn:jboss:domain:ee:4.0">
            <spec-descriptor-property-replacement>false</spec-descriptor-property-replacement>
            <concurrent>
                <context-services>
                    <context-service name="default" jndi-name="java:jboss/ee/concurrency/context/default" use-transaction-setup-provider="true"/>
                </context-services>
                <managed-thread-factories>
                    <managed-thread-factory name="default" jndi-name="java:jboss/ee/concurrency/factory/default" context-service="default"/>
                </managed-thread-factories>
                <managed-executor-services>
                    <managed-executor-service name="default" jndi-name="java:jboss/ee/concurrency/executor/default" context-service="default" hung-task-threshold="60000" keepalive-time="5000"/>
                </managed-executor-services>
                <managed-scheduled-executor-services>
                    <managed-scheduled-executor-service name="default" jndi-name="java:jboss/ee/concurrency/scheduler/default" context-service="default" hung-task-threshold="60000" keepalive-time="3000"/>
                </managed-scheduled-executor-services>
            </concurrent>
            <default-bindings context-service="java:jboss/ee/concurrency/context/default" datasource="java:jboss/datasources/ExampleDS" managed-executor-service="java:jboss/ee/concurrency/executor/default" managed-scheduled-executor-service="java:jboss/ee/concurrency/scheduler/default" managed-thread-factory="java:jboss/ee/concurrency/factory/default"/>
</subsystem>

9.1. コンテキストサービス

コンテキストサービス (javax.enterprise.concurrent.ContextService) を使用すると、既存のオブジェクトからコンテキストプロキシをビルドできます。コンテキストプロキシにより、コンテキストが作成または呼び出されたとき (呼び出しが元のオブジェクトに転送される前) に他のコンカレンシーユーティリティーによって使用される呼び出しコンテキストが準備されます。

コンテキストサービスコンカレンシーユーティリティーの属性には以下のものが含まれます。

  • name: すべてのコンテキストサービス内の一意の名前。
  • jndi-name: JNDI でコンテキストサービスを配置する場所を定義します。
  • use-transaction-setup-provider: オプション。プロキシオブジェクトを呼び出す場合に、コンテキストサービスによってビルドされたコンテキストプロキシがコンテキストでトランザクションを一時停止するかどうかを示します。デフォルト値は false ですが、デフォルトのコンテキストサービスの値は true です。

コンテキストサービスコンカレンシーユーティリティーの使用方法については、上記の例を参照してください。

新しいコンテキストサービスの追加

/subsystem=ee/context-service=newContextService:add(jndi-name=java:jboss/ee/concurrency/contextservice/newContextService)

コンテキストサービスの変更

/subsystem=ee/context-service=newContextService:write-attribute(name=jndi-name, value=java:jboss/ee/concurrency/contextservice/changedContextService)

この操作にはリロードが必要です。

コンテキストサービスの削除

/subsystem=ee/context-service=newContextService:remove()

この操作にはリロードが必要です。

9.2. 管理対象スレッドファクトリー

管理対象スレッドファクトリー (javax.enterprise.concurrent.ManagedThreadFactory) コンカレンシーユーティリティーを使用すると、Java EE アプリで Java スレッドを作成できます。JBoss EAP は管理対象スレッドファクトリーインスタンスを処理するため、Java EE アプリケーションはライフサイクル関連メソッドを呼び出すことができません。

管理対象スレッドファクトリーコンカレンシーユーティリティーの属性には以下のものがあります。

  • context-service: すべての管理対象スレッドファクトリー内の一意の名前。
  • jndi-name: JNDI で管理対象スレッドファクトリーを配置する場所を定義します。
  • priority: オプション。ファクトリーにより作成された新しいスレッドの優先度を示します。デフォルト値は 5 です。

新しい管理対象スレッドファクトリーの追加

/subsystem=ee/managed-thread-factory=newManagedTF:add(context-service=newContextService, jndi-name=java:jboss/ee/concurrency/threadfactory/newManagedTF, priority=2)

管理対象スレッドファクトリーの変更

/subsystem=ee/managed-thread-factory=newManagedTF:write-attribute(name=jndi-name, value=java:jboss/ee/concurrency/threadfactory/changedManagedTF)

この操作にはリロードが必要です。同様に、他の属性を変更することもできます。

管理対象スレッドファクトリーの削除

/subsystem=ee/managed-thread-factory=newManagedTF:remove()

この操作にはリロードが必要です。

9.3. 管理対象エグゼキューターサービス

管理対象エグゼキューターサービス (javax.enterprise.concurrent.ManagedExecutorService) を使用すると、Java EE アプリケーションで非同期実行向けタスクを送信できます。JBoss EAP は管理対象エグゼキューターサービスインスタンスを処理するため、Java EE アプリケーションはライフサイクル関連メソッドを呼び出すことができません。

管理対象エグゼキューターサービスコンカレンシーユーティリティーの属性には以下のものがあります。

  • context-service: オプション。既存のコンテキストサービスをその名前で参照します。指定された場合は、参照されたコンテキストサービスがタスクをエグゼキューターに送信したときに存在する呼び出しコンテキストを取得します (このコンテキストはタスクの実行時に使用されます)。
  • jndi-name: JNDI で管理対象スレッドファクトリーを配置する場所を定義します。
  • max-threads: エグゼキューターにより使用されるスレッドの最大数を定義します。デフォルト値は Integer.MAX_VALUE です。
  • thread-factory: 既存の管理対象スレッドファクトリーをその名前で参照して内部スレッドの作成を処理します。指定されない場合は、デフォルト設定の管理対象スレッドファクトリーが作成され、内部で使用されます。
  • core-threads: エグゼキューターのプールに保持するスレッド数を提供します (スレッドはアイドル状態であるものも含みます)。値が 0 の場合は、制限がないことを意味します。
  • keepalive-time: 内部スレッドをアイドル状態にできる時間 (ミリ秒単位) を定義します。属性のデフォルト値は 60000 です。
  • queue-length: 入力キューに格納できるタスクの数を示します。デフォルト値は 0 であり、キューの容量が無制限であることを意味します。
  • hung-task-threshold: ミリ秒単位の時間を定義します。この時間が経過すると、管理対象エグゼキューターサービスによってタスクがハング状態にあると見なされ、強制終了します。値が 0 (デフォルト値) の場合、タスクはハング状態にあると見なされません。
  • long-running-tasks: 長時間実行中のタスクの実行の最適化を推奨します。デフォルト値は false です。
  • reject-policy: タスクがエグゼキューターにより拒否されたときに使用するポリシーを定義します。属性値はデフォルトの ABORT (例外がスローされます) または RETRY_ABORT (エグゼキューターは例外をスローする前にもう一度例外を送信しようとします) のいずれかになります。

新しい管理対象エグゼキューターサービスの追加

/subsystem=ee/managed-executor-service=newManagedExecutorService:add(jndi-name=java:jboss/ee/concurrency/executor/newManagedExecutorService, core-threads=7, thread-factory=default)

管理対象エグゼキューターサービスの変更

/subsystem=ee/managed-executor-service=newManagedExecutorService:write-attribute(name=core-threads,value=10)

この操作にはリロードが必要です。同様に、他の属性を変更することもできます。

管理対象エグゼキューターサービスの削除

/subsystem=ee/managed-executor-service=newManagedExecutorService:remove()

この操作にはリロードが必要です。

9.4. 管理対象スケジュール済みエグゼキューターサービス

管理対象スケジュール済みエグゼキューターサービス (javax.enterprise.concurrent.ManagedScheduledExecutorService) を使用すると、Java EE アプリケーションで非同期実行向けタスクをスケジュールできます。JBoss EAP は管理対象スケジュール済みエグゼキューターサービスインスタンスを処理するため、Java EE アプリケーションはライフサイクル関連メソッドを呼び出すことができません。

管理対象エグゼキューターサービスコンカレンシーユーティリティーの属性には以下のものがあります。

  • context-service: 既存のコンテキストサービスをその名前で参照します。指定された場合は、参照されたコンテキストサービスがタスクをエグゼキューターに送信したときに存在する呼び出しコンテキストを取得します (このコンテキストはタスクの実行時に使用されます)。
  • hung-task-threshold: ミリ秒単位の時間を定義します。この時間が経過すると、管理対象スケジュール済みエグゼキューターサービスによってタスクがハング状態にあると見なされ、強制終了します。値が 0 (デフォルト値) の場合、タスクはハング状態にあると見なされません。
  • keepalive-time: 内部スレッドをアイドル状態にできる時間 (ミリ秒単位) を定義します。属性のデフォルト値は 60000 です。
  • reject-policy: タスクがエグゼキューターにより拒否されたときに使用するポリシーを定義します。属性値はデフォルトの ABORT (例外がスローされます) または RETRY_ABORT (エグゼキューターは例外をスローする前にもう一度例外を送信しようとします) のいずれかになります。
  • core-threads: エグゼキューターのプールに保持するスレッド数を提供します (スレッドはアイドル状態であるものも含みます)。値が 0 の場合は、制限がないことを意味します。
  • jndi-name: JNDI で管理対象スケジュール済みエグゼキューターサービスを配置する場所を定義します。
  • long-running-tasks: 長時間実行中のタスクの実行の最適化を推奨します。デフォルト値は false です。
  • thread-factory: 既存の管理対象スレッドファクトリーをその名前で参照して内部スレッドの作成を処理します。指定されない場合は、デフォルト設定の管理対象スレッドファクトリーが作成され、内部で使用されます。

新しい管理対象スケジュール済みエグゼキューターサービスの追加

/subsystem=ee/managed-scheduled-executor-service=newManagedScheduledExecutorService:add(jndi-name=java:jboss/ee/concurrency/scheduledexecutor/newManagedScheduledExecutorService, core-threads=7, context-service=default)

この操作にはリロードが必要です。

管理対象スケジュール済みエグゼキューターサービスの変更

/subsystem=ee/managed-scheduled-executor-service=newManagedScheduledExecutorService:write-attribute(name=core-threads, value=10)

この操作にはリロードが必要です。同様に、他の属性を変更することができます。

管理対象スケジュール済みエグゼキューターサービスの削除

/subsystem=ee/managed-scheduled-executor-service=newManagedScheduledExecutorService:remove()

この操作にはリロードが必要です。

第10章 Undertow

10.1. Undertow ハンドラーについて

Undertow は、ブロックタスクと非ブロックタスクの両方に使用するよう設計された Web サーバーです。JBoss EAP 7 では JBoss Web は Undertow に置き換わります。主な機能の一部は以下のとおりです。

  • ハイパフォーマンス
  • 組み込み可能
  • Servlet 3.1
  • Web ソケット
  • リバースプロキシー

リクエストライフサイクル

クライアントがサーバーに接続するときに、Undertow によって io.undertow.server.HttpServerConnection が作成されます。クライアントがリクエストを送信するときに、リクエストは Undertow パーサーによって解析され、生成される io.undertow.server.HttpServerExchange はルートハンドラーに渡されます。ルートハンドラーが完了すると、以下の 4 つのいずれかのことが起こります。

交換が完了する
リクエストチャネルと応答チャネルが完全に読み取られたり、書き込まれた場合に、交換が完了したと見なされます。リクエスト側は、GET や HEAD などのコンテンツがないリクエストの場合に、自動的に完全に読み取られたと見なされます。読み取り側は、ハンドラーが完全な応答を書き込み、応答チャネルを閉じ、応答チャネルを完全にフラッシュしたときに、完了したと見なされます。交換がすでに完了した場合は、どんなアクションも行われません。
交換を完了せずにルートハンドラーが通常どおり返される
この場合、交換は HttpServerExchange.endExchange() を呼び出して完了します。
ルートハンドラーが例外で返される
この場合、500 の応答コードが設定され、HttpServerExchange.endExchange() を使用して交換が終了します。
ルートハンドラーは、HttpServerExchange.dispatch() が呼び出された後、または非同期 IO が開始された後に返されることがあります。
この場合、ディスパッチされたタスクはディスパッチエグゼキューターに送信されます。また、非同期 IO がリクエストチャネルまたは応答チャネルのいずれかで開始された場合は、このタスクが開始されます。この場合、交換は完了しません。非同期タスクによって、処理が完了したときに交換が完了します。

HttpServerExchange.dispatch() の最も一般的な使用方法は、ワーカースレッドに対してブロックが許可されない IO スレッドから実行を移動することです (この結果、ブロック操作が許可されます)。このパターンは通常以下のようになります。

ワーカースレッドへのディスパッチ:

public void handleRequest(final HttpServerExchange exchange) throws Exception {
    if (exchange.isInIoThread()) {
      exchange.dispatch(this);
      return;
    }
    //handler code
}

交換は呼び出しスタックが返されるまで実際にはディスパッチされないため、交換で一度に複数のスレッドがアクティブにならないようにすることができます。交換はスレッドセーフではありません。ただし、交換は、両方のスレッドが一度に変更しようとしない限り、複数のスレッド間で渡すことができます。

交換の終了

交換を終了するには、リクエストチャネルを読み取り、応答チャネルで shutdownWrites() を呼び出し、フラッシュする方法と HttpServerExchange.endExchange() を呼び出す方法の 2 つがあります。endExchange() が呼び出された場合、Undertow はコンテンツが生成されたかどうかを確認します。生成された場合、Undertow はリクエストチャネルを単にドレインし、応答チャネルを閉じ、フラッシュします。生成されず、交換で登録されたデフォルトの応答リスナーがある場合は、Undertow によってそれらの各応答リスナーがデフォルトの応答を生成できるようになります。このメカニズムにより、デフォルトのエラーページが生成されます。

Undertow の詳細については、JBoss EAP Configuration GuideConfiguring the Web Server を参照してください。

10.2. デプロイメントでの既存の Undertow ハンドラーの使用

Undertow は、JBoss EAP にデプロイされたアプリケーションで使用できるハンドラーのデフォルトセットを提供します。利用可能なハンドラーとその属性の完全なリストはここで確認できます。

デプロイメントでハンドラーを使用するには、WEB-INF/undertow-handlers.conf ファイルを追加する必要があります。

WEB-INF/undertow-handlers.conf の例

allowed-methods(methods='GET')

すべてのハンドラーでは、特定のケースでそのハンドラーを適用するためにオプションの述語を指定することもできます。

オプションの述語がある WEB-INF/undertow-handlers.conf の例

path('/my-path') -> allowed-methods(methods='GET')

上記の例では、allowed-methods ハンドラーのみがパス /my-path に適用されます。

一部のハンドラーにはデフォルトのパラメーターがあり、名前を使用せずにハンドラー定義でそのパラメーターの値を指定できます。

デフォルトのパラメーターを使用した WEB-INF/undertow-handlers.conf の例

path('/a') -> redirect('/b')

また、WEB-INF/jboss-web.xml ファイルを更新して 1 つまたは複数のハンドラーの定義を含めることもできます (ただし、WEB-INF/undertow-handlers.conf を使用することが推奨されます)。

WEB-INF/jboss-web.xml の例

<jboss-web>
    <http-handler>
        <class-name>io.undertow.server.handlers.AllowedMethodsHandler</class-name>
        <param>
            <param-name>methods</param-name>
            <param-value>GET</param-value>
        </param>
    </http-handler>
</jboss-web>

提供された Undertow ハンドラーの完全なリストは、ここで確認できます。

10.3. カスタムハンドラーの作成

  • カスタムハンドラーは WEB-INF/jboss-web.xml ファイルで定義できます。
<jboss-web>
    <http-handler>
        <class-name>org.jboss.example.MyHttpHandler</class-name>
    </http-handler>
</jboss-web>

ハンドラークラスの例:

package org.jboss.example;

import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;

public class MyHttpHandler implements HttpHandler {
    private HttpHandler next;

    public MyHttpHandler(HttpHandler next) {
        this.next = next;
    }

    public void handleRequest(HttpServerExchange exchange) throws Exception {
        // do something
        next.handleRequest(exchange);
    }
}

  • カスタムハンドラーには、WEB-INF/jboss-web.xml ファイルを使用してパラメーターを設定することもできます。
<jboss-web>
    <http-handler>
        <class-name>org.jboss.example.MyHttpHandler</class-name>
        <param>
            <param-name>myParam</param-name>
            <param-value>foobar</param-value>
        </param>
    </http-handler>
</jboss-web>

これらのパラメーターが機能するには、ハンドラークラスに対応するセッターが必要です。

package org.jboss.example;

import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;

public class MyHttpHandler implements HttpHandler {
    private HttpHandler next;
    private String myParam;

    public MyHttpHandler(HttpHandler next) {
        this.next = next;
    }

    public void setMyParam(String myParam) {
        this.myParam = myParam;
    }

    public void handleRequest(HttpServerExchange exchange) throws Exception {
        // do something, use myParam
        next.handleRequest(exchange);
    }
}
  • ハンドラーの定義に WEB-INF/jboss-web.xml を使用する代わりに、ハンドラーは WEB-INF/undertow-handlers.confファイルで定義することもできます。

    myHttpHandler(myParam='foobar')

    WEB-INF/undertow-handlers.conf で定義されたハンドラーが機能するには、以下の 2 つのものを作成する必要があります。

    1. HandlerWrapper にラップされた HandlerBuilder (undertow-handlers.conf 向けの対応する構文を定義し、HttpHandler を作成します)。

      package org.jboss.example;
      
      import io.undertow.server.HandlerWrapper;
      import io.undertow.server.HttpHandler;
      import io.undertow.server.handlers.builder.HandlerBuilder;
      
      import java.util.Collections;
      import java.util.Map;
      import java.util.Set;
      
      public class MyHandlerBuilder implements HandlerBuilder {
          public String name() {
              return "myHttpHandler";
          }
      
          public Map<String, Class<?>> parameters() {
              return Collections.<String, Class<?>>singletonMap("myParam", String.class);
          }
      
          public Set<String> requiredParameters() {
              return Collections.emptySet();
      
          }
      
          public String defaultParameter() {
              return null;
      
          }
      
          public HandlerWrapper build(final Map<String, Object> config) {
              return new HandlerWrapper() {
                  public HttpHandler wrap(HttpHandler handler) {
                      MyHttpHandler result = new MyHttpHandler(handler);
                      result.setMyParam((String) config.get("myParam"));
                      return result;
                  }
              };
          }
      }
    2. ファイル META-INF/services/io.undertow.server.handlers.builder.HandlerBuilder 内のエントリー。このファイルは、たとえば WEB-INF/classes のクラスパス上にある必要があります。

      org.jboss.example.MyHandlerBuilder

第11章 Java トランザクション API (JTA)

11.1. 概要

11.1.1. Java トランザクション API (JTA) の概要

はじめに

これらのトピックは、 Java トランザクション API (JTA) の基礎的な内容について取り上げます。

11.2. トランザクションの概念

11.2.1. トランザクション

トランザクションは 2 つ以上のアクションで構成されており、アクションすべてが成功または失敗する必要があります。成功した場合はコミット、失敗した場合はロールバックが結果的に実行されます。ロールバックでは、トランザクションがコミットを試行する前に、各メンバーの状態が元の状態に戻ります。

よく設計されたトランザクションの通常の標準は Atomic, Consistent, Isolated, and Durable (ACID) です。

11.2.2. トランザクションの ACID プロパティー

ACID は 原子性 (Atomicity)、一貫性 (Consistency)、独立性 (Isolation)、永続性 (Durability) の略語です。通常、この用語はデータベースやトランザクション操作において使用されます。

原子性 (Atomicity)
トランザクションの原子性を保つには、すべてのトランザクションメンバーが同じ決定を行う必要があります。これらのメンバーはコミットまたはロールバックを行います。原子性が保たれない場合の結果はヒューリスティックな結果と呼ばれます。
一貫性
一貫性とは、データベーススキーマの観点から、データベースに書き込まれたデータが有効なデータであることを保証するという意味です。データベースあるいは他のデータソースは常に一貫した状態でなければなりません。一貫性のない状態の例には、操作が中断される前にデータの半分が書き込まれてしまったフィールドなどがあります。すべてのデータが書き込まれた場合や、書き込みが完了しなかった時に書き込みがロールバックされた場合に、一貫した状態となります。
独立性 (Isolation)
独立性とは、トランザクションのスコープ外のプロセスがデータを変更できないように、トランザクションで操作されたデータが変更前にロックされる必要があることを意味します。
永続性 (Durability)
永続性とは、トランザクションのメンバーにコミットの指示を出してから外部で問題が発生した場合、問題が解決されると全メンバーがトランザクションのコミットを継続できるという意味です。ここで言う問題とは、ハードウェア、ソフトウェア、ネットワークなどのシステムが関係する問題のことです。

11.2.3. トラザクションコーディネーターまたはトランザクションマネージャー

JBoss EAP のトランザクションでは、トランザクションコーディネーターとトランザクションマネージャー (TM) という言葉は、ほとんど同じことを意味します。トランザクションコーディネーターという言葉は通常、分散 JTS トランザクションのコンテキストで使用されます。

JTA トランザクションでは、TM は JBoss EAP 内で実行され、2 フェーズコミットのプロトコルでトランザクションの参加者と通信します。

TM はトランザクションの参加者に対して、他のトランザクションの参加者の結果に従い、データをコミットするか、ロールバックするか指示します。こうすることで、確実にトランザクションが ACID 標準に準拠するようにします。

11.2.4. トランザクションの参加者

トランザクションの参加者は、状態をコミットまたはロールバックできるトランザクション内のリソースであり、一般的にデータベースまたは JMS ブローカーを生成します。ただし、トランザクションインターフェースを実装することにより、ユーザーコードがトランザクションの参加者として動作することもできます。トランザクションの各参加者は、状態をコミットまたはロールバックできるかどうかを独自に決定します。すべての参加者がコミットできる場合のみ、トランザクション全体が成功します。コミットできない参加者がある場合は、各参加者がそれぞれの状態をロールバックし、トランザクション全体が失敗します。TM は、コミットおよびロールバック操作を調整し、トランザクションの結果を判断します。

11.2.5. Java Transactions API (JTA)

Java Transactions API (JTA) は Java Enterprise Edition 仕様の一部で、JSR-907 に定義されています。

JTA の実装は、JBoss EAP アプリケーションサーバーの Narayana プロジェクトに含まれる TM を使用して実行されます。TM により、単一のグローバルトランザクションを使用してアプリケーションがさまざまなリソース (データベースや JMS ブローカーなど) を割り当てることができるようになります。グローバルトランザクションは XA トランザクションと呼ばれます。一般的に、このようなトランザクションには XA 機能を持つリソースが含まれますが、XA 以外のリソースをグローバルトランザクションに含めることもできます。XA 以外のリソースを XA 対応リソースとして動作させるのに役に立つ複数の最適化があります。詳細については、1 フェーズコミットの LRCO 最適化 を参照してください。

本書では、JTA という用語は以下の 2 つのことを意味します。

  1. Java EE 仕様で定義された Java トランザクション API
  2. TM がトランザクションをどのように処理するかを示します。

    TM は JTA トランザクションモードで動作し、データはメモリーによって共有されます。また、トランザクションコンテキストはリモート EJB 呼び出しによって転送されます。JTS モードでは、データは CORBA (Common Object Request Broker Architecture) メッセージを送信して共有され、トランザクションコンテキストは IIOP 呼び出しによって転送されます。複数の JBoss EAP サーバー上におけるトランザクションの分散は両方のモードでサポートされます。

11.2.6. Java Transaction Service (JTS)

Java Transaction Service (JTS) は、Object Transaction Service (OTS) と Java のマッピングです。Java EE アプリケーションは JTA API を使用してトランザクションを管理します。JTA API はトランザクションマネージャーが JTS モードに切り替わったときに JTS トランザクション実装と対話します。JTS は IIOP プロトコル上で動作します。JTS を使用するトランザクションマネージャーは Object Request Broker (ORB) と呼ばれるプロセスと Common Object Request Broker Architecture (CORBA) と呼ばれる通信標準を使用してお互いに通信します。詳細については、JBoss EAP Configuration GuideORB Configuration を参照してください。

アプリケーションの観点で JTA API を使用すると、JTS トラザクションは JTA トランザクションと同じように動作します。

注記

JBoss EAP に含まれる JTS の実装は、分散トランザクションをサポートします。完全準拠の JTS トランザクションとの違いは、外部のサードパーティー ORB との相互運用性です。この機能は、JBoss EAP ではサポートされません。サポートされる設定では、複数の JBoss EAP コンテナーでのみトランザクションが分散されます。

11.2.7. XA リソースおよび XA トランザクション

XA は eXtended Architecture を表し、複数のバックエンドデータストアを使用するトランザクションを定義するために X/Open Group によって開発されました。XA 標準は、グローバル TM とローカルリソースマネージャーとの間のインターフェースを定義します。XA では、4 つの ACID プロパティーすべてを保持しながらアプリケーションサーバー、データベース、キャッシュ、メッセージキューなどの複数のリソースが同じトランザクションに参加できるようにします。4 つの ACID プロパティーの 1 つは原子性であり、これは参加者の 1 つが変更のコミットに失敗した場合に他の参加者がトランザクションを中止し、トランザクションが発生する前の状態に戻すことを意味します。XA リソースは XA グローバルトランザクションに参加できるリソースです。

XA トランザクションは、複数のリソースにまたがることができるトランザクションです。これには、コーディネートを行う TM が関係します。この TM は、すべてが 1 つのグローバル XA トランザクションに関与する 1 つ以上のデータベースまたは他のトランザクションリソースを持ちます。

11.2.8. XA リカバリー

TM は X/Open XA 仕様を実装し、複数の XA リソースで XA トランザクションをサポートします。

XA リカバリーは、トランザクションの参加者であるリソースのいずれかがクラッシュしたり使用できなくなったりしても、トランザクションの影響を受けたすべてのリソースが確実に更新またはロールバックされるようにするプロセスのことです。JBoss EAP の範囲内では、XA データソース、JMS メッセージキュー、JCA リソースアダプターなどの XA リソースまたはサブシステムに対して、transactions サブシステムが XA リカバリーのメカニズムを提供します。

XA リカバリーはユーザーの介入がなくても実行されます。XA リカバリーに失敗すると、エラーがログ出力に記録されます。サポートが必要な場合は、Red Hat グローバルサポートサービスまでご連絡ください。XA リカバリープロセスは、デフォルトで 2 分ごとに開始される定期リカバリースレッドにより開始されます。定期リカバリースレッドにより、未完了のすべてのトランザクションが処理されます。

注記

不明なトランザクションのリカバリーを完了するには 4〜8 分かかることがあります。これはリカバリープロセスを複数回実行する必要がある場合があるためです。

11.2.9. XA リカバリープロセスの制限

XA リカバリーには以下の制限があります。

トランザクションログが正常にコミットされたトランザクションから消去されないことがある

XAResource のコミットメソッドが正常に完了し、トランザクションをコミットしてからコーディネーターがログをアップデートするまでに JBoss EAP サーバーがクラッシュすると、サーバーの再起動時に以下の警告メッセージが表示されることがあります。

ARJUNA016037: Could not find new XAResource to use for recovering non-serializable XAResource XAResourceRecord

これは、リカバリー時に JBoss トランザクションマネージャー (TM) はログのトランザクション参加者を確認し、コミットを再試行しようとするからです。最終的に、JBoss TM はリソースがコミットされたと見なし、コミットを再試行しなくなります。このような場合、トランザクションはコミットされデータの損失はないため、警告を無視しても問題ありません。

警告が表示されないようにするには、com.arjuna.ats.jta.xaAssumeRecoveryComplete プロパティーの値を true に設定します。このプロパティーは、登録された XAResourceRecovery インスタンスから新しい XAResource インスタンスが見つからないとチェックされます。true に設定すると、リカバリーで、以前のコミットの試行が成功したと見なされ、これ以上リカバリーを試行しなくてもインスタンスをログから削除できます。このプロパティーはグローバルであり、適切に使用しないと XAResource インスタンスがコミットされていない状態のままになるため、注意して使用する必要があります。

注記

JBoss EAP 7.0 には、トランザクションが正常にコミットされた後にトランザクションログを消去する拡張機能が実装されているため、上記の状況は頻繁に発生しません。

XAResource.prepare() の最後にサーバーがクラッシュすると、JTS トランザクションに対するロールバックは呼び出されません。
XAResource prepare() メソッド呼び出しの完了後に JBoss EAP サーバーがクラッシュすると、参加している XAResources はすべて準備済みの状態でロックされ、サーバーの再起動時にその状態のままになります。トランザクションがタイムアウトするか、DBA で手動でリソースをロールバックしてトランザクションログを消去するまで、トランザクションはロールバックされずリソースはロックされたままになります。詳細については、https://issues.jboss.org/browse/JBTM-2124 を参照してください。
周期リカバリーはコミットされたトランザクションで発生する可能性があります。

サーバーに過剰な負荷がかかっている場合、サーバーログには以下の警告メッセージとそれに続くスタックトレースが含まれる場合があります。

ARJUNA016027: Local XARecoveryModule.xaRecovery got XA exception XAException.XAER_NOTA: javax.transaction.xa.XAException

負荷が大きいと、トランザクションの処理時間と周期リカバリープロセスの活動が重なることがあります。周期リカバリープロセスは進行中のトランザクションを検出してロールバックを実行しようとしますが、トランザクションは完了するまで続行されます。周期リカバリーがロールバックの試行に失敗すると、ロールバックの失敗をサーバーログに記録します。この問題の原因は今後のリリースで修正される予定ですが、問題の回避策を適用できます。

com.arjuna.ats.jta.orphanSafetyInterval プロパティーの値をデフォルト値の 10000 ミリ秒よりも大きくし、リカバリープロセスの 2 つのフェーズの間隔を増やします。40000 ミリ秒の値が推奨されます。この設定では問題は解決されず、問題が発生して警告メッセージがログに記録される可能性が減少することに注意してください。詳細については、https://developer.jboss.org/thread/266729 を参照してください。

11.2.10. 2 フェーズコミットプロトコル

2 フェーズコミット (2PC) プロトコルは、トランザクションの結果を決定するアルゴリズムを参照します。2PC は XA トランザクションを完了するプロセスとしてトランザクションマネージャー (TM) によって開始されます。

フェーズ 1: 準備

最初のフェーズでは、トランザクションをコミットできるか、あるいはロールバックする必要があるかをトランザクションの参加者がトランザクションコーディネーターに通知します。

フェーズ 2: コミット

2 番目のフェーズでは、トランザクションコーディネーターがトランザクション全体をコミットするか、またはロールバックするかを決定します。いずれの参加者もコミットできない場合、トランザクションはロールバックしなければなりません。それ以外の場合、トランザクションはコミットできます。コーディネーターは何を行うかをリソースに指示し、リソースはその完了時にコーディネーターに通知します。この時点で、トランザクションは完了します。

11.2.11. トランザクションタイムアウト

原子性を確保し、トランザクションを ACID 標準に準拠させるために、トランザクションの一部が長期間実行される場合があります。トランザクションの参加者は、コミット時にデータベーステーブルまたはキュー内のメッセージの一部である XA リソースをロックする必要があります。また、TM は各トランザクション参加者からの応答を待ってからすべての参加者にコミットあるいはロールバックの指示を出す必要があります。ハードウェアあるいはネットワークの障害のため、リソースが永久にロックされることがあります。

トランザクションのタイムアウトをトランザクションと関連付け、ライフサイクルを制御することができます。タイムアウトのしきい値がトランザクションのコミットあるいはロールバック前に渡された場合、タイムアウトにより、自動的にトランザクションがロールバックされます。

トランザクションサブシステム全体に対しデフォルトのタイムアウト値を設定できます。または、デフォルトのタイムアウト値を無効にし、トランザクションごとにタイムアウトを指定できます。

11.2.12. 分散トランザクション

分散トランザクションは、複数の JBoss EAP サーバー上に参加者が存在するトランザクションです。Java Transaction Service (JTS) 仕様では、異なるベンダーのアプリケーションサーバー間で JTS トランザクションを分散可能にすることが規定されています。Java Transaction API (JTA) はこれを定義していませんが、JBoss EAP は JBoss EAP サーバー間での分散 JTA トランザクションをサポートしています。

注記

異なるベンダーのサーバー間でのトランザクション分散はサポートされません。

注記

他のベンダーのアプリケーションサーバーのドキュメントでは、分散トランザクションという用語が XA トランザクションを意味することがあります。JBoss EAP のドキュメントでは、複数の JBoss EAP アプリケーションサーバー間で分散されるトランザクションを分散トランザクションと呼びます。また、本書では、異なるリソースで構成されるトランザクション (データベースリソースや jms リソースなど) を XA トランザクションと呼びます。詳細については、Java Transaction Service (JTS) および XA データソースおよび XA トランザクション を参照してください。

11.2.13. ORB 移植性 API

Object Request Broker (ORB) とは、複数のアプリケーションサーバーで分散されるトランザクションの参加者、コーディネーター、リソース、および他のサービスにメッセージを送受信するプロセスのことです。ORB は標準的なインターフェース記述言語 (IDL) を使用してメッセージを通信し解釈します。Common Object Request Broker Architecture (CORBA) は JBoss EAP の ORB によって使用される IDL です。

ORB を使用する主なタイプのサービスは、Java トランザクションサービス (JTS) 仕様を使用する分散 Java トランザクションのシステムです。レガシーシステムなどの他のシステムは、通信にリモートエンタープライズ JavaBeans や JAX-WS または JAX-RS Web サービスなどの他のメカニズムではなく ORB を使用することがあります。

ORB 移植性 API は ORB とやりとりするメカニズムを提供します。この API は ORB への参照を取得するメソッドや、ORB からの受信接続をリッスンするモードにアプリケーションを置くメソッドを提供します。API のメソッドの一部はすべての ORB によってサポートされていません。このような場合、例外が発生します。

API は 2 つの異なるクラスによって構成されます。

  • com.arjuna.orbportability.orb
  • com.arjuna.orbportability.oa

ORB 移植性 API に含まれるメソッドとプロパティーの詳細については、Red Hat カスタマーポータルの JBoss EAP Javadocs バンドルを参照してください。

11.3. トランザクションの最適化

11.3.1. トランザクション最適化の概要

JBoss EAP のトランザクションマネージャー (TM) には、アプリケーションで利用できる複数の最適化機能が含まれています。

最適化機能は、特定のケースで 2 フェーズコミットプロトコルを拡張するために使用します。一般的に、TM によって、2 フェーズコミットを介して渡されるグローバルトランザクションが開始されます。ただし、特定のケースでこれらのトランザクションを最適化する場合は、TM によって完全な 2 フェーズコミットを行う必要がないため、処理は速くなります。

TM により使用される別の最適化機能は、以下で詳細に説明されています。

11.3.2. 1 フェーズコミット (1PC) の LRCO 最適化

1 フェーズコミット (1PC)

トランザクションでは、2 フェーズコミットプロトコル (2PC) がより一般的に使用されますが、両フェーズに対応する必要がなかったり、対応できない場合もあります。そのような場合、1 フェーズコミット (1PC) プロトコルを使用できます。1 フェーズコミットプロトコルは、XA または非 XA リソースの 1 つがグローバルトランザクションの一部である場合に使用されます。

一般的に、準備フェースでは、第 2 フェーズが処理されるまでリソースがロックされます。1 フェーズコミットは、準備フェースが省略され、コミットのみがリソースに対して処理されることを意味します。指定されない場合は、グローバルトランザクションに参加者が 1 つだけ含まれるときに 1 フェーズコミット最適化機能が自動的に使用されます。

最終リソースコミット最適化 (LRCO: Last Resource Commit Optimization)

非 XA データソースが XA トランザクションに参加する場合は、最終リソースコミット最適化 (LRCO) と呼ばれる最適化機能が使用されます。このプロトコルにより、ほとんどのトランザクションは正常に完了しますが、一部のエラーによってトランザクションの結果の一貫性が失われることがあります。そのため、この方法は最終手段として使用してください。

非 XA リソースは準備フェーズの終了時に処理され、コミットが試行されます。コミットに成功すると、トランザクションログが書き込まれ、残りのリソースがコミットフェーズに移動します。最終リソースがコミットに失敗すると、トランザクションはロールバックされます。

ローカルの TX データソースが 1 つのみトランザクションで使用されると、LRCO が自動的に適用されます。

これまでは、LRCO メソッドを使用して非 XA リソースを XA トランザクションに追加していました。ただし、この場合は LRCO によって失敗することがあります。LRCO メソッドを用いて非 XA リソースを XA トランザクションに追加するには、以下の手順に従います。

  1. XA トランザクションの準備
  2. LRCO のコミット
  3. tx ログの書き込み
  4. XA トランザクションのコミット

手順 2 と手順 3 の間でクラッシュした場合、データが不整合になり、XA トランザクションをコミットできなくなることがあります。データの不整合が発生する理由は、LRCO 非 XA リソースがコミットされるが、XA リソースの準備に関する情報が記録されなかったことです。リカバリーマネージャーはサーバーの起動後にリソースをロールバックします。CMR ではこの制限がなくなり、非 XA が XA トランザクションに安定して参加できるようになります。

注記

CMR は特別な LRCO 最適化機能であり、データソースにのみ使用できます。非 XA リソースには適していません。

11.3.2.1. Commit Markable Resource (CMR)

概要

Commit Markable Resource (CMR) インターフェースを使用してリソースマネージャーへのアクセスを設定すると、非 XA データソースを XA (2PC) トランザクションに安定的に登録できます。これは、非 XA リソースを完全にリカバリー可能にする LRCO アルゴリズムの実装です。

CMR を設定するには、以下のことを行う必要があります。

  1. データベースでテーブルを作成します。
  2. データソースを接続可能にします。
  3. 参照を transactions サブシステムに追加します。
データベースでテーブルを作成する

トランザクションには CMR リソースを 1 つだけ含めることができます。以下の SQL が機能するテーブルを作成する必要があります。

SELECT xid,actionuid FROM _tableName_ WHERE transactionManagerID IN (String[])
DELETE FROM _tableName_ WHERE xid IN (byte[[]])
INSERT INTO _tableName_ (xid, transactionManagerID, actionuid) VALUES (byte[],String,byte[])

SQL クエリーの例

Sybase:

CREATE TABLE xids (xid varbinary(144), transactionManagerID varchar(64), actionuid varbinary(28))

Oracle:

CREATE TABLE xids (xid RAW(144), transactionManagerID varchar(64), actionuid RAW(28))
CREATE UNIQUE INDEX index_xid ON xids (xid)

IBM:

CREATE TABLE xids (xid VARCHAR(255) for bit data not null, transactionManagerID
varchar(64), actionuid VARCHAR(255) for bit data not null)
CREATE UNIQUE INDEX index_xid ON xids (xid)

SQL Server:

CREATE TABLE xids (xid varbinary(144), transactionManagerID varchar(64), actionuid varbinary(28))
CREATE UNIQUE INDEX index_xid ON xids (xid)

Postgres:

CREATE TABLE xids (xid bytea, transactionManagerID varchar(64), actionuid bytea)
CREATE UNIQUE INDEX index_xid ON xids (xid)

MariaDB:

CREATE TABLE xids (xid BINARY(144), transactionManagerID varchar(64), actionuid BINARY(28))
CREATE UNIQUE INDEX index_xid ON xids (xid)

MySQL:

CREATE TABLE xids (xid VARCHAR(255), transactionManagerID varchar(64), actionuid VARCHAR(255))
CREATE UNIQUE INDEX index_xid ON xids (xid)
データソースを接続可能にする

デフォルトでは、CMR 機能はデータソースに対して無効になっています。有効にするには、データソースの設定を作成または変更し、connectible 属性を true に設定する必要があります。以下に、サーバーの XML 設定ファイルのデータソースセクションの例を示します。

<datasource enabled="true" jndi-name="java:jboss/datasources/ConnectableDS" pool-name="ConnectableDS" jta="true" use-java-context="true" connectable="true"/>
注記

この機能は XA データソースには適用されません。

また、以下のように管理 CLI を使用してリソースマネージャーを CMR として有効にすることもできます。

/subsystem=datasources/data-source=ConnectableDS:add(enabled="true", jndi-name="java:jboss/datasources/ConnectableDS", jta="true", use-java-context="true", connectable="true", connection-url="validConnectionURL", exception-sorter="org.jboss.jca.adapters.jdbc.extensions.mssql.MSSQLExceptionSorter", driver-name="h2")
新しい CMR 機能を使用するために既存のリソースを更新

CMR 機能を使用するために既存のデータソースのみを更新する必要がある場合は、connectable 属性を変更します。

/subsystem=datasources/data-source=ConnectableDS:write-attribute(name=connectable,value=true)
参照をトランザクションサブシステムに追加する

transactions サブシステムは、以下のような transactions サブシステム設定セクションへのエントリーを用いて CMR 対応のデータソースを特定します。

<subsystem xmlns="urn:jboss:domain:transactions:3.0">
    ...
    <commit-markable-resources>
        <commit-markable-resource jndi-name="java:jboss/datasources/ConnectableDS">
            <xid-location name="xids" batch-size="100" immediate-cleanup="false"/>
        </commit-markable-resource>
        ...
    </commit-markable-resources>
</subsystem>
注記

transactions サブシステムで CMR 参照を追加したら、サーバーを再起動する必要があります。

注記

データソース設定で exception-sorter パラメーターを使用します。詳細については、JBoss EAP Configuration GuideExample Datasource Configurations を参照してください。

11.3.3. 推定中止 (presumed-abort) の最適化

トランザクションをロールバックする場合、この情報をローカルで記録し、エンリストされたすべての参加者に通知します。この通知は形式的なもので、トランザクションの結果には影響しません。すべての参加者が通知されると、このトランザクションに関する情報を削除できます。

トランザクションのステータスに対する後続の要求が行われる場合、利用可能な情報はありません。このような場合、要求側はトランザクションが中断され、ロールバックされたと見なします。推定中止 (presumed-abort) の最適化は、トランザクションがコミットの実行を決定するまで参加者に関する情報を永続化する必要がないことを意味します。これは、トランザクションがコミットの実行を決定する前に発生した障害はトランザクションの中止であると推定されるためです。

11.3.4. 読み取り専用の最適化

参加者は、準備するよう要求されると、トランザクション中に変更したデータがないことをコーディネーターに伝えることができます。参加者が最終的にどうなってもトランザクションに影響を与えることはないため、このような参加者にトランザクションの結果について通知する必要はありません。この読み取り専用の参加者はコミットプロトコルの第 2 フェーズから省略可能です。

11.4. トランザクションの結果

11.4.1. トランザクションの結果

可能なトランザクションの結果は次の 3 つになります。

コミット
トランザクションの参加者すべてがコミットできる場合、トランザクションコーディネーターはコミットの実行を指示します。詳細については、トランザクションのコミット を参照してください。
ロールバック
トランザクションの参加者のいずれかがコミットできなかったり、トランザクションコーディネーターが参加者にコミットを指示できない場合は、トランザクションがロールバックされます。詳細については、トランザクションロールバックを参照してください。
ヒューリスティックな結果
トランザクションの参加者の一部がコミットし、他の参加者がロールバックした場合をヒューリスティックな結果と呼びます。詳細については、ヒューリスティックな結果を参照してください。

11.4.2. トランザクションのコミット

トランザクションの参加者がコミットすると、新規の状態が永続化されます。新規の状態はトランザクションで作業を行った参加者により作成されます。トランザクションのメンバーがデータベースに記録を書き込む時などが最も一般的な例になります。

コミット後、トランザクションの情報はトランザクションコーディネーターから削除され、新たに書き込まれた状態が永続状態となります。

11.4.3. トランザクションロールバック

トランザクションの参加者はトランザクションの開始前に、状態を反映するため状態をリストアし、ロールバックを行います。ロールバック後の状態はトランザクション開始前の状態と同じになります。

11.4.4. ヒューリスティックな結果

ヒューリスティックな結果 (アトミックでない結果) は、トランザクションでの参加者の決定がトランザクションマネージャーのものとは異なる状況です。ヒューリスティックな結果が起こると、システムの整合性が保たれなくなることがあり、通常、解決に人的介入が必要になります。ヒューリスティックな結果に依存するようなコードは記述しないようにしてください。

通常、ヒューリスティックな結果は、2 フェーズコミット (2PC) プロトコルの 2 番目のフェーズで発生します。まれにですが、この結果は 1PC で発生することがあります。多くの場合、これは基盤のハードウェアまたは基盤のサーバーの通信サブシステムの障害によって引き起こされます。

ヒューリスティックな結果は、さまざまなサブシステムまたはリソースのタイムアウトにより可能になります (トランザクションマネージャーと完全なクラッシュリカバリーを使用)。何らかの形の分散契約が必要なシステムでは、グローバルな結果という点でシステムのいくつかの部分が分岐する状況が発生することがあります。

ヒューリスティックな結果には 4 種類あります。

ヒューリスティックロールバック

コミット操作はリソースをコミットできませんでしたが、すべての参加者はロールバックでき、アトミックな結果が実現されました。

ヒューリスティックコミット

参加者のすべてが一方的にコミットしたため、ロールバック操作に失敗します。たとえば、コーディネーターが正常にトランザクションを準備したにも関わらず、ログ更新の失敗などでコーディネーター側で障害が発生したため、ロールバックの実行を決定した場合などに発生します。暫定的に参加者がコミットの実行を決定する場合があります。

ヒューリスティック混合

一部の参加者がコミットし、その他の参加者はロールバックした状態です。

ヒューリスティックハザード

更新の一部の配置が不明な状態です。既知の更新結果はすべてコミットまたはロールバックされています。

11.4.5. JBoss Transactions エラーと例外

UserTransaction クラスのメソッドがスローする例外に関する詳細については、http://docs.oracle.com/javaee/7/api/javax/transaction/UserTransaction.htmlUserTransaction API の仕様を参照してください。

11.5. トランザクションライフサイクルの概要

11.5.1. トランザクションライフサイクル

Java Transactions API (JTA) の詳細については、Java Transactions API (JTA) を参照してください。

リソースがトランザクションへの参加を要求すると、一連のイベントが開始されます。トランザクションマネージャー (TM) は、アプリケーションサーバー内に存在するプロセスであり、トランザクションを管理します。トランザクションの参加者は、トランザクションに参加するオブジェクトです。リソースは、データソース、JMS 接続ファクトリー、または他の JCA 接続です。

  1. アプリケーションが新しいトランザクションを開始する

    トランザクションを開始するために、アプリケーションは JNDI から (EJB の場合はアノテーションから) UserTransaction クラスのインスタンスを取得します。UserTransaction インターフェースには、トップレベルのトランザクションを開始、コミット、およびロールバックするメソッドが含まれています。新規作成されたトランザクションは、そのトランザクションを呼び出すスレッドと自動的に関連付けされます。ネストされたトランザクションは JTA ではサポートされないため、すべてのトランザクションがトップレベルのトランザクションとなります。

    UserTransaction.begin() メソッドが呼び出されると、EJB がトランザクションを開始します。このトランザクションのデフォルトの動作は TransactionAttribute アノテーションまたは ejb.xml 記述子の使用によって影響を受けることがあります。この時点以降に使用されたリソースは、このトランザクションと関連付けられます。2 つ以上のリソースが登録された場合、トランザクションは XA トランザクションになり、コミット時に 2 フェーズコミットプロトコルに参加します。

    注記

    デフォルトでは、トランザクションは EJB のアプリケーションコンテナーによって駆動されます。これは Container Managed Transaction (CMT) と呼ばれます。トランザクションをユーザー駆動にするには、Transaction ManagementBean Managed Transaction (BMT) に変更する必要があります。BMT では、UserTransaction オブジェクトはユーザーがトランザクションを管理するために使用できます。

  2. アプリケーションが状態を変更する

    次の手順では、アプリケーションが作業を実行して状態を変更します (登録されたリソースに対してのみ)。

  3. アプリケーションがコミットまたはロールバックすることを決定する

    アプリケーションの状態の変更が完了すると、アプリケーションはコミットするか、またはロールバックするかを決定し、適切なメソッド (UserTransaction.commit() または UserTransaction.rollback()) を呼び出します。CMT の場合、このプロセスは自動的に駆動されますが、BMT の場合は、UserTransaction のメソッドコミットまたはロールバックを明示的に呼び出す必要があります。

  4. TM がレコードからトランザクションを削除する

    コミットあるいはロールバックが完了すると、TM はレコードをクリーンアップし、トランザクションログからトランザクションに関する情報を削除します。

障害リカバリー

リソース、トランザクションの参加者、またはアプリケーションサーバーがクラッシュするか、使用できなくなった場合は、障害が解決され、リソースが再度使用できるようになったときに Transaction Manager がリカバリーを実行します。このプロセスは自動的に実行されます。詳細については、XA リカバリーを参照してください。

11.6. トランザクションサブシステムの設定

11.6.1. トランザクション設定の概要

はじめに

transactions サブシステムでは、統計、タイムアウト値、トランザクションロギングなどのトランザクションマネージャー (TM) のオプションを設定できます。

詳細については、JBoss EAP Configuration GuideTransactions Subsystem Configuration を参照してください。

11.6.2. トランザクションマネージャーの設定

トランザクションマネージャーは、Web ベースの管理コンソールまたはコマンドライン管理 CLI を使用して設定できます。

管理コンソールを使用したトランザクションマネージャーの設定

以下の手順は、Web ベースの管理コンソールを使用してトランザクションマネージャーを設定する方法を示しています。

  1. 画面上部の Configuration タブを選択します。
  2. JBoss EAP を管理対象ドメインとして実行している場合は、変更する任意のプロファイルを選択します。
  3. Subsystem リストから、Transactions を選択し、View をクリックします。
  4. 編集する設定に応じたタブ (リカバリーオプションの場合の Recovery など) で Edit をクリックします。
  5. 必要な変更を行い、Save をクリックして変更を保存します。
  6. Need Help? をクリックしてヘルプテキストを表示します。
管理 CLI を使用したトランザクションマネージャーの設定

管理 CLI で一連のコマンドを使用してトランザクションマネージャーを設定できます。これらのコマンドはすべて /subsystem=transactions (スタンドアロンサーバー向け) または /profile=default/subsystem=transactions/ (管理対象ドメインの default プロファイル向け) で始まります。

トランザクションマネージャーのすべての設定オプションの詳細なリストについては、トランザクションマネージャーの設定オプションを参照してください。

11.6.3. トランザクションロギング

11.6.3.1. トランザクションログメッセージ

トランザクションロガーに DEBUG ログレベルを使用することにより、ログファイルを読み取り可能な状態に保ちつつトランザクションを追跡できます。詳細なデバッグの場合は、TRACE ログレベルを使用します。トランザクションロガーの設定に関する詳細については、トランザクションサブシステムのロギング設定を参照してください。

TRACE ログレベルでログを記録するよう設定すると、トランザクションマネージャー (TM) は多くのロギング情報を生成できます。最も一般的なメッセージの一部は次のとおりです。このリストは包括的ではなく、他のメッセージが表示されることもあります。

表11.1 トランザクション状態の変更

トランザクションの開始

トランザクションが開始されたら、クラス com.arjuna.ats.arjuna.coordinator.BasicAction のメソッド Begin が実行され、メッセージ BasicAction::Begin() for action-id <transaction uid> でログに示されます。

トランザクションのコミット

トランザクションがコミットされたら、クラス com.arjuna.ats.arjuna.coordinator.BasicAction のメソッド Commit が実行され、メッセージ BasicAction::Commit() for action-id <transaction uid> でログに示されます。

トランザクションのロールバック

トランザクションがロールバックされたら、クラス com.arjuna.ats.arjuna.coordinator.BasicAction のメソッド Rollback が実行され、メッセージ BasicAction::Rollback() for action-id <transaction uid> でログに示されます。

トランザクションのタイムアウト

トランザクションがタイムアウトすると、com.arjuna.ats.arjuna.coordinator.TransactionReaper のメソッド doCancellations が実行され、Reaper Worker <thread id> attempting to cancel <transaction uid> とログに示されます。この結果、上記のように同じスレッドがトランザクションをロールバックすることが示されます。

11.6.3.2. トランザクションサブシステムのロギング設定

JBoss EAP の他のログ設定に依存せずにログに記録されたトランザクションに関する情報の量を制御できます。ログ設定は、管理コンソールまたは管理 CLI を使用して設定できます。

管理コンソールを使用したトランザクションロガーの設定
  1. ロギングサブシステム設定に移動します。

    1. 管理コンソールで、Configuration タブをクリックします。管理対象ドメインを使用する場合は、最初に適切なサーバープロファイルを選択する必要があります。
    2. Logging サブシステムを選択し、View をクリックします。
  2. com.arjuna 属性を編集します。

    Log Categories タブを選択します。com.arjuna エントリーがすでに存在します。com.arjuna を選択し、Attributes セクションの Edit をクリックします。ログレベルを変更し、親ハンドラーを使用するかどうかを選択できます。

    • ログレベル:

      トランザクションにより大量のロギング出力が生成されることがあるため、サーバーのログがトランザクション出力で満たされないようデフォルトのロギングレベルは WARN に設定されます。トランザクション処理の詳細を確認する必要がある場合は、トランザクション ID が表示されるよう TRACE ログレベルを使用します。

    • 親ハンドラーの使用:

      親ハンドラーはロガーが出力を親ロガーに送信するかどうかを指定します。デフォルトの動作は true です。

  3. Save をクリックして変更を保存します。
管理 CLI を使用したトランザクションロガーの設定

以下のコマンドを使用して管理 CLI からログレベルを設定します。スタンドアロンサーバーの場合は、コマンドから /profile=default を削除します。

/profile=default/subsystem=logging/logger=com.arjuna:write-attribute(name=level,value=VALUE)

11.6.4. トランザクションの参照と管理

管理 CLI では、トランザクションレコードを参照および操作する機能がサポートされます。この機能は、TM と JBoss EAP の管理 API 間の対話によって提供されます。

TM は、待機中の各トランザクションとトランザクションに関連する参加者に関する情報を、オブジェクトストアと呼ばれる永続ストレージに格納します。管理 API は、オブジェクトストアを log-store と呼ばれるリソースとして公開します。probe と呼ばれる API 操作はトランザクションログを読み取り、各レコードに対してノードパスを作成します。probe コマンドは、log-store を更新する必要があるときに、いつでも手動で呼び出すことができます。トランザクションログが即座に表示され、非表示になるのは、正常な挙動です。

ログストアの更新

このコマンドは、管理対象ドメインでプロファイル default を使用するサーバーグループに対してログストアを更新します。スタンドアローンサーバーの場合は、コマンドから profile=default を削除します。

/profile=default/subsystem=transactions/log-store=log-store/:probe
準備済みトランザクションすべての表示

準備されたすべてのトランザクションを表示するには、最初にログストアを更新し (ログストアの更新 を参照)、ファイルシステムの ls コマンドに類似した機能を持つ次のコマンドを実行します。

ls /profile=default/subsystem=transactions/log-store=log-store/transactions

または、

/host=master/server=server-one/subsystem=transactions/log-store=log-store:read-children-names(child-type=transactions)

各トランザクションが一意の ID とともに表示されます。個々の操作は、各トランザクションに対して実行できます (トランザクションの管理 を参照)。

11.6.4.1. トランザクションの管理

トランザクションの属性の表示

JNDI 名、EIS 製品名およびバージョン、ステータスなどのトランザクションに関する情報を表示するには、:read-resource 管理 CLIコマンドを使用します。

/profile=default/subsystem=transactions/log-store=log-store/transactions=0\:ffff7f000001\:-b66efc2\:4f9e6f8f\:9:read-resource
トランザクションの参加者の表示

各トランザクションログには、participants (参加者) と呼ばれる子要素が含まれます。トランザクションの参加者を確認するには、この要素に対して read-resource 管理 CLI コマンドを使用します。参加者は JNDI 名によって識別されます。

/profile=default/subsystem=transactions/log-store=log-store/transactions=0\:ffff7f000001\:-b66efc2\:4f9e6f8f\:9/participants=java\:\/JmsXA:read-resource

結果は以下のようになります。

{
   "outcome" => "success",
   "result" => {
       "eis-product-name" => "ActiveMQ",
       "eis-product-version" => "2.0",
       "jndi-name" => "java:/JmsXA",
       "status" => "HEURISTIC",
       "type" => "/StateManager/AbstractRecord/XAResourceRecord"
   }
}

ここで示された結果ステータスは HEURISTIC であり、リカバリーが可能です。詳細については、トランザクションのリカバリーを参照してください。

特別な場合では、ログに対応するトランザクションレコードがないオーファンレコード (XAResourceRecords) をオブジェクトストアに作成できます。たとえば、準備済みに XA リソースが TM が記録する前にクラッシュし、ドメイン管理 API ではアクセスできません。このようなレコードにアクセスするには、管理オプション expose-all-logstrue に設定する必要があります。このオプションは管理モデルには保存されず、サーバーが再起動されると false にリストアされます。

/profile=default/subsystem=transactions/log-store=log-store:write-attribute(name=expose-all-logs, value=true)

代わりに以下のコマンドを実行すると、トラザクション参加者 ID が集約された形式で表示されます。

/host=master/server=server-one/subsystem=transactions/log-store=log-store/transactions=0\:ffff7f000001\:-b66efc2\:4f9e6f8f\:9:read-children-names(child-type=participants)
トランザクションの削除

各トランザクションログは、トランザクションを表すトランザクションログを削除する :delete 操作をサポートします。

/profile=default/subsystem=transactions/log-store=log-store/transactions=0\:ffff7f000001\:-b66efc2\:4f9e6f8f\:9:delete
トランザクションのリカバリー

トランザクションの各参加者は、:recover 管理 CLI コマンドを使用したリカバリーをサポートします。

/profile=default/subsystem=transactions/log-store=log-store/transactions=0\:ffff7f000001\:-b66efc2\:4f9e6f8f\:9/participants=2:recover
  • トランザクションの状態が HEURISTIC である場合は、リカバリー操作によって状態が PREPARE に変わり、リカバリーがトリガーされます。
  • トランザクションの参加者の 1 つがヒューリスティックな場合、リカバリー操作は commit 操作を再実行しようとします。成功した場合、トランザクションログから参加者が削除されます。これを確認するには、log-store 上で :probe 操作を再実行し、参加者がリストされていないことを確認します。これが最後の参加者の場合は、トランザクションも削除されます。
リカバリーが必要なトランザクションの状態を更新します。

トランザクションをリカバリーする必要がある場合は、リカバリーを試行する前に :refresh 管理 CLI コマンドを使用して、トランザクションのリカバリーが必要であるかを確認できます。

/profile=default/subsystem=transactions/log-store=log-store/transactions=0\:ffff7f000001\:-b66efc2\:4f9e6f8f\:9/participants=2:refresh

11.6.5. トランザクション統計情報の表示

トランザクションマネージャーの統計が有効になっていると、トランザクションマネージャーにより処理されたトラザクションの統計を表示できます。トランザクションマネージャーの統計を有効にする方法については、トランザクションマネージャーの設定を参照してください。

管理コンソールまたは管理 CLI を使用して統計を表示できます。管理コンソールでは、トラザクションの統計は Runtime タブから Transactions サブシステムに移動することにより利用できます。管理コンソールではすべての統計を利用できるわけではありません。

以下の表は、利用できる統計とその説明を示しています。

表11.2 トランザクションサブシステムの統計

統計説明

number-of-transactions

このサーバー上でトランザクションマネージャーにより処理されるトランザクションの合計数。

number-of-committed-transactions

このサーバー上でトランザクションマネージャーにより処理されるコミット済みトランザクションの数。

number-of-aborted-transactions

このサーバー上でトランザクションマネージャーにより処理されるアボートされたトランザクションの数。

number-of-timed-out-transactions

このサーバー上でトランザクションマネージャーにより処理されるタイムアウトのトランザクションの数。タイムアウトしたトランザクションの数はアボートされたトランザクションの数まで計算されます。

number-of-heuristics

ヒューリスティック状態のトランザクションの数。

number-of-inflight-transactions

開始した未完了のトランザクションの数。

number-of-application-rollbacks

障害の原因がアプリケーションであった失敗トランザクションの数。

number-of-resource-rollbacks

障害の原因がリソースであった失敗トランザクションの数。

11.7. 実際のトランザクションの使用

11.7.1. トランザクション使用の概要

次の手順は、アプリケーションでトランザクションを使用する必要がある場合に役に立ちます。

11.7.2. トランザクションの制御

はじめに

この手順のリストでは、JTS API を使用するアプリケーションでトランザクションを制御するさまざまな方法を概説します。

11.7.3. トランザクションの開始

この手順では、新しいトランザクションの開始方法を示します。実行するトランザクションマネージャー (TM) が JTA または JTS のいずれかで設定されていれば API は同じになります。

  1. UserTransaction のインスタンスを取得する

    @TransactionManagement(TransactionManagementType.BEAN) アノテーションを用いると、JNDI、インジェクション、または EJB のコンテキストを使用してインスタンスを取得できます (EJB が Bean 管理のトランザクションを使用する場合)。

    1. JNDI

      new InitialContext().lookup("java:comp/UserTransaction")
    2. インジェクション

      @Resource UserTransaction userTransaction;
    3. コンテキスト

      • ステートレス/ステートフル Bean の場合

        @Resource SessionContext ctx;
        ctx.getUserTransaction();
      • メッセージ駆動型 Bean の場合

        @Resource MessageDrivenContext ctx;
        ctx.getUserTransaction()
  2. データソースへの接続後に UserTransaction.begin() を呼び出す

    try {
        System.out.println("\nCreating connection to database: "+url);
        stmt = conn.createStatement();  // non-tx statement
        try {
            System.out.println("Starting top-level transaction.");
            userTransaction.begin();
            stmtx = conn.createStatement(); // will be a tx-statement
            ...
        }
    }
    JTS 仕様を使用して既存のトランザクションに参加する
    EJB (CMT または BMT のいずれかと使用) の利点の 1 つは、コンテナーがトランザクション処理の内部をすべて管理することです。そのため、ユーザーは JBoss EAP コンテナー間の XA トランザクションまたはトランザクションディストリビューションの一部であるトランザクションを処理する必要がありません。

結果

トランザクションが開始します。トランザクションをコミットまたはロールバックするまで、データソースのすべての使用でトランザクションに対応します。

完全な例については、JTA トランザクションの例 を参照してください。

11.7.4. ネストされたトランザクション

ネストされたトランザクションを用いると、アプリケーションは既存のトランザクションに埋め込まれるトランザクションを作成できます。このモデルでは、再帰的に複数のサブトランザクションをトランザクションに埋め込むことができます。親トランザクションをコミットまたはロールバックせずにサブトランザクションをコミットまたはロールバックできます。しかし、コミット操作の結果は、先祖のトランザクションがすべてコミットしたかどうかによって決まります。

実装固有の情報については、Narayana プロジェクトドキュメンテーションを参照してください。

ネストされたトランザクションは、JTS 仕様と使用した場合のみ利用できます。ネストされたトランザクションは JBoss EAP アプリケーションサーバーではサポートされない機能です。また、多くのデータベースベンダーがネストされたトランザクションをサポートしないため、ネストされたトランザクションをアプリケーションに追加する前にデータベースベンダーにお問い合わせください。

11.7.5. トランザクションのコミット

この手順では、Java Transaction API (JTA) を使用してトランザクションをコミットする方法を説明します。

前提条件

トランザクションは、コミットする前に開始する必要があります。トランザクションの開始方法については、トランザクションの開始を参照してください。

  1. UserTransaction の commit() メソッドを呼び出す

    UserTransaction の commit() メソッドを呼び出す場合、TM はトランザクションをコミットしようとします。

    @Inject
    private UserTransaction userTransaction;
    
    public void updateTable(String key, String value) {
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        try {
            userTransaction.begin();
            <!-- Perform some data manipulation using entityManager -->
            ...
            // Commit the transaction
            userTransaction.commit();
        } catch (Exception ex) {
            <!-- Log message or notify Web page -->
            ...
            try {
                userTransaction.rollback();
            } catch (SystemException se) {
                throw new RuntimeException(se);
            }
            throw new RuntimeException(ex);
        } finally {
            entityManager.close();
        }
    }
  2. Container Managed Transactions (CMT) を使用する場合は、手動でコミットする必要がない

    Bean がコンテナー管理トランザクションを使用するよう設定すると、コンテナーはコードで設定したアノテーションに基づいてトランザクションライフサイクルを管理します。

    @PersistenceContext
    private EntityManager em;
    
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void updateTable(String key, String value)
      <!-- Perform some data manipulation using entityManager -->
      ...
    }

結果

データソースがコミットされ、トランザクションが終了します。そうでない場合は、例外が発生します。

注記

完全な例については、JTA トランザクションの例 を参照してください。

11.7.6. トランザクションのロールバック

この手順では、Java Transaction API (JTA) を使用してトランザクションをロールバックする方法を説明します。

前提条件

トランザクションは、ロールバックする前に開始する必要があります。トランザクションの開始方法については、トランザクションの開始を参照してください。

  1. UserTransaction の rollback() メソッドを呼び出す

    UserTransactionrollback() メソッドを呼び出す場合、TM はトランザクションをロールバックし、データを前の状態に戻そうとします。

    @Inject
    private UserTransaction userTransaction;
    
    public void updateTable(String key, String value)
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        try {
            userTransaction.begin():
            <!-- Perform some data manipulation using entityManager -->
              ...
              // Commit the transaction
            userTransaction.commit();
        } catch (Exception ex) {
            <!-- Log message or notify Web page -->
            ...
            try {
                userTransaction.rollback();
            } catch (SystemException se) {
                throw new RuntimeException(se);
            }
            throw new RuntimeException(e);
        } finally {
            entityManager.close();
        }
    }
  2. コンテナー管理トランザクション (CMT) を使用する場合は、手動でトランザクションをロールバックする必要がない

    Bean がコンテナー管理トランザクションを使用するよう設定すると、コンテナーはコードで設定したアノテーションに基づいてトランザクションライフサイクルを管理します。

注記

CMT のロールバックは RuntimeException が発生すると実行されます。setRollbackOnly メソッドを明示的に呼び出してロールバックを発生させることもできます。または、 アプリケーション例外の @ApplicationException(rollback=true) を使用してロールバックできます。

結果

トランザクションは TM によりロールバックされます。

注記

完全な例については、JTA トランザクションの例 を参照してください。

11.7.7. トランザクションにおけるヒューリスティックな結果の処理方法

ヒューリスティックなトランザクションの結果はよく発生するものではなく、通常は例外的な原因が存在します。ヒューリスティックという言葉は「手動」を意味し、こうした結果は通常手動で処理する必要があります。トランザクションのヒューリスティックな結果については、ヒューリスティックな結果を参照してください。

この手順では、Java Transaction API (JTA) を使用してトランザクションのヒューリスティックな結果を処理する方法を説明します。

  1. 原因を調べる: トランザクションのヒューリスティックな結果の全体的な原因は、リソースマネージャーがコミットまたはロールバックの実行を約束したにも関わらず、約束を守らなかったことにあります。原因としては、サードパーティーコンポーネント、サードパーティーコンポーネントと JBoss EAP 6 間の統合レイヤー、または JBoss EAP 6 自体の問題が考えられます。

    ヒューリスティックなエラーの最も一般的な原因として圧倒的に多いのが、環境の一時的な障害とリソースマネージャーを扱うコードのコーディングエラーの 2 つです。

  2. 環境の一時的な障害を修復する: 通常、環境内で一時的な障害が発生した場合は、ヒューリスティックなエラーを発見する前に気づくはずです。原因としては、ネットワークの停止、ハードウェア障害、データベース障害、電源異常などが考えられます。

    ストレステストの実施中にテスト環境でヒューリスティックな結果が発生した場合は、使用している環境の脆弱性に関する情報が提供されます。

    警告

    JBoss EAP は、障害発生時に非ヒューリスティックな状態にあるトランザクションの自動リカバリーを行いますが、ヒューリスティックなトランザクションのリカバリーを試行しません。

  3. リソースマネージャーのベンダーに連絡する: 環境に明らかな障害がない場合や、ヒューリスティックな結果が容易に再現可能な場合は、コーディングエラーである可能性があります。サードパーティーのベンダーに連絡して、解決方法の有無を確認してください。JBoss EAP の TM 自体に問題があると思われる場合は、Red Hat グローバルサポートサービスまでご連絡ください。
  4. 管理 CLI から手動でトランザクションの回復を試行します。詳細については、トランザクションのリカバリーを参照してください。
  5. テスト環境でログを削除し、JBoss EAP を再起動する: テスト環境である場合や、データの整合性を気にしない場合は、トランザクションログを削除して JBoss EAP を再起動すると、ヒューリスティックな結果はなくなります。デフォルトでは、トランザクションログの場所はスタンドアロンサーバーでは EAP_HOME/standalone/data/tx-object-store/、管理対象ドメインでは EAP_HOME/domain/servers/SERVER_NAME/data/tx-object-store になります。管理対象ドメインの SERVER_NAME は、サーバーグループに参加している個々のサーバー名を示しています。

    注記

    トランザクションログの場所は、使用中のオブジェクトストアや oject-store-relative-to パラメーターおよび object-store-path パラメーターの値セットによっても異なります。ファイルシステムログ (標準のシャドウや Apache ActiveMQ Artemis ログなど) では、デフォルトの方向の場所が使用されますが、JDBC オブジェクトストアを使用する場合、トランザクションログはデータベースに保存されます。

  6. 手動で結果を解決する: トランザクションの結果を手動で解決するプロセスは、障害の厳密な状況によって大きく左右されます。通常は、以下の手順に従って、それぞれの状況に適用する必要があります。

    1. 関連するリソースマネージャーを特定する。
    2. TM の状態とリソースマネージャーを調べる。
    3. 関与する 1 つ以上のコンポーネント内でログのクリーンアップとデータ調整を手動で強制する。

      これらの手順を実行する方法の詳細は、本書の範囲外となります。

11.7.8. JTA トランザクションのエラー処理

11.7.8.1. トランザクションエラーの処理

トランザクションエラーは、多くの場合、タイミングに依存するため、解決するのが困難です。以下に、一部の一般的なエラーと、これらのエラーのトラブルシューティングに関するヒントを示します。

注記

これらのガイドラインはヒューリスティックエラーに適用されません。ヒューリスティックエラーが発生した場合は、トランザクションにおけるヒューリスティックな結果の処理方法 を参照し、Red Hat グローバルサポートサービスまでお問い合わせください。

トランザクションがタイムアウトになったが、ビジネスロジックスレッドが認識しませんでした。

多くの場合、このようなエラーは、Hibernate がレイジーロードのためにデータベース接続を取得できない場合に発生します。頻繁に発生する場合は、タイムアウト値を増加できます。トランザクションマネージャーの設定 を参照してください。

引き続き問題が解決されない場合は、パフォーマンスを向上させるために外部環境を調整するか、さらに効率的になるようコードを再構築できます。タイムアウトの問題が解消されない場合は、Red Hat グローバルサポートサービスにお問い合わせください。

トランザクションがすでにスレッドで実行されているか、NotSupportedException 例外が発生する

NotSupportedException 例外は、通常、JTA トランザクションをネストしようとし、ネストがサポートされていないことを示します。トランザクションをネストしようとしないときは、多くの場合、スレッドプールタスクで別のトランザクションが開始されますが、トランザクションを中断または終了せずにタスクが終了します。

通常、アプリケーションは、これを自動的に処理する UserTransaction を使用します。その場合は、フレームワークに問題があることがあります。

コードで TransactionManager メソッドまたは Transaction メソッドを直接使用する場合は、トランザクションをコミットまたはロールバックするときに次の動作に注意してください。コードで TransactionManager メソッドを使用してトランザクションを制御する場合は、トランザクションをコミットまたはロールバックすると、現在のスレッドからトランザクションの関連付けが解除されます。ただし、コードで Transaction メソッドを使用する場合は、トランザクションを、実行中のスレッドに関連付けることができず、スレッドプールにスレッドを返す前にスレッドからトランザクションの関連付けを手動で解除する必要があります。

2 番目のローカルリソースを登録することはできません。
このエラーは、2 番目の非 XA リソースをトランザクションに登録しようとした場合に、発生します。1 つのトランザクションで複数のリソースが必要な場合、それらのリソースは XA である必要があります。

11.8. トランザクションに関するリファレンス

11.8.1. JTA トランザクションの例

この例では、 JTA トランザクションを開始、コミット、およびロールバックする方法を示します。使用している環境に合わせて接続およびデータソースパラメーターを調整し、データベースで 2 つのテストテーブルをセットアップする必要があります。

public class JDBCExample {
    public static void main (String[] args) {
        Context ctx = new InitialContext();
        // Change these two lines to suit your environment.
        DataSource ds = (DataSource)ctx.lookup("jdbc/ExampleDS");
        Connection conn = ds.getConnection("testuser", "testpwd");
        Statement stmt = null; // Non-transactional statement
        Statement stmtx = null; // Transactional statement
        Properties dbProperties = new Properties();

        // Get a UserTransaction
        UserTransaction txn = new InitialContext().lookup("java:comp/UserTransaction");

        try {
            stmt = conn.createStatement();  // non-tx statement

            // Check the database connection.
            try {
                stmt.executeUpdate("DROP TABLE test_table");
                stmt.executeUpdate("DROP TABLE test_table2");
            }
            catch (Exception e) {
                throw new RuntimeException(e);
                // assume not in database.
            }

            try {
                stmt.executeUpdate("CREATE TABLE test_table (a INTEGER,b INTEGER)");
                stmt.executeUpdate("CREATE TABLE test_table2 (a INTEGER,b INTEGER)");
            }
            catch (Exception e) {
                throw new RuntimeException(e);
            }

            try {
                System.out.println("Starting top-level transaction.");

                txn.begin();

                stmtx = conn.createStatement(); // will be a tx-statement

                // First, we try to roll back changes

                System.out.println("\nAdding entries to table 1.");

                stmtx.executeUpdate("INSERT INTO test_table (a, b) VALUES (1,2)");

                ResultSet res1 = null;

                System.out.println("\nInspecting table 1.");

                res1 = stmtx.executeQuery("SELECT * FROM test_table");

                while (res1.next()) {
                    System.out.println("Column 1: "+res1.getInt(1));
                    System.out.println("Column 2: "+res1.getInt(2));
                }
                System.out.println("\nAdding entries to table 2.");

                stmtx.executeUpdate("INSERT INTO test_table2 (a, b) VALUES (3,4)");
                res1 = stmtx.executeQuery("SELECT * FROM test_table2");

                System.out.println("\nInspecting table 2.");

                while (res1.next()) {
                    System.out.println("Column 1: "+res1.getInt(1));
                    System.out.println("Column 2: "+res1.getInt(2));
                }

                System.out.print("\nNow attempting to rollback changes.");

                txn.rollback();

                // Next, we try to commit changes
                txn.begin();
                stmtx = conn.createStatement();
                System.out.println("\nAdding entries to table 1.");
                stmtx.executeUpdate("INSERT INTO test_table (a, b) VALUES (1,2)");
                ResultSet res2 = null;

                System.out.println("\nNow checking state of table 1.");

                res2 = stmtx.executeQuery("SELECT * FROM test_table");

                while (res2.next()) {
                    System.out.println("Column 1: "+res2.getInt(1));
                    System.out.println("Column 2: "+res2.getInt(2));
                }

                System.out.println("\nNow checking state of table 2.");

                stmtx = conn.createStatement();

                res2 = stmtx.executeQuery("SELECT * FROM test_table2");

                while (res2.next()) {
                    System.out.println("Column 1: "+res2.getInt(1));
                    System.out.println("Column 2: "+res2.getInt(2));
                }

                txn.commit();
            }
            catch (Exception ex) {
                throw new RuntimeException(ex);

            }
        }
        catch (Exception sysEx) {
            sysEx.printStackTrace();
            System.exit(0);
        }
    }
}

11.8.2. トランザクション API ドキュメンテーション

トランザクション JTA API ドキュメンテーションは以下の場所で javadoc として利用できます。

Red Hat JBoss Developer Studio を使用してアプリケーションを開発する場合は、 API ドキュメンテーションが Help メニューに含まれています。

第12章 Java 永続 API (JPA)

12.1. Java 永続 API (JPA) について

Java Persistence API (JPA) は、Java オブジェクトまたはクラスとリレーショナルデータベース間でデータのアクセス、永続化、および管理を行うための Java 仕様です。この JPA 仕様では、透過オブジェクトまたはリレーショナルマッピングのパラダイムが考慮されます。オブジェクトまたはリレーショナル永続化メカニズムに必要な基本的な API とメタデータが標準化されます。

注記

JPA 自体は製品ではなく仕様にすぎません。それ自体では永続化やその他の処理を実行できません。JPA はインターフェースセットにすぎず、実装を必要とします。

12.2. Hibernate Core

Hibernate Core は、Java 言語のオブジェクトリレーショナルマッピングフレームワークです。これは、オブジェクト指向ドメインモデルをリレーショナルデータベースにマッピングするためのフレームワークを提供するため、アプリケーションはデータベースとの直接対話を回避できます。Hibernate では、直接的な永続データベースアクセスを高レベルオブジェクト処理関数に置き換えることにより、オブジェクトリレーショナルインピーダンスの不一致の問題が解決されます。

12.3. Hibernate EntityManager

Hibernate EntityManager を使用すると、Java Persistence 2.1 仕様で定義されたように、プラグラミングインターフェースとライフサイクルルールが実装されます。このラッパーを Hibernate Annotations とともに使用することにより、 成熟した Hibernate Core の上に完全な (およびスタンドアロンの) JPA 永続化ソリューションが実装されます。プロジェクトのビジネス上のニーズまたは技術的なニーズに応じて、これら 3 つすべて、JPA プログラミングインターフェースなしのアノテーション、または純粋なネイティブ Hibernate Core の組み合わせを使用できます。いつでも Hibernate ネイティブ API、または必要な場合はネイティブ JDBC および SQL を使用できます。また、JBoss EAP が完全な Java 永続化ソリューションとともに提供されます。

JBoss EAP は Java Persistence 2.1 仕様に完全準拠しています。また、Hibernate はこの仕様に追加機能を提供します。JPA と JBoss EAP を使用するには、JBoss EAP に同梱されている bean-validationgreeter、および kitchensink クイックスタートを参照してください。クイックスタートのダウンロードおよび実行方法については、クイックスタートサンプルの使用を参照してください。

JPA の永続化は EJB 3 またはより新しい CDI、Java Context and Dependency Injection などのコンテナーと特定のコンテナーの外部で実行されるスタンドアロン Java SE アプリケーションで利用できます。両方の環境で以下のプログラミングインターフェースとアーティファクトを利用できます。

EntityManagerFactory
エンティティーマネージャーファクトリーはエンティティーマネージャーインスタンスを提供し、すべてのインスタンスは、特定の実装などで定義されたのと同じデフォルト設定を使用するために同じデータベースに接続するよう設定されます。複数のエンティティーマネージャーファクトリーを準備して複数のデータストアにアクセスできます。このインターフェースはネイティブ Hibernate の SessionFactory に似ています。
EntityManager
EntityManager API は、特定の作業単位でデータベースにアクセスするために使用されます。また、永続エンティティーインスタンスを作成および削除してプライマリーキー ID でエンティティーを見つけたり、すべてのエンティティーに対してクエリーを実行したりするためにも使用されます。このインターフェースは、Hibernate のセッションに似ています。
永続コンテキスト
永続コンテキストは、永続エンティティー ID が一意のエンティティーインスタンスであるエンティティーインスタンスのセットです。永続コンテキスト内で、エンティティーインスタンスとそのライフサイクルは特定のエンティティーマネージャーによって管理されます。このコンテキストのスコープはトランザクションまたは拡張された作業単位のいずれかです。
永続ユニット
該当するエンティティーマネージャーで管理できるエンティティータイプのセットは、永続ユニットにより定義されます。永続ユニットは、アプリケーションで関連付けまたはグループ化され、単一データストアに対するマッピングで併置する必要があるすべてのクラスのセットを定義します。
コンテナー管理エンティティーマネージャー
ライフサイクルがコンテナーにより管理されるエンティティーマネージャー。
アプリケーション管理エンティティーマネージャー
ライフサイクルがアプリケーションにより管理されるエンティティーマネージャー。
JTA エンティティーマネージャー
JTA トランザクションに関与するエンティティーマネージャー。
リソースローカルエンティティーマネージャー
リソーストランザクション (JTA トランザクションではない) を使用するエンティティーマネージャー。

12.4. 単純な JPA アプリケーションの作成

Red Hat Developer Studio で単純な JPA アプリケーションを作成する場合は、以下の手順を実行します。

  1. JBoss Developer Studio で JPA プロジェクトを作成します。

    1. Red Hat JBoss Developer Studio で、File-→ New -→ Project をクリックします。リストで JPA を見つけ、展開し、JPA Project を選択します。以下のダイアログが表示されます。

      図12.1 新規 JPA プロジェクトダイアログ

      This is the new JPA Project dialog.
    2. プロジェクト名を入力します。
    3. ターゲットランタイムを選択します。ターゲットランタイムがない場合は、Define New Server を使用した JBoss EAP Server の追加 に記載された手順に従って新しいサーバーとランタイムを定義します。
    4. JPA version (JPA バージョン) de 2.1 が選択されていることを確認します。
    5. Configuration (設定)Basic JPA Configuration (基本的な JPA 設定) を選択します。
    6. Finish をクリックします。
    7. 要求されたら、このタイプのプロジェクトを JPA パースペクティブウインドウに関連付けるかどうかを選択します。
  2. 新しい永続性設定ファイルを作成および設定します。

    1. Red Hat JBoss Developer Studio で EJB 3.x プロジェクトを開きます。
    2. Project Explorer (プロジェクトエクスプローラー) パネルでプロジェクトルートディレクトリーを右クリックします。
    3. New (新規)Other (その他)…​. を選択します。
    4. XML フォルダーから XML File (XML ファイル) を選択し、Next (次へ) をクリックします。
    5. 親ディレクトリーとして ejbModule/META-INF/ フォルダーを選択します。
    6. ファイルの名前を persistence.xml と指定し、Next (次へ) をクリックします。
    7. Create XML file from an XML schema file (XML スキーマファイルから XML ファイルを作成) を選択し、Next (次へ) をクリックします。
    8. Select XML Catalog entry (XML カタログエントリーの選択) リストから http://java.sun.com/xml/ns/persistence/persistence_2.0.xsd を選択し、Next (次へ) をクリックします。

      図12.2 永続 XML スキーマ

      Persistence XML Schema
    9. Finish (完了) をクリックしてファイルを作成します。persistence.xmlMETA-INF/ フォルダーに作成され、設定可能な状態になります。

      永続設定ファイルの例

      <persistence xmlns="http://java.sun.com/xml/ns/persistence"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
         version="2.0">
         <persistence-unit name="example" transaction-type="JTA">
            <provider>org.hibernate.ejb.HibernatePersistence</provider>
            <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
            <mapping-file>ormap.xml</mapping-file>
            <jar-file>TestApp.jar</jar-file>
            <class>org.test.Test</class>
            <shared-cache-mode>NONE</shared-cache-mode>
            <validation-mode>CALLBACK</validation-mode>
            <properties>
               <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
               <property name="hibernate.hbm2ddl.auto" value="create-drop"/>
            </properties>
         </persistence-unit>
      </persistence>

12.5. Configuration (設定)

12.5.1. Hibernate 設定プロパティー

表12.1 Hibernate Java プロパティー

プロパティー名説明

hibernate.dialect

Hibernate の org.hibernate.dialect.Dialect のクラス名。Hibernate で、特定のリレーショナルデータベースに最適化された SQL を生成できるようになります。

ほとんどのケースで、Hibernate は、JDBC ドライバーにより返された JDBC メタデータに基づいて正しい org.hibernate.dialect.Dialect 実装を選択できます。

hibernate.show_sql

ブール変数。SQL ステートメントをすべてコンソールに書き込みます。これは、ログカテゴリー org.hibernate.SQLdebug に設定することと同じです。

hibernate.format_sql

ブール変数。SQL をログとコンソールにプリティプリントします。

hibernate.default_schema

修飾されていないテーブル名を、生成された SQL の該当するスキーマ/テーブルスペースで修飾します。

hibernate.default_catalog

修飾されていないテーブル名を、生成された SQL の該当するカタログで修飾します。

hibernate.session_factory_name

org.hibernate.SessionFactory が、作成後に JNDI のこの名前に自動的にバインドされます。たとえば、jndi/composite/name のようになります。

hibernate.max_fetch_depth

シングルエンドのアソシエーション (1 対 1 や多対 1 など) に対して外部結合フェッチツリーの最大の深さを設定します。0 を設定するとデフォルトの外部結合フェッチが無効になります。推奨値は、0 から 3 までの値です。

hibernate.default_batch_fetch_size

関連付けの Hibernate 一括フェッチに対するデフォルトサイズを設定します。推奨値は、48、および 16 です。

hibernate.default_entity_mode

この SessionFactory から開かれたすべてのセッションに対するエンティティー表現のデフォルトモードを設定します。値には dynamic-mapdom4jpojo などがあります。

hibernate.order_updates

ブール変数。Hibernate で、更新されるアイテムの主キー値で SQL 更新の順番付けを行います。これにより、高度な並列システムにおけるトランザクションデッドロックが軽減されます。

hibernate.generate_statistics

ブール変数。有効にすると、Hibernate がパフォーマンスのチューニングに役に立つ統計情報を収集します。

hibernate.use_identifier_rollback

ブール変数。有効にすると、オブジェクトが削除されたときに、生成された識別子プロパティーがデフォルト値にリセットされます。

hibernate.use_sql_comments

ブール変数。有効にすると、デバッグを簡単にするために Hibernate が SQL 内にコメントを生成します。デフォルト値は false です。

hibernate.id.new_generator_mappings

ブール値。@GeneratedValue を使用する場合に関係するプロパティーです。新しい IdentifierGenerator 実装が javax.persistence.GenerationType.AUTO、javax.persistence.GenerationType.TABLE、または javax.persistence.GenerationType.SEQUENCE に対して使用されるかどうかを示します。デフォルト値は false です。

hibernate.ejb.naming_strategy

Hibernate EntityManager を使用している場合は、org.hibernate.cfg.NamingStrategy 実装を選択します。Hibernate 5.0 では hibernate.ejb.naming_strategy はサポートされなくなりました。これが使用された場合は、分割された ImplicitNamingStrategy と PhysicalNamingStrategy に置き換わり、サポートが停止され、削除されたことを示す廃止メッセージがログに記録されます。

アプリケーションが EntityManager を使用しない場合は、Hibernate Reference Documentation - Naming Strategies の手順に従って NamingStrategy を設定します。

MetadataBuilder を使用し、暗黙的なネーミングストラテジーを適用するネイティブブートストラップの例については、Hibernate 5.0 ドキュメンテーションの http://docs.jboss.org/hibernate/orm/5.0/userguide/html_single/Hibernate_User_Guide.html#bootstrap-native-metadata を参照してください。物理的なネーミングストラテジーは MetadataBuilder.applyPhysicalNamingStrategy() を使用して適用できます。org.hibernate.boot.MetadataBuilder の詳細については、https://docs.jboss.org/hibernate/orm/5.0/javadocs/ を参照してください。

hibernate.implicit_naming_strategy

使用する org.hibernate.boot.model.naming.ImplicitNamingStrategy クラスを指定します。また、hibernate.implicit_naming_strategy を使用して ImplicitNamingStrategy を実装するカスタムクラスを設定することもできます。この設定には以下の短い名前が定義されています。

  • default - ImplicitNamingStrategyJpaCompliantImpl
  • jpa - ImplicitNamingStrategyJpaCompliantImpl
  • legacy-jpa - ImplicitNamingStrategyLegacyJpaImpl
  • legacy-hbm - ImplicitNamingStrategyLegacyHbmImpl
  • component-path - ImplicitNamingStrategyComponentPathImpl

デフォルト設定は、default の短い名前の ImplicitNamingStrategy によって定義されます。デフォルト設定が空白の場合、フォールバックは ImplicitNamingStrategyJpaCompliantImpl を使用します。

hibernate.physical_naming_strategy

データベースオブジェクト名に物理的なネーミングルールを適用するプラグ可能なストラテジーコントラクト。使用する PhysicalNamingStrategy クラスを指定します。デフォルトでは PhysicalNamingStrategyStandardImpl が使用されます。hibernate.physical_naming_strategy を使用して、PhysicalNamingStrategy を実装するカスタムクラスを設定することもできます。

重要

新しいアプリケーションでは、hibernate.id.new_generator_mappings のデフォルト値を true のままにする必要があります。Hibernate 3.3.x を使用した既存のアプリケーションが継続してシーケンスオブジェクトやテーブルベースのジェネレーターを使用し、後方互換性を維持する場合は、デフォルト値を false に変更する必要がある場合があります。

12.5.2. Hibernate JDBC と接続プロパティー

表12.2 プロパティー

プロパティー名説明

hibernate.jdbc.fetch_size

JDBC のフェッチサイズを判断するゼロでない値です (Statement.setFetchSize() を呼び出します)。

hibernate.jdbc.batch_size

Hibernate による JDBC2 バッチ更新の使用を有効にするゼロでない値です。推奨値は、530 です。

hibernate.jdbc.batch_versioned_data

ブール変数。JDBC ドライバーが executeBatch() から正しい行数を返す場合は、このプロパティーを true に設定します。Hibernate は自動的にバージョン化されたデータにバッチ処理された DML を使用します。デフォルト値は false です。

hibernate.jdbc.factory_class

カスタム org.hibernate.jdbc.Batcher を選択します。ほとんどのアプリケーションにはこの設定プロパティーは必要ありません。

hibernate.jdbc.use_scrollable_resultset

ブール変数。Hibernate による JDBC2 のスクロール可能な結果セットの使用を有効にします。このプロパティーはユーザーが提供した JDBC 接続を使用する場合にのみ必要です。その他の場合、Hibernate は接続メタデータを使用します。

hibernate.jdbc.use_streams_for_binary

ブール変数。システムレベルのプロパティーです。binary または serializable 型を JDBC へ読み書きしたり、JDBC から読み書きしたりする場合にストリームを使用します。

hibernate.jdbc.use_get_generated_keys

ブール変数。JDBC3 PreparedStatement.getGeneratedKeys() を使用して、挿入後にネイティブで生成された鍵を取得できるようにします。JDBC3+ ドライバーと JRE1.4+ が必要です。JDBC ドライバーに Hibernate 識別子ジェネレーターの問題がある場合は false に設定します。デフォルトでは、接続メタデータを使用してドライバーの機能を判断しようとします。

hibernate.connection.provider_class

JDBC 接続を Hibernate に提供するカスタム org.hibernate.connection.ConnectionProvider のクラス名です。

hibernate.connection.isolation

JDBC トランザクションの分離レベルを設定します。java.sql.Connection で意味のある値をチェックしますが、ほとんどのデータベースはすべての分離レベルをサポートするとは限らず、一部のデータベースは標準的でない分離を追加的に定義します。標準的な値は 1, 2, 4, 8 です。

hibernate.connection.autocommit

ブール変数。このプロパティーの使用は推奨されません。JDBC でプールされた接続に対して自動コミットを有効にします。

hibernate.connection.release_mode

Hibernate が JDBC 接続を解放するタイミングを指定します。デフォルトでは、セッションが明示的に閉じられるか切断されるまで JDBC 接続が保持されます。デフォルト値である auto では、JTA および CMT トランザクションストラテジーに対して after_statement が選択され、JDBC トランザクションストラテジーに対して after_transaction が選択されます。

利用可能な値は auto (デフォルト)、on_close、after_transactionafter_statement です。

この設定により、SessionFactory.openSession から返されたセッションのみが影響を受けます。 SessionFactory.getCurrentSession から取得されたセッションの場合は、使用のために設定された CurrentSessionContext 実装がこれらのセッションの接続リリースモードを制御します。

hibernate.connection.<propertyName>

JDBC プロパティー <propertyName>DriverManager.getConnection() に渡します。

hibernate.jndi.<propertyName>

プロパティー <propertyName> を JNDI InitialContextFactory に渡します。

12.5.3. Hibernate キャッシュプロパティー

表12.3 プロパティー

プロパティー名説明

hibernate.cache.region.factory_class

カスタム CacheProvider のクラス名。

hibernate.cache.use_minimal_puts

ブール変数です。2 次キャッシュの操作を最適化し、読み取りを増やして書き込みを最小限にします。これはクラスター化されたキャッシュで最も便利な設定であり、Hibernate 3 ではクラスター化されたキャッシュの実装に対してデフォルトで有効になっています。

hibernate.cache.use_query_cache

ブール変数です。クエリーキャッシュを有効にします。各クエリーをキャッシュ可能に設定する必要があります。

hibernate.cache.use_second_level_cache

ブール変数です。<cache> マッピングを指定するクラスに対してデフォルトで有効になっている 2 次 キャッシュを完全に無効にするため使用されます。

hibernate.cache.query_cache_factory

カスタム QueryCache インターフェースのクラス名です。デフォルト値は組み込みの StandardQueryCache です。

hibernate.cache.region_prefix

2 次キャッシュのリージョン名に使用する接頭辞です。

hibernate.cache.use_structured_entries

ブール変数です。人間が解読可能な形式でデータを 2 次キャッシュに保存するよう Hibernate を設定します。

hibernate.cache.default_cache_concurrency_strategy

@Cacheable または @Cache が使用される場合に使用するデフォルトの org.hibernate.annotations.CacheConcurrencyStrategy の名前を付与するため使用される設定です。このデフォルト値を上書するには、@Cache(strategy="..") を使用します。

12.5.4. Hibernate トランザクションプロパティー

表12.4 プロパティー

プロパティー名説明

hibernate.transaction.factory_class

Hibernate Transaction API と使用する TransactionFactory のクラス名です。デフォルト値は JDBCTransactionFactory です。

jta.UserTransaction

アプリケーションサーバーから JTA UserTransaction を取得するために JTATransactionFactory により使用される JNDI 名。

hibernate.transaction.manager_lookup_class

TransactionManagerLookup のクラス名。JVM レベルのキャッシングが有効になっている場合や、JTA 環境の hilo ジェネレーターを使用する場合に必要です。

hibernate.transaction.flush_before_completion

ブール変数。有効な場合、トランザクションの完了前フェーズの間にセッションが自動的にフラッシュされます。ビルトインおよび自動セッションコンテキスト管理が推奨されます。

hibernate.transaction.auto_close_session

ブール変数。有効な場合、トランザクションの完了後フェーズの間にセッションが自動的に閉じられます。ビルトインおよび自動セッションコンテキスト管理が推奨されます。

12.5.5. その他の Hibernate プロパティー

表12.5 プロパティー

プロパティー名説明

hibernate.current_session_context_class

「現在」の Session のスコープに対するカスタムストラテジーを提供します。値には jtathreadmanagedcustom.Class があります。

hibernate.query.factory_class

org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory または org.hibernate.hql.internal.classic.ClassicQueryTranslatorFactory の HQL パーサー実装を選択します。

hibernate.query.substitutions

Hibernate クエリーのトークンと SQL トークンとのマッピングに使用します (トークンは関数名またはリテラル名である場合があります)。たとえば、hqlLiteral=SQL_LITERAL, hqlFunction=SQLFUNC のようになります。

hibernate.hbm2ddl.auto

SessionFactory が作成されると、スキーマ DDL を自動的に検証し、データベースにエクスポートします。create-drop を使用すると、 SessionFactory が明示的に閉じられたときにデータベーススキーマが破棄されます。プロパティー値のオプションは、validateupdatecreatecreate-drop です。

hibernate.hbm2ddl.import_files

SessionFactory の作成中に実行される SQL DML ステートメントが含まれる任意のファイルの名前 (コンマ区切り)。テストやデモに便利です。たとえば、INSERT ステートメントを追加すると、デプロイ時に最小限のデータセットがデータベースに入力されます。値の例としては、/humans.sql,/dogs.sql のようになります。

特定ファイルのステートメントは後続ファイルのステートメントの前に実行されるため、ファイルの順番に注意する必要があります。これらのステートメントはスキーマが作成された場合のみ実行されます (hibernate.hbm2ddl.autocreate または create-drop に設定された場合など)。

hibernate.hbm2ddl.import_files_sql_extractor

カスタム ImportSqlCommandExtractor のクラス名。デフォルト値は組み込みの SingleLineSqlCommandExtractor です。各インポートファイルから単一の SQL ステートメントを抽出する専用のパーサーを実装する時に便利です。Hibernate は、複数行にまたがる命令/コメントおよび引用符で囲まれた文字列をサポートする MultipleLinesSqlCommandExtractor も提供します (各ステートメントの最後にセミコロンが必要です)。

hibernate.bytecode.use_reflection_optimizer

ブール値。hibernate.cfg.xml ファイルで設定できないシステムレベルのプロパティーです。ランタイムリフレクションの代わりにバイトコード操作の使用を有効にします。リフレクションは、トラブルシューティングを行うときに役に立つ場合があります。オプティマイザーが無効の場合でも Hibernate には cglib または javassist が常に必要です。

hibernate.bytecode.provider

javassist または cglib をバイト操作エンジンとして使用することができます。デフォルトでは javassist が使用されます。プロパティー値は javassist または cglib のいずれかです。

12.5.6. Hibernate SQL 方言

重要

hibernate.dialect プロパティーをアプリケーションデータベースの適切な org.hibernate.dialect.Dialect サブクラスに設定する必要があります。方言が指定されている場合、Hibernate は他のプロパティーの一部に実用的なデフォルトを使用します。そのため、これらのプロパティーを手作業で指定する必要はありません。

表12.6 SQL 方言 (hibernate.dialect)

RDBMS方言

DB2

org.hibernate.dialect.DB2Dialect

DB2 AS/400

org.hibernate.dialect.DB2400Dialect

DB2 OS390

org.hibernate.dialect.DB2390Dialect

Firebird

org.hibernate.dialect.FirebirdDialect

FrontBase

org.hibernate.dialect.FrontbaseDialect

H2 Database

org.hibernate.dialect.H2Dialect

HypersonicSQL

org.hibernate.dialect.HSQLDialect

Informix

org.hibernate.dialect.InformixDialect

Ingres

org.hibernate.dialect.IngresDialect

Interbase

org.hibernate.dialect.InterbaseDialect

MariaDB 10

org.hibernate.dialect.MySQL57InnoDBDialect

Mckoi SQL

org.hibernate.dialect.MckoiDialect

Microsoft SQL Server 2000

org.hibernate.dialect.SQLServerDialect

Microsoft SQL Server 2005

org.hibernate.dialect.SQLServer2005Dialect

Microsoft SQL Server 2008

org.hibernate.dialect.SQLServer2008Dialect

Microsoft SQL Server 2012

org.hibernate.dialect.SQLServer2012Dialect

Microsoft SQL Server 2014

org.hibernate.dialect.SQLServer2012Dialect

MySQL5

org.hibernate.dialect.MySQL5Dialect

MySQL5.7

org.hibernate.dialect.MySQL57InnoDBDialect

InnoDB を用いる MYSQL5

org.hibernate.dialect.MySQL5InnoDBDialect

MyISAM を用いる MySQL

org.hibernate.dialect.MySQLMyISAMDialect

Oracle (全バージョン)

org.hibernate.dialect.OracleDialect

Oracle 9i

org.hibernate.dialect.Oracle9iDialect

Oracle 10g

org.hibernate.dialect.Oracle10gDialect

Oracle 11g

org.hibernate.dialect.Oracle10gDialect

Oracle 12c

org.hibernate.dialect.Oracle12cDialect

Pointbase

org.hibernate.dialect.PointbaseDialect

PostgreSQL

org.hibernate.dialect.PostgreSQLDialect

PostgreSQL 9.2

org.hibernate.dialect.PostgreSQL9Dialect

PostgreSQL 9.3

org.hibernate.dialect.PostgreSQL9Dialect

PostgreSQL 9.4

org.hibernate.dialect.PostgreSQL94Dialect

Postgres Plus Advanced Server

org.hibernate.dialect.PostgresPlusDialect

Progress

org.hibernate.dialect.ProgressDialect

SAP DB

org.hibernate.dialect.SAPDBDialect

Sybase

org.hibernate.dialect.SybaseASE15Dialect

Sybase 15.7

org.hibernate.dialect.SybaseASE157Dialect

Sybase Anywhere

org.hibernate.dialect.SybaseAnywhereDialect

12.6. 2 次キャッシュ

12.6.1. 2 次キャッシュ

2 次キャッシュとは、アプリケーションセッション以外で永続的に情報を保持するローカルのデータストアのことです。このキャッシュは永続プロバイダーにより管理されており、アプリケーションとデータを分けることでランタイム効率の改善をはかることができます。

JBoss EAP では、以下の目的のためにキャッシュがサポートされます。

  • Web セッションのクラスタリング
  • ステートフルセッション Bean のクラスタリング
  • SSO クラスタリング
  • Hibernate 2 次キャッシュ

各キャッシュコンテナーは 「repl」 と 「dist」 キャッシュを定義します。これらのキャッシュは、ユーザーアプリケーションで直接使用しないでください。

12.6.2. Hibernate 用 2 次キャッシュの設定

Hibernate 向けの 2 次レベルキャッシュとして動作する Infinispan の設定は、以下の 2 つの方法で行なえます。

  • Hibernate ネイティブアプリケーション経由 (hibernate.cfg.xml を使用)
  • JPA アプリケーション経由 (persistence.xml を使用)
Hibernate ネイティブアプリケーションを使用した Hibernate 用 2 次キャッシュの設定
  1. デプロイメントのクラスパスに hibernate.cfg.xml を作成します。
  2. 以下の XML を hibernate.cfg.xml に追加します。XML は <session-factory> タグ内に存在する必要があります。

    <property name="hibernate.cache.use_second_level_cache">true</property>
    <property name="hibernate.cache.use_query_cache">true</property>
    <property name="hibernate.cache.region.factory_class">org.jboss.as.jpa.hibernate5.infinispan.InfinispanRegionFactory</property>
JPA ネイティブアプリケーションを使用した Hibernate 用 2 次キャッシュの設定
  1. Red Hat JBoss Developer Studio で Hibernate 設定ファイルを作成する方法については、単純な JPA アプリケーションの作成を参照してください。
  2. 以下の内容を persistence.xml ファイルに追加します。

    <persistence-unit name="...">
      (...) <!-- other configuration -->
      <shared-cache-mode>$SHARED_CACHE_MODE</shared-cache-mode>
      <properties>
        <property name="hibernate.cache.use_second_level_cache" value="true" />
        <property name="hibernate.cache.use_query_cache" value="true" />
      </properties>
    </persistence-unit>
    注記

    $SHARED_CACHE_MODE の値には以下のものがあります。

    • ALL - すべてのエンティティーがキャッシュ可能である見なされます。
    • ENABLE_SELECTIVE - キャッシュ可能とマークされたエンティティーのみがキャッシュ可能であると見なされます。
    • DISABLE_SELECTIVE - キャッシュ不可と明示的に示されたものを除くすべてのエンティティーはキャッシュ可能と見なされます。

12.7. Hibernate アノテーション

org.hibernate.annotations パッケージには、標準的な JPA アノテーション以外に Hibernate により提供される一部のアノテーションが含まれます。

表12.7 一般的なアノテーション

アノテーション説明

Check

クラス、プロパティー、コレクションのいずれかのレベルで定義できる任意の SQL チェック制約です。

Immutable

エンティティーまたはコレクションを不変としてマーク付けします。アノテーションがない場合、要素は可変となります。

不変のエンティティーはアプリケーションによって更新されないことがあります。不変エンティティーへの更新は無視されますが、例外は発生しません。

@Immutable をコレクションに付けるとコレクションは不変になるため、コレクションからの追加や削除およびコレクションへの追加や削除は許可されません。この結果、HibernateException が発生します。

表12.8 キャッシュエンティティー

アノテーション説明

Cache

ルートエンティティーまたはコレクションにキャッシングストラテジーを追加します。

表12.9 コレクション関連のアノテーション

アノテーション説明

MapKeyType

永続マップのキータイプを定義します。

ManyToAny

異なるエンティティータイプを参照する ToMany アソシエーションを定義します。 メタデータ識別子カラムを介してエンティティータイプの照合が行われます。このようなマッピングはできるだけ行わないでください。

OrderBy

SQL の順序付け (HQL の順序付けではない) を使用してコレクションの順序を付けます。

OnDelete

コレクションやアレイ、結合されたサブクラスの削除に使用されるストラテジーです。OnDelete の 2 次テーブルはサポートされていません。

Persister

カスタムパーシスターを指定します。

Sort

コレクションのソート (Java レベルのソート)。

Where

要素エンティティーまたはコレクションのターゲットエンティティーへ追加する where 節。この節は SQL で書かれます。

WhereJoinTable

コレクション結合テーブルへ追加する where 節。この節は SQL で書かれます。

表12.10 CRUD 操作用のカスタム SQL

アノテーション説明

Loader

Hibernate のデフォルトである FIND メソッドを上書きします。

SQLDelete

Hibernate のデフォルトである DELETE メソッドを上書きします。

SQLDeleteAll

Hibernate のデフォルトである DELETE ALL メソッドを上書きします。

SQLInsert

Hibernate のデフォルトである INSERT INTO メソッドを上書きします。

SQLUpdate

Hibernate のデフォルトである UPDATE メソッドを上書きします。

Subselect

不変の読み取り専用エンティティーを指定のサブセレクト表現へマッピングします。

Synchronize

自動フラッシュが適切に行われ、派生したエンティティーに対するクエリーが古いデータを返さないようにします。ほとんどの場合、Subselect と共に使用されます。

表12.11 Entity

アノテーション説明

Cascade

アソシエーションにカスケードストラテジーを適用します。

Entity

標準的な @Entity で定義されたもの以外に必要になることがあるメタデータを追加します。

  • mutable: このエンティティーが変更可能であるかどうか
  • dynamicInsert: 挿入に動的 SQL を許可する
  • dynamicUpdate: 更新に動的 SQL を許可する
  • selectBeforeUpdate: オブジェクトが実際に変更されない限り、Hibernate が SQL UPDATE を実行しないことを指定します。
  • polymorphism: エンティティーのポリモーフィズムが PolymorphismType.IMPLICIT (default) であるか、または PolymorphismType.EXPLICIT であるか
  • optimisticLock: オプティミスティックロッキングストラテジー (OptimisticLockType.VERSION、OptimisticLockType.NONE、OptimisticLockType.DIRTY、または OptimisticLockType.ALL)

    注記

    アノテーション「Entity」は非推奨になり、今後のリリースで削除される予定です。個々の属性または値はアノテーションにする必要があります。

Polymorphism

Hibernate がエンティティーの階層に適用する多様性タイプを定義するために使用されます。

Proxy

特定クラスのレイジーおよびプロキシ設定。

Table

1 次または 2 次テーブルへの補足情報。

Tables

Table の複数アノテーション。

Target

明示的なターゲットを定義し、リフレクションやジェネリクスで解決しないようにします。

Tuplizer

1 つのエンティティーまたはコンポーネントに対して単一の tuplizer を定義します。

Tuplizers

1 つのエンティティーまたはコンポーネントに対して tuplizer のセットを定義します。

表12.12 Fetching

アノテーション説明

BatchSize

SQL ローディングのバッチサイズ。

FetchProfile

フェッチングストラテジープロファイルを定義します。

FetchProfiles

@FetchProfile の複数アノテーション。

表12.13 Filters

アノテーション説明

Filter

エンティティーまたはコレクションのターゲットエンティティーにフィルターを追加します。

FilterDef

フィルター定義。

FilterDefs

フィルター定義のアレイ。

FilterJoinTable

結合テーブルのコレクションへフィルターを追加します。

FilterJoinTables

複数の @FilterJoinTable をコレクションへ追加します。

Filters

複数の @Filter を追加します。

ParamDef

パラメーターの定義。

表12.14 主キー

アノテーション説明

Generated

このアノテーション付けされたプロパティーはデータベースによって生成されます。

GenericGenerator

Hibernate ジェネレーターをデタイプ (detyped) で記述するジェネレーターアノテーションです。

GenericGenerators

汎用ジェネレーター定義のアレイ。

NaturalId

プロパティーがエンティティーのナチュラル ID の一部であることを指定します。

Parameter

キーと値のパターン。

RowId

Hibernate の ROWID マッピング機能をサポートします。

表12.15 継承

アノテーション説明

DiscriminatorFormula

ルートエントリーに置かれる識別子の公式です。

DiscriminatorOptions

Hibernate 固有の識別子プロパティーを表現する任意のアノテーションです。

MetaValue

該当する識別子の値を対応するエンティティータイプにマッピングします。

表12.16 JP-QL/HQL クエリーのマッピング

アノテーション説明

NamedNativeQueries

Hibernate NamedNativeQuery オブジェクトを保持するよう NamedNativeQueries を拡張します。

NamedNativeQuery

NamedNativeQuery を Hibernate の機能で拡張します。

NamedQueries

NamedQuery オブジェクトを保持するよう NamedQueries を拡張します。

NamedQuery

Hibernate の機能で NamedQuery を拡張します。

表12.17 単純なプロパティーのマッピング

アノテーション説明

AccessType

プロパティーのアクセスタイプ。

Columns

カラムのアレイをサポートします。コンポーネントユーザータイプのマッピングに便利です。

ColumnTransformer

カラムからの値の読み取りやカラムへの値の書き込みに使用されるカスタム SQL 表現です。直接的なオブジェクトのロードや保存、クエリーに使用されます。write 表現には必ず値に対して 1 つの 「?」 プレースホルダーが含まれなければなりません。

ColumnTransformers

@ColumnTransformer の複数アノテーションです。複数のカラムがこの挙動を使用する場合に便利です。

表12.18 プロパティー

アノテーション説明

Formula

ほとんどの場所で @Column の代替として使用されます。公式は有効な SQL フラグメントである必要があります。

Index

データベースのインデックスを定義します。

JoinFormula

ほとんどの場所で @JoinColumn の代替として使用されます。公式は有効な SQL フラグメントである必要があります。

Parent

所有者 (通常は所有するエンティティー) へのポインターとしてプロパティーを参照します。

Type

Hibernate のタイプ。

TypeDef

Hibernate タイプの定義。

TypeDefs

Hibernate タイプ定義のアレイ。

表12.19 単一アソシエーション関連のアノテーション

アノテーション説明

Any

複数のエンティティータイプを参照する ToOne を定義します。対応するエンティティータイプの照合は、メタデータ識別子カラムを介して行われます。このようなマッピングは最低限行う必要があります。

AnyMetaDef

@Any および @ManyToAny メタデータを定義します。

AnyMetaDefs

メタデータの @Any および @ManyToAny セットを定義します。エンティティーまたはパッケージレベルで定義できます。

Fetch

特定のアソシエーションに使用されるフェッチングストラテジーを定義します。

LazyCollection

コレクションのレイジー状態を定義します。

LazyToOne

ToOne アソシエーション (つまり、OneToOne または ManyToOne) のレイジーステータスを定義します。

NotFound

アソシエーション上で要素が見つからなかった時に実行するアクションです。

表12.20 オプティミスティックロッキング

アノテーション説明

OptimisticLock

アノテーション付けされたプロパティーの変更によってエンティティーのバージョン番号が増加するかどうか。アノテーション付けされていない場合、プロパティーは楽観的ロックストラテジー (デフォルト) に関与します。

OptimisticLocking

エンティティーに適用される楽観的ロックのスタイルを定義するために使用されます。階層ではルートエンティティーのみに有効です。

Source

バージョンおよびタイムスタンプバージョンプロパティーと併用するのに最適なアノテーションです。アノテーション値はタイムスタンプが生成される場所を決定します。

12.8. Hibernate クエリー言語

12.8.1. Hibernate クエリー言語

JPQL の概要

Java Persistence Query Language (JPQL) は、Java Persistence API (JPA) 仕様の一部として定義されたプラットフォーム非依存のオブジェクト指向クエリー言語です。JPQL は、リレーショナルデータベースに格納されたエンティティーに対してクエリーを実行するために使用されます。この言語は SQL から大きな影響を受けており、クエリーの構文は SQL クエリーに類似しますが、データベーステーブルと直接動作するのではなく JPA エンティティーオブジェクトに対して動作します。

HQL の概要

Hibernate Query Language (HQL) は、強力なクエリー言語であり、見た目は SQL に似ています。ただし、SQL と比較して、HQL は完全なオブジェクト指向であり、継承、ポリモーフィズム、アソシエーションなどの概念を理解します。

HQL は JPQL のスーパーセットです。HQL クエリーは有効な JPQL クエリーでないこともありますが、JPQL クエリーは常に有効な HQL クエリーになります。

HQL と JPQL は共にタイプセーフでないクエリー操作を実行します。基準 (criteria) クエリーがタイプセーフなクエリーを提供します。

12.8.2. HQL ステートメントについて

HQL と JPQL では、SELECTUPDATE、および DELETE ステートメントを使用できます。HQL では、SQL の INSERT-SELECT に似た形式で INSERT ステートメントも使用できます。

重要

更新操作または削除操作を一括で実行する場合は、アクティブな永続コンテキストでデータベースとエンティティーとの間に不整合が発生することがあるため、注意が必要です。一般的に、一括の更新操作または削除操作は、新しい永続コンテキストのトランザクション内、またはこのような操作により状態が影響を受ける可能性があるエンティティーを取得する前またはアクセスする前にのみ実行します。

表12.21 HQL ステートメント

ステートメント説明

SELECT

HQL での SELECT ステートメントの BNF は以下のとおりです。

select_statement :: =
        [select_clause]
        from_clause
        [where_clause]
        [groupby_clause]
        [having_clause]
        [orderby_clause]

UDPATE

HQL の UPDATE ステートメントの BNF は JPQL と同じです。

DELETE

HQL の DELETE ステートメントの BNF は JPQL と同じです。

12.8.3. INSERT ステートメント

HQL は INSERT ステートメントを定義する機能を追加します。これに相当するステートメントは JPQL にはありません。HQL の INSERT ステートメントの BNF は次のとおりです。

insert_statement ::= insert_clause select_statement

insert_clause ::= INSERT INTO entity_name (attribute_list)

attribute_list ::= state_field[, state_field ]*

attribute_list は、SQL INSERT ステートメントの column specification と似ています。マップされた継承に関係するエンティティーでは、名前付きエンティティー上で直接定義された属性のみを attribute_list で使用することが可能です。スーパークラスプロパティーは許可されず、サブクラスプロパティーは意味がありません。よって、 INSERT ステートメントは本質的に非多形となります。

警告

select_statement はあらゆる有効な HQL select クエリーになりえますが、戻り値の型は挿入が想定する型と一致しなければなりません。現在、型の一致はクエリーのコンパイル中にチェックされ、そのチェックはデータベースへ委譲されません。そのため、同じ Hibernate タイプではなく相当する Hibernate タイプの間で問題が生じる可能性があります。たとえば、データベースによって区別されず、変換処理を行える可能性があっても、org.hibernate.type.DateType としてマップされた属性と、 org.hibernate.type.TimestampType として定義された属性との不一致が問題となる可能性があります。

insert ステートメントは id 属性に対して 2 つのオプションを提供します。1 つ目は、id 属性を attribute_list に明示的に指定するオプションです。この場合、値は対応する select 式から取得されます。2 つ目は attribute_list に指定しないオプションであり、この場合生成された値が使用されます。2 つ目のオプションは、「データベース内」で動作する id ジェネレーターを使用する場合のみ選択可能です。このオプションを「インメモリ」タイプのジェネレーターで使用しようとすると、構文解析中に例外が生じます。

insert ステートメントは楽観的ロックの属性に対しても 2 つのオプションを提供します。1 つ目は attribute_list に属性を指定するオプションです。この場合、値は対応する select 式から取得されます。2 つ目は attribute_list に指定しないオプションです。この場合、対応する org.hibernate.type.VersionType によって定義される seed value が使用されます。

例: INSERT クエリーステートメント

String hqlInsert = "insert into DelinquentAccount (id, name) select c.id, c.name from Customer c where ...";
int createdEntities = s.createQuery( hqlInsert ).executeUpdate();

12.8.4. FROM 節

FROM 節の役割は、他のクエリーが使用できるオブジェクトモデルタイプの範囲を定義することです。また、他のクエリーが使用できる「ID 変数」もすべて定義します。

12.8.5. WITH 節

HQL は WITH 節を定義し、結合条件を限定します。これは HQL に固有の機能で、JPQL はこの機能を定義しません。

例: With

select distinct c
from Customer c
    left join c.orders o
        with o.value > 5000.00

生成された SQL では、with clause の条件が生成された SQL の on clause の一部となりますが、本項の他のクエリーでは HQL/JPQL の条件が生成された SQL の where clause の一部となることが重要な違いです。この例に特有の違いは重要ではないでしょう。さらに複雑なクエリーでは、with clause が必要になることがあります。

明示的な結合は、アソシエーションまたはコンポーネント/埋め込み属性を参照することがあります。コンポーネント/埋め込み属性では、結合は論理的であり、物理 (SQL) 結合に関連しません。

12.8.6. HQL の順序付け

クエリーの結果を順序付けすることも可能です。ORDER BY 節を使用して、結果を順序付けするために使用される選択値を指定します。order-by 節の一部として有効な式タイプには以下が含まれます。

  • ステートフィールド
  • コンポーネント/埋め込み可能属性
  • 算術演算や関数などのスカラー式
  • 前述の式タイプのいずれかに対する select 節に宣言された ID 変数

HQL は、order-by 節で参照されたすべての値が select 節で名付けされることを強制しませんが、JPQL では必要となります。データベースの移植性を要求するアプリケーションは、select 節で参照されない order-by 節の参照値をサポートしないデータベースがあることを認識する必要があります。

order-by の各式は、ASC (昇順) または DESC (降順) で希望の順序を示すよう修飾することができます。

例: Order-by

// legal because p.name is implicitly part of p
select p
from Person p
order by p.name

select c.id, sum( o.total ) as t
from Order o
    inner join o.customer c
group by c.id
order by t

12.8.7. 一括更新、一括送信、および一括削除

Hibernate では、Data Manipulation Language (DML) を使用して、マップ済みデータベースのデータを直接、一括挿入、一括更新、および一括削除できます (Hibernate Query Language を使用)。

警告

DML を使用すると、オブジェクト/リレーショナルマッピングに違反し、オブジェクトの状態に影響が出ることがあります。オブジェクトの状態はメモリーでは変わりません。DML を使用することにより、基礎となるデータベースで実行された操作に応じて、メモリー内オブジェクトの状態は影響を受けません。DML を使用する場合、メモリー内データは注意を払って使用する必要があります。

UPDATE ステートメントと DELETE ステートメントの擬似構文:

( UPDATE | DELETE ) FROM? EntityName (WHERE where_conditions)?.

注記

FROM キーワードと WHERE Clause はオプションです。

UPDATE ステートメントまたは DELETE ステートメントの実行結果は、実際に影響 (更新または削除) を受けた行の数です。

例: 一括更新ステートメント

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

String hqlUpdate = "update Company set name = :newName where name = :oldName";
int updatedEntities = s.createQuery( hqlUpdate )
        .setString( "newName", newName )
        .setString( "oldName", oldName )
        .executeUpdate();
tx.commit();
session.close();

例: 一括削除ステートメント

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

String hqlDelete = "delete Company where name = :oldName";
int deletedEntities = s.createQuery( hqlDelete )
        .setString( "oldName", oldName )
        .executeUpdate();
tx.commit();
session.close();

Query.executeUpdate() メソッドにより返された int 値は、操作で影響を受けたデータベース内のエンティティー数を示します。

内部的に、データベースは複数の SQL ステートメントを使用して DML 更新または削除の要求に対する操作を実行することがあります。多くの場合、これは、更新または削除する必要があるテーブルと結合テーブル間に存在する関係のためです。

たとえば、上記の例のように削除ステートメントを発行すると、oldName で指定された会社用の Company テーブルだけでなく、結合テーブルに対しても削除が実行されることがあります。したがって、Employee テーブルとの関係が BiDirectional ManyToMany である Company テーブルで、以前の例の正常な実行結果として、対応する結合テーブル Company_Employee から複数の行が失われます。

上記の int deletedEntries 値には、この操作により影響を受けたすべての行 (結合テーブルの行を含む) の数が含まれます。

INSERT ステートメントの擬似構文は INSERT INTO EntityName properties_list select_statement です。

注記

INSERT INTO …​ SELECT …​ という形式のみサポートされ、INSERT INTO …​ VALUES …​ という形式はサポートされません。

例: 一括挿入ステートメント

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

String hqlInsert = "insert into Account (id, name) select c.id, c.name from Customer c where ...";
int createdEntities = s.createQuery( hqlInsert )
        .executeUpdate();
tx.commit();
session.close();

SELECT ステートメントを介して id 属性の値を提供しない場合は、基礎となるデータベースが自動生成されたキーをサポートする限り、ユーザーに対して ID が生成されます。この一括挿入操作の戻り値は、データベースで実際に作成されたエントリーの数です。

12.8.8. コレクションメンバーの参照

コレクション値 (collection-valued) アソシエーションへの参照は、実際はコレクションのを参照します。

例: コレクションの参照

select c
from Customer c
    join c.orders o
    join o.lineItems l
    join l.product p
where o.status = 'pending'
  and p.status = 'backorder'

// alternate syntax
select c
from Customer c,
    in(c.orders) o,
    in(o.lineItems) l
    join l.product p
where o.status = 'pending'
  and p.status = 'backorder'

この例では、ID 変数 o が、Customer#orders アソシエーションの要素の型であるオブジェクトモデル型 Order を実際に参照します。

更にこの例には、IN 構文を使用してコレクションアソシエーション結合を指定する代替の構文があります。構文は両方同等です。アプリケーションが使用する構文は任意に選択できます。

12.8.9. 修飾パス式

コレクション値 (collection-valued) のアソシエーションは、実際にはそのコレクションのを参照すると前項で説明しました。コレクションのタイプを基に、明示的な修飾式のセットも使用可能です。

表12.22 修飾パス式

表現説明

VALUE

コレクション値を参照します。修飾子を指定しないことと同じです。目的を明示的に表す場合に便利です。コレクション値 (collection-valued) の参照のすべてのタイプに対して有効です。

INDEX

HQL ルールに基づき、マップキーまたはリストの場所 (OrderColumn の値) を参照するよう javax.persistence.OrderColumn アノテーションを指定するマップとリストに対して有効です。ただし、JPQL ではリストでの使用に対して確保され、マップに対して KEY が追加されます。JPA プロバイダーの移植性に関係するアプリケーションは、この違いを認識する必要があります。

KEY

マップに対してのみ有効です。マップのキーを参照します。キー自体がエンティティーである場合は、さらにナビゲートすることが可能です。

ENTRY

マップに対してのみ有効です。マップの論理 java.util.Map.Entry タプル (キーと値の組み合わせ) を参照します。ENTRY は終端パスとしてのみ有効であり、select 句のみで有効になります。

例: 修飾されたコレクションの参照

// Product.images is a Map<String,String> : key = a name, value = file path

// select all the image file paths (the map value) for Product#123
select i
from Product p
    join p.images i
where p.id = 123

// same as above
select value(i)
from Product p
    join p.images i
where p.id = 123

// select all the image names (the map key) for Product#123
select key(i)
from Product p
    join p.images i
where p.id = 123

// select all the image names and file paths (the 'Map.Entry') for Product#123
select entry(i)
from Product p
    join p.images i
where p.id = 123

// total the value of the initial line items for all orders for a customer
select sum( li.amount )
from Customer c
        join c.orders o
        join o.lineItems li
where c.id = 123
  and index(li) = 1

12.8.10. スカラー関数

HQL は、使用される基盤のデータに関係なく使用できる一部の標準的な機能を定義します。また、HQL は方言やアプリケーションによって定義された追加の機能も理解することができます。

12.8.11. HQL の標準化された関数について

使用される基盤のデータベースに関係なく HQL で使用できる関数は次のとおりです。

表12.23 HQL の標準化された関数

機能説明

BIT_LENGTH

バイナリーデータの長さを返します。

CAST

SQL キャストを実行します。キャストターゲットは使用する Hibernate マッピングタイプを命名する必要があります。

EXTRACT

datetime 値で SQL の抽出を実行します。抽出により、datetime 値の一部が抽出されます (年など)。以下の省略形を参照してください。

SECOND

秒を抽出する抽出の省略形。

MINUTE

分を抽出する抽出の省略形。

HOUR

時間を抽出する抽出の省略形。

DAY

日を抽出する抽出の省略形。

MONTH

月を抽出する抽出の省略形。

YEAR

年を抽出する抽出の省略形。

STR

値を文字データとしてキャストする省略形。

アプリケーション開発者は独自の関数セットを提供することもできます。通常、カスタム SQL 関数か SQL スニペットのエイリアスで表します。このような関数は、org.hibernate.cfg.Configuration の addSqlFunction メソッドを使用して宣言します。

12.8.12. 連結演算

HQL は、連結 (CONCAT) 関数をサポートするだけでなく、連結演算子も定義します。連結演算子は JPQL によっては定義されないため、移植可能なアプリケーションでは使用しないでください。連結演算子は SQL の連結演算子である || を使用します。

例: 連結操作

select 'Mr. ' || c.name.first || ' ' || c.name.last
from Customer c
where c.gender = Gender.MALE

12.8.13. 動的インスタンス化

select 節でのみ有効な特別な式タイプがありますが、Hibernate では「動的インスタンス化」と呼びます。JPQL はこの機能の一部をサポートし、「コンストラクター式」と呼びます。

例: 動的インスタンス化 - コンストラクター

select new Family( mother, mate, offspr )
from DomesticCat as mother
    join mother.mate as mate
    left join mother.kittens as offspr

Object[] に対処せずに、クエリーの結果として返されるタイプセーフの Java オブジェクトで値をラッピングします。クラス参照は完全修飾する必要があり、一致するコンストラクターがなければなりません。

ここでは、クラスをマッピングする必要はありません。エンティティーを表す場合、結果となるインスタンスは NEW 状態で返されます (管理されません)。

この部分は JPQL もサポートします。HQL は他の「動的インスタンス化」もサポートします。最初に、スカラーの結果に対して Object[] ではなくリストを返すよう、クエリーで指定できます。

例: 動的インスタンス化 - リスト

select new list(mother, offspr, mate.name)
from DomesticCat as mother
    inner join mother.mate as mate
    left outer join mother.kittens as offspr

このクエリーの結果は List<Object[]> ではなく List<List>

また、HQL はマップにおけるスカラーの結果のラッピングもサポートします。

例: 動的インスタンス化 - マップ

select new map( mother as mother, offspr as offspr, mate as mate )
from DomesticCat as mother
    inner join mother.mate as mate
    left outer join mother.kittens as offspr

select new map( max(c.bodyWeight) as max, min(c.bodyWeight) as min, count(*) as n )
from Cat cxt

このクエリーの結果は List<Object[]> ではなく List<Map<String,Object>> になります。マップのキーは select 式へ提供されたエイリアスによって定義されます。

12.8.14. HQL 述語

述語は where 句、having 句、および検索 case 式の基盤を形成します。これらは通常は TRUE または FALSE の真理値に解決される式ですが、一般的に NULL 値が関係するブール値の比較は UNKNOWN に解決されます。

HQL 述語
  • Null 述語

    NULL の値をチェックします。基本的な属性参照、エンティティー参照、およびパラメーターへ適用できます。HQL では、コンポーネント/埋め込み可能タイプにも適用できます。

    Null チェックの例

    // select everyone with an associated address
    select p
    from Person p
    where p.address is not null
    
    // select everyone without an associated address
    select p
    from Person p
      where p.address is null
  • LIKE 述語

    文字列値で LIKE 比較を実行します。構文は次のとおりです。

    like_expression ::=
           string_expression
           [NOT] LIKE pattern_value
           [ESCAPE escape_character]

    セマンティックは SQL の LIKE 式に従います。pattern_value は、string_expression で一致を試みるパターンです。SQL と同様に、pattern_value には _ (アンダースコア) と % (パーセント) をワイルドカードとして使用できます。意味も同じであり、_ はあらゆる 1 つの文字と一致し、% はあらゆる数の文字と一致します。

    任意の escape_character は、pattern_value_% をエスケープするために使用するエスケープ文字を指定するために使用されます。これは _% が含まれるパターンを検索する必要がある場合に役立ちます。

    Like 述語の例

    select p
    from Person p
    where p.name like '%Schmidt'
    
    select p
    from Person p
    where p.name not like 'Jingleheimmer%'
    
    // find any with name starting with "sp_"
    select sp
    from StoredProcedureMetadata sp
    where sp.name like 'sp|_%' escape '|'
  • BETWEEN 述語

    SQL の BETWEEN 式と同様です。値が他の 2 つの値の間にあることを評価するために実行します。演算対象はすべて比較可能な型を持つ必要があります。

    Between 述語の例

    select p
    from Customer c
        join c.paymentHistory p
    where c.id = 123
      and index(p) between 0 and 9
    
    select c
    from Customer c
    where c.president.dateOfBirth
            between {d '1945-01-01'}
                and {d '1965-01-01'}
    
    select o
    from Order o
    where o.total between 500 and 5000
    
    select p
    from Person p
    where p.name between 'A' and 'E'
  • IN 述語

    IN 述語は、値のリストに特定の値があることを確認するチェックを行います。構文は次のとおりです。

    in_expression ::= single_valued_expression
                [NOT] IN single_valued_list
    
    single_valued_list ::= constructor_expression |
                (subquery) |
                collection_valued_input_parameter
    
    constructor_expression ::= (expression[, expression]*)

    single_valued_expression のタイプと single_valued_list の各値は一致しなければなりません。JPQL は有効なタイプを文字列、数字、日付、時間、タイムスタンプ、列挙型に限定します。JPQL では、 single_valued_expression は下記のみを参照できます。

    • 簡単な属性を表す「ステートフィールド」。アソシエーションとコンポーネント/埋め込み属性を明確に除外します。
    • エンティティータイプの式。

      HQL では、single_valued_expression はさらに広範囲の式タイプを参照することが可能です。単一値のアソシエーションは許可されます。コンポーネント/埋め込み属性も許可されますが、この機能は、基礎となるデータベースのタプルまたは「行値コンストラクター構文」へのサポートのレベルに依存します。また、HQL は値タイプを制限しませんが、基礎となるデータベースのべンダーによってはサポートが制限されるタイプがあることをアプリケーション開発者は認識しておいたほうがよいでしょう。これが JPQL の制限の主な原因となります。

      値のリストは複数の異なるソースより取得することが可能です。constructor_expressioncollection_valued_input_parameter では、空の値のリストは許可されず、最低でも 1 つの値が含まれなければなりません。

      In 述語の例

      select p
      from Payment p
      where type(p) in (CreditCardPayment, WireTransferPayment)
      
      select c
      from Customer c
      where c.hqAddress.state in ('TX', 'OK', 'LA', 'NM')
      
      select c
      from Customer c
      where c.hqAddress.state in ?
      
      select c
      from Customer c
      where c.hqAddress.state in (
          select dm.state
          from DeliveryMetadata dm
          where dm.salesTax is not null
      )
      
      // Not JPQL compliant!
      select c
      from Customer c
      where c.name in (
          ('John','Doe'),
          ('Jane','Doe')
      )
      
      // Not JPQL compliant!
      select c
      from Customer c
      where c.chiefExecutive in (
          select p
          from Person p
          where ...
      )

12.8.15. 関係比較

比較には比較演算子 (=、>、>=、<、⇐、<>) の 1 つが関与します。また、HQL によって != は <>. と同義の比較演算子として定義されます。オペランドは同じ型でなければなりません。

例: 相対比較

// numeric comparison
select c
from Customer c
where c.chiefExecutive.age < 30

// string comparison
select c
from Customer c
where c.name = 'Acme'

// datetime comparison
select c
from Customer c
where c.inceptionDate < {d '2000-01-01'}

// enum comparison
select c
from Customer c
where c.chiefExecutive.gender = com.acme.Gender.MALE

// boolean comparison
select c
from Customer c
where c.sendEmail = true

// entity type comparison
select p
from Payment p
where type(p) = WireTransferPayment

// entity value comparison
select c
from Customer c
where c.chiefExecutive = c.chiefTechnologist

比較には、サブクエリー修飾子である ALLANYSOME も関与します。SOMEANY は同義です。

サブクエリーの結果にあるすべての値に対して比較が true である場合、ALL 修飾子は true に解決されます。サブクエリーの結果が空の場合は false に解決されます。

例: ALL サブクエリー比較修飾子

// select all players that scored at least 3 points
// in every game.
select p
from Player p
where 3 > all (
   select spg.points
   from StatsPerGame spg
   where spg.player = p
)

サブクエリーの結果にある値の一部 (最低でも 1 つ) に対して比較が true の場合、ANY または SOME 修飾子は true に解決されます。サブクエリーの結果が空である場合、false に解決されます。

12.9. Hibernate サービス

12.9.1. Hibernate サービス

サービスは、さまざまな機能タイプのプラグ可能な実装を Hibernate に提供するクラスです。サービスは特定のサービスコントラクトインターフェースの実装です。インターフェースはサービスロールとして知られ、実装クラスはサービス実装として知られています。通常、ユーザーはすべての標準的なサービスロールの代替実装へプラグインできます (オーバーライド)。また、サービスロールのベースセットを越えた追加サービスを定義できます (拡張)。

12.9.2. サービスコントラクト

マーカーインターフェース org.hibernate.service.Service を実装することがサービスの基本的な要件になります。Hibernate は基本的なタイプセーフのために内部でこのインターフェースを使用します。

起動と停止の通知を受け取るために、サービスは org.hibernate.service.spi.Startable および org.hibernate.service.spi.Stoppable インターフェースを任意で実装することもできます。その他に、JMX 統合が有効になっている場合に JMX でサービスを管理可能としてマーク付けする org.hibernate.service.spi.Manageable という任意のサービスコントラクトがあります。

12.9.3. サービス依存関係のタイプ

サービスは、以下の 2 つの方法のいずれかを使用して、他のサービスに依存関係を宣言できます。

@org.hibernate.service.spi.InjectService
単一のパラメーターを受け取るサービス実装クラスのメソッドと @InjectService アノテーションが付けられているメソッドは、他のサービスのインジェクションを要求していると見なされます。
デフォルトでは、メソッドパラメーターのタイプは、インジェクトされるサービスロールであると想定されます。パラメータータイプがサービスロールではない場合は、InjectServiceserviceRole 属性を使用してロールを明示的に指定する必要があります。
デフォルトでは、インジェクトされたサービスは必須のサービスであると見なされます。そのため、名前付けされた依存サービスがない場合は、起動に失敗します。インジェクトされるサービスが任意のサービスである場合は、InjectService の required 属性を false (デフォルト値は true) として宣言する必要があります。
org.hibernate.service.spi.ServiceRegistryAwareService
2 つ目の方法は、単一の injectServices メソッドを宣言する任意のサービスインターフェース org.hibernate.service.spi.ServiceRegistryAwareService をサービスが実装する方法です。
起動中、Hibernate は org.hibernate.service.ServiceRegistry 自体をこのインターフェースを実装するサービスにインジェクトします。その後、サービスは ServiceRegistry 参照を使用して、必要な他のサービスを見つけることができます。

12.9.4. サービスレジストリー

12.9.4.1. ServiceRegistry

サービス自体以外の中央サービス API は org.hibernate.service.ServiceRegistry インターフェースです。サービスレジストリーの主な目的は、サービスを保持および管理し、サービスへのアクセスを提供することです。

サービスレジストリーは階層的です。レジストリーのサービスは、同じレジストリーおよび親レジストリーにあるサービスへの依存や利用が可能です。

org.hibernate.service.ServiceRegistryBuilder を使用して org.hibernate.service.ServiceRegistry インスタンスをビルドします。

例: ServiceRegistryBuilder を使用した ServiceRegistry の作成

ServiceRegistryBuilder registryBuilder = new ServiceRegistryBuilder( bootstrapServiceRegistry );
    ServiceRegistry serviceRegistry = registryBuilder.buildServiceRegistry();

12.9.5. カスタムサービス

12.9.5.1. カスタムサービス

org.hibernate.service.ServiceRegistry がビルドされると、不変であると見なされます。サービス自体は再設定を許可することもありますが、ここで言う不変とはサービスの追加や置換を意味します。そのため org.hibernate.service.ServiceRegistryBuilder によって提供される別のロールにより、生成された org.hibernate.service.ServiceRegistry に格納されるサービスを微調整できるようになります。

カスタムサービスについて org.hibernate.service.ServiceRegistryBuilder に通知する方法は 2 つあります。

  • org.hibernate.service.spi.BasicServiceInitiator クラスを実装してサービスクラスの要求に応じた構築を制御し、addInitiator メソッドを介して org.hibernate.service.ServiceRegistryBuilder へ追加します。
  • サービスクラスをインスタンス化し、addService メソッドを介して org.hibernate.service.ServiceRegistryBuilder へ追加します。

サービスを追加する方法とイニシエーターを追加する方法はいずれも、レジストリーの拡張 (新しいサービスロールの追加) やサービスのオーバーライド (サービス実装の置換) に対して有効です。

例: ServiceRegistryBuilder を用いた既存サービスのカスタマーサービスへの置き換え

   ServiceRegistryBuilder registryBuilder = new ServiceRegistryBuilder( bootstrapServiceRegistry );
   registryBuilder.addService( JdbcServices.class, new FakeJdbcService() );
   ServiceRegistry serviceRegistry = registryBuilder.buildServiceRegistry();


   public class FakeJdbcService implements JdbcServices{

       @Override
       public ConnectionProvider getConnectionProvider() {
           return null;
       }

       @Override
       public Dialect getDialect() {
           return null;
       }

       @Override
       public SqlStatementLogger getSqlStatementLogger() {
           return null;
       }

       @Override
       public SqlExceptionHelper getSqlExceptionHelper() {
           return null;
       }

       @Override
       public ExtractedDatabaseMetaData getExtractedMetaDataSupport() {
           return null;
       }

       @Override
       public LobCreator getLobCreator(LobCreationContext lobCreationContext) {
           return null;
       }

       @Override
       public ResultSetWrapper getResultSetWrapper() {
           return null;
       }
   }

12.9.6. ブートストラップレジストリー

12.9.6.1. ブートストラップレジストリー

ブートストラップレジストリーは、ほとんどの操作を行うために必ず必要になるサービスを保持します。主なサービスは ClassLoaderService (代表的な例) です。設定ファイルの解決にもクラスローディングサービス (リソースのルックアップ) へのアクセスが必要になります。通常の使用では、これがルートレジストリー (親なし) になります。

ブートストラップレジストリーのインスタンスは org.hibernate.service.BootstrapServiceRegistryBuilder クラスを使用してビルドされます。

12.9.6.2. BootstrapServiceRegistryBuilder の使用

例: BootstrapServiceRegistryBuilder の使用

BootstrapServiceRegistry bootstrapServiceRegistry = new BootstrapServiceRegistryBuilder()
        // pass in org.hibernate.integrator.spi.Integrator instances which are not
        // auto-discovered (for whatever reason) but which should be included
        .with( anExplicitIntegrator )
        // pass in a class loader that Hibernate should use to load application classes
        .with( anExplicitClassLoaderForApplicationClasses )
        // pass in a class loader that Hibernate should use to load resources
        .with( anExplicitClassLoaderForResources )
        // see BootstrapServiceRegistryBuilder for rest of available methods
        ...
        // finally, build the bootstrap registry with all the above options
        .build();

12.9.6.3. BootstrapRegistry サービス

org.hibernate.service.classloading.spi.ClassLoaderService

Hibernate はクラスローダーと対話する必要がありますが、Hibernate (またはライブラリー) がクラスローダーと対話する方法は、アプリケーションをホストするランタイム環境によって異なります。クラスローディングの要件は、アプリケーションサーバー、OSGi コンテナー、およびその他のモジュラークラスローディングシステムによって限定されます。このサービスは、このような環境の複雑性の抽象化を Hibernate に提供しますが、単一のスワップ可能なコンポーネントを用いることも重要な点になります。

クラスローダーとの対話では、Hibernate に以下の機能が必要になります。

  • アプリケーションクラスを見つける機能
  • 統合クラスを見つける機能
  • リソース (プロパティーファイル、xml ファイルなど) を見つける機能
  • java.util.ServiceLoader をロードする機能
注記

現在、アプリケーションクラスをロードする機能と統合クラスをロードする機能は、サービス上の 1 つの「ロードクラス」機能として組み合わされていますが、今後のリリースで変更になる可能性があります。

org.hibernate.integrator.spi.IntegratorService

アプリケーション、アドオン、およびその他のモジュールは Hibernate と統合する必要があります。以前の方法では、各モジュールの登録を調整するためにコンポーネント (通常はアプリケーション) が必要でした。この登録は各モジュールのインタグレーターの代わりに実行されました。

このサービスはディスカバリーに重点を置きます。org.hibernate.service.classloading.spi.ClassLoaderService によって提供される標準の Java java.util.ServiceLoader 機能を使用して、org.hibernate.integrator.spi.Integrator コントラクトの実装を検出します。

インテグレーターは /META-INF/services/org.hibernate.integrator.spi.Integrator という名前のファイルを定義し、クラスパス上で使用できるようにします。

このファイルは java.util.ServiceLoader メカニズムによって使用され、org.hibernate.integrator.spi.Integrator インターフェースを実装するクラスの完全修飾名を 1 行に 1 つずつリストします。

12.9.7. SessionFactory レジストリー

12.9.7.1. SessionFactory レジストリー

すべてのレジストリータイプのインスタンスを指定の org.hibernate.SessionFactory のターゲットとして扱うことが最良の方法ですが、このグループのサービスのインスタンスは明示的に 1 つの org.hibernate.SessionFactory に属します。

違いは開始する必要があるタイミングになります。一般的に開始する org.hibernate.SessionFactory にアクセスする必要があります。この特別なレジストリーは org.hibernate.service.spi.SessionFactoryServiceRegistry です。

12.9.7.2. SessionFactory サービス

org.hibernate.event.service.spi.EventListenerRegistry

説明
イベントリスナーを管理するサービス。
イニシエーター
org.hibernate.event.service.internal.EventListenerServiceInitiator
実装
org.hibernate.event.service.internal.EventListenerRegistryImpl

12.9.8. インテグレーター

12.9.8.1. インテグレーター

org.hibernate.integrator.spi.Integrator の目的は、機能する SessionFactory のビルドプロセスに開発者がフックできるようにする簡単な手段を提供することです。org.hibernate.integrator.spi.Integrator インターフェースは、ビルドプロセスにフックできるようにする integrate と、終了する SessionFactory にフックできるようにする disintegrate の 2 つのメソッドを定義します。

注記

org.hibernate.cfg.Configuration の代わりに org.hibernate.metamodel.source.MetadataImplementor を受け入れるオーバーロード形式の integrate は、org.hibernate.integrator.spi.Integrator で定義される 3 つ目のメソッドになります。この形式は 5.0 で完了予定であった新しいメタモデルコードでの使用向けです。

IntegratorService によって提供されるディスカバリー以外に、BootstrapServiceRegistry のビルド時にアプリケーションはインテグレーターを手動で登録することができます。

12.9.8.2. インテグレーターのユースケース

現在、org.hibernate.integrator.spi.Integrator の主なユースケースは、イベントリスナーの登録とサービスの提供です (org.hibernate.integrator.spi.ServiceContributingIntegrator を参照)。5.0 では、オブジェクトとリレーショナルモデルとの間のマッピングを定義するメタモデルを変更できるようにするための拡張を計画しています。

例: イベントリスナーの登録

public class MyIntegrator implements org.hibernate.integrator.spi.Integrator {

    public void integrate(
            Configuration configuration,
            SessionFactoryImplementor sessionFactory,
            SessionFactoryServiceRegistry serviceRegistry) {
        // As you might expect, an EventListenerRegistry is the thing with which event listeners are registered  It is a
        // service so we look it up using the service registry
        final EventListenerRegistry eventListenerRegistry = serviceRegistry.getService( EventListenerRegistry.class );

        // If you wish to have custom determination and handling of "duplicate" listeners, you would have to add an
        // implementation of the org.hibernate.event.service.spi.DuplicationStrategy contract like this
        eventListenerRegistry.addDuplicationStrategy( myDuplicationStrategy );

        // EventListenerRegistry defines 3 ways to register listeners:
        //     1) This form overrides any existing registrations with
        eventListenerRegistry.setListeners( EventType.AUTO_FLUSH, myCompleteSetOfListeners );
        //     2) This form adds the specified listener(s) to the beginning of the listener chain
        eventListenerRegistry.prependListeners( EventType.AUTO_FLUSH, myListenersToBeCalledFirst );
        //     3) This form adds the specified listener(s) to the end of the listener chain
        eventListenerRegistry.appendListeners( EventType.AUTO_FLUSH, myListenersToBeCalledLast );
    }
}

12.10. Envers

12.10.1. Hibernate Envers

Hibernate Envers は監査およびバージョニングシステムであり、永続クラスへの変更履歴を追跡する方法を JBoss EAP に提供します。エンティティーに対する変更履歴を保存する @Audited アノテーションが付けられたエンティティーに対して監査テーブルが作成されます。その後、データの取得と問い合わせが可能になります。

Envers により開発者は次の作業を行うことが可能になります。

  • JPA 仕様によって定義されるすべてのマッピングの監査
  • JPA 仕様を拡張するすべての Hibernate マッピングの監査
  • ネイティブ Hibernate API によりマッピングされた監査エンティティー
  • リビジョンエンティティーを用いて各リビジョンのデータをログに記録
  • 履歴データのクエリー

12.10.2. 永続クラスの監査

JBoss EAP では、Hibernate Envers と @Audited アノテーションを使用して永続クラスの監査を行います。アノテーションがクラスに適用されると、エンティティーのリビジョン履歴が保存されるテーブルが作成されます。

クラスに変更が加えられるたびに監査テーブルにエントリーが追加されます。エントリーにはクラスへの変更が含まれ、リビジョン番号が付けられます。そのため、変更をロールバックしたり、以前のリビジョンを表示したりすることが可能です。

12.10.3. 監査ストラテジー

12.10.3.1. 監査ストラテジー

監査ストラテジーは、監査情報の永続化、クエリー、および格納の方法を定義します。Hibernate Envers には、現在 2 つの監査ストラテジーが存在します。

デフォルトの監査ストラテジー
  • このストラテジーは監査データと開始リビジョンを共に永続化します。監査テーブルで挿入、更新、削除された各行については、開始リビジョンの有効性と合わせて、1 つ以上の行が監査テーブルに挿入されます。
  • 監査テーブルの行は挿入後には更新されません。監査情報のクエリーはサブクエリーを使い監査テーブルの該当行を選択します (これは時間がかかり、インデックス化が困難です)。
妥当性監査ストラテジー
  • このストラテジーは監査情報の開始リビジョンと最終リビジョンの両方を格納します。監査テーブルで挿入、更新、または削除された各行については、開始リビジョンの有効性とあわせて、1 つ以上の行が監査テーブルに挿入されます。
  • 同時に、以前の監査行 (利用可能な場合) の最終リビジョンフィールドがこのリビジョンに設定されます。監査情報に対するクエリーは、サブクエリーの代わりに開始と最終リビジョンのいずれかを使用します。つまり、更新の数が増えるため監査情報の永続化には今までより少し時間がかかりますが、監査情報の取得は非常に早くなります。
  • インデックスを増やすことで改善することも可能です。

監査の詳細については、永続クラスの監査を参照してください。アプリケーションの監査ストラテジーを設定するには、 監査ストラテジーの設定を参照してください。

12.10.3.2. 監査ストラテジーの設定

JBoss EAP では、2 つの監査ストラテジーがサポートされます。

  • デフォルトの監査ストラテジー
  • 妥当性監査ストラテジー
監査ストラテジーの定義

アプリケーションの persistence.xml ファイルで org.hibernate.envers.audit_strategy プロパティーを設定します。このプロパティーが persistence.xml ファイルで設定されていない場合は、デフォルトの監査ストラテジーが使用されます。

デフォルトの監査ストラテジーの設定

<property name="org.hibernate.envers.audit_strategy" value="org.hibernate.envers.strategy.DefaultAuditStrategy"/>

妥当性監査ストラテジーの設定

<property name="org.hibernate.envers.audit_strategy" value="org.hibernate.envers.strategy.ValidityAuditStrategy"/>

12.10.4. JPA エンティティーへの監査サポートの追加

JBoss EAP は、エンティティーの監査を使用して (Hibernate Envers を参照)、永続クラスの変更履歴を追跡します。本トピックでは、JPA エンティティーに対する監査サポートを追加する方法について説明します。

JPA エンティティーへの監査サポートの追加

  1. デプロイメントに適した使用可能な監査パラメーターを設定します (Envers パラメーターの設定を参照)。
  2. 監査対象となる JPA エンティティーを開きます。
  3. org.hibernate.envers.Audited インターフェースをインポートします。
  4. 監査対象となる各フィールドまたはプロパティーに @Audited アノテーションを付けます。または、1 度にクラス全体へアノテーションを付けます。

    例: 2 つのフィールドの監査

    import org.hibernate.envers.Audited;
    
    import javax.persistence.Entity;
    import javax.persistence.Id;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Column;
    
    @Entity
    public class Person {
        @Id
        @GeneratedValue
        private int id;
    
        @Audited
        private String name;
    
        private String surname;
    
        @ManyToOne
        @Audited
        private Address address;
    
        // add getters, setters, constructors, equals and hashCode here
    }

    例: クラス全体の監査

    import org.hibernate.envers.Audited;
    
    import javax.persistence.Entity;
    import javax.persistence.Id;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Column;
    
    @Entity
    @Audited
    public class Person {
        @Id
        @GeneratedValue
        private int id;
    
        private String name;
    
        private String surname;
    
        @ManyToOne
        private Address address;
    
        // add getters, setters, constructors, equals and hashCode here
    }

JPA エンティティーの監査が設定されると、変更履歴を保存するために _AUD という名前のテーブルが作成されます。

12.10.5. Configuration (設定)

12.10.5.1. Envers パラメーターの設定

JBoss EAP は、Hibernate Envers からエンティティーの監査を使用して、永続クラスの変更履歴を追跡します。

利用可能な Envers パラメーターの設定

  1. アプリケーションの persistence.xml ファイルを開きます。
  2. 必要に応じて Envers プロパティーを追加、削除、または設定します。使用可能なプロパティーの一覧については、Envers の設定プロパティーを参照してください。

    例: Envers パラメーター

    <persistence-unit name="mypc">
      <description>Persistence Unit.</description>
      <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
      <shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
      <properties>
        <property name="hibernate.hbm2ddl.auto" value="create-drop" />
        <property name="hibernate.show_sql" value="true" />
        <property name="hibernate.cache.use_second_level_cache" value="true" />
        <property name="hibernate.cache.use_query_cache" value="true" />
        <property name="hibernate.generate_statistics" value="true" />
        <property name="org.hibernate.envers.versionsTableSuffix" value="_V" />
        <property name="org.hibernate.envers.revisionFieldName" value="ver_rev" />
      </properties>
    </persistence-unit>

12.10.5.2. ランタイム時に監査を有効または無効にする

ランタイム時にエンティティーバージョン監査を有効または無効にする

  1. AuditEventListener クラスをサブクラス化します。
  2. Hibernate イベント上で呼び出される次のメソッドを上書きします。

    • onPostInsert
    • onPostUpdate
    • onPostDelete
    • onPreUpdateCollection
    • onPreRemoveCollection
    • onPostRecreateCollection
  3. イベントのリスナーとしてサブクラスを指定します。
  4. 変更を監査すべきであるか判断します。
  5. 変更を監査する必要がある場合は、呼び出しをスーパークラスへ渡します。

12.10.5.3. 条件付き監査の設定

Hibernate Envers は一連のイベントリスナーを使用して、さまざまな Hibernate イベントに対して監査データを永続化します。Envers jar がクラスパスにある場合、これらのリスナーは自動的に登録されます。

条件付き監査の実装

  1. persistence.xml ファイルで hibernate.listeners.envers.autoRegister の Hibernate プロパティーを false に設定します。
  2. 上書きする各イベントリスナーをサブクラス化します。条件付き監査の論理をサブクラスに置き、監査の実行が必要な場合はスーパーメソッドを呼び出します。
  3. org.hibernate.envers.event.EnversIntegrator と似ている org.hibernate.integrator.spi.Integrator のカスタム実装を作成します。デフォルトのクラスではなく、手順 2 で作成したイベントリスナーサブクラスを使用します。
  4. jar に META-INF/services/org.hibernate.integrator.spi.Integrator ファイルを追加します。このファイルにはインターフェースを実装するクラスの完全修飾名を含める必要があります。

12.10.5.4. Envers の設定プロパティー

表12.24 エンティティーデータのバージョニング設定パラメーター

プロパティー名デフォルト値説明

org.hibernate.envers.audit_table_prefix

デフォルト値なし

監査エンティティーの名前の前に付けられた文字列。監査情報を保持するエンティティーの名前を作成します。

org.hibernate.envers.audit_table_suffix

_AUD

監査情報を保持するエンティティーの名前を作成する監査エンティティーの名前に追加された文字列。たとえば、Person のテーブル名を持つエンティティーが監査される場合は、Envers により履歴データを格納する Person_AUD と呼ばれるテーブルが生成されます。

org.hibernate.envers.revision_field_name

REV

改訂番号を保持する監査エンティティーのフィールド名。

org.hibernate.envers.revision_type_field_name

REVTYPE

リビジョンタイプを保持する監査エンティティーのフィールド名。挿入、変更、または削除のための現在のリビジョンタイプは、それぞれ addmod、および del です。

org.hibernate.envers.revision_on_collection_change

true

このプロパティーは、所有されていない関係フィールドが変更された場合にリビジョンを生成するかどうかを決定します。これは、一対多関係のコレクションまたは一対一関係の mappedBy 属性を使用したフィールドのいずれかです。

org.hibernate.envers.do_not_audit_optimistic_locking_field

true

true の場合、オプティミスティックロッキングに使用したプロパティー (@Version アノテーションが付いたもの) は自動的に監査から除外されます。

org.hibernate.envers.store_data_at_delete

false

このプロパティーは、ID のみではなく、他の全プロパティーが null とマークされたエンティティーが削除される場合にエンティティーデータをリビジョンに保存すべきかどうかを定義します。このデータは最終リビジョンに存在するため、これは通常必要ありません。最終リビジョンのデータにアクセスする方が簡単で効率的ですが、この場合、削除前にエンティティーに含まれたデータが 2 回保存されることになります。

org.hibernate.envers.default_schema

null (通常のテーブルと同じ)

監査テーブルに使用されるデフォルトのスキーマ名。@AuditTable(schema="…​") アノテーションを使用してオーバーライドできます。このスキーマがない場合、スキーマは通常のテーブルのスキーマと同じです。

org.hibernate.envers.default_catalog

null (通常のテーブルと同じ)

監査テーブルに使用するデフォルトのカタログ名。@AuditTable(catalog="…​") アノテーションを使用してオーバーライドできます。このカタログがない場合、カタログは通常のテーブルのカタログと同じです。

org.hibernate.envers.audit_strategy

org.hibernate.envers.strategy.DefaultAuditStrategy

このプロパティーは、監査データを永続化する際に使用する監査ストラテジーを定義します。デフォルトでは、エンティティーが変更されたリビジョンのみが保存されます。あるいは、org.hibernate.envers.strategy.ValidityAuditStrategy が、開始リビジョンと最終リビジョンの両方を保存します。これらは、監査行が有効である場合に定義されます。

org.hibernate.envers.audit_strategy_validity_end_rev_field_name

REVEND

監査エンティティーのリビジョン番号を保持するカラムの名前。このプロパティーは、妥当性監査ストラテジーが使用されている場合のみ有効です。

org.hibernate.envers.audit_strategy_validity_store_revend_timestamp

false

このプロパティーは、データが最後に有効だった最終リビジョンのタイムスタンプを最終リビジョンとともに格納するかどうかを定義します。これは、テーブルパーティショニングを使用することにより、関係データベースから以前の監査レコードを削除する場合に役に立ちます。パーティショニングには、テーブル内に存在する列が必要です。このプロパティーは、ValidityAuditStrategy が使用される場合にのみ評価されます。

org.hibernate.envers.audit_strategy_validity_revend_timestamp_field_name

REVEND_TSTMP

データが有効であった最終リビジョンのタイムスタンプの列名。ValidityAuditStrategy が使用され、org.hibernate.envers.audit_strategy_validity_store_revend_timestamp が true と評価された場合のみ使用されます。

12.10.6. クエリーを介した監査情報の読み出し

Hibernate Envers は、クエリーから監査情報を読み出しする機能を提供します。

注記

監査されたデータのクエリーは相関サブセレクトが関与するため、多くの場合で live データの対応するクエリーよりも大幅に処理が遅くなります。

特定のリビジョンでクラスのエンティティーをクエリーする

このようなクエリーのエントリーポイントは次のとおりです。

AuditQuery query = getAuditReader()
    .createQuery()
    .forEntitiesAtRevision(MyEntity.class, revisionNumber);

AuditEntity ファクトリクラスを使用して制約を指定することができます。以下のクエリーは、name プロパティーが John と同等である場合のみエンティティーを選択します。

query.add(AuditEntity.property("name").eq("John"));

以下のクエリーは特定のエンティティーと関連するエンティティーのみを選択します。

query.add(AuditEntity.property("address").eq(relatedEntityInstance));
// or
query.add(AuditEntity.relatedId("address").eq(relatedEntityId));

結果を順序付けや制限付けしたり、凝集 (aggregations) および射影 (projections) のセット (グループ化を除く) を持つことが可能です。以下はフルクエリーの例になります。

List personsAtAddress = getAuditReader().createQuery()
    .forEntitiesAtRevision(Person.class, 12)
    .addOrder(AuditEntity.property("surname").desc())
    .add(AuditEntity.relatedId("address").eq(addressId))
    .setFirstResult(4)
    .setMaxResults(2)
    .getResultList();

特定クラスのエンティティーが変更された場合のクエリーリビジョン

このようなクエリーのエントリーポイントは次のとおりです。

AuditQuery query = getAuditReader().createQuery()
    .forRevisionsOfEntity(MyEntity.class, false, true);

前の例と同様に、このクエリーへ制約を追加することが可能です。このクエリーに以下を追加することも可能です。

AuditEntity.revisionNumber()
監査されたエンティティーが修正されたリビジョン番号の制約や射影、順序付けを指定します。
AuditEntity.revisionProperty(propertyName)
監査されたエンティティーが修正されたリビジョンに対応するリビジョンエンティティーのプロパティーの制約や射影、順序付けを指定します。
AuditEntity.revisionType()
リビジョンのタイプ (ADD、MOD、DEL) へのアクセスを提供します。

クエリー結果を必要に応じて調整することが可能です。次のクエリーは、リビジョン番号 42 の後に entityId ID を持つ MyEntity クラスのエンティティーが変更された最小のリビジョン番号を選択します。

Number revision = (Number) getAuditReader().createQuery()
    .forRevisionsOfEntity(MyEntity.class, false, true)
    .setProjection(AuditEntity.revisionNumber().min())
    .add(AuditEntity.id().eq(entityId))
    .add(AuditEntity.revisionNumber().gt(42))
    .getSingleResult();

リビジョンのクエリーはプロパティーを最小化および最大化することも可能です。次のクエリーは、特定エンティティーの actualDate 値が指定の値よりは大きく、可能な限り小さいリビジョンを選択します。

Number revision = (Number) getAuditReader().createQuery()
    .forRevisionsOfEntity(MyEntity.class, false, true)
    // We are only interested in the first revision
    .setProjection(AuditEntity.revisionNumber().min())
    .add(AuditEntity.property("actualDate").minimize()
        .add(AuditEntity.property("actualDate").ge(givenDate))
        .add(AuditEntity.id().eq(givenEntityId)))
    .getSingleResult();

minimize() および maximize() メソッドは制約を追加できる基準を返します。最大化または最小化されたプロパティーを持つエンティティーはこの基準を満たさなければなりません。

クエリー作成時に渡されるブール変数パラメーターは 2 つあります。

selectEntitiesOnly

このパラメーターは、明示的なプロジェクションが設定されていない場合のみ有効です。
true の場合、クエリーの結果は指定された制約を満たすリビジョンで変更されたエンティティーのリストです。
false の場合、結果は 3 つの要素アレイのリストです。最初の要素は変更されたエンティティーインスタンスです。2 つ目の要素はリビジョンデータを含むエンティティーです。カスタムエンティティーが使用されない場合、これは DefaultRevisionEntity のインスタンスです。3 つ目の要素アレイはリビジョンの種類 (ADD、MOD、DEL) です。
selectDeletedEntities
このパラメーターは、エンティティーが削除されたリビジョンが結果に含まれなければならない場合に指定されます。true の場合、エンティティーのリビジョンタイプが DEL になり、id 以外のすべてのフィールドの値が null になります。

特定のプロパティーを修正したエンティティーのクエリーリビジョン

下記のクエリーは、actualDate プロパティーが変更された、指定の ID を持つ MyEntity のすべてのリビジョンを返します。

AuditQuery query = getAuditReader().createQuery()
  .forRevisionsOfEntity(MyEntity.class, false, true)
  .add(AuditEntity.id().eq(id));
  .add(AuditEntity.property("actualDate").hasChanged())

hasChanged 条件は他の基準と組み合わせることができます。次のクエリーは、revisionNumber の生成時に MyEntity の水平スライスを返します。これは、prop2 ではなく prop1 を変更したリビジョンに限定されます。

AuditQuery query = getAuditReader().createQuery()
  .forEntitiesAtRevision(MyEntity.class, revisionNumber)
  .add(AuditEntity.property("prop1").hasChanged())
  .add(AuditEntity.property("prop2").hasNotChanged());

結果セットには revisionNumber よりも小さい番号のリビジョンも含まれます。これは、このクエリーが「prop1 が変更され、prop2 が変更されない revisionNumber で変更された MyEntities をすべて返す」とは読み取られないことを意味します。

次のクエリーは forEntitiesModifiedAtRevision クエリーを使用してこの結果をどのように返すことができるかを示します。

AuditQuery query = getAuditReader().createQuery()
  .forEntitiesModifiedAtRevision(MyEntity.class, revisionNumber)
  .add(AuditEntity.property("prop1").hasChanged())
  .add(AuditEntity.property("prop2").hasNotChanged());

特定のリビジョンで修正されたクエリーエンティティー

次の例は、特定のリビジョンで変更されたエンティティーに対する基本的なクエリーになります。読み出される特定のリビジョンで、エンティティー名と対応する Java クラスを変更できます。

Set<Pair<String, Class>> modifiedEntityTypes = getAuditReader()
    .getCrossTypeRevisionChangesReader().findEntityTypes(revisionNumber);

org.hibernate.envers.CrossTypeRevisionChangesReader からもアクセスできる他のクエリーは以下のとおりです。

List<Object> findEntities(Number)
特定のリビジョンで変更 (追加、更新、削除)されたすべての監査済みエンティティーのスナップショットを返します。n+1 個の SQL クエリを実行します (n は指定のリビジョン内で変更された異なるエンティティークラスの数になります)。
List<Object> findEntities(Number, RevisionType)
変更タイプによってフィルターされた特定のリビジョンで変更 (追加、更新、削除) されたすべての監査済みエンティティーのスナップショットを返します。n+1 個の SQL クエリーを実行します (n は指定のリビジョン内で変更された異なるエンティティークラスの数です)。Map<RevisionType, List<Object>>
findEntitiesGroupByRevisionType(Number)
修正操作 (追加、更新、削除など) によってグループ化されたエンティティースナップショットの一覧が含まれるマップを返します。3n+1 個の SQL クエリーを実行します (n は指定のリビジョン内で変更された異なるエンティティークラスの数になります)。

12.11. パフォーマンスチューニング

12.11.1. 代替のバッチローディングアルゴリズム

Hibernate では、4 つのフェッチングストラテジー (join、select、subselect、および batch) のいずれかを使用してアソシエーションのデータをロードできます。batch ローディングは select フェッチングの最適化ストラテジーであるため、パフォーマンスを最大化できます。このストラテジーでは、主キーまたは外部キーのリストを指定することで、Hibernate が単一の SELECT ステートメントでエンティティーインスタンスまたはコレクションのバッチを読み出します。batch フェッチングは、レイジー select フェッチングストラテジーの最適化です。

batch フェッチングを設定する方法には、クラスごとのレベルと、コレクションごとのレベルの 2 つの方法があります。

  • クラスごとのレベル

    Hibernate がクラスごとのレベルでデータをロードする場合、クエリー時に事前ロードするアソシエーションのバッチサイズが必要になります。たとえば、起動時に car オブジェクトの 30 個のインスタンスがセッションでロードされるとします。各 car オブジェクトは owner オブジェクトに属します。lazy ローディングで、すべての car オブジェクトを繰り返し、これらの owner (所有者) を要求する場合、Hibernate は owner ごとに 1 つ、合計 30 個の select ステートメントを発行します。これは、パフォーマンス上のボトルネックになります。

    この代わりに、クエリーによって要求される前に次の owner のバッチに対してデータを事前ロードするよう Hibernate を指示できます。owner オブジェクトがクエリーされると、Hibernate は同じ SELECT ステートメントでこれらのオブジェクトをさらに多くクエリーします。

    事前にクエリーされる owner オブジェクトの数は、設定時に指定された batch-size パラメーターによって異なります。

    <class name="owner" batch-size="10"></class>

    これは、今後必要になると見込まれる最低 10 個の owner オブジェクトをクエリーするよう Hibernate に指示します。ユーザーが car Aowner をクエリーする場合、car Bowner はすでにバッチローディングの一部としてロードされていることがあります。ユーザーが実際に car Bowner を必要とする場合、データーベースにアクセスして SELECT ステートメントを発行する代わりに、現在のセッションから値を読み出すことができます。

    Hibernate 4.2.0 には、batch-size パラメーターの他に、バッチローディングのパフォーマンスを向上する新しい設定項目が追加されました。この設定項目は Batch Fetch Style 設定と呼ばれ、hibernate.batch_fetch_style パラメーターによって指定されます。

    LEGACY、PADDED、および DYNAMIC の 3 つのバッチフェッチスタイルがサポートされます。使用するスタイルを指定するには、org.hibernate.cfg.AvailableSettings#BATCH_FETCH_STYLE を使用します。

    • LEGACY: LEGACY スタイルのローディングでは、ArrayHelper.getBatchSizes(int) に基づいた一連の事前ビルド済みバッチサイズが使用されます。バッチは、既存のバッチ可能な識別子の数から、次に小さい事前ビルド済みバッチサイズを使用してロードされます。

      前述の例を用いた場合、batch-size の設定が 30 であると、事前ビルドされたバッチサイズは [30, 15, 10, 9, 8, 7, .., 1] になります。29 個の識別子をバッチロードしようとすると、バッチは 15、10、および 4 になります。対応する 3 つの SQL クエリーが生成され、各クエリーはデータベースより 15、10、および 4 人の owner をロードします。

    • PADDED - PADDED は LEGACY スタイルのバッチローディングと似ています。PADDED も事前ビルドされたバッチサイズを使用しますが、次に大きなバッチサイズを使用し、余分な識別子プレースホルダーを埋め込みます。

      上記の例で、30 個の owner オブジェクトが初期化される場合は、データベースに対して 1 つのクエリーのみが実行されます。

      29 個の owner オブジェクトが初期化される場合、Hibernate は同様にバッチサイズが 30 の SQL select ステートメントを 1 つ実行しますが、余分なスペースが繰り替えされる識別子で埋め込みされます。

    • DYNAMIC - DYNAMIC スタイルのバッチローディングはバッチサイズの制限に準拠しますが、実際にロードされるオブジェクトの数を使用して SQL SELECT ステートメントを動的にビルドします。

      たとえば、30 個の owner オブジェクトで最大バッチサイズが 30 の場合、 30 個の owner オブジェクトの読み出しは 1 つの SQL SELECT ステートメントによって実行されます。35 個の owner オブジェクトを読み出す場合は、SQL ステートメントが 2 つになり、それぞれのバッチサイズが 30 と 5 になります。Hibernate は、制限どおりにバッチサイズを 30 以下とし、2 つ目の SQL ステートメントを動的に変更して、必要数である 5 にします。PADDED とは異なり、2 つ目の SQL は埋め込みされません。また、2 つ目の SQL ステートメントは動的に作成され、固定サイズでないことが LEGACY とは異なります。

      30 個未満のクエリーでは、このスタイルは要求された識別子の数のみを動的にロードします。

  • コレクションごとのレベル

    Hibernate は、前項の「クラスごとのレベル」で説明したバッチフェッチサイズとスタイルを維持してコレクションをバッチロードすることもできます。

    前項の例を逆にして、各 owner オブジェクトによって所有されるすべての car オブジェクトをロードする必要があるとします。10 個の owner オブジェクトがすべての owner を繰り返し、現セッションにロードされた場合、getCars() メソッドの呼び出しごとに 1 つの SELECT ステートメントが生成されるため、合計で 10 個の SELECT ステートメントが生成されます。owner のマッピングでの car コレクションのバッチフェッチングを有効にすると、Hibernate は下記のようにこれらのコレクションを事前フェッチできます。

    <class name="Owner"><set name="cars" batch-size="5"></set></class>

    よって、バッチサイズが 5 でレガシーバッチスタイルを使用して 10 個のコレクションをロードする場合、Hibernate は 2 つの SELECT ステートメントを実行し、各ステートメントは 5 つのコレクションを読み出します。

12.11.2. 不変データのオブジェクト参照の 2 次キャッシング

Hibernate はパフォーマンスを向上するため、自動的にデータをメモリー内にキャッシュします。これは、データベースのルックアップが必要となる回数を削減する (特にほとんど変更されないデータに対し) インメモリーキャッシュによって実現されます。

Hibernate は 2 つのタイプのキャッシュを保持します。1 次キャッシュ (プライマリーキャッシュ) は必須のキャッシュです。このキャッシュは現在のセッションと関連し、すべてのリクエストが通過する必要があります。2 次キャッシュ (セカンダリーキャッシュ) は任意のキャッシュで、1 次キャッシュがアクセスされた後でのみアクセスされます。

データは、最初にステートアレイに逆アセンブルされ、2 次キャッシュに保存されます。このアレイはディープコピーされ、ディープコピーがキャッシュに格納されます。キャッシュからデータを読み取る場合は、この逆のプロセスが発生します。この仕組みは、変化するデータ (可変データ) ではうまく機能しますが、不変データでは不十分です。

データのディープコピーは、メモリーの使用と処理速度に負荷のかかる操作です。大きなデータセットでは、メモリーおよび処理速度がパフォーマンスを制限する要素になります。Hibernate では、不変データがコピーされずに参照されるよう指定できます。Hibernate はデータセット全体をコピーする代わりに、データへの参照をキャッシュに保存できます。

これは、設定 hibernate.cache.use_reference_entries の値を true に変更することによって行えます。デフォルトでは、hibernate.cache.use_reference_entriesfalse に設定されます。

hibernate.cache.use_reference_entriestrue に設定されると、アソシエーションを持たない不変データオブジェクトは 2 次キャッシュにコピーされず、不変データオブジェクトへの参照のみが保存されます。

警告

hibernate.cache.use_reference_entriestrue に設定されても、アソシエーションを持つ不変データオブジェクトは 2 次キャッシュにディープコピーされます。