はじめに
今ではJavaコミュニティのほとんどだれもがJakarta Commonsのことを知っています。Jakarta CommonsはJakartaのさまざまなプロジェクトで使われている再利用可能なクラスの集まりです。しかし、これらのクラスを独立したコンポーネントとして自分のJavaプロジェクトの中でも利用できることは案外知られていないのではないでしょうか。本稿はJakarta Commonsのさまざまなコンポーネントを紹介する3回シリーズの第1弾です。このシリーズでは実際のサンプルアプリケーションを通じてJakarta Commonsコンポーネントの使い方を説明していきます。これらのサンプルはJakarta Commonsコンポーネントを例示するだけのものではなく、典型的なJavaプロジェクトで再利用できる有用な機能を盛り込んだ完全なアプリケーションです。
本稿では次のコンポーネントを取り上げます。
- Validator
- Collections
- Chain
- Functors
- Lang
本稿には完全なソースコードが付属しており、各サンプルのテストケースをJUnitで起動することで実行できます。
Validator
Commons Validatorは、StrutsでWebアプリケーションのフォームの妥当性検査に使用するコンポーネントです。このコンポーネントは任意のJava Beanの妥当性を検査するように作られていますが、ドメインレイヤオブジェクトや、Swingアプリケーションへのユーザー入力、あるいはWebサービスに送られるXML形式のデータなどの妥当性検査にも使用できます。
私が見てきた大部分のプロジェクトでは、妥当性検査が次の3つの方法のいずれかで書かれています。
- ドメインオブジェクト内で
- ユーティリティクラス内で
- カスタムフレームワークとして
最初の2つのアプローチを採用した場合は、結果的にデザインが好ましくないものになります。なぜなら、開発者がそれぞれ独自に妥当性検査メソッドを書く傾向にあるからです。たとえ単にエラーを記録したり、例外をスローしたり、それをユーザーに表示したりするだけの妥当性検査コードであっても、それがアプリケーションのあちこちに点在するのは望ましくありません。もう1つのデメリットは、せっかくエラーメッセージをリソースバンドルから読み取って表示する仕組みを実現しても、そのためのコードがあちこちに重複して存在することになる、という点です。たとえ妥当性検査のためのコードであっても、コードの重複は望ましくありません。
今回は、試しにCommons Validatorを使ってJava Swingアプリケーションへのユーザー入力の妥当性検査を行います。図1は、ユーザーが登録情報を入力するGUIのスナップショットです。
このアプリケーションでCommons Validatorをどのように利用しているかを詳しく説明する前に、このデザインで重要な役割を果たすクラスを紹介しておきます(ソースコードを見ると理解しやすいでしょう)。以下のクラスはパッケージ「in.co.narayanan.commons.validator」の一部です。
RegistrationScreen | 他のGUIコンポーネントを含むメインフレーム。Singletonパターンのクラスです。 |
Registration | ユーザー登録情報を表現するドメインオブジェクト。 |
Listeners | GUIイベントを処理して、それらを適切なコマンドに委譲するためのアダプタクラス。 |
SubmitCommand | Commandパターンを使って送信操作を表現します(妥当性検査はこのクラスから開始されます)。 |
RegistrationContext | Mediatorパターンを使ってイベントリスナおよびコマンドクラスに必要なコンテキスト情報を渡します。 |
ValidatorEngine | Commons Validatorを使ってRegistration Beanの妥当性を検査し、結果を処理します。 |
このサンプルの目的は、登録時にユーザーが入力したログイン名(proposedLoginName
)とメールアドレス(mailAddress
)の妥当性を検査することです。SubmitCommand
の中で、ユーザーが入力した値がRegistration
ドメインオブジェクトに格納されます。
Commons Validatorの使用手順
Commons Validatorコンポーネントを作成するには、通常は次の手順を行います。
1. POJO妥当性検査クラスを書くか再利用する
Commons Validatorを使用するための最初のステップは、一群のデータに対して妥当性検査を行うクラスを書くことです。このサンプルでは、proposedLoginName
とmailAddress
の妥当性検査を行うためにGenericBeanValidator
というクラスを書きました。GenericBeanValidator
クラスの構造は次のようになっています。
public class GenericBeanValidator { public static boolean validateRequired(Object bean, Field field) { String value = ValidatorUtils.getValueAsString(bean, field.getProperty()); return !GenericValidator.isBlankOrNull(value); } public static boolean validateEmail(Object bean, Field field) { String value = ValidatorUtils.getValueAsString(bean, field.getProperty()); return GenericValidator.isEmail(value); } }
この妥当性検査クラスはどんなAPIにも依存しない、まったくのPOJOクラスなので、単体テストが簡単にできます。この目標を達成するために、今回はValidatorUtils
クラスとGenericValidator
クラスを使用しました。これらのクラスはCommons Validatorの一部です。getValueAsString
メソッドはRegistration
Beanからプロパティの値を取り出し、文字列を返します。GenericBeanValidator
クラスはGenericValidator
のラッパーのように機能してBeanの妥当性を検査します。GenericValidator
クラスには他にも種々の妥当性検査を行うためのユーティリティメソッドが数多く含まれています(Commons Validatorを最大限に利用するには、javadocを参照してください)。
2. マッピングXMLファイルを定義する
次のステップでは、妥当性検査クラス(前のステップで書いたものと同じようなクラス)を宣言して、それらを妥当性検査すべきBeanに結び付けるためのXMLファイルを定義します。ValidatorエンジンはこのXMLファイルを使用します(後ほど詳しく説明します)。このサンプルの「registration.xml」ファイルは次のようになります。
<form-validation> <global> <validator name="required" classname= "in.co.narayanan.commons.validator.GenericBeanValidator" method="validateRequired" methodParams= "java.lang.Object, org.apache.commons.validator.Field" msg="required.field"/> <validator name="email" classname= "in.co.narayanan.commons.validator.GenericBeanValidator" method="validateEmail" methodParams= "java.lang.Object, org.apache.commons.validator.Field" msg="invalid.email"/> </global> <formset> <form name="userRegistration"> <field property="proposedLoginName" depends="required"> <arg0 key= "userRegistration.proposedLoginName.displayname"/> </field> <field property="mailAddress" depends="email"> <arg0 key="userRegistration.mailAddress.displayname"/> </field> </form> </formset> </form-validation>
妥当性検査クラスは<validation>
タグ内で宣言する必要があります。ここで重要なプロパティは、論理名(name
)、クラス名(classname
)、メソッド名(method
)、メソッド引数(methodParams
)、およびメッセージキー(msg
)で、これは妥当性検査が失敗した場合にエラーメッセージを取得するのに使われます。このサンプルでは、proposedLoginName
とmailAddress
の妥当性検査を行うために2つのエントリが定義されています。
<form>
タグは<validator>
定義をJava Beanプロパティにマップするために使われています。このサンプルの<arg0>
タグはJava Beanプロパティの外部化されたフィールド名を取得するために使われています。これはロケール固有の表示用フィールド名をユーザーに表示するときに便利です。表示用フィールド名とエラーメッセージは外部化され、ファイル「applicationResource.properties」の中で利用できるようになっています。<field>
定義内では任意の数の<argX>
タグを使用できます。
3. Validatorを初期化して実行する
Commons Validatorエンジンを実行すると、プロパティに関する妥当性検査がXMLファイルマッピングで定義されているかどうかがチェックされます。定義されている場合は、該当する妥当性検査クラスメソッドが呼び出され、必要な引数が渡されます。すべてのプロパティについてこのプロセスが繰り返され、結果がエラーメッセージとともに蓄積されます(結果の処理方法については後で説明します)。
次のコードは、SubmitCommand
が使用するValidatorEngine
クラスから抜粋したものです。
InputStream definitionStream = reg.getClass().getResourceAsStream(definitionXml); .... resources = new ValidatorResources(definitionStream); .... Validator validator = new Validator(resources, Registration.NAME); validator.setParameter(Validator.BEAN_PARAM, reg); .... results = validator.validate(); .... return parseResults(formName, reg, results, resources);
ValidationResources
クラスはXMLファイル内の定義を表しています。このクラスを初期化するにはdefinitionXml
ストリームを渡します。その後、このインスタンスを妥当性検査の対象となるBeanの名前と一緒にValidatorに渡します。妥当性検査クラスで必要な追加パラメータがある場合は、setParameter
メソッドを使ってValidatorに渡すことができます。GenericBeanValidator
クラスのvalidateXXXX
メソッドには、java.lang.Object
とorg.apache.commons.validator.Field
という2つの引数があったことを思い出してください。Validatorは、実行時にこの最初のパラメータにBeanのインスタンスを渡し、2番目のパラメータに適切なフィールド参照を渡します。
ところで、Validatorは他のオブジェクト(たとえばHttpServletRequest
オブジェクト)の参照をどうやって妥当性検査メソッドに渡すのでしょうか。実は完全修飾クラス名とHttpServletRequest
クラスのインスタンスへの参照を指定して、ValidatorのsetParameter
メソッドを呼び出すのです。Validator内の妥当性検査メソッドを呼び出せば妥当性検査を開始することができます。
4. 結果を処理する
Validatorは実行時にValidatorResults
のインスタンスを返します。このインスタンスには、実行される妥当性検査についての情報がすべて格納されます。結果の視覚的な構造は次のようになります。
[ [property1] [action1] [action2] [property2] [action1] ]
すべてのBeanプロパティの結果はValidatorAction
インスタンスの形で結び付けられます。これらのプロパティを反復処理し、ValidatorResult
クラスのisValid
メソッドを呼び出すことにより(パラメータにはアクション名を指定)、妥当性検査が成功したか失敗したかを判断します。具体的な解析ロジックについては、ValidatorEngine
クラスのparseResults
メソッドのコードを見てください。
その他の用途
このサンプルでは、Commons ValidatorをJava Beanアプリケーションに対するユーザー入力の妥当性検査に利用してみましたが、Commons Validatorは他にもさまざまなアプリケーションで使用でき、モジュール型設計、高い生産性、容易なメンテナンスといった利点が得られます。たとえば次のような使い方が考えられます。
- XML形式でWebサービスレイヤに送られる注文情報に妥当性検査を適用する
- ある時点におけるアプリケーション内のクラスの状態に妥当性検査を適用する(クラスの状態が要求を満たすものでなければ適切な例外をスローするなど)
- JMXと併用してアプリケーションを監視する
- 完全にJavaで実装されたルールエンジンの中で使用する(妥当性検査クラスを複雑なビジネスアプリケーション内のドメインオブジェクトに作用する個別ルールとして使用する)
このフレームワークには、Webアプリケーションにおける電子メールやURLなどの情報をクライアント側で妥当性検査するためのJavaScript関数もいくつか含まれています。