CodeZine(コードジン)

特集ページ一覧

Jakarta Commonsを使ってJDKクラスを拡張する:パート1

Commons Validator/Collections/Chain/Functors/Langの利用

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加
2006/01/27 12:40

Jakarta Commonsセットに含まれているコンポーネントをよく見てみると、JDKの一部にした方がよいのではと思うものが数多くあります。本稿では、その中から皆さんのJavaプロジェクトで役立つコンポーネントをいくつかご紹介します。

目次

はじめに

 今ではJavaコミュニティのほとんどだれもがJakarta Commonsのことを知っています。Jakarta CommonsはJakartaのさまざまなプロジェクトで使われている再利用可能なクラスの集まりです。しかし、これらのクラスを独立したコンポーネントとして自分のJavaプロジェクトの中でも利用できることは案外知られていないのではないでしょうか。本稿はJakarta Commonsのさまざまなコンポーネントを紹介する3回シリーズの第1弾です。このシリーズでは実際のサンプルアプリケーションを通じてJakarta Commonsコンポーネントの使い方を説明していきます。これらのサンプルはJakarta Commonsコンポーネントを例示するだけのものではなく、典型的なJavaプロジェクトで再利用できる有用な機能を盛り込んだ完全なアプリケーションです。

 本稿では次のコンポーネントを取り上げます。

  • Validator
  • Collections
  • Chain
  • Functors
  • Lang

 本稿には完全なソースコードが付属しており、各サンプルのテストケースをJUnitで起動することで実行できます。

著者注
 Commonsコンポーネントのアーキテクチャと用法を理解するためには、オブジェクト指向プログラミング(OOP)とGang of Fourのデザインパターン(Command、Decorator、Singleton、Factory、Chain of Responsibility、Composite)についての基本的知識が非常に役立ちます。

Validator

 Commons Validatorは、StrutsでWebアプリケーションのフォームの妥当性検査に使用するコンポーネントです。このコンポーネントは任意のJava Beanの妥当性を検査するように作られていますが、ドメインレイヤオブジェクトや、Swingアプリケーションへのユーザー入力、あるいはWebサービスに送られるXML形式のデータなどの妥当性検査にも使用できます。

 私が見てきた大部分のプロジェクトでは、妥当性検査が次の3つの方法のいずれかで書かれています。

  1. ドメインオブジェクト内で
  2. ユーティリティクラス内で
  3. カスタムフレームワークとして

 最初の2つのアプローチを採用した場合は、結果的にデザインが好ましくないものになります。なぜなら、開発者がそれぞれ独自に妥当性検査メソッドを書く傾向にあるからです。たとえ単にエラーを記録したり、例外をスローしたり、それをユーザーに表示したりするだけの妥当性検査コードであっても、それがアプリケーションのあちこちに点在するのは望ましくありません。もう1つのデメリットは、せっかくエラーメッセージをリソースバンドルから読み取って表示する仕組みを実現しても、そのためのコードがあちこちに重複して存在することになる、という点です。たとえ妥当性検査のためのコードであっても、コードの重複は望ましくありません。

 今回は、試しにCommons Validatorを使ってJava Swingアプリケーションへのユーザー入力の妥当性検査を行います。図1は、ユーザーが登録情報を入力するGUIのスナップショットです。

図1 Swingアプリケーションのスナップショット
図1 Swingアプリケーションのスナップショット

 このアプリケーションでCommons Validatorをどのように利用しているかを詳しく説明する前に、このデザインで重要な役割を果たすクラスを紹介しておきます(ソースコードを見ると理解しやすいでしょう)。以下のクラスはパッケージ「in.co.narayanan.commons.validator」の一部です。

RegistrationScreen他のGUIコンポーネントを含むメインフレーム。Singletonパターンのクラスです。
Registrationユーザー登録情報を表現するドメインオブジェクト。
ListenersGUIイベントを処理して、それらを適切なコマンドに委譲するためのアダプタクラス。
SubmitCommandCommandパターンを使って送信操作を表現します(妥当性検査はこのクラスから開始されます)。
RegistrationContextMediatorパターンを使ってイベントリスナおよびコマンドクラスに必要なコンテキスト情報を渡します。
ValidatorEngineCommons Validatorを使ってRegistration Beanの妥当性を検査し、結果を処理します。

 このサンプルの目的は、登録時にユーザーが入力したログイン名(proposedLoginName)とメールアドレス(mailAddress)の妥当性を検査することです。SubmitCommandの中で、ユーザーが入力した値がRegistrationドメインオブジェクトに格納されます。

Commons Validatorの使用手順

 Commons Validatorコンポーネントを作成するには、通常は次の手順を行います。

1. POJO妥当性検査クラスを書くか再利用する

 Commons Validatorを使用するための最初のステップは、一群のデータに対して妥当性検査を行うクラスを書くことです。このサンプルでは、proposedLoginNamemailAddressの妥当性検査を行うために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)で、これは妥当性検査が失敗した場合にエラーメッセージを取得するのに使われます。このサンプルでは、proposedLoginNamemailAddressの妥当性検査を行うために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.Objectorg.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関数もいくつか含まれています。


  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加

あなたにオススメ

著者プロフィール

  • Narayanan A.R.(Narayanan A.R.)

    テスト駆動開発、アジャイル方法論、Javaテクノロジ、およびデザインパターンの熱烈な擁護者。Javaテクノロジを使ったソフトウェアのデザインおよび開発に携わって数年になる。

  • japan.internet.com(ジャパンインターネットコム)

    japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.com や EarthWeb.c...

バックナンバー

連載:japan.internet.com翻訳記事

もっと読む

All contents copyright © 2005-2021 Shoeisha Co., Ltd. All rights reserved. ver.1.5