JaValidとは

今回はJaValidを紹介したい。JaValidはJavaオブジェクトの状態を検証するためのオープンソースのバリデーションフレームワークである。JaValidを用いることで、Javaオブジェクトが取り得る状態の条件を設定して検証することができるようになる。

条件の設定にはアノテーションを利用する。たとえば@NotNullを指定されたオブジェクトに対してバリデーションを実行した場合、もしそのオブジェクトがNULLであったらエラーメッセージを出力する。条件をグループ化し、グループごとにバリデーションを実行できるような機能も提供される。

JaValidは単体で用いることもできるが、JSFやFacelets、Spring Framework、そして各種データベースとのインテグレーションもサポートしている。したがってこれらのフレームワークを利用している場合においても、自然な形でバリデーション機構を追加することができるようになっている。

なお、JaValidと同様にJavaオブジェクトのバリデーションを行うためのツールにはHibernate ValidatorやSpring Validationなどがある。またJSR 303ではJava SEやJava EEへの将来的な統合を目指してバリデーション用APIの標準化が進められている。JaValidの開発者であるMartijn Reuvers氏によると、JaValidでも将来のバージョンにおいてJSR 303をサポートする予定だいう。また他のツールとの比較については、すべてのツールにそれぞれ強味/弱味があるので、実際に試した上でそれぞれのニーズに合ったものを選択するのがいいと指摘している。

JaValidによるバリデーションの手順

JaValidはEclipse Public License v1.0に基づいて公開されており、このページよりバイナリ版とソースコード版のそれぞれをダウンロードできる。今回は最新版であるバージョン1.2のバイナリを入手して使用する。

