blob: 0526f2f099d79c20da0f241ca6d7ab1777ef8f7d [file] [log] [blame]
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
= CDIのイベントの操作
:jbake-type: tutorial
:jbake-tags: tutorials
:jbake-status: published
:icons: font
:syntax: true
:source-highlighter: pygments
:toc: left
:toc-title:
:description: CDIのイベントの操作 - Apache NetBeans
:keywords: Apache NetBeans, Tutorials, CDIのイベントの操作
_執筆: Andy Gibson_
== コンテキストと依存性の注入
1. link:cdi-intro.html[+CDIおよびJSF 2.0の開始+]
2. link:cdi-inject.html[+CDIの注入および修飾子の操作+]
3. link:cdi-validate.html[+@Alternative Beanおよびライフサイクル注釈の適用+]
4. *CDIのイベントの操作*
* <<event,イベントの利用>>
* <<scopes,スコープの処理>>
* <<seealso,関連項目>>
link:http://jcp.org/en/jsr/detail?id=299[+JSR-299+]で指定されているコンテキストと依存性の注入(CDI: Contexts and Dependency Injection)はJava EE 6の不可欠な部分であり、サーブレット、エンタープライズBean、JavaBeansなどのJava EEコンポーネントが、アプリケーションのライフサイクル内で明確なスコープを持って存在できるようにするためのアーキテクチャを提供します。また、CDIサービスによって、EJBセッションBeanやJSF (JavaServer Faces)管理対象BeanなどのJava EEコンポーネントが注入可能になり、イベントの起動や監視による疎結合方式の対話が可能になります。
このチュートリアルは、Andy Gibson氏によって投稿されたlink:http://www.andygibson.net/blog/index.php/2010/01/11/getting-started-with-jsf-2-0-and-cdi-part-3/[+CDI入門パート3 - イベント+]というタイトルのブログをベースにしています。ここでは、Java EEの_イベント_の概念を活用する方法を示します。この方法は、アプリケーション内のイベントの生成およびイベントへのサブスクライブ(つまり_監視_)について、プロデューサとオブザーバの間でコードを分離して管理できます。`javax.enterprise.event.Event`クラスを使用してイベントを作成し、CDIの`@Observes`注釈を使用してイベントにサブスクライブします。
NetBeans IDEは、コンテキストと依存性の注入のサポートを組込みでサポートしています。これには、プロジェクト作成時に`beans.xml` CDI構成ファイルを生成するオプション、注釈のためのエディタおよびナビゲーション・サポート、一般的に使用されるCDIアーティファクトを作成するための各種ウィザードなどが含まれています。
このチュートリアルを完了するには、次のソフトウェアとリソースが必要です。
|===
|ソフトウェアまたはリソース |必須バージョン
|link:https://netbeans.org/downloads/index.html[+NetBeans IDE+] |7.2、7.3、7.4、8.0、Java EEバージョン
|link:http://www.oracle.com/technetwork/java/javase/downloads/index.html[+Java Development Kit (JDK)+] |バージョン7または8
|link:http://glassfish.dev.java.net/[+GlassFishサーバー+] |Open Source Edition 3.xまたは4x
|link:https://netbeans.org/projects/samples/downloads/download/Samples%252FJavaEE%252FcdiDemo3.zip[+cdiDemo3.zip+] |n/a
|===
[NOTE]
====
* NetBeans IDEJava EEバンドル版には、Java EE準拠のコンテナであるGlassFish Server Open Source Editionも含まれています。
* このチュートリアルのサンプル・ソリューション・プロジェクトをダウンロードできます: link:https://netbeans.org/projects/samples/downloads/download/Samples%252FJavaEE%252FcdiDemoComplete.zip[+cdiDemoComplete.zip+]
====
[[event]]
== イベントの利用
前のlink:cdi-validate.html[+@Alternative Beanおよびライフサイクル注釈の適用+]のチュートリアルでは、項目の一覧を取得してそれを検証し、無効な項目が見つかったら特定のアクションを起こすアプリケーションを作成しました。仮に、今後システムを拡大して、無効な項目が見つかった場合に発生するあらゆることを処理できるようにするとします。これには、電子メールの送信、他のデータの変更(注文の取消しなど)、またはファイルやデータベース表への却下リストの格納など、様々なものがあります。実装を完全に分離するために、Java EE_イベント_を使用できます。イベントは、イベント・_プロデューサ_によって生成され、イベント・_オブザーバ_からサブスクライブされます。ほとんどのCDIと同様にイベントの生成およびサブスクライブは型保証であるため、修飾子は、監視するイベント・オブザーバを判別できます。
このシリーズで前のチュートリアルからビルドしているアプリケーションを使用した場合、それほど多くの変更をしなくてもこの実装が可能です。項目を処理するたびにイベントを生成する、もう1つの`ItemErrorHandler` (link:cdi-validate.html[+前のチュートリアル+]で作成)の実装を提供するのみです。このクラスに`EventItemHandler`という名前を付けて`ItemProcessor`に注入します。注入対象を選択するには、`Notify`修飾子を使用します。
image::images/cdi-diagram-events.png[title="アプリケーションの疎結合クラスへのCDI注入の使用"]
1. まず、`cdiDemo3.zip`ファイル(上記の<<requiredSoftware,必要なリソースの一覧表>>を参照)からサンプルのスタート・プロジェクトを抽出します。「ファイル」>「プロジェクトを開く」([Ctrl]-[Shift]-[O]、Macの場合は[⌘]-[Shift]-[O])を選択してから、コンピュータ上のこのプロジェクトの場所を選択することで、IDEでプロジェクトを開きます。
2. `EventItemHandler`という名前のクラスを作成します。「新規ファイル」(image:images/new-file-btn.png[])ボタンをクリックするか、[Ctrl]-[N] (Macの場合は[⌘]-[N])を押してファイル・ウィザードを開きます。
3. Java」カテゴリから「Javaクラス」を選択します。「次」をクリックします。
4. クラス名として「*EventItemHandler*」、パッケージとして「*exercise4*」と入力します。
5. 「終了」をクリックします。新しいクラスおよびパッケージが生成され、エディタでクラスが開きます。
6. 次のようにして*EventItemHandler*を実装します。
[source,java]
----
public class EventItemHandler *implements ItemErrorHandler* {
*@Inject
private Event<Item> itemEvent;
@Override
public void handleItem(Item item) {
System.out.println("Firing Event");
itemEvent.fire(item);
}*
}
----
イベント・ペイロードが`Item`になる`Event`のインスタンスを注入します。イベント・ペイロードとはイベント・プロデューサからイベント・オブザーバに渡される状態データのことで、この場合は却下されたItemが渡されます。無効な項目が処理されたら、イベントを起動して、受け取った無効な項目を渡します。このイベント・ベースの項目ハンドラは、他の項目ハンドラと同じように注入されるため、いつでも必要なときに交換したり、テスト中に取り換えたりできます。
. すべてのインポートを修正します。エディタを右クリックして「インポートを修正」を選択するか、[Ctrl]-[Shift]-[I] (Macの場合は[⌘]-[Shift]-[I])を押します。必ず`Event`クラスの完全修飾名として`javax.enterprise.event.Event`を選択するようにしてください。
image::images/fix-all-imports.png[title="エディタで右クリックして「インポートを修正」を選択し、「インポートを修正」ダイアログを呼び出す"]
[tips]#`Event`の上で[Ctrl]-[Space]を押して、クラスのJavadoc定義を表示します。上記で使用した`fire()`メソッドも定義されています。#
image::images/event-javadoc.png[title="Ctrl-Space を押して、APIのクラスのJavadocドキュメントを表示する"]
. `Notify`という名前の修飾子を作成します。(修飾子についてはlink:cdi-inject.html[+CDIの注入および修飾子の操作+]に記載。)
. 「新規ファイル」(image:images/new-file-btn.png[])ボタンをクリックするか、[Ctrl]-[N] (Macの場合は[⌘]-[N])を押してファイル・ウィザードを開きます。
. 「コンテキストと依存性の注入」カテゴリから「修飾子タイプ」を選択します。「次」をクリックします。
. クラス名として「*Notify*」、パッケージとして「*exercise4*」と入力します。
. 「終了」をクリックします。新しい`Notify`修飾子がエディタで開きます。
[source,java]
----
@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface Notify {
}
----
. `EventItemHandler``@Notify`注釈を追加します。
[source,java]
----
*@Notify*
public class EventItemHandler implements ItemErrorHandler {
...
}
----
このエラー・ハンドラを注入のために識別し、`ItemProcessor`で注入ポイントに追加して使用できる`@Notify`修飾子注釈を作成しました。
. `exercise2.ItemProcessor`で、`EventItemHandler`の注入ポイントに`@Notify`注釈を追加します。
[source,java]
----
@Named
@RequestScoped
public class ItemProcessor {
@Inject @Demo
private ItemDao itemDao;
@Inject
private ItemValidator itemValidator;
@Inject *@Notify*
private ItemErrorHandler itemErrorHandler;
public void execute() {
List<Item> items = itemDao.fetchItems();
for (Item item : items) {
if (!itemValidator.isValid(item)) {
itemErrorHandler.handleItem(item);
}
}
}
}
----
(エディタのヒントを使用して`exercise4.Notify`のインポート文を追加します。)
. 「プロジェクトの実行」(image:images/run-project-btn.png[])ボタンをクリックして、プロジェクトを実行します。
. ブラウザで「`Execute`」ボタンをクリックしてからIDEに戻り、「出力」ウィンドウ([Ctrl]-[4]、Macの場合は[⌘]-[4])でサーバー・ログを調べます。ビルドしてきたアプリケーションは、現時点で`DefaultItemDao`を使用して4つの`Item`を設定してから`Item``RelaxedItemValidator`を適用するため、`itemErrorHandler`2度起動するのが確認できるはずです。
image::images/output-window.png[title="「出力」ウィンドウに表示されたGlassFishサーバー・ログの確認"]
しかし、現時点ではイベントを監視しているものはありません。これは、`@Observes`注釈を使用して_オブザーバ_・メソッドを作成すれば修正できます。イベントを監視するために必要な手順はこれのみです。これを示すため、`FileErrorReporter` (link:cdi-validate.html[+前のチュートリアル+]で作成)にこの`handleItem()`メソッドをコールするオブザーバ・メソッドを追加して、起動されたイベントに応答するように変更できます。
. `FileErrorReporter`がイベントに応答するようにするには、クラスに次のメソッドを追加します。
[source,java]
----
public class FileErrorReporter implements ItemErrorHandler {
*public void eventFired(@Observes Item item) {
handleItem(item);
}*
...
}
----
(エディタのヒントを使用して`javax.enterprise.event.Observes`のインポート文を追加します。)
. 再びプロジェクトを実行([F6]、Macの場合は[fn]-[F6])し、「`Execute`」ボタンをクリックしてからIDEに戻り、「出力」ウィンドウでサーバー・ログを調べます。
image::images/output-window2.png[title="「出力」ウィンドウに表示されたGlassFishサーバー・ログの確認"]
先ほどと同じく無効なオブジェクトでイベントが起動されますが、今度は各イベントの起動時に項目の情報が保存されるようになったのが確認できます。また、起動されたイベントごとに`FileErrorReporter` Beanが作成されて閉じられているため、ライフサイクル・イベントが監視されていることもわかります。(`@PostConstruct``@PreDestroy`などのライフサイクル注釈については、link:cdi-validate.html[+@Alternative Beanおよびライフサイクル注釈の適用+]を参照。)
上記の手順で示したように、`@Observes`注釈はイベントを監視するための簡単な方法を提供します。
イベントおよびオブザーバは、修飾子を使用して注釈を付けることによって、オブザーバが項目の特定のイベントのみを監視するようにもできます。デモについては、link:http://www.andygibson.net/blog/index.php/2010/01/11/getting-started-with-jsf-2-0-and-cdi-part-3/[+CDI入門パート3 – イベント+]を参照してください。
[[scopes]]
== スコープの処理
現状のアプリケーションでは、イベントが生成されるたびに`FileErrorReporter` Beanが作成されます。この場合、項目ごとにファイルを開いて閉じる必要はないため、毎回新しいBeanを作成することは望ましくありません。ただし、プロセスの開始時にファイルを開き、プロセスの完了時にファイルを閉じる必要があります。このために、`FileErrorReporter` Bean_スコープ_について考慮する必要があります。
現時点では、`FileErrorReporter` Beanに定義されたスコープはありません。定義されたスコープがない場合、CDIはデフォルトの依存擬似スコープを使用します。これは実際のところ、Beanが非常に短い期間(通常はメソッド・コールの期間)で作成および破棄されることを意味します。現在のシナリオでは、起動されたイベントの期間でBeanが作成および破棄されます。これを修正するために、手動でスコープ注釈を追加してBeanのスコープを延ばすことができます。このBean`@RequestScoped`を指定して、最初のイベント起動時にBeanが作成されたら、リクエストの期間存在し続けるようにします。これはまた、このBeanを注入できるどの注入ポイントにおいても、同じBeanインスタンスが注入されることを意味します。
1. `FileErrorReporter`クラスに、`@RequestScope`注釈および対応する`javax.enterprise.context.RequestScoped`のインポート文を追加します。
[source,java]
----
*import javax.enterprise.context.RequestScoped;*
...
*@RequestScoped*
public class FileErrorReporter implements ItemErrorHandler { ... }
----
[tips]#入力中に[Ctrl]-[Space]を押すと、エディタのコード補完サポートを呼び出せます。コード補完で項目を選択すると、関連付けられたすべてのインポート文が自動的にクラスに追加されます。#
image::images/code-completion.png[title="入力中に [Ctrl]-[Space]を押してコード補完の候補を呼び出す"]
. 再びプロジェクトを実行([F6]、Macの場合は[fn]-[F6])し、「`Execute`」ボタンをクリックしてからIDEに戻り、「出力」ウィンドウでサーバー・ログを調べます。
image::images/output-window3.png[title="「出力」ウィンドウに表示されたGlassFishサーバー・ログの確認"]
`FileErrorReporter` Beanが最初のイベントの起動時にのみ作成され、最後のイベントの起動後に閉じられます。
[source,java]
----
INFO: Firing Event
*INFO: Creating file error reporter*
INFO: Saving exercise2.Item@48ce88f6 [Value=34, Limit=7] to file
INFO: Firing Event
INFO: Saving exercise2.Item@3cae5788 [Value=89, Limit=32] to file
*INFO: Closing file error reporter*
----
システムの各部分をモジュール式で分離するには、イベントの使用をお薦めします。イベントを使用すると、イベントのオブザーバとプロデューサは互いのことを意識する必要がなくなり、そのための構成の必要もなくなります。イベントのプロデューサにオブザーバを意識させることなく、イベントにサブスクライブするコード部分を追加できます。(イベントを使用しない場合、通常は手動でイベントのプロデューサにオブザーバをコールさせる必要があります。)たとえば、だれかが注文ステータスを更新したら営業担当に電子メールを送るイベントや、技術サポートの問題が未解決のまま1週間を超えたらアカウント・マネージャに通知するイベントを追加できます。このような種類のルールはイベントを使用しなくても実装できますが、イベントを使用するとビジネス・ロジックを簡単に分離できるようになります。さらに、コンパイル時やビルド時の依存性がなくなります。ただアプリケーションにモジュールを追加するのみで、自動的にイベントの監視および生成が始まります。
link:/about/contact_form.html?to=3&subject=Feedback:%20Working%20with%20Events%20in%20CDI[+このチュートリアルに関するご意見をお寄せください+]
[[seealso]]
== 関連項目
CDIおよびJava EEの詳細は、次のリソースを参照してください。
=== NetBeansリソース
* link:cdi-intro.html[+コンテキストと依存性の注入およびJSF 2.0の開始+]
* link:cdi-inject.html[+CDIの注入および修飾子の操作+]
* link:cdi-validate.html[+@Alternative Beanおよびライフサイクル注釈の適用+]
* link:javaee-gettingstarted.html[+Java EEアプリケーションの開始+]
* link:../web/jsf20-intro.html[+JavaServer Faces 2.0入門+]
=== 外部リソース
* link:http://blogs.oracle.com/enterprisetechtips/entry/using_cdi_and_dependency_injection[+エンタープライズ技術ヒント: JSF 2.0アプリケーションでのJavaのCDIおよび依存性の注入の使用+]
* link:http://download.oracle.com/javaee/6/tutorial/doc/gjbnr.html[+Java EE 6チュートリアル、パートV: Java EEプラットフォームのコンテキストと依存性の注入+]
* link:http://jcp.org/en/jsr/detail?id=299[+JSR 299: コンテキストと依存性の注入の仕様+]
* link:http://jcp.org/en/jsr/detail?id=316[+JSR 316: Java Platform Enterprise Edition 6の仕様+]