JaValdの本体はdistディレクトリにあるjavalid-core-1.2.jarである。JaValidを使うためにはそれ以外にApache Log4J(http://logging.apache.org/log4j/)とJuel(http://juel.sourceforge.net/)が必要だが、それそれのバイナリもlibディレクトリに格納されているのでこれを利用すればよい。なお、dist以下にあるその他の3つのjarファイルは、それぞれJSF、Spring、データベースとのインテグレーションの際に必要となるものだ。

JaValidでバリデーション条件を設定する基本的な手順は、まず@ValidateDeinitionアノテーションを用いてクラス全体に対するバリデーションの設定を行い、そのクラスの各フィールドやメソッドに対してそれぞれアノテーションで条件を指定する。メソッドに対してアノテーションが指定された場合は、そのメソッドの戻り値がチェックの対象となる。バリデーション条件を指定するアノテーションには次のようなものがある。

**アノテーション** **チェックされる内容** **対象となるオブジェクト**
@NotNull オブジェクトがNullでないか
@NotEmpty オブジェクトが空でないか 文字列、配列、Collection、Map
@MinLength 文字列の長さが指定された値以上であるか 文字列
@MaxLength 文字列の長さが指定された値以下であるか 文字列
@ExactLength 文字列の長さが指定された値と等しいか 文字列
@BetweenLength 文字列の長さが指定された値の範囲に収まるか 文字列
@MinValue 数値が指定された値以上であるか 数値オブジェクト
@MaxValue 数値が指定された値以下であるか 数値オブジェクト
@LovConsistraint 配列またはコレクションが指定された値を含むか 配列、Collection
@RegularExpression 指定された正規表現にマッチするか 文字列
@ValidateList Listの要素であるクラスにバリデーション条件が指定されている場合、すべての要素がその条件を満たしているか List
@ValidateMap Mapの要素であるクラスにバリデーション条件が指定されている場合、すべての要素がその条件を満たしているか Map
@CollectionSize コレクションやMapのサイズ(要素数)が指定された条件を満たすか Collection、Map
@DateBefore 日付が指定日よりも前か Date
@DateAfter 日付が指定日よりも後か Date
@DateEqual 日付が指定日であるかどうか Date
@DateNotEqual 日付が指定日でないかどうか Date

各アノテーションにはそれぞれ指定すべきプロパティが用意されており、それを用いて条件を設定できるようになっている。たとえば@MinLengthの場合はlengthプロパティに長さを設定すればよい。また、各アノテーションごとに対象となるオブジェクトの種類が決められているので注意が必要。たとえば@xxxLength系のアノテーションはStringやStringBuffer、StringBuilderなどの文字列オブジェクトにしか指定することができない。

また上記とは別に「クラスレベルアノテーション」と呼ばれるアノテーションがある。これはクラスに対して指定することで、そのクラスの複数のフィールドやメソッドに対して同じ条件を設定することができる。クラスレベルアノテーションには次のようなものがある。対象となるフィールドおよびメソッドはvaluesプロパティで指定する。

**アノテーション** **チェックされる内容**
@NotNullAll 指定されたすべての値がNullでないか
@NotEmptyAll 指定されたすべての値が空でないか
@NullOrNotNullAll 指定され値がすべてNullか、またはすべてNullでないか
@EmptyOrNotEmpty 指定され値がすべて空か、またはすべて空でないか

クラスのオブジェクトに対して実際にバリデーションを実行するには、まずAnnotationValidatorのインスタンスを生成し、validateObject()メソッドにそのオブジェクトを渡せばよい。このメソッドはバリデーションを実行した上でエラーメッセージを格納したValidationMessageのリストを返すので、これを調べればどの条件が満たされなかったのかを知ることができる。

バリデーションの実行例

それでは、実際に条件を設定したクラスを作成してバリデーションを実行してみよう。次のコードのUserクラスには、username、password、emailの3つのプロパティが定義されており、そのゲッターメソッドにそれぞれアノテーションを設定してある。このようにゲッターメソッドにアノテーションを指定することにより、各プロパティの値を検査することができるようになる。

リスト1の場合、usernameは空文字列(およびNull)以外、passwordはNull以外でかつ8文字以上、そしてemailはメールアドレスの形式でなければならない。

リスト1

package jp.co.mycom.toolde;

import org.javalid.annotations.core.ValidateDefinition;
import org.javalid.annotations.validation.MinLength;
import org.javalid.annotations.validation.NotEmpty;
import org.javalid.annotations.validation.NotNull;
import org.javalid.annotations.validation.RegularExpression;

@ValidateDefinition
public class User {
    private String username;
    private String password;
    private String email;

    public User(String username, String password, String email) {
        this.username = username;
        this.password = password;
        this.email = email;
    }

    @NotEmpty
    public String getUsername() {
        return username;
    }

    @NotNull
    @MinLength(length=8)
    public String getPassword() {
        return password;
    }

    @RegularExpression(pattern="[\\w\\.\\-]+@(?:[\\w\\-]+\\.)+[\\w\\-]+")
    public String getEmail() {
        return this.email;
    }
}

バリデーションを実施するコードはリスト2のようになる。

リスト2

package jp.co.mycom.toolde;

import java.util.Arrays;
import java.util.List;
import org.javalid.core.AnnotationValidator;
import org.javalid.core.AnnotationValidatorImpl;
import org.javalid.core.ValidationMessage;

public class UserTest {
    public UserTest() {
        AnnotationValidator validator = null;
        List<ValidationMessage> messages = null;
        User user = null;

        // バリデーターの生成
        validator = new AnnotationValidatorImpl();

        // [1] エラー無し
        user = new User("takaaki", "hogehoge", "takaaki@example.jp");
        messages = validator.validateObject(user);
        printErrorMessages(messages);

        // [2] usernameとpasswordが不正
        user = new User("", null, "takaaki@example.jp");
        messages = validator.validateObject(user);
        printErrorMessages(messages);

        // [3] passwordが不正
        user = new User("takaaki", "", "takaaki@example.jp");
        messages = validator.validateObject(user);
        printErrorMessages(messages);

        // [4] emailが不正
        user = new User("takaaki", "hogehoge", "takaaki.jp");
        messages = validator.validateObject(user);
        printErrorMessages(messages);
    }

    /*
     * エラーメッセージの出力
     */
    private void printErrorMessages(List<ValidationMessage> messages) {
        if (messages.size() <= 0) {
            System.out.println("No validation error.");
        }
        else {
            for (ValidationMessage message: messages) {
                System.out.println("Vallidation error from: " + message.getPath());
                System.out.println("    Message: " + message.getMessage());
                System.out.println("    Values:  " + Arrays.toString(message.getValues()));
            }
        }
        System.out.println("");
    }

    public static void main(String[] args) {
        UserTest doit = new UserTest();
    }
}

この例では、[1]は条件を満たしているが、[2]はusernameが空文字列かつpasswordがnullであり、[3]はpasswordが8文字未満、[4]はメールアドレス形式になっておらず、それぞれ条件を満たしていない。これを実行するとプロンプト1のように出力され、どの条件を満たしていないのかがわかる。

プロンプト1

No validation error.

Vallidation error from: username
    Message: jv_error_valueIsEmpty
    Values:  []
Vallidation error from: password
    Message: jv_error_valueIsNull
    Values:  null

Vallidation error from: password
    Message: jv_error_valueIsNotMinLength
    Values:  [, 8]

Vallidation error from: email
    Message: jv_error_valueNotMatchesRegPattern
    Values:  [takaaki.jp, [\w\.\-]+@(?:[\w\-]+\.)+[\w\-]+]

このようにJaValidの使い方そのものは非常にシンプルだ。基本的な利用においては特別な設定ファイルなどを必要とせず、手軽に適用できることが大きな利点と言える。